This repository has no description
7.9 kB
213 lines
1{ inputs, ... }:
2{
3 perSystem =
4 { pkgs, lib, ... }:
5 let
6 # nnn's nerd icon table hardcodes xterm-256 colours, which would be the
7 # one part of the UI that ignores the terminal theme. Remap them onto the
8 # ANSI palette; 0 means "use the file's own colour".
9 iconColors = {
10 COLOR_ARCHIVE = 1;
11 COLOR_AUDIO = 3;
12 COLOR_AUDIO1 = 13;
13 COLOR_C = 12;
14 COLOR_CSS = 5;
15 COLOR_DOCS = 9;
16 COLOR_DOCUMENT = 0;
17 COLOR_ELIXIR = 5;
18 COLOR_FSHARP = 6;
19 COLOR_IMAGE = 10;
20 COLOR_JAVA = 4;
21 COLOR_JAVASCRIPT = 3;
22 COLOR_LUA = 4;
23 COLOR_PYTHON = 11;
24 COLOR_REACT = 14;
25 COLOR_RUBY = 1;
26 COLOR_SCALA = 9;
27 COLOR_SHELL = 2;
28 COLOR_VIDEO = 14;
29 COLOR_VIDEO1 = 11;
30 COLOR_VIM = 2;
31 };
32
33 # Anything outside 0-15 left in the table means nnn grew an icon colour
34 # we have not mapped, so fail the build instead of shipping a stray hue.
35 unmapped = "COLOR_X\\(COLOR_[A-Z0-9_]+, *(1[6-9]|[2-9][0-9]|[0-9]{3})\\)";
36
37 nnn = (pkgs.nnn.override { withNerdIcons = true; }).overrideAttrs (old: {
38 postPatch = (old.postPatch or "") + ''
39 sed -i -E ${
40 lib.concatStringsSep " " (
41 lib.mapAttrsToList (
42 name: code: "-e 's/COLOR_X\\(${name},[^)]*\\)/COLOR_X(${name}, ${toString code})/'"
43 ) iconColors
44 )
45 } src/icons.h
46 if grep -nE '${unmapped}' src/icons.h; then
47 echo "nnn icon colours above are outside the ANSI palette" >&2
48 exit 1
49 fi
50 '';
51 });
52
53 # Asks the terminal which graphics protocol it speaks. Run once per nnn
54 # session so no image preview has to wait on a terminal round trip.
55 term-caps = pkgs.writeShellApplication {
56 name = "nnn-term-caps";
57 runtimeInputs = [ pkgs.coreutils ];
58 text = builtins.readFile ./term-caps.sh;
59 };
60
61 imgpreview = pkgs.writeShellApplication {
62 name = "nnn-imgpreview";
63 runtimeInputs = [
64 pkgs.chafa
65 pkgs.coreutils
66 ];
67 text = builtins.readFile ./imgpreview.sh;
68 };
69
70 # Tools the preview/open plugins probe for at runtime. They are prefixed
71 # onto $PATH rather than replacing it, so a tmux/kitty already in the
72 # environment stays reachable.
73 pluginTools = [
74 imgpreview
75 pkgs.bat
76 pkgs.coreutils
77 pkgs.exiftool
78 pkgs.fd
79 pkgs.ffmpegthumbnailer
80 pkgs.file
81 pkgs.findutils
82 pkgs.fzf
83 pkgs.gnutar
84 pkgs.jq
85 pkgs.less
86 pkgs.libarchive
87 pkgs.librsvg
88 pkgs.mdcat
89 pkgs.mediainfo
90 pkgs.ncurses
91 pkgs.poppler-utils
92 pkgs.procps
93 pkgs.tree
94 pkgs.unzip
95 pkgs.viu
96 pkgs.w3m
97 ];
98
99 # nnn resolves bare plugin names inside $XDG_CONFIG_HOME/nnn/plugins,
100 # which we do not own, so the plugins live here and are referenced by
101 # absolute path from NNN_PLUG.
102 plugins =
103 let
104 pathLine = ''export PATH="${lib.makeBinPath pluginTools}:$PATH"'';
105 in
106 pkgs.runCommand "nnn-plugins" { } ''
107 mkdir -p $out/libexec/nnn
108 cp ${nnn}/share/plugins/.nnn-plugin-helper $out/libexec/nnn/
109 for plugin in preview-tui fzcd fzopen nuke; do
110 install -m755 ${nnn}/share/plugins/"$plugin" $out/libexec/nnn/"$plugin"
111 sed -i '2i ${pathLine}' $out/libexec/nnn/"$plugin"
112 done
113
114 # glow renders markdown with a fixed hex palette and, as of 2.1.2,
115 # emits no colour at all outside a tty. mdcat renders to plain ANSI
116 # attributes, so markdown previews follow the terminal like the rest.
117 substituteInPlace $out/libexec/nnn/preview-tui \
118 --replace-fail 'md) if exists glow; then' 'md) if exists mdcat; then' \
119 --replace-fail 'fifo_pager glow -s dark "$1"' 'fifo_pager mdcat --columns "$cols" "$1"'
120 substituteInPlace $out/libexec/nnn/nuke \
121 --replace-fail 'if type glow >/dev/null 2>&1; then' 'if type mdcat >/dev/null 2>&1; then' \
122 --replace-fail 'glow -sdark "''${FPATH}" | eval "$PAGER"' 'mdcat "''${FPATH}" | eval "$PAGER"'
123
124 # NNN_IMGFORMAT is decided before nnn starts, and preview-tui only
125 # forwards the variables on this list into the preview pane.
126 substituteInPlace $out/libexec/nnn/preview-tui \
127 --replace-fail \
128 '"NNN_PREVIEWIMGPROG=''${NNN_PREVIEWIMGPROG:-}"' \
129 '"NNN_PREVIEWIMGPROG=''${NNN_PREVIEWIMGPROG:-}" "NNN_IMGFORMAT=''${NNN_IMGFORMAT:-symbols}"'
130 '';
131
132 # Every colour below is an xterm-256 index in 0x00-0x0f, i.e. an entry of
133 # the terminal's own ANSI palette. Retheming the terminal - including
134 # switch-theme repainting a local kitty while nnn runs on the far end of
135 # an ssh session - recolours nnn without nnn or the remote host being
136 # involved at all.
137 #
138 # Order is fixed, see nnn(1) NNN_FCOLORS.
139 fileColors = lib.concatStrings [
140 "0b" # block device bright yellow
141 "0b" # char device bright yellow
142 "0c" # directory bright blue
143 "0a" # executable bright green
144 "00" # regular file terminal default
145 "05" # hard link magenta
146 "0e" # symlink bright cyan
147 "08" # file details bright black
148 "09" # orphaned symlink bright red
149 "03" # fifo yellow
150 "0d" # socket bright magenta
151 "01" # unknown / empty red
152 ];
153
154 # 8 contexts as 256-colour indices, with the 8-colour fallback after ';'
155 # for terminals that never report 256 colour support.
156 contextColors = "#0c0a0d0e0b090605;42563165";
157 in
158 {
159 packages.nnn = inputs.wrappers.lib.wrapPackage {
160 inherit pkgs;
161 package = nnn;
162 aliases = [ "n" ];
163 runtimeInputs = [ term-caps ];
164 env = {
165 NNN_FCOLORS = fileColors;
166 NNN_COLORS = contextColors;
167 # bat ships an "ansi" theme that only emits palette colours
168 NNN_BATTHEME = "ansi";
169 NNN_BATSTYLE = "numbers";
170 NNN_OPENER = "${plugins}/libexec/nnn/nuke";
171 NNN_PREVIEWIMGPROG = lib.getExe imgpreview;
172 NNN_PLUG = lib.concatStringsSep ";" [
173 "p:${plugins}/libexec/nnn/preview-tui"
174 "f:${plugins}/libexec/nnn/fzcd"
175 "o:${plugins}/libexec/nnn/fzopen"
176 ];
177 };
178 preHook = ''
179 # Ask the terminal once, on a tty nothing else is drawing on yet.
180 NNN_IMGFORMAT=$(nnn-term-caps)
181 export NNN_IMGFORMAT
182
183 # nuke only reaches for graphical applications when a display exists.
184 if [ -n "''${WAYLAND_DISPLAY:-}" ] || [ -n "''${DISPLAY:-}" ]; then
185 export GUI=1
186 fi
187
188 # preview-tui splits the current pane; with no multiplexer and no
189 # remote-controllable terminal it falls back to spawning $NNN_TERMINAL
190 # (xterm), which is useless over ssh. Only auto-open the preview where
191 # it can actually split.
192 nnn_extra=()
193 if [ -n "''${TMUX:-}" ] || [ -n "''${KITTY_LISTEN_ON:-}" ] ||
194 [ -n "''${WEZTERM_PANE:-}" ] || [ -n "''${ZELLIJ:-}" ]; then
195 nnn_extra+=(-P p)
196 fi
197 '';
198 # -a: auto NNN_FIFO for the preview plugin, -c: NNN_OPENER is cli-only
199 wrapper =
200 {
201 exePath,
202 envString,
203 preHook,
204 ...
205 }:
206 ''
207 ${envString}
208 ${preHook}
209 exec ${exePath} -a -c "''${nnn_extra[@]}" "$@"
210 '';
211 };
212 };
213}