@@ -67,15 +67,15 @@ The following command installs all plugins: | |||||
Plugins are installed to `${XDG_CONFIG_HOME:-$HOME/.config}/nnn/plugins`. You can run the `getplugs` plugin later to update the plugins. It backs up earlier plugins. | Plugins are installed to `${XDG_CONFIG_HOME:-$HOME/.config}/nnn/plugins`. You can run the `getplugs` plugin later to update the plugins. It backs up earlier plugins. | ||||
## Ways to execute a plugin | ## Ways to invoke a plugin | ||||
1. Directly with <kbd>;key</kbd> or <kbd>xkey</kbd>: | 1. Execute directly with <kbd>;key</kbd> or <kbd>xkey</kbd>: | ||||
export NNN_PLUG='o:fzopen;p:mocplay;d:diffs;m:nmount;n:notes;v:imgviu;t:imgthumb' | export NNN_PLUG='o:fzopen;p:mocplay;d:diffs;m:nmount;n:notes;v:imgviu;t:imgthumb' | ||||
Now plugin `fzopen` can be run with the keybind <kbd>;o</kbd>, `mocplay` can be run with <kbd>;p</kbd> and so on... The key vs. plugin pairs are shown in the help and config screen. | Now plugin `fzopen` can be run with the keybind <kbd>;o</kbd>, `mocplay` can be run with <kbd>;p</kbd> and so on... The key vs. plugin pairs are shown in the help and config screen. | ||||
2. Use the _pick plugin_ keybind to visit the plugin directory and execute a plugin. Repeat the keybind to cancel and return to the original directory. | 2. Use the _pick plugin_ keybind to visit the plugin directory, select and run a plugin. Repeat the keybind to cancel and return to the original directory. | ||||
#### Skip directory refresh after running a plugin | #### Skip directory refresh after running a plugin | ||||
@@ -41,7 +41,7 @@ static int xprintf(int fd, const char *fmt, ...) | |||||
va_start(ap, fmt); | va_start(ap, fmt); | ||||
r = vsnprintf(buf, sizeof(buf), fmt, ap); | r = vsnprintf(buf, sizeof(buf), fmt, ap); | ||||
if (r > 0) | if (r > 0 && (unsigned int)r < sizeof(buf)) | ||||
r = write(fd, buf, r); | r = write(fd, buf, r); | ||||
va_end(ap); | va_end(ap); | ||||
return r; | return r; | ||||
@@ -2090,6 +2090,7 @@ static int filterentries(char *path, char *lastname) | |||||
case '=': // fallthrough /* Launch app */ | case '=': // fallthrough /* Launch app */ | ||||
case ']': // fallthorugh /*Prompt key */ | case ']': // fallthorugh /*Prompt key */ | ||||
case ';': // fallthrough /* Run plugin key */ | case ';': // fallthrough /* Run plugin key */ | ||||
case ',': // falltrough /* Pin CWD */ | |||||
case '?': /* Help and config key, '?' is an invalid regex */ | case '?': /* Help and config key, '?' is an invalid regex */ | ||||
if (len == 1) | if (len == 1) | ||||
goto end; | goto end; | ||||
@@ -3433,14 +3434,13 @@ static void printkv(kv *kvarr, FILE *fp, uchar max) | |||||
static void printkeys(kv *kvarr, char *buf, uchar max) | static void printkeys(kv *kvarr, char *buf, uchar max) | ||||
{ | { | ||||
uchar i = 0; | uchar i = 0; | ||||
uchar j = 0; | |||||
for (; i < max && kvarr[i].key; ++i, j+=2) { | for (; i < max && kvarr[i].key; ++i) { | ||||
buf[j] = ' '; | buf[i << 1] = ' '; | ||||
buf[j+1] = kvarr[i].key; | buf[(i << 1) + 1] = kvarr[i].key; | ||||
} | } | ||||
buf[j] = '\0'; | buf[i << 1] = '\0'; | ||||
} | } | ||||
/* | /* | ||||
@@ -3464,10 +3464,10 @@ static void show_help(const char *path) | |||||
"9Lt h Parent%-12c~ ` @ - HOME, /, start, last\n" | "9Lt h Parent%-12c~ ` @ - HOME, /, start, last\n" | ||||
"5Ret Rt l Open%-20c' First file\n" | "5Ret Rt l Open%-20c' First file\n" | ||||
"9g ^A Top%-21c. Toggle hidden\n" | "9g ^A Top%-21c. Toggle hidden\n" | ||||
"9G ^E End%-21c+ Pin CWD\n" | "9G ^E End%-21cL Lock terminal\n" | ||||
"9b ^B Bookmark key%-12c, Visit pinned\n" | "9b ^/ Bookmark key%-12c, Pin CWD\n" | ||||
"cN Context N%-9c(Sh)Tab Cycle context\n" | "cN Context N%-9c(Sh)Tab Cycle context\n" | ||||
"c/ Filter%-13cIns ^/ Filter mode toggle\n" | "c/ Filter%-17c^N Nav-as-you-type toggle\n" | ||||
"aEsc Exit prompt%-9cF5 ^L Redraw/clear prompt\n" | "aEsc Exit prompt%-9cF5 ^L Redraw/clear prompt\n" | ||||
"c? Help, conf%-13c^G QuitCD\n" | "c? Help, conf%-13c^G QuitCD\n" | ||||
"9Q ^Q Quit%-20cq Quit context\n" | "9Q ^Q Quit%-20cq Quit context\n" | ||||
@@ -3475,23 +3475,22 @@ static void show_help(const char *path) | |||||
"b^O Open with...%-12cn Create new/link\n" | "b^O Open with...%-12cn Create new/link\n" | ||||
"cD File details%-12cd Detail view toggle\n" | "cD File details%-12cd Detail view toggle\n" | ||||
"cr Batch rename%-8cF2 ^R Rename/duplicate\n" | "cr Batch rename%-8cF2 ^R Rename/duplicate\n" | ||||
"5Space ^J Sel toggle%-14ca Sel all\n" | "5Space ^J (Un)select%-11cm ^K Select range, clear\n" | ||||
"9m ^K Sel range, clear%-8cM List sel\n" | "ca Select all%-14cy List sel\n" | ||||
"cP Copy sel here%-11cK Edit sel\n" | "cP Copy sel here%-10c^Y Edit sel\n" | ||||
"cV Move sel here%-11cw Copy/move sel as\n" | "cV Move sel here%-10c^V Copy/move sel as\n" | ||||
"cX Delete sel%-13c^X Delete entry\n" | "cX Delete sel%-13c^X Delete entry\n" | ||||
"cf Archive%-14co ^F Archive ops\n" | "cf Archive%-16c^F Archive ops\n" | ||||
"ce Edit in EDITOR%-10cp Open in PAGER\n" | "ce Edit in EDITOR%-10cp Open in PAGER\n" | ||||
"1ORDER TOGGLES\n" | "1ORDER TOGGLES\n" | ||||
"cS Disk usage%-14cA Apparent du\n" | "cS Disk usage%-14cA Apparent du\n" | ||||
"cz Size%-20ct Time\n" | "cz Size%-20ct Time\n" | ||||
"cv version%-17cE Extension\n" | "cv version%-17cE Extension\n" | ||||
"1MISC\n" | "1MISC\n" | ||||
"9! ^] Shell%-17c; x Plugin key\n" | "9! ^] Shell%-17c; x Execute plugin\n" | ||||
"9] ^P Prompt%-15ci ^V Pick plugin\n" | "9] ^T Cmd prompt%-13c^P Pick plugin\n" | ||||
"cs Manage session%-10c= Launch app\n" | "cs Manage session%-10c= Launch app\n" | ||||
"cc Connect remote%-10cu Unmount\n" | "cc Connect remote%-10cu Unmount\n" | ||||
"cL Lock%-0c\n" | |||||
}; | }; | ||||
fd = create_tmp_file(); | fd = create_tmp_file(); | ||||
@@ -4588,8 +4587,7 @@ nochange: | |||||
case SEL_CDHOME: // fallthrough | case SEL_CDHOME: // fallthrough | ||||
case SEL_CDBEGIN: // fallthrough | case SEL_CDBEGIN: // fallthrough | ||||
case SEL_CDLAST: // fallthrough | case SEL_CDLAST: // fallthrough | ||||
case SEL_CDROOT: // fallthrough | case SEL_CDROOT: | ||||
case SEL_VISIT: | |||||
switch (sel) { | switch (sel) { | ||||
case SEL_CDHOME: | case SEL_CDHOME: | ||||
dir = home; | dir = home; | ||||
@@ -4600,12 +4598,9 @@ nochange: | |||||
case SEL_CDLAST: | case SEL_CDLAST: | ||||
dir = lastdir; | dir = lastdir; | ||||
break; | break; | ||||
case SEL_CDROOT: | default: /* SEL_CDROOT */ | ||||
dir = "/"; | dir = "/"; | ||||
break; | break; | ||||
default: /* SEL_VISIT */ | |||||
dir = mark; | |||||
break; | |||||
} | } | ||||
if (!dir || !*dir) { | if (!dir || !*dir) { | ||||
@@ -4634,18 +4629,28 @@ nochange: | |||||
setdirwatch(); | setdirwatch(); | ||||
goto begin; | goto begin; | ||||
case SEL_BOOKMARK: | case SEL_BOOKMARK: | ||||
xstrlcpy(g_buf, messages[MSG_BOOKMARK_KEYS], CMD_LEN_MAX); | r = xstrlcpy(g_buf, messages[MSG_BOOKMARK_KEYS], CMD_LEN_MAX); | ||||
printkeys(bookmark, g_buf + strlen(g_buf), BM_MAX); | if (mark) { /* There is a pinned directory */ | ||||
g_buf[--r] = ' '; | |||||
g_buf[++r] = ','; | |||||
g_buf[++r] = '\0'; | |||||
++r; | |||||
} | |||||
printkeys(bookmark, g_buf + r - 1, BM_MAX); | |||||
printprompt(g_buf); | printprompt(g_buf); | ||||
fd = get_input(NULL); | fd = get_input(NULL); | ||||
if (!get_kv_val(bookmark, newpath, fd, BM_MAX, TRUE)) { | r = FALSE; | ||||
printwait(messages[MSG_INVALID_KEY], &presel);; | if (fd == ',') /* Visit pinned directory */ | ||||
goto nochange; | mark ? xstrlcpy(newpath, mark, PATH_MAX) : (r = MSG_NOT_SET); | ||||
} | else if (!get_kv_val(bookmark, newpath, fd, BM_MAX, TRUE)) | ||||
r = MSG_INVALID_KEY; | |||||
if (!r && !xdiraccess(newpath)) | |||||
r = MSG_ACCESS; | |||||
if (!xdiraccess(newpath)) { | if (r) { | ||||
printwait(messages[MSG_ACCESS], &presel); | printwait(messages[r], &presel); | ||||
goto nochange; | goto nochange; | ||||
} | } | ||||
@@ -5218,8 +5223,8 @@ nochange: | |||||
} | } | ||||
if (sel == SEL_PLUGKEY) { | if (sel == SEL_PLUGKEY) { | ||||
xstrlcpy(g_buf, messages[MSG_PLUGIN_KEYS], CMD_LEN_MAX); | r = xstrlcpy(g_buf, messages[MSG_PLUGIN_KEYS], CMD_LEN_MAX); | ||||
printkeys(plug, g_buf + strlen(g_buf), PLUGIN_MAX); | printkeys(plug, g_buf + r - 1, PLUGIN_MAX); | ||||
printprompt(g_buf); | printprompt(g_buf); | ||||
r = get_input(NULL); | r = get_input(NULL); | ||||
tmp = get_kv_val(plug, NULL, r, PLUGIN_MAX, FALSE); | tmp = get_kv_val(plug, NULL, r, PLUGIN_MAX, FALSE); | ||||
@@ -52,7 +52,6 @@ enum action { | |||||
SEL_CDBEGIN, | SEL_CDBEGIN, | ||||
SEL_CDLAST, | SEL_CDLAST, | ||||
SEL_CDROOT, | SEL_CDROOT, | ||||
SEL_VISIT, | |||||
SEL_BOOKMARK, | SEL_BOOKMARK, | ||||
SEL_CYCLE, | SEL_CYCLE, | ||||
SEL_CYCLER, | SEL_CYCLER, | ||||
@@ -155,11 +154,9 @@ static struct key bindings[] = { | |||||
{ '-', SEL_CDLAST }, | { '-', SEL_CDLAST }, | ||||
/* Go to / */ | /* Go to / */ | ||||
{ '`', SEL_CDROOT }, | { '`', SEL_CDROOT }, | ||||
/* Visit marked directory */ | |||||
{ ',', SEL_VISIT }, | |||||
/* Leader key */ | /* Leader key */ | ||||
{ 'b', SEL_BOOKMARK }, | { 'b', SEL_BOOKMARK }, | ||||
{ CONTROL('B'), SEL_BOOKMARK }, | { CONTROL('_'), SEL_BOOKMARK }, | ||||
/* Cycle contexts in forward direction */ | /* Cycle contexts in forward direction */ | ||||
{ '\t', SEL_CYCLE }, | { '\t', SEL_CYCLE }, | ||||
/* Cycle contexts in reverse direction */ | /* Cycle contexts in reverse direction */ | ||||
@@ -170,12 +167,11 @@ static struct key bindings[] = { | |||||
{ '3', SEL_CTX3 }, | { '3', SEL_CTX3 }, | ||||
{ '4', SEL_CTX4 }, | { '4', SEL_CTX4 }, | ||||
/* Mark a path to visit later */ | /* Mark a path to visit later */ | ||||
{ '+', SEL_PIN }, | { ',', SEL_PIN }, | ||||
/* Filter */ | /* Filter */ | ||||
{ '/', SEL_FLTR }, | { '/', SEL_FLTR }, | ||||
/* Toggle filter mode */ | /* Toggle filter mode */ | ||||
{ KEY_IC, SEL_MFLTR }, | { CONTROL('N'), SEL_MFLTR }, | ||||
{ CONTROL('_'), SEL_MFLTR }, | |||||
/* Toggle hide .dot files */ | /* Toggle hide .dot files */ | ||||
{ '.', SEL_TOGGLEDOT }, | { '.', SEL_TOGGLEDOT }, | ||||
/* Detailed listing */ | /* Detailed listing */ | ||||
@@ -208,15 +204,15 @@ static struct key bindings[] = { | |||||
/* Select all files in current dir */ | /* Select all files in current dir */ | ||||
{ 'a', SEL_SELALL }, | { 'a', SEL_SELALL }, | ||||
/* Show list of copied files */ | /* Show list of copied files */ | ||||
{ 'M', SEL_SELLIST }, | { 'y', SEL_SELLIST }, | ||||
/* Edit selection buffer */ | /* Edit selection buffer */ | ||||
{ 'K', SEL_SELEDIT }, | { CONTROL('Y'), SEL_SELEDIT }, | ||||
/* Copy from selection buffer */ | /* Copy from selection buffer */ | ||||
{ 'P', SEL_CP }, | { 'P', SEL_CP }, | ||||
/* Move from selection buffer */ | /* Move from selection buffer */ | ||||
{ 'V', SEL_MV }, | { 'V', SEL_MV }, | ||||
/* Copy/move from selection buffer and rename */ | /* Copy/move from selection buffer and rename */ | ||||
{ 'w', SEL_CPMVAS }, | { CONTROL('V'), SEL_CPMVAS }, | ||||
/* Delete from selection buffer */ | /* Delete from selection buffer */ | ||||
{ 'X', SEL_RMMUL }, | { 'X', SEL_RMMUL }, | ||||
/* Delete currently selected */ | /* Delete currently selected */ | ||||
@@ -231,7 +227,6 @@ static struct key bindings[] = { | |||||
/* Rename contents of current dir */ | /* Rename contents of current dir */ | ||||
{ 'r', SEL_RENAMEMUL }, | { 'r', SEL_RENAMEMUL }, | ||||
/* Mount an archive */ | /* Mount an archive */ | ||||
{ 'o', SEL_ARCHIVEOPS }, | |||||
{ CONTROL('F'), SEL_ARCHIVEOPS }, | { CONTROL('F'), SEL_ARCHIVEOPS }, | ||||
/* Connect to server over SSHFS */ | /* Connect to server over SSHFS */ | ||||
{ 'c', SEL_REMOTE }, | { 'c', SEL_REMOTE }, | ||||
@@ -243,8 +238,7 @@ static struct key bindings[] = { | |||||
{ 'x', SEL_PLUGKEY }, | { 'x', SEL_PLUGKEY }, | ||||
{ ';', SEL_PLUGKEY }, | { ';', SEL_PLUGKEY }, | ||||
/* Run a plugin */ | /* Run a plugin */ | ||||
{ 'i', SEL_PLUGIN }, | { CONTROL('P'), SEL_PLUGIN }, | ||||
{ CONTROL('V'), SEL_PLUGIN }, | |||||
/* Run command */ | /* Run command */ | ||||
{ '!', SEL_SHELL }, | { '!', SEL_SHELL }, | ||||
{ CONTROL(']'), SEL_SHELL }, | { CONTROL(']'), SEL_SHELL }, | ||||
@@ -252,7 +246,7 @@ static struct key bindings[] = { | |||||
{ '=', SEL_LAUNCH }, | { '=', SEL_LAUNCH }, | ||||
/* Run a command */ | /* Run a command */ | ||||
{ ']', SEL_RUNCMD }, | { ']', SEL_RUNCMD }, | ||||
{ CONTROL('P'), SEL_RUNCMD }, | { CONTROL('T'), SEL_RUNCMD }, | ||||
/* Open in EDITOR or PAGER */ | /* Open in EDITOR or PAGER */ | ||||
{ 'e', SEL_RUNEDIT }, | { 'e', SEL_RUNEDIT }, | ||||
{ 'p', SEL_RUNPAGE }, | { 'p', SEL_RUNPAGE }, | ||||