diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index c1b834e5e..f81efc4b3 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -52,6 +52,9 @@ /modules/programs/lesspipe.nix @rycee +/modules/programs/lf.nix @owm111 +/tests/modules/programs/lf @owm111 + /modules/programs/lieer.nix @tadfisher /modules/programs/lsd.nix @marsam diff --git a/modules/misc/news.nix b/modules/misc/news.nix index a8ef25ef1..041c8d478 100644 --- a/modules/misc/news.nix +++ b/modules/misc/news.nix @@ -1474,6 +1474,13 @@ in A new module is available: 'services.mako' ''; } + + { + time = "2020-04-23T19:45:26+00:00"; + message = '' + A new module is available: 'programs.lf' + ''; + } ]; }; } diff --git a/modules/modules.nix b/modules/modules.nix index 40b590483..21db8b373 100644 --- a/modules/modules.nix +++ b/modules/modules.nix @@ -76,6 +76,7 @@ let (loadModule ./programs/keychain.nix { }) (loadModule ./programs/kitty.nix { }) (loadModule ./programs/lesspipe.nix { }) + (loadModule ./programs/lf.nix { }) (loadModule ./programs/lsd.nix { }) (loadModule ./programs/man.nix { }) (loadModule ./programs/matplotlib.nix { }) diff --git a/modules/programs/lf.nix b/modules/programs/lf.nix new file mode 100644 index 000000000..dba3d8d9a --- /dev/null +++ b/modules/programs/lf.nix @@ -0,0 +1,219 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.programs.lf; + + knownSettings = { + anchorfind = types.bool; + color256 = types.bool; + dircounts = types.bool; + dirfirst = types.bool; + drawbox = types.bool; + globsearch = types.bool; + icons = types.bool; + hidden = types.bool; + ignorecase = types.bool; + ignoredia = types.bool; + incsearch = types.bool; + preview = types.bool; + reverse = types.bool; + smartcase = types.bool; + smartdia = types.bool; + wrapscan = types.bool; + wrapscroll = types.bool; + number = types.bool; + relativenumber = types.bool; + findlen = types.int; + period = types.int; + scrolloff = types.int; + tabstop = types.int; + errorfmt = types.str; + filesep = types.str; + ifs = types.str; + promptfmt = types.str; + shell = types.str; + sortby = types.str; + timefmt = types.str; + ratios = types.str; + info = types.str; + shellopts = types.str; + }; + + lfSettingsType = types.submodule { + options = let + opt = name: type: + mkOption { + type = types.nullOr type; + default = null; + visible = false; + }; + in mapAttrs opt knownSettings; + }; +in { + meta.maintainers = [{ github = "owm111"; }]; + + options = { + programs.lf = { + enable = mkEnableOption "lf"; + + settings = mkOption { + type = lfSettingsType; + default = { }; + example = { + tabstop = 4; + number = true; + ratios = "1:1:2"; + }; + description = '' + An attribute set of lf settings. The attribute names and corresponding + values must be among the following supported options. + + + ${concatStringsSep "\n" (mapAttrsToList (n: v: '' + + ${n} + ${v.description} + + '') knownSettings)} + + + See the lf documentation for detailed descriptions of these options. + Note, use previewer to set lf's + previewer option, and + extraConfig for any other option not listed above. + All string options are quoted with double quotes. + ''; + }; + + commands = mkOption { + type = with types; attrsOf (nullOr str); + default = { }; + example = { + get-mime-type = ''%xdg-mime query filetype "$f"''; + open = "$$OPENER $f"; + }; + description = '' + Commands to declare. Commands set to null or an empty string are + deleted. + ''; + }; + + keybindings = mkOption { + type = with types; attrsOf (nullOr str); + default = { }; + example = { + gh = "cd ~"; + D = "trash"; + i = "$less $f"; + U = "!du -sh"; + gg = null; + }; + description = + "Keys to bind. Keys set to null or an empty string are deleted."; + }; + + cmdKeybindings = mkOption { + type = with types; attrsOf (nullOr str); + default = { }; + example = literalExample ''{ "" = "cmd-escape"; }''; + description = '' + Keys to bind to command line commands which can only be one of the + builtin commands. Keys set to null or an empty string are deleted. + ''; + }; + + previewer.source = mkOption { + type = with types; nullOr path; + default = null; + example = literalExample '' + pkgs.writeShellScript "pv.sh" ''' + #!/bin/sh + + case "$1" in + *.tar*) tar tf "$1";; + *.zip) unzip -l "$1";; + *.rar) unrar l "$1";; + *.7z) 7z l "$1";; + *.pdf) pdftotext "$1" -;; + *) highlight -O ansi "$1" || cat "$1";; + esac + ''' + ''; + description = '' + Script or executable to use to preview files. Sets lf's + previewer option. + ''; + }; + + previewer.keybinding = mkOption { + type = with types; nullOr str; + default = null; + example = "i"; + description = '' + Key to bind to the script at previewer.source and + pipe through less. Setting to null will not bind any key. + ''; + }; + + extraConfig = mkOption { + type = types.lines; + default = ""; + example = '' + $mkdir -p ~/.trash + ''; + description = "Custom lfrc lines."; + }; + }; + }; + + config = mkIf cfg.enable { + home.packages = [ pkgs.lf ]; + + xdg.configFile."lf/lfrc".text = let + fmtSetting = k: v: + optionalString (v != null) "set ${ + if isBool v then + "${optionalString (!v) "no"}${k}" + else + "${k} ${if isInt v then toString v else ''"${v}"''}" + }"; + + settingsStr = concatStringsSep "\n" (filter (x: x != "") + (mapAttrsToList fmtSetting + (builtins.intersectAttrs knownSettings cfg.settings))); + + fmtCmdMap = before: k: v: + "${before} ${k}${optionalString (v != null && v != "") " ${v}"}"; + fmtCmd = fmtCmdMap "cmd"; + fmtMap = fmtCmdMap "map"; + fmtCmap = fmtCmdMap "cmap"; + + commandsStr = concatStringsSep "\n" (mapAttrsToList fmtCmd cfg.commands); + keybindingsStr = + concatStringsSep "\n" (mapAttrsToList fmtMap cfg.keybindings); + cmdKeybindingsStr = + concatStringsSep "\n" (mapAttrsToList fmtCmap cfg.cmdKeybindings); + + previewerStr = optionalString (cfg.previewer.source != null) '' + set previewer ${cfg.previewer.source} + ${optionalString (cfg.previewer.keybinding != null) '' + map ${cfg.previewer.keybinding} ''$${cfg.previewer.source} "$f" | less -R + ''} + ''; + in '' + ${settingsStr} + + ${commandsStr} + + ${keybindingsStr} + + ${cmdKeybindingsStr} + + ${previewerStr} + + ${cfg.extraConfig} + ''; + }; +} diff --git a/tests/default.nix b/tests/default.nix index e38975802..00ad24989 100644 --- a/tests/default.nix +++ b/tests/default.nix @@ -33,6 +33,7 @@ import nmt { ./modules/programs/git ./modules/programs/gpg ./modules/programs/kakoune + ./modules/programs/lf ./modules/programs/lieer ./modules/programs/mbsync ./modules/programs/neomutt diff --git a/tests/modules/programs/lf/all-options.nix b/tests/modules/programs/lf/all-options.nix new file mode 100644 index 000000000..a25467a26 --- /dev/null +++ b/tests/modules/programs/lf/all-options.nix @@ -0,0 +1,86 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + pvScript = builtins.toFile "pv.sh" "cat $1"; + expected = builtins.toFile "settings-expected" '' + set icons + set noignorecase + set ratios "2:2:3" + set tabstop 4 + + cmd added :echo "foo" + cmd multiline :{{ + push gg + echo "bar" + push i + }} + cmd removed + + map aa should-be-added + map ab + + cmap should-be-added + cmap + + set previewer ${pvScript} + map i ${"$"}${pvScript} "$f" | less -R + + + + # More config... + + ''; +in { + config = { + programs.lf = { + enable = true; + + cmdKeybindings = { + "" = "should-be-added"; + "" = null; + }; + + commands = { + added = '':echo "foo"''; + removed = null; + multiline = '' + :{{ + push gg + echo "bar" + push i + }}''; + }; + + extraConfig = '' + # More config... + ''; + + keybindings = { + aa = "should-be-added"; + ab = null; + }; + + previewer = { + keybinding = "i"; + source = pvScript; + }; + + settings = { + ignorecase = false; + icons = true; + tabstop = 4; + ratios = "2:2:3"; + }; + }; + + nixpkgs.overlays = + [ (self: super: { lf = pkgs.writeScriptBin "dummy-lf" ""; }) ]; + + nmt.script = '' + assertFileExists home-files/.config/lf/lfrc + assertFileContent home-files/.config/lf/lfrc ${expected} + ''; + }; +} diff --git a/tests/modules/programs/lf/default.nix b/tests/modules/programs/lf/default.nix new file mode 100644 index 000000000..65cf24fcf --- /dev/null +++ b/tests/modules/programs/lf/default.nix @@ -0,0 +1,5 @@ +{ + lf-all-options = ./all-options.nix; + lf-minimal-options = ./minimal-options.nix; + lf-no-pv-keybind = ./no-pv-keybind.nix; +} diff --git a/tests/modules/programs/lf/minimal-options.nix b/tests/modules/programs/lf/minimal-options.nix new file mode 100644 index 000000000..b3c26ba9b --- /dev/null +++ b/tests/modules/programs/lf/minimal-options.nix @@ -0,0 +1,18 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let expected = builtins.toFile "settings-expected" "\n\n\n\n\n\n\n\n\n\n\n"; +in { + config = { + programs.lf = { enable = true; }; + + nixpkgs.overlays = + [ (self: super: { lf = pkgs.writeScriptBin "dummy-lf" ""; }) ]; + + nmt.script = '' + assertFileExists home-files/.config/lf/lfrc + assertFileContent home-files/.config/lf/lfrc ${expected} + ''; + }; +} diff --git a/tests/modules/programs/lf/no-pv-keybind.nix b/tests/modules/programs/lf/no-pv-keybind.nix new file mode 100644 index 000000000..524a41a36 --- /dev/null +++ b/tests/modules/programs/lf/no-pv-keybind.nix @@ -0,0 +1,43 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + pvScript = builtins.toFile "pv.sh" "cat $1"; + expected = builtins.toFile "settings-expected" '' + + + + + + + + + set previewer ${pvScript} + + + + # More config... + + ''; +in { + config = { + programs.lf = { + enable = true; + + extraConfig = '' + # More config... + ''; + + previewer = { source = pvScript; }; + }; + + nixpkgs.overlays = + [ (self: super: { lf = pkgs.writeScriptBin "dummy-lf" ""; }) ]; + + nmt.script = '' + assertFileExists home-files/.config/lf/lfrc + assertFileContent home-files/.config/lf/lfrc ${expected} + ''; + }; +}