@@ -62,6 +62,7 @@ Add to that an awesome [Wiki](https://github.com/jarun/nnn/wiki)! | |||||
- Batch renamer (feature-limited) for selection or dir | - Batch renamer (feature-limited) for selection or dir | ||||
- Copy (as), move (as), delete, archive, link selection | - Copy (as), move (as), delete, archive, link selection | ||||
- Notification on cp, mv, rm completion | - Notification on cp, mv, rm completion | ||||
- Copy file paths to system clipboard on select | |||||
- Create (with parents), rename, duplicate (anywhere) files and dirs | - Create (with parents), rename, duplicate (anywhere) files and dirs | ||||
- Launch GUI apps, run commands, execute file, spawn a shell | - Launch GUI apps, run commands, execute file, spawn a shell | ||||
- Hovered file set as `$nnn` at prompt and spawned shell | - Hovered file set as `$nnn` at prompt and spawned shell | ||||
@@ -162,7 +163,6 @@ There is no config file. Associated files are stored under `${XDG_CONFIG_HOME:-$ | |||||
| `NNN_SSHFS_OPTS='sshfs -o reconnect,idmap=user'` | specify SSHFS options | | | `NNN_SSHFS_OPTS='sshfs -o reconnect,idmap=user'` | specify SSHFS options | | ||||
| `NNN_RCLONE_OPTS='rclone mount --read-only'` | specify rclone options | | | `NNN_RCLONE_OPTS='rclone mount --read-only'` | specify rclone options | | ||||
| `NNN_IDLE_TIMEOUT=300` | idle seconds to lock terminal [default: disabled] | | | `NNN_IDLE_TIMEOUT=300` | idle seconds to lock terminal [default: disabled] | | ||||
| `NNN_COPIER=copier` | clipboard copier script [default: none] | | |||||
| `NNN_TRASH=1` | trash files to the desktop Trash [default: delete] | | | `NNN_TRASH=1` | trash files to the desktop Trash [default: delete] | | ||||
#### Cmdline options | #### Cmdline options | ||||
@@ -195,6 +195,7 @@ optional args: | |||||
-S du mode | -S du mode | ||||
-t disable dir auto-select | -t disable dir auto-select | ||||
-v show version | -v show version | ||||
-x notis, sel to system clipboard | |||||
-h show help | -h show help | ||||
``` | ``` | ||||
@@ -31,6 +31,7 @@ _nnn () | |||||
-S | -S | ||||
-t | -t | ||||
-v | -v | ||||
-x | |||||
-h | -h | ||||
) | ) | ||||
if [[ $prev == -b ]]; then | if [[ $prev == -b ]]; then | ||||
@@ -30,4 +30,5 @@ complete -c nnn -s s -d 'use substring match for filters' | |||||
complete -c nnn -s S -d 'start in disk usage analyzer mode' | complete -c nnn -s S -d 'start in disk usage analyzer mode' | ||||
complete -c nnn -s t -d 'disable dir auto-select' | complete -c nnn -s t -d 'disable dir auto-select' | ||||
complete -c nnn -s v -d 'show program version and exit' | complete -c nnn -s v -d 'show program version and exit' | ||||
complete -c nnn -s x -d 'notis, sel to system clipboard' | |||||
complete -c nnn -s h -d 'show program help' | complete -c nnn -s h -d 'show program help' |
@@ -28,6 +28,7 @@ args=( | |||||
'(-S)-S[start in disk usage analyzer mode]' | '(-S)-S[start in disk usage analyzer mode]' | ||||
'(-t)-t[disable dir auto-select]' | '(-t)-t[disable dir auto-select]' | ||||
'(-v)-v[show program version and exit]' | '(-v)-v[show program version and exit]' | ||||
'(-x)-x[notis, sel to system clipboard]' | |||||
'(-h)-h[show program help]' | '(-h)-h[show program help]' | ||||
'*:filename:_files' | '*:filename:_files' | ||||
) | ) | ||||
@@ -1,23 +0,0 @@ | |||||
#!/usr/bin/env sh | |||||
# Description: Copy selection to clipboard | |||||
# | |||||
# Shell: POSIX compliant | |||||
# Author: Arun Prakash Jana | |||||
SELECTION=${XDG_CONFIG_HOME:-$HOME/.config}/nnn/.selection | |||||
# Linux | |||||
xargs -0 < "$SELECTION" | xsel -bi | |||||
# macOS | |||||
# xargs -0 < "$SELECTION" | pbcopy | |||||
# Termux | |||||
# xargs -0 < "$SELECTION" | termux-clipboard-set | |||||
# Cygwin | |||||
# xargs -0 < "$SELECTION" | clip | |||||
# Wayland | |||||
# xargs -0 < "$SELECTION" | wl-copy |
@@ -23,6 +23,7 @@ | |||||
.Op Ar -s | .Op Ar -s | ||||
.Op Ar -S | .Op Ar -S | ||||
.Op Ar -v | .Op Ar -v | ||||
.Op Ar -x | |||||
.Op Ar -h | .Op Ar -h | ||||
.Op Ar PATH | .Op Ar PATH | ||||
.Sh DESCRIPTION | .Sh DESCRIPTION | ||||
@@ -100,6 +101,9 @@ supports the following options: | |||||
.Fl v | .Fl v | ||||
show version and exit | show version and exit | ||||
.Pp | .Pp | ||||
.Fl x | |||||
show notis on selection cp, mv, rm completion; copy path to system clipboard on select | |||||
.Pp | |||||
.Fl h | .Fl h | ||||
show program help and exit | show program help and exit | ||||
.Sh CONFIGURATION | .Sh CONFIGURATION | ||||
@@ -166,7 +170,7 @@ There are 3 groups of shortcuts to add files to selection: | |||||
.Pp | .Pp | ||||
The selection can now be listed, copied, moved, removed, archived or linked. | The selection can now be listed, copied, moved, removed, archived or linked. | ||||
.Pp | .Pp | ||||
Absolute paths of the selected files are copied to the temporary file \fB.selection\fR in the config directory. The path is shown in the help and configuration screen. If \fB$NNN_COPIER\fR is set (see ENVIRONMENT section below) the file paths are also copied to the system clipboard. | Absolute paths of the selected files are copied to the temporary file \fB.selection\fR in the config directory. The path is shown in the help and configuration screen. | ||||
.Pp | .Pp | ||||
To flush the selection without running any operation use the _edit, flush selection_ key. The list is flushed even if unchanged. Use this key to remove a file from selection after you navigate away from its directory. Flushing doesn't end the selection mode. You can add more files to the selection and edit/flush the list again. Flushing doesn't end the selection mode. You can add more files to the selection and edit/flush the list again. | To flush the selection without running any operation use the _edit, flush selection_ key. The list is flushed even if unchanged. Use this key to remove a file from selection after you navigate away from its directory. Flushing doesn't end the selection mode. You can add more files to the selection and edit/flush the list again. Flushing doesn't end the selection mode. You can add more files to the selection and edit/flush the list again. | ||||
.Pp | .Pp | ||||
@@ -239,8 +243,6 @@ when dealing with the !, e and p commands respectively. A single combination to | |||||
.Pp | .Pp | ||||
\fBNNN_IDLE_TIMEOUT:\fR set idle timeout (in seconds) to invoke terminal locker (default: disabled). | \fBNNN_IDLE_TIMEOUT:\fR set idle timeout (in seconds) to invoke terminal locker (default: disabled). | ||||
.Pp | .Pp | ||||
\fBNNN_COPIER:\fR system clipboard copier script. The project page has some sample copier scripts. | |||||
.Pp | |||||
\fBNNN_TRASH:\fR trash (instead of \fIdelete\fR) files to desktop Trash. | \fBNNN_TRASH:\fR trash (instead of \fIdelete\fR) files to desktop Trash. | ||||
.Bd -literal | .Bd -literal | ||||
export NNN_TRASH=1 | export NNN_TRASH=1 | ||||
@@ -0,0 +1,45 @@ | |||||
#!/usr/bin/env sh | |||||
# Description: Copy selection to system clipboard as newline-separated entries | |||||
# Requires: tr and | |||||
# xclip/xsel (Linux) | |||||
# pbcopy (macOS) | |||||
# termux-clipboard-set (Termux) | |||||
# clip.exe (WSL) | |||||
# clip (Cygwin) | |||||
# wl-copy (Wayland) | |||||
# | |||||
# LIMITATION: breaks if a filename has newline in it | |||||
# | |||||
# Note: For a space-separated list: | |||||
# xargs -0 < "$SELECTION" | |||||
# | |||||
# Shell: POSIX compliant | |||||
# Author: Arun Prakash Jana | |||||
IFS="$(printf '%b_' '\n')"; IFS="${IFS%_}" # protect trailing \n | |||||
SELECTION=${XDG_CONFIG_HOME:-$HOME/.config}/nnn/.selection | |||||
if which xsel >/dev/null 2>&1; then | |||||
# Linux | |||||
tr '\0' '\n' < "$SELECTION" | xsel -bi | |||||
elif which xclip >/dev/null 2>&1; then | |||||
# Linux | |||||
tr '\0' '\n' < "$SELECTION" | xclip -sel clip | |||||
elif which pbcopy >/dev/null 2>&1; then | |||||
# macOS | |||||
tr '\0' '\n' < "$SELECTION" | pbcopy | |||||
elif which termux-clipboard-set >/dev/null 2>&1; then | |||||
# Termux | |||||
tr '\0' '\n' < "$SELECTION" | termux-clipboard-set | |||||
elif which clip.exe >/dev/null 2>&1; then | |||||
# WSL | |||||
tr '\0' '\n' < "$SELECTION" | clip.exe | |||||
elif which clip >/dev/null 2>&1; then | |||||
# Cygwin | |||||
tr '\0' '\n' < "$SELECTION" | clip | |||||
elif which wl-copy >/dev/null 2>&1; then | |||||
# Wayland | |||||
tr '\0' '\n' < "$SELECTION" | wl-copy | |||||
fi |
@@ -227,8 +227,9 @@ typedef struct { | |||||
uint selmode : 1; /* Set when selecting files */ | uint selmode : 1; /* Set when selecting files */ | ||||
uint showdetail : 1; /* Clear to show fewer file info */ | uint showdetail : 1; /* Clear to show fewer file info */ | ||||
uint ctxactive : 1; /* Context active or not */ | uint ctxactive : 1; /* Context active or not */ | ||||
uint reserved : 3; | uint reserved : 2; | ||||
/* The following settings are global */ | /* The following settings are global */ | ||||
uint x11 : 1; /* Copy to system clipboard and show notis */ | |||||
uint curctx : 2; /* Current context number */ | uint curctx : 2; /* Current context number */ | ||||
uint dircolor : 1; /* Current status of dir color */ | uint dircolor : 1; /* Current status of dir color */ | ||||
uint picker : 1; /* Write selection to user-specified file */ | uint picker : 1; /* Write selection to user-specified file */ | ||||
@@ -281,6 +282,7 @@ static settings cfg = { | |||||
0, /* showdetail */ | 0, /* showdetail */ | ||||
1, /* ctxactive */ | 1, /* ctxactive */ | ||||
0, /* reserved */ | 0, /* reserved */ | ||||
0, /* x11 */ | |||||
0, /* curctx */ | 0, /* curctx */ | ||||
0, /* dircolor */ | 0, /* dircolor */ | ||||
0, /* picker */ | 0, /* picker */ | ||||
@@ -310,7 +312,6 @@ static uint idletimeout, selbufpos, lastappendpos, selbuflen; | |||||
static char *bmstr; | static char *bmstr; | ||||
static char *pluginstr; | static char *pluginstr; | ||||
static char *opener; | static char *opener; | ||||
static char *copier; | |||||
static char *editor; | static char *editor; | ||||
static char *enveditor; | static char *enveditor; | ||||
static char *pager; | static char *pager; | ||||
@@ -379,7 +380,8 @@ static bool g_plinit = FALSE; | |||||
#define UTIL_SH 14 | #define UTIL_SH 14 | ||||
#define UTIL_FZF 15 | #define UTIL_FZF 15 | ||||
#define UTIL_FZY 16 | #define UTIL_FZY 16 | ||||
#define UTIL_NOTIFY 17 | #define UTIL_NTFY 17 | ||||
#define UTIL_CBCP 18 | |||||
/* Utilities to open files, run actions */ | /* Utilities to open files, run actions */ | ||||
static char * const utils[] = { | static char * const utils[] = { | ||||
@@ -412,7 +414,8 @@ static char * const utils[] = { | |||||
"sh", | "sh", | ||||
"fzf", | "fzf", | ||||
"fzy", | "fzy", | ||||
".notify", | ".ntfy", | ||||
".cbcp", | |||||
}; | }; | ||||
/* Common strings */ | /* Common strings */ | ||||
@@ -507,18 +510,16 @@ static const char * const messages[] = { | |||||
#define NNN_OPENER 1 | #define NNN_OPENER 1 | ||||
#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 NNNLVL 4 | ||||
#define NNNLVL 5 | #define NNN_PIPE 5 /* strings end here */ | ||||
#define NNN_PIPE 6 /* strings end here */ | #define NNN_USE_EDITOR 6 /* flags begin here */ | ||||
#define NNN_USE_EDITOR 7 /* flags begin here */ | #define NNN_TRASH 7 | ||||
#define NNN_TRASH 8 | |||||
static const char * const env_cfg[] = { | static const char * const env_cfg[] = { | ||||
"NNN_BMS", | "NNN_BMS", | ||||
"NNN_OPENER", | "NNN_OPENER", | ||||
"NNN_CONTEXT_COLORS", | "NNN_CONTEXT_COLORS", | ||||
"NNN_IDLE_TIMEOUT", | "NNN_IDLE_TIMEOUT", | ||||
"NNN_COPIER", | |||||
"NNNLVL", | "NNNLVL", | ||||
"NNN_PIPE", | "NNN_PIPE", | ||||
"NNN_USE_EDITOR", | "NNN_USE_EDITOR", | ||||
@@ -597,6 +598,7 @@ static inline bool getutil(char *util); | |||||
static size_t mkpath(const char *dir, const char *name, char *out); | static size_t mkpath(const char *dir, const char *name, char *out); | ||||
static void updateselbuf(const char *path, char *newpath); | static void updateselbuf(const char *path, char *newpath); | ||||
static char *xgetenv(const char *name, char *fallback); | static char *xgetenv(const char *name, char *fallback); | ||||
static void run_plugin(const char *plugin, char *newpath, const uchar flags); | |||||
/* Functions */ | /* Functions */ | ||||
@@ -1040,7 +1042,8 @@ static void endselection(const char *path, char *newpath) | |||||
if (selbufpos) { /* File path(s) written to the buffer */ | if (selbufpos) { /* File path(s) written to the buffer */ | ||||
writesel(pselbuf, selbufpos - 1); /* Truncate NULL from end */ | writesel(pselbuf, selbufpos - 1); /* Truncate NULL from end */ | ||||
spawn(copier, NULL, NULL, NULL, F_NOTRACE); | if (cfg.x11) | ||||
run_plugin(utils[UTIL_CBCP], newpath, F_NOWAIT | F_NOTRACE); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
@@ -1127,7 +1130,6 @@ static int editselection(void) | |||||
nselected = lines; | nselected = lines; | ||||
writesel(pselbuf, selbufpos - 1); | writesel(pselbuf, selbufpos - 1); | ||||
spawn(copier, NULL, NULL, NULL, F_NOTRACE); | |||||
return 1; | return 1; | ||||
@@ -3631,6 +3633,13 @@ static bool run_selected_plugin(char **path, const char *file, char *newpath, ch | |||||
return TRUE; | return TRUE; | ||||
} | } | ||||
static void run_plugin(const char *plugin, char *newpath, const uchar flags) | |||||
{ | |||||
mkpath(plugindir, plugin, newpath); | |||||
if (!access(newpath, X_OK)) | |||||
spawn(newpath, NULL, NULL, NULL, flags); | |||||
} | |||||
static void launch_app(const char *path, char *newpath) | static void launch_app(const char *path, char *newpath) | ||||
{ | { | ||||
int r = F_NORMAL; | int r = F_NORMAL; | ||||
@@ -4854,7 +4863,8 @@ nochange: | |||||
selbufpos = lastappendpos; | selbufpos = lastappendpos; | ||||
appendfpath(newpath, mkpath(path, dents[cur].name, newpath)); | appendfpath(newpath, mkpath(path, dents[cur].name, newpath)); | ||||
writesel(pselbuf, selbufpos - 1); /* Truncate NULL from end */ | writesel(pselbuf, selbufpos - 1); /* Truncate NULL from end */ | ||||
spawn(copier, NULL, NULL, NULL, F_NOTRACE); | if (cfg.x11) | ||||
run_plugin(utils[UTIL_CBCP], newpath, F_NOWAIT | F_NOTRACE); | |||||
lastappendpos = selbufpos; | lastappendpos = selbufpos; | ||||
selbufpos = utmp; | selbufpos = utmp; | ||||
} | } | ||||
@@ -4938,7 +4948,8 @@ nochange: | |||||
if (selbufpos != utmp) { | if (selbufpos != utmp) { | ||||
writesel(pselbuf, selbufpos - 1); /* Truncate NULL from end */ | writesel(pselbuf, selbufpos - 1); /* Truncate NULL from end */ | ||||
spawn(copier, NULL, NULL, NULL, F_NOTRACE); | if (cfg.x11) | ||||
run_plugin(utils[UTIL_CBCP], newpath, F_NOWAIT | F_NOTRACE); | |||||
/* Restore current selection buffer position */ | /* Restore current selection buffer position */ | ||||
lastappendpos = selbufpos; | lastappendpos = selbufpos; | ||||
selbufpos = utmp; | selbufpos = utmp; | ||||
@@ -4962,7 +4973,8 @@ nochange: | |||||
= (!r ? messages[MSG_0_SELECTED] : messages[MSG_FAILED]); | = (!r ? messages[MSG_0_SELECTED] : messages[MSG_FAILED]); | ||||
printwait(msg, &presel); | printwait(msg, &presel); | ||||
goto nochange; | goto nochange; | ||||
} | } else if (cfg.x11) | ||||
run_plugin(utils[UTIL_CBCP], newpath, F_NOWAIT | F_NOTRACE); | |||||
break; | break; | ||||
case SEL_CP: // fallthrough | case SEL_CP: // fallthrough | ||||
case SEL_MV: // fallthrough | case SEL_MV: // fallthrough | ||||
@@ -4975,8 +4987,8 @@ nochange: | |||||
goto nochange; | goto nochange; | ||||
/* Show notification on operation complete */ | /* Show notification on operation complete */ | ||||
mkpath(plugindir, utils[UTIL_NOTIFY], newpath); | if (cfg.x11) | ||||
spawn(newpath, NULL, NULL, NULL, F_NOWAIT | F_NOTRACE); | run_plugin(utils[UTIL_NTFY], newpath, F_NOWAIT | F_NOTRACE); | ||||
if (ndents) | if (ndents) | ||||
copycurname(); | copycurname(); | ||||
@@ -5479,6 +5491,7 @@ static void usage(void) | |||||
" -S du mode\n" | " -S du mode\n" | ||||
" -t disable dir auto-select\n" | " -t disable dir auto-select\n" | ||||
" -v show version\n" | " -v show version\n" | ||||
" -x notis, sel to system clipboard\n" | |||||
" -h show help\n\n" | " -h show help\n\n" | ||||
"v%s\n%s\n", __func__, VERSION, GENERAL_INFO); | "v%s\n%s\n", __func__, VERSION, GENERAL_INFO); | ||||
} | } | ||||
@@ -5621,7 +5634,7 @@ int main(int argc, char *argv[]) | |||||
bool progress = FALSE; | bool progress = FALSE; | ||||
#endif | #endif | ||||
while ((opt = getopt(argc, argv, "HSKiab:cde:Efnop:rRstvh")) != -1) { | while ((opt = getopt(argc, argv, "HSKiab:cde:Efnop:rRstvxh")) != -1) { | ||||
switch (opt) { | switch (opt) { | ||||
case 'S': | case 'S': | ||||
cfg.blkorder = 1; | cfg.blkorder = 1; | ||||
@@ -5699,6 +5712,9 @@ int main(int argc, char *argv[]) | |||||
case 'v': | case 'v': | ||||
fprintf(stdout, "%s\n", VERSION); | fprintf(stdout, "%s\n", VERSION); | ||||
return _SUCCESS; | return _SUCCESS; | ||||
case 'x': | |||||
cfg.x11 = 1; | |||||
break; | |||||
case 'h': | case 'h': | ||||
usage(); | usage(); | ||||
return _SUCCESS; | return _SUCCESS; | ||||
@@ -5837,9 +5853,6 @@ int main(int argc, char *argv[]) | |||||
if (!set_tmp_path()) | if (!set_tmp_path()) | ||||
return _FAILURE; | return _FAILURE; | ||||
/* Get the clipboard copier, if set */ | |||||
copier = getenv(env_cfg[NNN_COPIER]); | |||||
#ifdef __linux__ | #ifdef __linux__ | ||||
if (!progress) { | if (!progress) { | ||||
cp[5] = cp[4]; | cp[5] = cp[4]; | ||||