diff --git a/Makefile b/Makefile index 3c0e08a7..004c5248 100644 --- a/Makefile +++ b/Makefile @@ -57,6 +57,10 @@ ifeq ($(O_NOBATCH),1) CPPFLAGS += -DNOBATCH endif +ifeq ($(O_NOFIFO),1) + CPPFLAGS += -DNOFIFO +endif + ifeq ($(shell $(PKG_CONFIG) ncursesw && echo 1),1) CFLAGS_CURSES ?= $(shell $(PKG_CONFIG) --cflags ncursesw) LDLIBS_CURSES ?= $(shell $(PKG_CONFIG) --libs ncursesw) diff --git a/nnn.1 b/nnn.1 index 5048bb07..95e93a64 100644 --- a/nnn.1 +++ b/nnn.1 @@ -423,6 +423,13 @@ separated by \fI;\fR: .Pp \fBNNN_SEL:\fR absolute path to custom selection file. .Pp +\fBNNN_FIFO:\fR path of a named pipe to write current file path: +.Bd -literal + export NNN_FIFO='/tmp/nnn.fifo' + + NOTE: If the FIFO file doesn't exist it will be created, but it will never be removed. +.Ed +.Pp \fBnnn:\fR this is a special variable set to the hovered entry before executing a command from the command prompt or spawning a shell. .Pp diff --git a/plugins/README.md b/plugins/README.md index e8f89585..40386bb9 100644 --- a/plugins/README.md +++ b/plugins/README.md @@ -190,7 +190,7 @@ Usage examples can be found in the Examples section below. There are many plugins provided by `nnn` which can be used as examples. Here are a few simple selected examples. - Show the git log of changes to the particular file along with the code for a quick and easy review. - ```sh + ```sh #!/usr/bin/env sh git log -p -- "$1" ``` @@ -220,6 +220,40 @@ There are many plugins provided by `nnn` which can be used as examples. Here are printf "%s" "0c$dir" > "$NNN_PIPE" ``` +#### Get notified on file hover + +If `NNN_FIFO` is set, `nnn` will open it and write every hovered files. +This can be used in plugins, e.g. to implement file previews. + +If a `NNN_FIFO` is set globally, each `nnn` instance will write to it, and a process reading from the pipe will get hovered path from every instance, interleaved. + +If you want to prevent this and be sure to have a private pipe to one `nnn` instance, you can unlink (remove) the FIFO file. +If you opened the FIFO before and you have read from it (so that `nnn` have it opened too), you can still read from it while you don't close it. +But new `nnn` instances will recreate a new FIFO not linked to the previous one. + +Don't forget to fork in the background to avoid blocking `nnn`. + +Example (send every hovered file to X selection): + +```sh +#!/usr/bin/env sh +if [ -z "$NNN_FIFO" ] ; then + exit 1 +fi + +while read FILE ; do + if [ -n "$NNN_FIFO" ] ; then + # If you want to remove the FIFO, + # don't do it before first read, + # nnn won't have it opened yet + rm "$NNN_FIFO" + NNN_FIFO= + fi + printf "%s" "$FILE" | xsel +done < "$NNN_FIFO" & +disown +``` + ## Contributing plugins 1. Add informative sections like _Description_, _Notes_, _Dependencies_, _Shell_, _Author_ etc. in the plugin. diff --git a/src/nnn.c b/src/nnn.c index 19adf930..e6463931 100644 --- a/src/nnn.c +++ b/src/nnn.c @@ -330,6 +330,9 @@ static context g_ctx[CTX_MAX] __attribute__ ((aligned)); static int ndents, cur, last, curscroll, last_curscroll, total_dents = ENTRY_INCR; static int nselected; +#ifndef NOFIFO +static int fifofd = -1; +#endif static uint idletimeout, selbufpos, lastappendpos, selbuflen; static ushort xlines, xcols; static ushort idle; @@ -350,6 +353,9 @@ static char *prefixpath; static char *plugindir; static char *sessiondir; static char *pnamebuf, *pselbuf; +#ifndef NOFIFO +static char *fifopath; +#endif static ull *ihashbmp; static struct entry *dents; static blkcnt_t ent_blocks; @@ -4667,6 +4673,40 @@ static void populate(char *path, char *lastname) last_curscroll = -1; } +#ifndef NOFIFO +static void notify_fifo() +{ + if (fifofd == -1) { + fifofd = open(fifopath, O_WRONLY|O_NONBLOCK); + if (fifofd == -1) { + if (errno != ENXIO) + /* Unexpected error, the FIFO file might have been removed */ + /* We give up FIFO notification */ + fifopath = NULL; + return; + } + } + + static char *name = NULL; + + if (dents[cur].name == name) + return; + + name = dents[cur].name; + + char path[PATH_MAX]; + size_t len = mkpath(g_ctx[cfg.curctx].c_path, ndents ? name : "", path); + + path[len - 1] = '\n'; + + ssize_t ret = write(fifofd, path, len); + + if (ret != (ssize_t)len && !(ret == -1 && (errno == EAGAIN || errno == EPIPE))) { + DPRINTF_S(strerror(errno)); + } +} +#endif + static void move_cursor(int target, int ignore_scrolloff) { int onscreen = xlines - 4; /* Leave top 2 and bottom 2 lines */ @@ -4693,6 +4733,11 @@ static void move_cursor(int target, int ignore_scrolloff) } curscroll = MIN(curscroll, MIN(cur, ndents - onscreen)); curscroll = MAX(curscroll, MAX(cur - (onscreen - 1), 0)); + +#ifndef NOFIFO + if (fifopath) + notify_fifo(); +#endif } static void handle_screen_move(enum action sel) @@ -7022,6 +7067,19 @@ int main(int argc, char *argv[]) DPRINTF_S(getenv("PWD")); +#ifndef NOFIFO + /* Create fifo */ + fifopath = getenv("NNN_FIFO"); + if (fifopath) { + if (mkfifo(fifopath, 0600) != 0 && !(errno == EEXIST && access(fifopath, W_OK) == 0)) { + xerror(); + return _FAILURE; + } + + signal(SIGPIPE, SIG_IGN); + } +#endif + #ifdef LINUX_INOTIFY /* Initialize inotify */ inotify_fd = inotify_init1(IN_NONBLOCK); @@ -7145,5 +7203,10 @@ int main(int argc, char *argv[]) haiku_close_nm(haiku_hnd); #endif +#ifndef NOFIFO + if (fifofd != -1) + close(fifofd); +#endif + return opt; }