Use key-plugin pairs for running plugins

This commit is contained in:
Arun Prakash Jana 2019-08-14 19:57:44 +05:30
parent 7b59a7dba9
commit 02b3273233
No known key found for this signature in database
GPG key ID: A75979F35C080412
4 changed files with 87 additions and 87 deletions

View file

@ -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 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. 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 | | Example `export` | Description |
| --- | --- | | --- | --- |
| `NNN_BMS='d:~/Documents;D:~/Docs archive/'` | specify bookmarks (max 10) | | `NNN_BMS='d:~/Documents;D:~/Docs archive/'` | key-bookmark pairs [max 10] |
| `NNN_PLUG='fzy-open;mocplay;nmount;thumb'` | plugins to run with <kbd>xN</kbd> | | `NNN_PLUG='o:fzy-open;p:mocplay;m:nmount;t:thumb'` | key-plugin pairs (<kbd>x-key</kbd> to run) [max 10] |
| `NNN_USE_EDITOR=1` | open text files in `$VISUAL` (else `$EDITOR`, fallback vi) | | `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_CONTEXT_COLORS='1234'` | specify per context color [default: '4444' (all blue)] |
| `NNN_SSHFS_OPTS='sshfs -o reconnect,idmap=user'` | specify SSHFS options | | `NNN_SSHFS_OPTS='sshfs -o reconnect,idmap=user'` | specify SSHFS options |
@ -289,7 +291,7 @@ Press <kbd>?</kbd> in `nnn` to see the list anytime.
^W Random s Size t Time modified ^W Random s Size t Time modified
MISC MISC
! ^] Shell L Lock C Execute entry ! ^] 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 c SSHFS mount u Unmount
^P Prompt ^N Note = Launcher ^P Prompt ^N Note = Launcher
``` ```

12
nnn.1
View file

@ -146,7 +146,15 @@ when dealing with the !, e and p commands respectively. A single combination to
.Bd -literal .Bd -literal
export NNN_BMS='d:~/Documents;u:/home/user/Cam Uploads;D:~/Downloads/' 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 .Ed
.Pp .Pp
\fBNNN_USE_EDITOR:\fR use VISUAL (else EDITOR, preferably CLI, fallback vi) to handle text \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). \fBNNN_CP_MV_PROG:\fR show progress of copy, move operations (Linux-only, needs advcpmv).
.Bd -literal .Bd -literal
export NNN_CP_MV_PROG=1 export NNN_CP_MV_PROG=1
NOTE: BSD and macOS users can press '^T' to check the progress.
.Ed .Ed
.Sh KNOWN ISSUES .Sh KNOWN ISSUES
If you are using urxvt you might have to set backspace key to DEC. If you are using urxvt you might have to set backspace key to DEC.

View file

@ -48,13 +48,15 @@ Each script has a _Description_ section which provides more details on what the
#### Usage #### 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 <kbd>xN</kbd>: 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 <kbd>x-key</kbd>:
With this, plugin `fzy-open` can be run with the keybind <kbd>x1</kbd>, `mocplay` can be run with <kbd>x2</kbd> 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 <kbd>xo</kbd>, `mocplay` can be run with <kbd>xp</kbd> and so on... The key vs. plugin pairs are shown in the help and config screen.
#### Contributing plugins #### Contributing plugins

142
src/nnn.c
View file

