@@ -27,7 +27,7 @@ | |||||
`nnn` works seamlessly with your DE and favourite GUI utilities. It runs on Linux, macOS, Raspberry Pi, BSD, Cygwin, Linux subsystem for Windows and Termux on Android. | `nnn` works seamlessly with your DE and favourite GUI utilities. It runs on Linux, macOS, Raspberry Pi, BSD, Cygwin, Linux subsystem for Windows and Termux on Android. | ||||
Have as many scripts as you want to extend the power of `nnn`! Pick from the [scripts repository](https://github.com/jarun/nnn/tree/master/user-scripts) or add your own. | Have as many scripts as you want to extend the power of `nnn`! Pick from the available [plugins](https://github.com/jarun/nnn/tree/master/plugins) or add your own. | ||||
[Quickstart](#quickstart) and see how `nnn` simplifies long desktop sessions. When you are ready for more, start [hacking `nnn`](https://github.com/jarun/nnn/wiki/hacking-nnn). | [Quickstart](#quickstart) and see how `nnn` simplifies long desktop sessions. When you are ready for more, start [hacking `nnn`](https://github.com/jarun/nnn/wiki/hacking-nnn). | ||||
@@ -60,7 +60,7 @@ Have as many scripts as you want to extend the power of `nnn`! Pick from the [sc | |||||
- [File indicators](#file-indicators) | - [File indicators](#file-indicators) | ||||
- [Configuration](#configuration) | - [Configuration](#configuration) | ||||
- [Help](#help) | - [Help](#help) | ||||
- [User scripts](#user-scripts) | - [Plugins](#plugins) | ||||
- [Troubleshooting](#troubleshooting) | - [Troubleshooting](#troubleshooting) | ||||
- [Tmux configuration](#tmux-configuration) | - [Tmux configuration](#tmux-configuration) | ||||
- [BSD terminal issue](#bsd-terminal-issue) | - [BSD terminal issue](#bsd-terminal-issue) | ||||
@@ -102,13 +102,12 @@ Have as many scripts as you want to extend the power of `nnn`! Pick from the [sc | |||||
- Copy, move, delete, archive, link selection | - Copy, move, delete, archive, link selection | ||||
- FreeDesktop compliant trash (needs trash-cli) | - FreeDesktop compliant trash (needs trash-cli) | ||||
- Show copy, move progress on Linux (needs avdcpmv) | - Show copy, move progress on Linux (needs avdcpmv) | ||||
- Script to view directory and file diff for selection | - Plugin repository | ||||
- Transfer files using lftp | - Transfer files using lftp | ||||
- Batch rename (needs vidir) | - Batch rename (needs vidir) | ||||
- Per-context directory color (default: blue) | - Per-context directory color (default: blue) | ||||
- Spawn a shell in the current directory | - Spawn a shell in the current directory | ||||
- Launch applications, run a command | - Launch applications, run a command | ||||
- Repository of custom scripts | |||||
- Run current file as executable | - Run current file as executable | ||||
- Change directory at exit (*easy* shell integration) | - Change directory at exit (*easy* shell integration) | ||||
- Edit file in EDITOR or open in PAGER | - Edit file in EDITOR or open in PAGER | ||||
@@ -143,7 +142,7 @@ Have as many scripts as you want to extend the power of `nnn`! Pick from the [sc | |||||
| advcpmv (Linux) ([integration](https://github.com/jarun/nnn/wiki/hacking-nnn#show-cp-mv-progress)) | copy, move progress | | | advcpmv (Linux) ([integration](https://github.com/jarun/nnn/wiki/hacking-nnn#show-cp-mv-progress)) | copy, move progress | | ||||
| $EDITOR (overridden by $VISUAL, if defined) | edit files (fallback vi) | | | $EDITOR (overridden by $VISUAL, if defined) | edit files (fallback vi) | | ||||
| $PAGER (less, most) | page through files (fallback less) | | | $PAGER (less, most) | page through files (fallback less) | | ||||
| $SHELL | spawn a shell, run script (fallback sh) | | | $SHELL | spawn a shell, run some commands (fallback sh) | | ||||
#### From a package manager | #### From a package manager | ||||
@@ -199,7 +198,7 @@ Option completion scripts for Bash, Fish and Zsh can be found in respective subd | |||||
5. To use `nnn` as a GUI app launcher with fuzzy selection menu, drop [`nlaunch`](https://github.com/jarun/nnn/blob/master/scripts/nlaunch/nlaunch) somewhere in your `$PATH`. Note that the launcher requires fzy. | 5. To use `nnn` as a GUI app launcher with fuzzy selection menu, drop [`nlaunch`](https://github.com/jarun/nnn/blob/master/scripts/nlaunch/nlaunch) somewhere in your `$PATH`. Note that the launcher requires fzy. | ||||
6. Don't memorize keys. Arrows, <kbd>/</kbd> and <kbd>q</kbd> suffice. Press <kbd>?</kbd> for help on keyboard shortcuts anytime. | 6. Don't memorize keys. Arrows, <kbd>/</kbd> and <kbd>q</kbd> suffice. Press <kbd>?</kbd> for help on keyboard shortcuts anytime. | ||||
- For additional functionality [setup custom scripts](#user-scripts). | - For additional functionality [setup plugins](#plugins). | ||||
- Visit the wiki page [hacking `nnn`](https://github.com/jarun/nnn/wiki/hacking-nnn) for many more specific use cases. | - Visit the wiki page [hacking `nnn`](https://github.com/jarun/nnn/wiki/hacking-nnn) for many more specific use cases. | ||||
- To set `nnn` as the default file manager, follow these [instructions](https://github.com/jarun/nnn/wiki/nnn-as-default-file-manager). | - To set `nnn` as the default file manager, follow these [instructions](https://github.com/jarun/nnn/wiki/nnn-as-default-file-manager). | ||||
@@ -264,7 +263,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 | ||||
! ^] Spawn SHELL C Execute entry | ! ^] Spawn SHELL C Execute entry | ||||
R ^V Run script L Lock terminal | R ^V Pick plugin L Lock terminal | ||||
^P Prompt ^N Note = Launcher | ^P Prompt ^N Note = Launcher | ||||
``` | ``` | ||||
@@ -379,8 +378,8 @@ The following indicators are used in the detail view: | |||||
| `NNN_OPENER_DETACH=1` | do not block when invoking file opener | | | `NNN_OPENER_DETACH=1` | do not block when invoking file opener | | ||||
| `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_IDLE_TIMEOUT=300` | idle seconds before locking terminal [default: disabled] | | | `NNN_IDLE_TIMEOUT=300` | idle seconds before locking terminal [default: disabled] | | ||||
| `NNN_COPIER='copier.sh'` | system clipboard copier script [default: none] | | | `NNN_COPIER='/path/to/copier.sh'` | system clipboard copier script [default: none] | | ||||
| `NNN_SCRIPT_DIR=/home/user/scripts` | absolute path to script dir | | | `NNN_PLUGIN_DIR=/home/user/nnn-plugins` | absolute path to plugins dir | | ||||
| `NNN_NOTE=/home/user/Dropbox/Public/notes` | path to note file [default: none] | | | `NNN_NOTE=/home/user/Dropbox/Public/notes` | path to note file [default: none] | | ||||
| `NNN_TMPFILE=/tmp/nnn` | file to write current open dir path to for cd on quit | | | `NNN_TMPFILE=/tmp/nnn` | file to write current open dir path to for cd on quit | | ||||
| `NNN_USE_EDITOR=1` | Open text files in `$EDITOR` (`$VISUAL`, if defined; fallback vi) | | | `NNN_USE_EDITOR=1` | Open text files in `$EDITOR` (`$VISUAL`, if defined; fallback vi) | | ||||
@@ -396,17 +395,17 @@ The following indicators are used in the detail view: | |||||
$ man nnn | $ man nnn | ||||
To lookup keyboard shortcuts at runtime, press <kbd>?</kbd>. | To lookup keyboard shortcuts at runtime, press <kbd>?</kbd>. | ||||
#### USER SCRIPTS | #### PLUGINS | ||||
`nnn` can invoke custom scripts in the current directory (`$PWD` for the script) with the currently selected file name as the argument. | `nnn` can invoke plugins in the current directory (`$PWD` for the plugin) with the currently selected file name as the argument. | ||||
Copy the scripts of your interest from the [user-scripts](https://github.com/jarun/nnn/tree/master/user-scripts) directory and let `nnn` know the location: | Copy the plugins of your interest from the [plugins](https://github.com/jarun/nnn/tree/master/plugins) directory and let `nnn` know the location: | ||||
export NNN_SCRIPT_DIR=/absolute/path/to/scripts_dir | export NNN_PLUGIN_DIR=/absolute/path/to/plugins_dir | ||||
Use the run script shortcut to jump to the script directory and pick a script. Repeating the same shortcut cancels the operation and puts you back in the original directory. | Use the pick plugin shortcut to visit the plugin directory and pick a plugin. Repeating the same shortcut cancels the operation and puts you back in the original directory. | ||||
If you have an interesting script feel free to raise a PR. | If you have an interesting plugin feel free to raise a PR. | ||||
#### TROUBLESHOOTING | #### TROUBLESHOOTING | ||||
@@ -174,9 +174,9 @@ when dealing with the !, e and p commands respectively. A single combination to | |||||
The path is shown in the help and configuration screen. | The path is shown in the help and configuration screen. | ||||
.Ed | .Ed | ||||
.Pp | .Pp | ||||
\fBNNN_SCRIPT_DIR:\fR \fIabsolute\fR path to script directory. Selected script is invoked with currently selected file name as argument 1. | \fBNNN_PLUGIN_DIR:\fR \fIabsolute\fR path to plugin directory. Selected plugin is invoked with currently selected file name as argument 1. | ||||
.Bd -literal | .Bd -literal | ||||
export NNN_SCRIPT_DIR=/absolute/path/to/scripts_dir | export NNN_PLUGIN_DIR=/absolute/path/to/plugins_dir | ||||
.Ed | .Ed | ||||
.Pp | .Pp | ||||
\fBNNN_NOTE:\fR \fIabsolute\fR path to a note file. | \fBNNN_NOTE:\fR \fIabsolute\fR path to a note file. | ||||
@@ -1,6 +1,5 @@ | |||||
| Script (a-z) | Lang | Deps | Description | | | Plugin (a-z) | Lang | Deps | Description | | ||||
| --- | --- | --- | --- | | | --- | --- | --- | --- | | ||||
| copier | sh | OS-specific | Copy selection to clipboard | | |||||
| edit | sh | fzy | Fuzzy find a file in directory subtree and edit in vim | | | edit | sh | fzy | Fuzzy find a file in directory subtree and edit in vim | | ||||
| fzy | sh | fzy | Fuzzy find a file in directory subtree and open using xdg-open | | | fzy | sh | fzy | Fuzzy find a file in directory subtree and open using xdg-open | | ||||
| hexview | sh | xxd, $PAGER | view a file in hex | | | hexview | sh | xxd, $PAGER | view a file in hex | | ||||
@@ -16,15 +15,15 @@ | |||||
| transfer | sh | curl | Upload current file to transfer.sh | | | transfer | sh | curl | Upload current file to transfer.sh | | ||||
| upgrade | sh | wget | Upgrade to latest nnn version manually on Debian 9 Stretch | | | upgrade | sh | wget | Upgrade to latest nnn version manually on Debian 9 Stretch | | ||||
### File access from scripts | ### File access from plugins | ||||
The design is flexible so a script can access: | The design is flexible so a plugin can access: | ||||
- all files in the directory (`nnn` switches to the dir where the script is to be run so the dir is `$PWD` for the script) | - all files in the directory (`nnn` switches to the dir where the plugin is to be run so the dir is `$PWD` for the plugin) | ||||
- the currently highlighted file (the file name is passed as the first argument to a script) | - the currently highlighted file (the file name is passed as the argument to a plugin) | ||||
- the current selection (by reading the file .nnncp, see the script `copier`) | - the current selection (by reading the file .nnncp, see the plugin `ndiff`) | ||||
### Contributing scripts | ### Contributing plugins | ||||
All scripting languages should work. However, POSIX-compliant shell scripts runnable in `sh` are preferred. If that's too rudimentary for your use case, use Python, Perl or Ruby. Please keep non-portable commands (like `notify-send`) commented so users from any other OS/DE aren't surprised. | Plugins are scripts and all scripting languages should work. However, POSIX-compliant shell scripts runnable in `sh` are preferred. If that's too rudimentary for your use case, use Python, Perl or Ruby. Please keep non-portable commands (like `notify-send`) commented so users from any other OS/DE aren't surprised. | ||||
The scripts should be executable. Please add an entry in the table above. | The plugins should be executable. Please add an entry in the table above. |
@@ -217,8 +217,8 @@ typedef struct { | |||||
uint autoselect : 1; /* Auto-select dir in nav-as-you-type mode */ | uint autoselect : 1; /* Auto-select dir in nav-as-you-type mode */ | ||||
uint metaviewer : 1; /* Index of metadata viewer in utils[] */ | uint metaviewer : 1; /* Index of metadata viewer in utils[] */ | ||||
uint useeditor : 1; /* Use VISUAL to open text files */ | uint useeditor : 1; /* Use VISUAL to open text files */ | ||||
uint runscript : 1; /* Choose script to run mode */ | uint runplugin : 1; /* Choose plugin mode */ | ||||
uint runctx : 2; /* The context in which script is to be run */ | uint runctx : 2; /* The context in which plugin is to be run */ | ||||
uint restrict0b : 1; /* Restrict 0-byte file opening */ | uint restrict0b : 1; /* Restrict 0-byte file opening */ | ||||
uint filter_re : 1; /* Use regex filters */ | uint filter_re : 1; /* Use regex filters */ | ||||
uint wild : 1; /* Do not sort entries on dir load */ | uint wild : 1; /* Do not sort entries on dir load */ | ||||
@@ -257,7 +257,7 @@ static settings cfg = { | |||||
1, /* autoselect */ | 1, /* autoselect */ | ||||
0, /* metaviewer */ | 0, /* metaviewer */ | ||||
0, /* useeditor */ | 0, /* useeditor */ | ||||
0, /* runscript */ | 0, /* runplugin */ | ||||
0, /* runctx */ | 0, /* runctx */ | ||||
0, /* restrict0b */ | 0, /* restrict0b */ | ||||
1, /* filter_re */ | 1, /* filter_re */ | ||||
@@ -385,7 +385,7 @@ static const char * const messages[] = { | |||||
#define NNN_CONTEXT_COLORS 2 | #define NNN_CONTEXT_COLORS 2 | ||||
#define NNN_IDLE_TIMEOUT 3 | #define NNN_IDLE_TIMEOUT 3 | ||||
#define NNN_COPIER 4 | #define NNN_COPIER 4 | ||||
#define NNN_SCRIPT_DIR 5 | #define NNN_PLUGIN_DIR 5 | ||||
#define NNN_NOTE 6 | #define NNN_NOTE 6 | ||||
#define NNN_TMPFILE 7 | #define NNN_TMPFILE 7 | ||||
#define NNNLVL 8 /* strings end here */ | #define NNNLVL 8 /* strings end here */ | ||||
@@ -405,7 +405,7 @@ static const char * const env_cfg[] = { | |||||
"NNN_CONTEXT_COLORS", | "NNN_CONTEXT_COLORS", | ||||
"NNN_IDLE_TIMEOUT", | "NNN_IDLE_TIMEOUT", | ||||
"NNN_COPIER", | "NNN_COPIER", | ||||
"NNN_SCRIPT_DIR", | "NNN_PLUGIN_DIR", | ||||
"NNN_NOTE", | "NNN_NOTE", | ||||
"NNN_TMPFILE", | "NNN_TMPFILE", | ||||
"NNNLVL", | "NNNLVL", | ||||
@@ -2158,7 +2158,7 @@ static void savecurctx(settings *curcfg, char *path, char *curname, int r /* nex | |||||
xstrlcpy(g_ctx[r].c_name, curname, NAME_MAX + 1); | xstrlcpy(g_ctx[r].c_name, curname, NAME_MAX + 1); | ||||
g_ctx[r].c_fltr[0] = g_ctx[r].c_fltr[1] = '\0'; | g_ctx[r].c_fltr[0] = g_ctx[r].c_fltr[1] = '\0'; | ||||
g_ctx[r].c_cfg = cfg; | g_ctx[r].c_cfg = cfg; | ||||
g_ctx[r].c_cfg.runscript = 0; | g_ctx[r].c_cfg.runplugin = 0; | ||||
} | } | ||||
cfg.curctx = r; | cfg.curctx = r; | ||||
@@ -2386,7 +2386,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! ^] Spawn SHELL C Execute entry\n" | "9! ^] Spawn SHELL C Execute entry\n" | ||||
"9R ^V Run script L Lock terminal\n" | "9R ^V Pick plugin L Lock terminal\n" | ||||
"b^P Prompt ^N Note = Launcher\n"}; | "b^P Prompt ^N Note = Launcher\n"}; | ||||
if (g_tmpfpath[0]) | if (g_tmpfpath[0]) | ||||
@@ -2845,7 +2845,7 @@ static void browse(char *ipath) | |||||
struct stat sb; | struct stat sb; | ||||
char *path, *lastdir, *lastname; | char *path, *lastdir, *lastname; | ||||
char *dir, *tmp; | char *dir, *tmp; | ||||
char *scriptpath = getenv(env_cfg[NNN_SCRIPT_DIR]); | char *pluginpath = getenv(env_cfg[NNN_PLUGIN_DIR]); | ||||
atexit(dentfree); | atexit(dentfree); | ||||
@@ -3002,11 +3002,11 @@ nochange: | |||||
if (cfg.nonavopen && sel == SEL_NAV_IN) | if (cfg.nonavopen && sel == SEL_NAV_IN) | ||||
continue; | continue; | ||||
/* Handle script selection mode */ | /* Handle plugin selection mode */ | ||||
if (cfg.runscript) { | if (cfg.runplugin) { | ||||
if (!scriptpath || (cfg.runctx != cfg.curctx) | if (!pluginpath || (cfg.runctx != cfg.curctx) | ||||
/* Must be in script directory to select script */ | /* Must be in plugin directory to select plugin */ | ||||
|| (strcmp(path, scriptpath) != 0)) | || (strcmp(path, pluginpath) != 0)) | ||||
continue; | continue; | ||||
mkpath(path, dents[cur].name, newpath); | mkpath(path, dents[cur].name, newpath); | ||||
@@ -3019,7 +3019,7 @@ nochange: | |||||
} else | } else | ||||
spawn(newpath, NULL, NULL, path, F_NORMAL); | spawn(newpath, NULL, NULL, path, F_NORMAL); | ||||
rundir[0] = '\0'; | rundir[0] = '\0'; | ||||
cfg.runscript = 0; | cfg.runplugin = 0; | ||||
setdirwatch(); | setdirwatch(); | ||||
goto begin; | goto begin; | ||||
} | } | ||||
@@ -3739,7 +3739,7 @@ nochange: | |||||
} | } | ||||
case SEL_EXEC: // fallthrough | case SEL_EXEC: // fallthrough | ||||
case SEL_SHELL: // fallthrough | case SEL_SHELL: // fallthrough | ||||
case SEL_SCRIPT: // fallthrough | case SEL_PLUGIN: // fallthrough | ||||
case SEL_LAUNCH: // fallthrough | case SEL_LAUNCH: // fallthrough | ||||
case SEL_RUNCMD: | case SEL_RUNCMD: | ||||
switch (sel) { | switch (sel) { | ||||
@@ -3766,13 +3766,13 @@ nochange: | |||||
case SEL_SHELL: | case SEL_SHELL: | ||||
spawn(shell, NULL, NULL, path, F_CLI); | spawn(shell, NULL, NULL, path, F_CLI); | ||||
break; | break; | ||||
case SEL_SCRIPT: | case SEL_PLUGIN: | ||||
if (!scriptpath) { | if (!pluginpath) { | ||||
printwait("set NNN_SCRIPT_DIR", &presel); | printwait("set NNN_PLUGIN_DIR", &presel); | ||||
goto nochange; | goto nochange; | ||||
} | } | ||||
if (stat(scriptpath, &sb) == -1) { | if (stat(pluginpath, &sb) == -1) { | ||||
printwarn(); | printwarn(); | ||||
goto nochange; | goto nochange; | ||||
} | } | ||||
@@ -3781,13 +3781,13 @@ nochange: | |||||
if (!S_ISDIR(sb.st_mode)) | if (!S_ISDIR(sb.st_mode)) | ||||
break; | break; | ||||
cfg.runscript ^= 1; | cfg.runplugin ^= 1; | ||||
if (!cfg.runscript && rundir[0]) { | if (!cfg.runplugin && rundir[0]) { | ||||
/* | /* | ||||
* If toggled, and still in the script dir, | * If toggled, and still in the plugin dir, | ||||
* switch to original directory | * switch to original directory | ||||
*/ | */ | ||||
if (strcmp(path, scriptpath) == 0) { | if (strcmp(path, pluginpath) == 0) { | ||||
xstrlcpy(path, rundir, PATH_MAX); | xstrlcpy(path, rundir, PATH_MAX); | ||||
xstrlcpy(lastname, runfile, NAME_MAX); | xstrlcpy(lastname, runfile, NAME_MAX); | ||||
rundir[0] = runfile[0] = '\0'; | rundir[0] = runfile[0] = '\0'; | ||||
@@ -3798,11 +3798,11 @@ nochange: | |||||
} | } | ||||
/* Check if directory is accessible */ | /* Check if directory is accessible */ | ||||
if (!xdiraccess(scriptpath)) | if (!xdiraccess(pluginpath)) | ||||
goto nochange; | goto nochange; | ||||
xstrlcpy(rundir, path, PATH_MAX); | xstrlcpy(rundir, path, PATH_MAX); | ||||
xstrlcpy(path, scriptpath, PATH_MAX); | xstrlcpy(path, pluginpath, PATH_MAX); | ||||
if (ndents) | if (ndents) | ||||
xstrlcpy(runfile, dents[cur].name, NAME_MAX); | xstrlcpy(runfile, dents[cur].name, NAME_MAX); | ||||
cfg.runctx = cfg.curctx; | cfg.runctx = cfg.curctx; | ||||
@@ -88,7 +88,7 @@ enum action { | |||||
SEL_HELP, | SEL_HELP, | ||||
SEL_EXEC, | SEL_EXEC, | ||||
SEL_SHELL, | SEL_SHELL, | ||||
SEL_SCRIPT, | SEL_PLUGIN, | ||||
SEL_LAUNCH, | SEL_LAUNCH, | ||||
SEL_RUNCMD, | SEL_RUNCMD, | ||||
SEL_RUNEDIT, | SEL_RUNEDIT, | ||||
@@ -225,9 +225,9 @@ static struct key bindings[] = { | |||||
/* Run command */ | /* Run command */ | ||||
{ '!', SEL_SHELL }, | { '!', SEL_SHELL }, | ||||
{ CONTROL(']'), SEL_SHELL }, | { CONTROL(']'), SEL_SHELL }, | ||||
/* Run a custom script */ | /* Run a plugin */ | ||||
{ 'R', SEL_SCRIPT }, | { 'R', SEL_PLUGIN }, | ||||
{ CONTROL('V'), SEL_SCRIPT }, | { CONTROL('V'), SEL_PLUGIN }, | ||||
/* Launcher */ | /* Launcher */ | ||||
{ '=', SEL_LAUNCH }, | { '=', SEL_LAUNCH }, | ||||
/* Run a command */ | /* Run a command */ | ||||