diff --git a/README.md b/README.md index 23b8b44d..22cfcb42 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,9 @@ `nnn` is a full-featured file manager for low-end devices and the regular desktop. It's extremely light and fast (**[performance](https://github.com/jarun/nnn/wiki/performance)**). -`nnn` is also a disk usage analyzer, a fuzzy app launcher, a batch file renamer and a file picker. Many **[plugins](https://github.com/jarun/nnn/tree/master/plugins)** are available to extend its power. Custom plugins are easy to add. There's an independent [(neo)vim picker plugin](https://github.com/mcchrish/nnn.vim) project. +`nnn` is also a disk usage analyzer, a fuzzy app launcher, a batch file renamer and a file picker. + +Many **[plugins](https://github.com/jarun/nnn/tree/master/plugins)** are available to extend its power. Plugins can be run directly with custom keybinds. There's an independent [(neo)vim picker plugin](https://github.com/mcchrish/nnn.vim) project. Custom plugins are easy to add. It runs on Linux, macOS, Raspberry Pi, BSD, Cygwin, Linux subsystem for Windows and Termux on Android. `nnn` works seamlessly with DEs and GUI utilities. It's nearly zero-config (with sensible defaults) and can be setup in less than 5 minutes. @@ -216,8 +218,8 @@ Option completion scripts for Bash, Fish and Zsh can be found in respective subd | Example `export` | Description | | --- | --- | -| `NNN_BMS='d:~/Documents;D:~/Docs archive/'` | specify bookmarks (max 10) | -| `NNN_PLUG='fzy-open;mocplay;nmount;thumb'` | plugins to run with xN | +| `NNN_BMS='d:~/Documents;D:~/Docs archive/'` | key-bookmark pairs [max 10] | +| `NNN_PLUG='o:fzy-open;p:mocplay;m:nmount;t:thumb'` | key-plugin pairs (x-key to run) [max 10] | | `NNN_USE_EDITOR=1` | open text files in `$VISUAL` (else `$EDITOR`, fallback vi) | | `NNN_CONTEXT_COLORS='1234'` | specify per context color [default: '4444' (all blue)] | | `NNN_SSHFS_OPTS='sshfs -o reconnect,idmap=user'` | specify SSHFS options | @@ -289,7 +291,7 @@ Press ? in `nnn` to see the list anytime. ^W Random s Size t Time modified MISC ! ^] Shell L Lock C Execute entry - R ^V Pick plugin xN Run plugin N + R ^V Pick plugin xK Run plugin key K c SSHFS mount u Unmount ^P Prompt ^N Note = Launcher ``` diff --git a/nnn.1 b/nnn.1 index 05f754fe..eae9f6d7 100644 --- a/nnn.1 +++ b/nnn.1 @@ -146,7 +146,15 @@ when dealing with the !, e and p commands respectively. A single combination to .Bd -literal export NNN_BMS='d:~/Documents;u:/home/user/Cam Uploads;D:~/Downloads/' - NOTE: Bookmark keys should be single-character to use them in combination with the Leader key. + NOTE: To go to a bookmark, press the leader key followed by the bookmark key. +.Ed +.Pp +\fBNNN_PLUG:\fR directly executable plugins as \fIkey_char:location\fR pairs (max 10) separated by +\fI;\fR: +.Bd -literal + export NNN_PLUG='o:fzy-open;p:mocplay;d:ndiff;m:nmount;t:thumb' + + NOTE: To run a plugin directly, press 'x' followed by the plugin key. .Ed .Pp \fBNNN_USE_EDITOR:\fR use VISUAL (else EDITOR, preferably CLI, fallback vi) to handle text @@ -201,6 +209,8 @@ files. \fBNNN_CP_MV_PROG:\fR show progress of copy, move operations (Linux-only, needs advcpmv). .Bd -literal export NNN_CP_MV_PROG=1 + + NOTE: BSD and macOS users can press '^T' to check the progress. .Ed .Sh KNOWN ISSUES If you are using urxvt you might have to set backspace key to DEC. diff --git a/plugins/README.md b/plugins/README.md index 2e7d3b07..182e2485 100644 --- a/plugins/README.md +++ b/plugins/README.md @@ -48,13 +48,15 @@ Each script has a _Description_ section which provides more details on what the #### Usage -Use the _pick plugin_ shortcut to visit the plugin directory and execute a plugin. Repeating the same shortcut cancels the operation and puts you back in the original directory. +There are 2 ways to run plugins: -To run (up to 8) plugins directly with xN: +1. Use the _pick plugin_ shortcut to visit the plugin directory and execute a plugin. Repeating the same shortcut cancels the operation and puts you back in the original directory. - export NNN_PLUG='fzy-open;mocplay;ndiff;nmount;viuimg;pdfview' +2. To run (up to 10) plugins directly with x-key: -With this, plugin `fzy-open` can be run with the keybind x1, `mocplay` can be run with x2 and so on... + export NNN_PLUG='o:fzy-open;p:mocplay;d:ndiff;m:nmount;t:thumb' + + With this, plugin `fzy-open` can be run with the keybind xo, `mocplay` can be run with xp and so on... The key vs. plugin pairs are shown in the help and config screen. #### Contributing plugins diff --git a/src/nnn.c b/src/nnn.c index 1d35f95a..3c730d45 100644 --- a/src/nnn.c +++ b/src/nnn.c @@ -120,7 +120,7 @@ #define MSGWAIT '$' #define REGEX_MAX 48 #define BM_MAX 10 -#define PLUGIN_MAX 8 +#define PLUGIN_MAX 10 #define ENTRY_INCR 64 /* Number of dir 'entry' structures to allocate per shot */ #define NAMEBUF_INCR 0x800 /* 64 dir entries at once, avg. 32 chars per filename = 64*32B = 2KB */ #define DESCRIPTOR_LEN 32 @@ -195,14 +195,11 @@ typedef struct entry { uchar flags; /* Flags specific to the file */ } __attribute__ ((aligned(_ALIGNMENT))) *pEntry; -/* Bookmark */ +/* Key-value pairs from env */ typedef struct { int key; - char *loc; -} bm; - -/* Plugins */ -static char *plug[PLUGIN_MAX] = {NULL}; + char *val; +} kv; /* * Settings @@ -299,7 +296,8 @@ static struct entry *dents; static blkcnt_t ent_blocks; static blkcnt_t dir_blocks; static ulong num_files; -static bm bookmark[BM_MAX]; +static kv bookmark[BM_MAX]; +static kv plug[PLUGIN_MAX]; static size_t g_tmpfplen; static uchar g_crc; static uchar BLK_SHIFT = 9; @@ -2113,76 +2111,79 @@ static int xlink(char *suffix, char *path, char *buf, int *presel, int type) return count; } -static bool parsebmstr(void) +static bool parsekvpair(kv *kvarr, char **envcpy, const char *cfgstr, uchar maxitems) { int i = 0; char *nextkey; - char *bms = getenv(env_cfg[NNN_BMS]); + char *ptr = getenv(cfgstr); - if (!bms || !*bms) + if (!ptr || !*ptr) return TRUE; - bmstr = strdup(bms); - bms = bmstr; - nextkey = bms; + *envcpy = strdup(ptr); + ptr = *envcpy; + nextkey = ptr; - while (*bms && i < BM_MAX) { - if (bms == nextkey) { - bookmark[i].key = *bms; - if (*++bms != ':') + while (*ptr && i < maxitems) { + if (ptr == nextkey) { + kvarr[i].key = *ptr; + if (*++ptr != ':') return FALSE; - if (*++bms == '\0') + if (*++ptr == '\0') return FALSE; - bookmark[i].loc = bms; + kvarr[i].val = ptr; ++i; } - if (*bms == ';') { + if (*ptr == ';') { /* Remove trailing space */ - if (i > 0 && *(bms - 1) == '/') - *(bms - 1) = '\0'; + if (i > 0 && *(ptr - 1) == '/') + *(ptr - 1) = '\0'; - *bms = '\0'; - nextkey = bms + 1; + *ptr = '\0'; + nextkey = ptr + 1; } - ++bms; + ++ptr; } - if (i < BM_MAX) { - if (*bookmark[i - 1].loc == '\0') + if (i < maxitems) { + if (*kvarr[i - 1].val == '\0') return FALSE; - bookmark[i].key = '\0'; + kvarr[i].key = '\0'; } return TRUE; } /* - * Get the real path to a bookmark + * Get the value corresponding to a key * * NULL is returned in case of no match, path resolution failure etc. * buf would be modified, so check return value before access */ -static char *get_bm_loc(char *buf, int key) +static char *get_kv_val(kv *kvarr, char *buf, int key, uchar max, bool path) { int r = 0; - for (; bookmark[r].key && r < BM_MAX; ++r) { - if (bookmark[r].key == key) { - if (bookmark[r].loc[0] == '~') { + for (; kvarr[r].key && r < max; ++r) { + if (kvarr[r].key == key) { + if (!path) + return kvarr[r].val; + + if (kvarr[r].val[0] == '~') { ssize_t len = strlen(home); - ssize_t loclen = strlen(bookmark[r].loc); + ssize_t loclen = strlen(kvarr[r].val); if (!buf) buf = (char *)malloc(len + loclen); xstrlcpy(buf, home, len + 1); - xstrlcpy(buf + len, bookmark[r].loc + 1, loclen); + xstrlcpy(buf + len, kvarr[r].val + 1, loclen); return buf; } - return realpath(bookmark[r].loc, buf); + return realpath(kvarr[r].val, buf); } } @@ -2190,32 +2191,6 @@ static char *get_bm_loc(char *buf, int key) return NULL; } -static void parseplugins(void) -{ - int i = 0; - char *nextplug; - char *plugins = getenv("NNN_PLUG"); - - if (!plugins || !*plugins) - return; - - pluginstr = strdup(plugins); - plugins = pluginstr; - nextplug = plugins; - - while (*plugins && i < PLUGIN_MAX) { - if (plugins == nextplug) { - plug[i] = nextplug; - ++i; - } else if (*plugins == ';') { - *plugins = '\0'; - nextplug = plugins + 1; - } - - ++plugins; - } -} - static inline void resetdircolor(int flags) { if (cfg.dircolor && !(flags & DIR_OR_LINK_TO_DIR)) { @@ -2825,6 +2800,14 @@ static void lock_terminal(void) spawn(tmp, NULL, NULL, NULL, F_NORMAL); } +static void printkv(kv *kvarr, int fd, uchar max) +{ + uchar i = 0; + + for (; i < max && kvarr[i].key; ++i) + dprintf(fd, " %c: %s\n", (char)kvarr[i].key, kvarr[i].val); +} + /* * The help string tokens (each line) start with a HEX value * which indicates the number of spaces to print before the @@ -2867,7 +2850,7 @@ static bool show_help(const char *path) "b^W Random s Size t Time modified\n" "1MISC\n" "9! ^] Shell L Lock C Execute entry\n" - "9R ^V Pick plugin xN Run plugin N\n" + "9R ^V Pick plugin xK Run plugin key K\n" "cc SSHFS mount u Unmount\n" "b^P Prompt ^N Note = Launcher\n"}; @@ -2889,24 +2872,22 @@ static bool show_help(const char *path) dprintf(fd, "\nVOLUME: %s of ", coolsize(get_fs_info(path, FREE))); dprintf(fd, "%s free\n\n", coolsize(get_fs_info(path, CAPACITY))); - if (bookmark[0].loc) { + if (bookmark[0].val) { dprintf(fd, "BOOKMARKS\n"); - for (i = 0; i < BM_MAX && bookmark[i].key; ++i) - dprintf(fd, " %c: %s\n", (char)bookmark[i].key, bookmark[i].loc); + printkv(bookmark, fd, BM_MAX); dprintf(fd, "\n"); } - if (plug[0]) { + if (plug[0].val) { dprintf(fd, "PLUGIN KEYS\n"); - for (i = 0; i < PLUGIN_MAX && plug[i]; ++i) - dprintf(fd, " %d: %s\n", i + 1, plug[i]); + printkv(plug, fd, PLUGIN_MAX); dprintf(fd, "\n"); } for (i = NNN_OPENER; i <= NNN_TRASH; ++i) { start = getenv(env_cfg[i]); if (start) - dprintf(fd, "%s: %s\n", env_cfg[i], start); + dprintf(fd, "%s: %s\n", env_cfg[i], start); } if (g_cppath) @@ -3801,7 +3782,7 @@ nochange: goto begin; } - if (!get_bm_loc(newpath, fd)) { + if (!get_kv_val(bookmark, newpath, fd, BM_MAX, TRUE)) { printwait(messages[STR_INVBM_KEY], &presel); goto nochange; } @@ -4424,11 +4405,12 @@ nochange: if (sel == SEL_PLUGKEY) { - r = get_input("") - '0'; - if ((r < 1 || r > PLUGIN_MAX) || !plug[r - 1]) + r = get_input(""); + tmp = get_kv_val(plug, NULL, r, PLUGIN_MAX, FALSE); + if (!tmp) goto nochange; - mkpath(plugindir, plug[r - 1], newpath); + mkpath(plugindir, tmp, newpath); if (ndents) spawn(newpath, dents[cur].name, NULL, path, F_NORMAL); else @@ -4845,15 +4827,19 @@ int main(int argc, char *argv[]) DPRINTF_S(opener); /* Parse bookmarks string */ - if (!parsebmstr()) { + if (!parsekvpair(bookmark, &bmstr, env_cfg[NNN_BMS], BM_MAX)) { fprintf(stderr, "%s\n", env_cfg[NNN_BMS]); return _FAILURE; } - parseplugins(); + /* Parse plugins string */ + if (!parsekvpair(plug, &pluginstr, "NNN_PLUG", PLUGIN_MAX)) { + fprintf(stderr, "%s\n", "NNN_PLUG"); + return _FAILURE; + } if (arg) { /* Open a bookmark directly */ - if (arg[1] || (initpath = get_bm_loc(NULL, *arg)) == NULL) { + if (arg[1] || (initpath = get_kv_val(bookmark, NULL, *arg, BM_MAX, TRUE)) == NULL) { fprintf(stderr, "%s\n", messages[STR_INVBM_KEY]); return _FAILURE; }