mirror of
https://github.com/jarun/nnn.git
synced 2024-11-24 11:51:27 +00:00
Use key-plugin pairs for running plugins
This commit is contained in:
parent
7b59a7dba9
commit
02b3273233
10
README.md
10
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 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
12
nnn.1
|
@ -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.
|
||||||
|
|
|
@ -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
142
src/nnn.c
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue