diff --git a/README.md b/README.md index 34ba51d1..e5bc4c49 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,7 @@ Add to that an awesome [Wiki](https://github.com/jarun/nnn/wiki)! - Batch renamer (feature-limited) for selection or dir - Copy (as), move (as), delete, archive, link selection - Notification on cp, mv, rm completion + - Copy file paths to system clipboard on select - Create (with parents), rename, duplicate (anywhere) files and dirs - Launch GUI apps, run commands, execute file, spawn a 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_RCLONE_OPTS='rclone mount --read-only'` | specify rclone options | | `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] | #### Cmdline options @@ -195,6 +195,7 @@ optional args: -S du mode -t disable dir auto-select -v show version + -x notis, sel to system clipboard -h show help ``` diff --git a/misc/auto-completion/bash/nnn-completion.bash b/misc/auto-completion/bash/nnn-completion.bash index bddf84f8..00e34d5a 100644 --- a/misc/auto-completion/bash/nnn-completion.bash +++ b/misc/auto-completion/bash/nnn-completion.bash @@ -31,6 +31,7 @@ _nnn () -S -t -v + -x -h ) if [[ $prev == -b ]]; then diff --git a/misc/auto-completion/fish/nnn.fish b/misc/auto-completion/fish/nnn.fish index 36133437..c1f2facc 100644 --- a/misc/auto-completion/fish/nnn.fish +++ b/misc/auto-completion/fish/nnn.fish @@ -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 t -d 'disable dir auto-select' 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' diff --git a/misc/auto-completion/zsh/_nnn b/misc/auto-completion/zsh/_nnn index d5d5c1dc..34db851e 100644 --- a/misc/auto-completion/zsh/_nnn +++ b/misc/auto-completion/zsh/_nnn @@ -28,6 +28,7 @@ args=( '(-S)-S[start in disk usage analyzer mode]' '(-t)-t[disable dir auto-select]' '(-v)-v[show program version and exit]' + '(-x)-x[notis, sel to system clipboard]' '(-h)-h[show program help]' '*:filename:_files' ) diff --git a/misc/clipboard-copier/copier b/misc/clipboard-copier/copier deleted file mode 100755 index 6ac3ba6b..00000000 --- a/misc/clipboard-copier/copier +++ /dev/null @@ -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 diff --git a/nnn.1 b/nnn.1 index cb0f2998..a9fd6629 100644 --- a/nnn.1 +++ b/nnn.1 @@ -23,6 +23,7 @@ .Op Ar -s .Op Ar -S .Op Ar -v +.Op Ar -x .Op Ar -h .Op Ar PATH .Sh DESCRIPTION @@ -100,6 +101,9 @@ supports the following options: .Fl v show version and exit .Pp +.Fl x + show notis on selection cp, mv, rm completion; copy path to system clipboard on select +.Pp .Fl h show program help and exit .Sh CONFIGURATION @@ -166,7 +170,7 @@ There are 3 groups of shortcuts to add files to selection: .Pp The selection can now be listed, copied, moved, removed, archived or linked. .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 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 @@ -239,8 +243,6 @@ when dealing with the !, e and p commands respectively. A single combination to .Pp \fBNNN_IDLE_TIMEOUT:\fR set idle timeout (in seconds) to invoke terminal locker (default: disabled). .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. .Bd -literal export NNN_TRASH=1 diff --git a/plugins/.cbcp b/plugins/.cbcp new file mode 100755 index 00000000..022e88a3 --- /dev/null +++ b/plugins/.cbcp @@ -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 diff --git a/plugins/.notify b/plugins/.ntfy similarity index 100% rename from plugins/.notify rename to plugins/.ntfy diff --git a/src/nnn.c b/src/nnn.c index 7500d01a..c3981796 100644 --- a/src/nnn.c +++ b/src/nnn.c @@ -227,8 +227,9 @@ typedef struct { uint selmode : 1; /* Set when selecting files */ uint showdetail : 1; /* Clear to show fewer file info */ uint ctxactive : 1; /* Context active or not */ - uint reserved : 3; + uint reserved : 2; /* The following settings are global */ + uint x11 : 1; /* Copy to system clipboard and show notis */ uint curctx : 2; /* Current context number */ uint dircolor : 1; /* Current status of dir color */ uint picker : 1; /* Write selection to user-specified file */ @@ -281,6 +282,7 @@ static settings cfg = { 0, /* showdetail */ 1, /* ctxactive */ 0, /* reserved */ + 0, /* x11 */ 0, /* curctx */ 0, /* dircolor */ 0, /* picker */ @@ -310,7 +312,6 @@ static uint idletimeout, selbufpos, lastappendpos, selbuflen; static char *bmstr; static char *pluginstr; static char *opener; -static char *copier; static char *editor; static char *enveditor; static char *pager; @@ -379,7 +380,8 @@ static bool g_plinit = FALSE; #define UTIL_SH 14 #define UTIL_FZF 15 #define UTIL_FZY 16 -#define UTIL_NOTIFY 17 +#define UTIL_NTFY 17 +#define UTIL_CBCP 18 /* Utilities to open files, run actions */ static char * const utils[] = { @@ -412,7 +414,8 @@ static char * const utils[] = { "sh", "fzf", "fzy", - ".notify", + ".ntfy", + ".cbcp", }; /* Common strings */ @@ -507,18 +510,16 @@ static const char * const messages[] = { #define NNN_OPENER 1 #define NNN_CONTEXT_COLORS 2 #define NNN_IDLE_TIMEOUT 3 -#define NNN_COPIER 4 -#define NNNLVL 5 -#define NNN_PIPE 6 /* strings end here */ -#define NNN_USE_EDITOR 7 /* flags begin here */ -#define NNN_TRASH 8 +#define NNNLVL 4 +#define NNN_PIPE 5 /* strings end here */ +#define NNN_USE_EDITOR 6 /* flags begin here */ +#define NNN_TRASH 7 static const char * const env_cfg[] = { "NNN_BMS", "NNN_OPENER", "NNN_CONTEXT_COLORS", "NNN_IDLE_TIMEOUT", - "NNN_COPIER", "NNNLVL", "NNN_PIPE", "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 void updateselbuf(const char *path, char *newpath); static char *xgetenv(const char *name, char *fallback); +static void run_plugin(const char *plugin, char *newpath, const uchar flags); /* Functions */ @@ -1040,7 +1042,8 @@ static void endselection(const char *path, char *newpath) if (selbufpos) { /* File path(s) written to the buffer */ 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; writesel(pselbuf, selbufpos - 1); - spawn(copier, NULL, NULL, NULL, F_NOTRACE); return 1; @@ -3631,6 +3633,13 @@ static bool run_selected_plugin(char **path, const char *file, char *newpath, ch 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) { int r = F_NORMAL; @@ -4854,7 +4863,8 @@ nochange: selbufpos = lastappendpos; appendfpath(newpath, mkpath(path, dents[cur].name, newpath)); 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; selbufpos = utmp; } @@ -4938,7 +4948,8 @@ nochange: if (selbufpos != utmp) { 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 */ lastappendpos = selbufpos; selbufpos = utmp; @@ -4962,7 +4973,8 @@ nochange: = (!r ? messages[MSG_0_SELECTED] : messages[MSG_FAILED]); printwait(msg, &presel); goto nochange; - } + } else if (cfg.x11) + run_plugin(utils[UTIL_CBCP], newpath, F_NOWAIT | F_NOTRACE); break; case SEL_CP: // fallthrough case SEL_MV: // fallthrough @@ -4975,8 +4987,8 @@ nochange: goto nochange; /* Show notification on operation complete */ - mkpath(plugindir, utils[UTIL_NOTIFY], newpath); - spawn(newpath, NULL, NULL, NULL, F_NOWAIT | F_NOTRACE); + if (cfg.x11) + run_plugin(utils[UTIL_NTFY], newpath, F_NOWAIT | F_NOTRACE); if (ndents) copycurname(); @@ -5479,6 +5491,7 @@ static void usage(void) " -S du mode\n" " -t disable dir auto-select\n" " -v show version\n" + " -x notis, sel to system clipboard\n" " -h show help\n\n" "v%s\n%s\n", __func__, VERSION, GENERAL_INFO); } @@ -5621,7 +5634,7 @@ int main(int argc, char *argv[]) bool progress = FALSE; #endif - while ((opt = getopt(argc, argv, "HSKiab:cde:Efnop:rRstvh")) != -1) { + while ((opt = getopt(argc, argv, "HSKiab:cde:Efnop:rRstvxh")) != -1) { switch (opt) { case 'S': cfg.blkorder = 1; @@ -5699,6 +5712,9 @@ int main(int argc, char *argv[]) case 'v': fprintf(stdout, "%s\n", VERSION); return _SUCCESS; + case 'x': + cfg.x11 = 1; + break; case 'h': usage(); return _SUCCESS; @@ -5837,9 +5853,6 @@ int main(int argc, char *argv[]) if (!set_tmp_path()) return _FAILURE; - /* Get the clipboard copier, if set */ - copier = getenv(env_cfg[NNN_COPIER]); - #ifdef __linux__ if (!progress) { cp[5] = cp[4];