Support link creation for hovered file

This commit is contained in:
Arun Prakash Jana 2019-11-29 20:58:12 +05:30
parent 965499c671
commit f9058e1aaa
No known key found for this signature in database
GPG key ID: A75979F35C080412
3 changed files with 55 additions and 38 deletions

BIN
LICENSE

Binary file not shown.

12
nnn.1
View file

@ -154,23 +154,19 @@ auto selects the directory and enters it in this mode.
.Sh SELECTION .Sh SELECTION
There are 3 groups of shortcuts to add files to selection: There are 3 groups of shortcuts to add files to selection:
.Pp .Pp
(1) add an individual file to selection (1) hovered file selection toggle (indicated by '+' symbol on/off)
.br .br
(2) add a range of files to selection (2) add a range of files to selection
.br .br
(3) add all files in the current directory to selection (3) add all files in the current directory to selection
.Pp .Pp
Selected files are visually indicated by a \fI+\fR before the entries.
.br
The selection can now be listed, copied, moved, removed, archived or linked. The selection can now be listed, copied, moved, removed, archived or linked.
.br .Pp
Absolute paths of the selected files are copied to the temporary file \fB.selection\fR in the config directory. The path is shown in the help and configuration screen. If \fB$NNN_COPIER\fR is set (see ENVIRONMENT section below) the file paths are also copied to the system clipboard. Absolute paths of the selected files are copied to the temporary file \fB.selection\fR in the config directory. The path is shown in the help and configuration screen. If \fB$NNN_COPIER\fR is set (see ENVIRONMENT section below) the file paths are also copied to the system clipboard.
.Pp .Pp
Repeat range selection on the same entry to clear selection. It's also possible to edit the current selection. To flush the selection without running any operation use the _edit, flush selection_ key. The list is flushed even if unchanged. Use this key to remove a file from selection after you navigate away from its directory.
.Pp .Pp
Deselecting a single file is not implemented because substantial string processing would be required if thousands of files are selected. It would have been trivial if the selection was limited to a single directory (use a flag for each file and select the files with the flag set). However, Repeat range selection on the same entry twice to clear selection completely.
.Nm
allows selection across directories making it non-trivial to do that. Also, the buffer used to select is a compact one (no byte wasted) so \fBmemmove\fR() would be required to deselect each intermediate file. Considering these, the alternatives were chosen.
.Sh FILE SIZE .Sh FILE SIZE
The minimum file size unit is byte (B). The rest are K, M, G, T, P, E, Z, Y (powers of 1024), same as the default units in \fIls\fR. The minimum file size unit is byte (B). The rest are K, M, G, T, P, E, Z, Y (powers of 1024), same as the default units in \fIls\fR.
.Sh ENVIRONMENT .Sh ENVIRONMENT

View file

