Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot be installed with Nix [includes PR] #2

Closed
higherorderfunctor opened this issue Jan 11, 2024 · 7 comments
Closed

Cannot be installed with Nix [includes PR] #2

higherorderfunctor opened this issue Jan 11, 2024 · 7 comments
Labels
enhancement New feature or request

Comments

@higherorderfunctor
Copy link
Contributor

Awesome plugin! Appreciate the work to make key binds more accessible.

I am unable to install with Nix as it makes the plugin directory read-only. To remedy this I added some basic support for XDG directories to enable this plugin to be installed and used on immutable or declarative operating systems.

By default this would put he config in ~/.config/tmux/plugins/tmux-which-key and the generated init file in ~/.local/share/tmux/plugins/tmux-which-key.

I also added an example config for installing with Nix via Home Manager. The config can be fully managed via Nix in a declarative manor. The init file can then be written to a directory which is cleared every boot (immutable via temporary file system).

#1

@alexwforsythe
Copy link
Owner

Merged. Sorry for the delay, and thanks for your contribution!

@jasper-clarke
Copy link

@higherorderfunctor
Now the PR has been merged how do I install this plugin for Tmux on NixOS with home manager?

I have this at the moment but when reloading my tmux i get the error plugin/path returned 1

  tmux-which-key = pkgs.tmuxPlugins.mkTmuxPlugin {
    pluginName = "plugin.sh";
    version = "1.0";
    src = pkgs.fetchFromGitHub {
      owner = "alexwforsythe";
      repo = "tmux-which-key";
      rev = "b4cd9d28da4d0a418d2af5f426a0d4b4e544ae10";
      hash = "sha256-ADUgh0sSs1N2AsLC7+LzZ8UPGnmMqvythy97lK4fYgw=";
    };
  };

The path is valid and is being found but something else is going wrong, what is the solution?

@higherorderfunctor
Copy link
Contributor Author

@jasper-at-windswept I can take a look this weekend if my setup is still working. Was working last time I used it, but its been a few weeks.

@higherorderfunctor
Copy link
Contributor Author

higherorderfunctor commented Jun 30, 2024

@jasper-at-windswept there is a basic config in the README. I only used the default config so did not go through the hassle of setting up python to convert the yaml to tmux.

tmux-which-key/README.md

Lines 301 to 336 in b4cd9d2

```nix
{
lib,
pkgs,
...
}: let
tmux-which-key =
pkgs.tmuxPlugins.mkTmuxPlugin
{
pluginName = "tmux-which-key";
version = "2024-01-10";
src = pkgs.fetchFromGitHub {
owner = "alexwforsythe";
repo = "tmux-which-key";
rev = "<commit hash>";
sha256 = lib.fakeSha256;
};
rtpFilePath = "plugin.sh.tmux";
};
in {
xdg.configFile = {
"tmux/plugins/tmux-which-key/config.yaml".text = lib.generators.toYAML {} {
command_alias_start_index = 200;
# rest of config here
};
};
programs.tmux.plugins = [
{
plugin = tmux-which-key;
extraConfig = ''
set -g @tmux-which-key-xdg-enable 1;
'';
}
];
}
```

I decided to work on a full module. I can probably make a flake for this repo to make it easier to install, but it may take me a week or two as I have some other things I am working on. I'll share what I am using now.

This is the module. You can place it wherever in your configuration you have modules.

modules/home-manager/tmux-which-key.nix
{
  config,
  lib,
  pkgs,
  ...
}: let
  cfg = config.programs.tmux.tmux-which-key;
  rtpPath = "tmux-plugins/tmux-which-key";