@ -120,7 +120,7 @@
#define MSGWAIT '$' #define MSGWAIT '$'
#define REGEX_MAX 48 #define REGEX_MAX 48
#define BM_MAX 10 #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 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 NAMEBUF_INCR 0x800 /* 64 dir entries at once, avg. 32 chars per filename = 64*32B = 2KB */
#define DESCRIPTOR_LEN 32 #define DESCRIPTOR_LEN 32
@ -195,14 +195,11 @@ typedef struct entry {
uchar flags; /* Flags specific to the file */ uchar flags; /* Flags specific to the file */
} __attribute__ ((aligned(_ALIGNMENT))) *pEntry; } __attribute__ ((aligned(_ALIGNMENT))) *pEntry;
/* Bookmark */ /* Key-value pairs from env */
typedef struct { typedef struct {
int key; int key;
char *loc; char *val;
} bm; } kv;
/* Plugins */
static char *plug[PLUGIN_MAX] = {NULL};
/* /*
* Settings * Settings
@ -299,7 +296,8 @@ static struct entry *dents;
static blkcnt_t ent_blocks; static blkcnt_t ent_blocks;
static blkcnt_t dir_blocks; static blkcnt_t dir_blocks;
static ulong num_files; 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 size_t g_tmpfplen;
static uchar g_crc; static uchar g_crc;
static uchar BLK_SHIFT = 9; static uchar BLK_SHIFT = 9;
@ -2113,76 +2111,79 @@ static int xlink(char *suffix, char *path, char *buf, int *presel, int type)
return count; return count;
} }
static bool parsebmstr(void) static bool parsekvpair(kv *kvarr, char **envcpy, const char *cfgstr, uchar maxitems)
{ {
int i = 0; int i = 0;
char *nextkey; char *nextkey;
char *bms = getenv(env_cfg[NNN_BMS]); char *ptr = getenv(cfgstr);
if (!bms || !*bms) if (!ptr || !*ptr)
return TRUE; return TRUE;
bmstr = strdup(bms); *envcpy = strdup(ptr);
bms = bmstr; ptr = *envcpy;
nextkey = bms; nextkey = ptr;
while (*bms && i < BM_MAX) { while (*ptr && i < maxitems) {
if (bms == nextkey) { if (ptr == nextkey) {
bookmark[i].key = *bms; kvarr[i].key = *ptr;
if (*++bms != ':') if (*++ptr != ':')
return FALSE; return FALSE;
if (*++bms == '\0') if (*++ptr == '\0')
return FALSE; return FALSE;
bookmark[i].loc = bms; kvarr[i].val = ptr;
++i; ++i;
} }
if (*bms == ';') { if (*ptr == ';') {
/* Remove trailing space */ /* Remove trailing space */
if (i > 0 && *(bms - 1) == '/') if (i > 0 && *(ptr - 1) == '/')
*(bms - 1) = '\0'; *(ptr - 1) = '\0';
*bms = '\0'; *ptr = '\0';
nextkey = bms + 1; nextkey = ptr + 1;
} }
++bms; ++ptr;
} }
if (i < BM_MAX) { if (i < maxitems) {
if (*bookmark[i - 1].loc == '\0') if (*kvarr[i - 1].val == '\0')
return FALSE; return FALSE;
bookmark[i].key = '\0'; kvarr[i].key = '\0';
} }
return TRUE; 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. * NULL is returned in case of no match, path resolution failure etc.
* buf would be modified, so check return value before access * 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; int r = 0;
for (; bookmark[r].key && r < BM_MAX; ++r) { for (; kvarr[r].key && r < max; ++r) {
if (bookmark[r].key == key) { if (kvarr[r].key == key) {
if (bookmark[r].loc[0] == '~') { if (!path)
return kvarr[r].val;
if (kvarr[r].val[0] == '~') {
ssize_t len = strlen(home); ssize_t len = strlen(home);
ssize_t loclen = strlen(bookmark[r].loc); ssize_t loclen = strlen(kvarr[r].val);
if (!buf) if (!buf)
buf = (char *)malloc(len + loclen); buf = (char *)malloc(len + loclen);
xstrlcpy(buf, home, len + 1); xstrlcpy(buf, home, len + 1);
xstrlcpy(buf + len, bookmark[r].loc + 1, loclen); xstrlcpy(buf + len, kvarr[r].val + 1, loclen);
return buf; 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; 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) static inline void resetdircolor(int flags)
{ {
if (cfg.dircolor && !(flags & DIR_OR_LINK_TO_DIR)) { 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); 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 * The help string tokens (each line) start with a HEX value
* which indicates the number of spaces to print before the * 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" "b^W Random s Size t Time modified\n"
"1MISC\n" "1MISC\n"
"9! ^] Shell L Lock C Execute entry\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" "cc SSHFS mount u Unmount\n"
"b^P Prompt ^N Note = Launcher\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, "\nVOLUME: %s of ", coolsize(get_fs_info(path, FREE)));
dprintf(fd, "%s free\n\n", coolsize(get_fs_info(path, CAPACITY))); dprintf(fd, "%s free\n\n", coolsize(get_fs_info(path, CAPACITY)));
if (bookmark[0].loc) { if (bookmark[0].val) {
dprintf(fd, "BOOKMARKS\n"); dprintf(fd, "BOOKMARKS\n");
for (i = 0; i < BM_MAX && bookmark[i].key; ++i) printkv(bookmark, fd, BM_MAX);
dprintf(fd, " %c: %s\n", (char)bookmark[i].key, bookmark[i].loc);
dprintf(fd, "\n"); dprintf(fd, "\n");
} }
if (plug[0]) { if (plug[0].val) {
dprintf(fd, "PLUGIN KEYS\n"); dprintf(fd, "PLUGIN KEYS\n");
for (i = 0; i < PLUGIN_MAX && plug[i]; ++i) printkv(plug, fd, PLUGIN_MAX);
dprintf(fd, " %d: %s\n", i + 1, plug[i]);
dprintf(fd, "\n"); dprintf(fd, "\n");
} }
for (i = NNN_OPENER; i <= NNN_TRASH; ++i) { for (i = NNN_OPENER; i <= NNN_TRASH; ++i) {
start = getenv(env_cfg[i]); start = getenv(env_cfg[i]);
if (start) if (start)
dprintf(fd, "%s: %s\n", env_cfg[i], start); dprintf(fd, "%s: %s\n", env_cfg[i], start);
} }
if (g_cppath) if (g_cppath)
@ -3801,7 +3782,7 @@ nochange:
goto begin; goto begin;
} }
if (!get_bm_loc(newpath, fd)) { if (!get_kv_val(bookmark, newpath, fd, BM_MAX, TRUE)) {
printwait(messages[STR_INVBM_KEY], &presel); printwait(messages[STR_INVBM_KEY], &presel);
goto nochange; goto nochange;
} }
@ -4424,11 +4405,12 @@ nochange:
if (sel == SEL_PLUGKEY) if (sel == SEL_PLUGKEY)
{ {
r = get_input("") - '0'; r = get_input("");
if ((r < 1 || r > PLUGIN_MAX) || !plug[r - 1]) tmp = get_kv_val(plug, NULL, r, PLUGIN_MAX, FALSE);
if (!tmp)
goto nochange; goto nochange;
mkpath(plugindir, plug[r - 1], newpath); mkpath(plugindir, tmp, newpath);
if (ndents) if (ndents)
spawn(newpath, dents[cur].name, NULL, path, F_NORMAL); spawn(newpath, dents[cur].name, NULL, path, F_NORMAL);
else else
@ -4845,15 +4827,19 @@ int main(int argc, char *argv[])
DPRINTF_S(opener); DPRINTF_S(opener);
/* Parse bookmarks string */ /* Parse bookmarks string */
if (!parsebmstr()) { if (!parsekvpair(bookmark, &bmstr, env_cfg[NNN_BMS], BM_MAX)) {
fprintf(stderr, "%s\n", env_cfg[NNN_BMS]); fprintf(stderr, "%s\n", env_cfg[NNN_BMS]);
return _FAILURE; 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) { /* 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]); fprintf(stderr, "%s\n", messages[STR_INVBM_KEY]);
return _FAILURE; return _FAILURE;
} }