@ -411,10 +411,10 @@ static char * const utils[] = {
#define MSG_FAILED 6 #define MSG_FAILED 6
#define MSG_SSN_NAME 7 #define MSG_SSN_NAME 7
#define MSG_CP_MV_AS 8 #define MSG_CP_MV_AS 8
#define MSG_RENAME_SEL 9 #define MSG_RENAME_OPTS 9
#define MSG_FORCE_RM 10 #define MSG_FORCE_RM 10
#define MSG_CREATE_CTX 11 #define MSG_CREATE_CTX 11
#define MSG_ARCHIVE_SEL 12 #define MSG_CUR_SEL_OPTS 12
#define MSG_NEW_OPTS 13 #define MSG_NEW_OPTS 13
#define MSG_CLI_MODE 14 #define MSG_CLI_MODE 14
#define MSG_OVERWRITE 15 #define MSG_OVERWRITE 15
@ -454,10 +454,10 @@ static const char * const messages[] = {
"failed!", "failed!",
"session name: ", "session name: ",
"'c'p / 'm'v as?", "'c'p / 'm'v as?",
"rename sel?", "'c'urrent dir / 's'election?",
"forcibly remove %s file%s (unrecoverable)?", "forcibly remove %s file%s (unrecoverable)?",
"Create context %d?", "create context %d?",
"archive sel?", "'c'urrent / 's'election?",
"'f'ile / 'd'ir / 's'ym / 'h'ard?", "'f'ile / 'd'ir / 's'ym / 'h'ard?",
"cli mode?", "cli mode?",
"overwrite?", "overwrite?",
@ -1516,6 +1516,20 @@ static bool batch_rename(const char *path)
char foriginal[TMP_LEN_MAX] = {0}; char foriginal[TMP_LEN_MAX] = {0};
char buf[sizeof(batchrenamecmd) + (PATH_MAX << 1)]; char buf[sizeof(batchrenamecmd) + (PATH_MAX << 1)];
if (selbufpos) {
if (ndents) {
i = get_input(messages[MSG_RENAME_OPTS]);
if (i == 'c') {
selbufpos = 0; /* Clear the selection */
dir = TRUE;
} else if (i != 's')
return ret;
}
} else if (ndents)
dir = TRUE; /* Rename entries in dir */
else
return ret;
fd1 = create_tmp_file(); fd1 = create_tmp_file();
if (fd1 == -1) if (fd1 == -1)
return ret; return ret;
@ -1529,18 +1543,6 @@ static bool batch_rename(const char *path)
return ret; return ret;
} }
if (selbufpos) {
i = get_input(messages[MSG_RENAME_SEL]);
if (i != 'y' && i != 'Y') {
if (!ndents)
return TRUE;
selbufpos = 0; /* Clear the selection */
dir = TRUE;
}
} else
dir = TRUE; /* Rename entries in dir */
if (dir) if (dir)
for (i = 0; i < ndents; ++i) for (i = 0; i < ndents; ++i)
appendfpath(dents[i].name, NAME_MAX); appendfpath(dents[i].name, NAME_MAX);
@ -2344,24 +2346,43 @@ static size_t mkpath(const char *dir, const char *name, char *out)
* Create symbolic/hard link(s) to file(s) in selection list * Create symbolic/hard link(s) to file(s) in selection list
* Returns the number of links created * Returns the number of links created
*/ */
static int xlink(char *suffix, char *path, char *buf, int *presel, int type) static int xlink(char *suffix, char *path, char *curfname, char *buf, int *presel, int type)
{ {
int count = 0; int count = 0, choice = 's';
char *pbuf = pselbuf, *fname; char *pbuf = pselbuf, *fname;
size_t pos = 0, len, r; size_t pos = 0, len, r;
int (*link_fn)(const char *, const char *) = NULL; int (*link_fn)(const char *, const char *) = NULL;
/* Check if selection is empty */ if (selbufpos) {
if (!selbufpos) { if (ndents) {
printwait(messages[MSG_0_SELECTED], presel); choice = get_input(messages[MSG_CUR_SEL_OPTS]);
if (choice != 'c' && choice != 's')
return -1; return -1;
} }
} else if (ndents)
choice = 'c';
else
return -1;
if (type == 's') /* symbolic link */ if (type == 's') /* symbolic link */
link_fn = &symlink; link_fn = &symlink;
else /* hard link */ else /* hard link */
link_fn = &link; link_fn = &link;
if (choice == 'c') {
char lnpath[PATH_MAX];
mkpath(path, curfname, buf);
r = mkpath(path, curfname, lnpath);
xstrlcpy(lnpath + r - 1, suffix, PATH_MAX - r - 1);
if (!link_fn(buf, lnpath))
return 1;
printwarn(presel);
return 0; /* One link created */
}
while (pos < selbufpos) { while (pos < selbufpos) {
len = strlen(pbuf); len = strlen(pbuf);
fname = xbasename(pbuf); fname = xbasename(pbuf);
@ -4907,20 +4928,19 @@ nochange:
switch (sel) { switch (sel) {
case SEL_ARCHIVE: case SEL_ARCHIVE:
r = get_input(messages[MSG_ARCHIVE_SEL]);
if (r == 'y' || r == 'Y') {
endselection(path, newpath); endselection(path, newpath);
r = get_input(messages[MSG_CUR_SEL_OPTS]);
if (r == 's') {
if (!selsafe()) { if (!selsafe()) {
presel = MSGWAIT; presel = MSGWAIT;
goto nochange; goto nochange;
} }
tmp = NULL; tmp = NULL;
} else if (!ndents) { } else if (!ndents)
printwait(messages[MSG_0_FILES], &presel);
goto nochange; goto nochange;
} else else
tmp = dents[cur].name; tmp = dents[cur].name;
tmp = xreadline(tmp, messages[MSG_ARCHIVE_NAME]); tmp = xreadline(tmp, messages[MSG_ARCHIVE_NAME]);
break; break;
@ -5058,7 +5078,8 @@ nochange:
if (tmp[0] == '@' && tmp[1] == '\0') if (tmp[0] == '@' && tmp[1] == '\0')
tmp[0] = '\0'; tmp[0] = '\0';
r = xlink(tmp, path, newpath, &presel, r); r = xlink(tmp, path, (ndents ? dents[cur].name : NULL),
newpath, &presel, r);
close(fd); close(fd);
if (r <= 0) if (r <= 0)