in {
  options.programs.tmux.tmux-which-key = {
    enable = lib.mkEnableOption "tmux-which-key";
    package = lib.mkPackageOption pkgs ["tmuxPlugins" "tmux-which-key"] {};
    settings = lib.mkOption {
      type = with lib.types; let
        valueType =
          nullOr (oneOf [
            bool
            int
            float
            str
            path
            (attrsOf valueType)
            (listOf valueType)
          ])
          // {
            description = "tmux-which-key configuration value";
          };
      in
        valueType;
      default = let
        fromYaml = file: let
          convertedJson =
            pkgs.runCommandNoCC "config.json" {
              nativeBuildInputs = [pkgs.yj];
            } ''
              ${lib.getExe pkgs.yj} < ${file} > $out
            '';
        in
          builtins.fromJSON (builtins.readFile "${convertedJson}");
      in
        fromYaml "${cfg.package}/share/tmux-plugins/tmux-which-key/config.example.yaml";
    };
  };

  config = let
    configYaml = lib.generators.toYAML {} cfg.settings;
    configTmux =
      pkgs.runCommandNoCC "init.tmux" {
        nativeBuildInputs = cfg.package.propagatedBuildInputs;
      } ''
        set -x
        echo '${configYaml}' > config.yaml
        python3 "${cfg.package}/share/tmux-plugins/tmux-which-key/plugin/build.py" \
          config.yaml $out
      '';
  in
    lib.mkIf cfg.enable {
      xdg = {
        configFile."${rtpPath}/config.yaml".text = configYaml;
        dataFile."${rtpPath}/init.tmux".source = configTmux;
      };
      programs.tmux.plugins = [
        {
          plugin = cfg.package;
          extraConfig = ''
            set -g @tmux-which-key-xdg-enable 1;
            set -g @tmux-which-key-disable-autobuild 1
            set -g @tmux-which-key-xdg-plugin-path "${rtpPath}"
          '';
        }
      ];
    };
}

This is my overlay with python.

overlays/tmux-overlay.nix
_: (final: prev: let
  nv = (import ./nvpkgs.nix)."tmux-which-key";
  inherit (final) python3;
in {
  tmuxPlugins =
    prev.tmuxPlugins
    // {
      tmux-which-key = prev.tmuxPlugins.mkTmuxPlugin {
        pluginName = "tmux-which-key";
        inherit (nv) version;
        propagatedBuildInputs = [
          python3
        ];
        src = builtins.fetchGit {
          inherit (nv.src) url rev;
        };
        preInstall = ''
          rm -rf plugin/pyyaml
          cp -r ${python3.pkgs.pyyaml.src} plugin/pyyaml
        '';
        rtpFilePath = "plugin.sh.tmux";
      };
    };
})

I use nvfetcher to track repos without a flake.nix which is what the nv is in the above. You can hard code the src values like you shared though also.

nvfetcher.toml
[tmux-which-key]
src.git = "https://github.com/alexwforsythe/tmux-which-key"
src.use_commit = true
src.branch = "main"
fetch.git = "https://github.com/alexwforsythe/tmux-which-key"

To configure I have something like this which is just the default YAML converted to nix attrsets. This is optional if using the default config.

home/{{user}}/features/cli/tmux/tmux-which-key-config.nix
{
  command_alias_start_index = 200;
  keybindings = {
    root_table = "C-Space";
    prefix_table = "Space";
  };
  title = {
    style = "align=centre,bold";
    prefix = "tmux";
    prefix_style = "fg=green,align=centre,bold";
  };
  position = {
    x = "R";
    y = "P";
  };
  custom_variables = [
    {
      name = "log_info";
      value = "#[fg=green,italics] [info]#[default]#[italics]";
    }
  ];
  macros = [
    {
      name = "reload-config";
      commands = [
        "display \"#{log_info} Loading config... \""
        "source-file $HOME/.tmux.conf"
        "display -p \"\\n\\n... Press ENTER to continue\""
      ];
    }
    {
      name = "restart-pane";
      commands = [
        "display \"#{log_info} Restarting pane\""
        "respawnp -k -c #{pane_current_path}"
      ];
    }
  ];
  items = [
    {
      name = "Run";
      key = "space";
      command = "command-prompt";
    }
    {
      name = "Last window";
      key = "tab";
      command = "last-window";
    }
    {
      name = "Last pane";
      key = "`";
      command = "last-pane";
    }
    {
      name = "Copy";
      key = "c";
      menu = [
        {
          name = "Copy";
          key = "c";
          command = "copy-mode";
        }
        {
          name = "List buffers";
          key = "#";
          command = "list-buffers";
        }
      ];
    }
    {separator = true;}
    {
      name = "+Windows";
      key = "w";
      menu = [
        {
          name = "Last";
          key = "tab";
          command = "last-window";
        }
        {
          name = "Choose";
          key = "w";
          command = "choose-tree -Zw";
        }
        {
          name = "Previous";
          key = "p";
          command = "previous-window";
        }
        {
          name = "Next";
          key = "n";
          command = "next-window";
        }
        {
          name = "New";
          key = "c";
          command = "neww -c #{pane_current_path}";
        }
        {separator = true;}
        {
          name = "+Layout";
          key = "l";
          menu = [
            {
              name = "Next";
              key = "l";
              command = "nextl";
              transient = true;
            }
            {
              name = "Tiled";
              key = "t";
              command = "selectnl tiled";
            }
            {
              name = "Horizontal";
              key = "h";
              command = "selectl even-horizontal";
            }
            {
              name = "Vertical";
              key = "v";
              command = "selectl even-vertical";
            }
            {
              name = "Horizontal main";
              key = "H";
              command = "selectl main-horizontal";
            }
            {
              name = "Vertical main";
              key = "V";
              command = "selectl main-vertical";
            }
          ];
        }
        {
          name = "Split horiztonal";
          key = "/";
          command = "splitw -h -c #{pane_current_path}";
        }
        {
          name = "Split vertical";
          key = "-";
          command = "splitw -v -c #{pane_current_path}";
        }
        {
          name = "Rotate";
          key = "o";
          command = "rotatew";
          transient = true;
        }
        {
          name = "Rotate reverse";
          key = "O";
          command = "rotatew -D";
          transient = true;
        }
        {separator = true;}
        {
          name = "Rename";
          key = "R";
          command = "command-prompt -I \"#W\" \"renamew -- \\\"%%\\\"\"";
        }
        {
          name = "Kill";
          key = "X";
          command = "confirm -p \"Kill window #W? (y/n)\" killw";
        }
      ];
    }
    {
      name = "+Panes";
      key = "p";
      menu = [
        {
          name = "Last";
          key = "tab";
          command = "lastp";
        }
        {
          name = "Choose";
          key = "p";
          command = "displayp -d 0";
        }
        {separator = true;}
        {
          name = "Left";
          key = "h";
          command = "selectp -L";
        }
        {
          name = "Down";
          key = "j";
          command = "selectp -D";
        }
        {
          name = "Up";
          key = "k";
          command = "selectp -U";
        }
        {
          name = "Right";
          key = "l";
          command = "selectp -R";
        }
        {separator = true;}
        {
          name = "Zoom";
          key = "z";
          command = "resizep -Z";
        }
        {
          name = "+Resize";
          key = "r";
          menu = [
            {
              name = "Left";
              key = "h";
              command = "resizep -L";
              transient = true;
            }
            {
              name = "Down";
              key = "j";
              command = "resizep -D";
              transient = true;
            }
            {
              name = "Up";
              key = "k";
              command = "resizep -U";
              transient = true;
            }
            {
              name = "Right";
              key = "l";
              command = "resizep -R";
              transient = true;
            }
            {
              name = "Left more";
              key = "H";
              command = "resizep -L 10";
              transient = true;
            }
            {
              name = "Down more";
              key = "J";
              command = "resizep -D 10";
              transient = true;
            }
            {
              name = "Up more";
              key = "K";
              command = "resizep -U 10";
              transient = true;
            }
            {
              name = "Right more";
              key = "L";
              command = "resizep -R 10";
              transient = true;
            }
          ];
        }
        {
          name = "Swap left";
          key = "H";
          command = "swapp -t \"{left-of}\"";
        }
        {
          name = "Swap down";
          key = "J";
          command = "swapp -t \"{down-of}\"";
        }
        {
          name = "Swap up";
          key = "K";
          command = "swapp -t \"{up-of}\"";
        }
        {
          name = "Swap right";
          key = "L";
          command = "swapp -t \"{right-of}\"";
        }
        {
          name = "Break";
          key = "!";
          command = "break-pane";
        }
        {separator = true;}
        {
          name = "Mark";
          key = "m";
          command = "selectp -m";
        }
        {
          name = "Unmark";
          key = "M";
          command = "selectp -M";
        }
        {
          name = "Capture";
          key = "C";
          command = "capture-pane";
        }
        {
          name = "Respawn pane";
          key = "R";
          macro = "restart-pane";
        }
        {
          name = "Kill";
          key = "X";
          command = "confirm -p \"Kill pane #P? (y/n)\" killp";
        }
      ];
    }
    {
      name = "+Buffers";
      key = "b";
      menu = [
        {
          name = "Choose";
          key = "b";
          command = "choose-buffer -Z";
        }
        {
          name = "List";
          key = "l";
          command = "lsb";
        }
        {
          name = "Paste";
          key = "p";
          command = "pasteb";
        }
      ];
    }
    {
      name = "+Sessions";
      key = "s";
      menu = [
        {
          name = "Choose";
          key = "s";
          command = "choose-tree -Zs";
        }
        {
          name = "New";
          key = "N";
          command = "new";
        }
        {
          name = "Rename";
          key = "r";
          command = "rename";
        }
      ];
    }
    {
      name = "+Client";
      key = "C";
      menu = [
        {
          name = "Choose";
          key = "c";
          command = "choose-client -Z";
        }
        {
          name = "Last";
          key = "l";
          command = "switchc -l";
        }
        {
          name = "Previous";
          key = "p";
          command = "switchc -p";
        }
        {
          name = "Next";
          key = "n";
          command = "switchc -n";
        }
        {separator = true;}
        {
          name = "Refresh";
          key = "R";
          command = "refresh";
        }
        {
          name = "+Plugins";
          key = "P";
          menu = [
            {
              name = "Install";
              key = "i";
              command = "run-shell $TMUX_PLUGIN_MANAGER_PATH/tpm/bindings/install_plugins";
            }
            {
              name = "Update";
              key = "u";
              command = "run-shell $TMUX_PLUGIN_MANAGER_PATH/tpm/bindings/update_plugins";
            }
            {
              name = "Clean";
              key = "c";
              command = "run-shell $TMUX_PLUGIN_MANAGER_PATH/tpm/bindings/clean_plugins";
            }
          ];
        }
        {
          name = "Detach";
          key = "D";
          command = "detach";
        }
        {
          name = "Suspend";
          key = "Z";
          command = "suspendc";
        }
        {separator = true;}
        {
          name = "Reload config";
          key = "r";
          macro = "reload-config";
        }
        {
          name = "Customize";
          key = ",";
          command = "customize-mode -Z";
        }
      ];
    }
    {separator = true;}
    {
      name = "Time";
      key = "T";
      command = "clock-mode";
    }
    {
      name = "Show messages";
      key = "~";
      command = "show-messages";
    }
    {
      name = "+Keys";
      key = "?";
      command = "list-keys -N";
    }
  ];
}

Finally, to enable, my tmux config now looks like this.

home/{{user}}/features/cli/tmux/default.nix
  imports = [
    ../../../../../modules/home-manager/tmux-which-key.nix
  ];

  programs = {
    tmux = {
      enable = true;
      tmux-which-key = {
        enable = true;
        settings = import ./tmux-which-key-config.nix;  # optional if using default config
      };
  };
}

My repo is a bit messy as I usually only have enough bandwidth to add things here and there, but the full setup is in my dotfiles branch of my nixos-config repo: https://github.com/higherorderfunctor/nixos-config/tree/dotfiles.

@higherorderfunctor
Copy link
Contributor Author

higherorderfunctor commented Jun 30, 2024

@jasper-at-windswept, I decided to just bang out the flake support and put in a PR: #9. Instructions are here currently: https://github.com/higherorderfunctor/tmux-which-key/tree/feat/adds-nix-flake?tab=readme-ov-file#nix-with-home-manager-flake-installation.

If you want to test it out early, you should just be able to replace all instances of github:alexwforsythe/tmux-which-key with github:higherorderfunctor/tmux-which-key?ref=feat/adds-nix-flake in the short term. So something like below as your input and nix run "github:higherorderfunctor/tmux-which-key?ref=feat/adds-nix-flake#generate-config" to generate a config if going that configuration method.

    tmux-which-key = {
      url = "github:higherorderfunctor/tmux-which-key?ref=feat/adds-nix-flake";
      inputs.nixpkgs.follows = "nixpkgs";
    };

I've updated my persona nixos-config to use this method for now.

@jasper-clarke
Copy link

Hey @higherorderfunctor
Thanks so much for that.
What is the generate config method just wondering?
I'm not quite sure how configuring this plugin works yet since I haven't been able to install it.

@higherorderfunctor
Copy link
Contributor Author

@jasper-at-windswept it just converts the example config in the repo from YAML to Nix. Essentially it goes YAML -> JSON -> Nix.

You can use straight YAML also. I put an example in the README.

Can also just not supply a config at all to use the defaults.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants