Trying to setup Tmux plugin that requires python3 and libtmux - derivation help

Hey folks, long time reader, first time poster! Just made an account for this :sweat_smile:

Anyway, I’m tryign to setup this tmux plugin that’s not available in nixpkgs called tmux-window-name which automatically changes window names depending on which application you’ve got open, which file in nvim is open, etc. Its really useful!

Anyway, I’ve got a custom tmux plugin i’m using as input to the rest of my tmux config like this:

{ pkgs, input, ... }:
let
  tmux-window-name = pkgs.tmuxPlugins.mkTmuxPlugin {
    pluginName = "tmux-window-name";
    version = "2024-03-08";
    src = pkgs.fetchFromGitHub {
      owner = "ofirgall";
      repo = "tmux-window-name";
      rev = "34026b6f442ceb07628bf25ae1b04a0cd475e9ae";
      sha256 = "sha256-BNgxLk/BkaQkGlB4g2WKVs39y4VHL1Y2TdTEoBy7yo0=";
    };
    postInstall = ''
      find $target -type f -print0 | xargs -0 sed -i -e 's|python3 |${pkgs.python3}/bin/python3 |g'
    '';
    rtpFilePath = "tmux_window_name.tmux";
  };
in

Separately, I have these python packages installed:

{ pkgs, input, ... }:
{
  environment.systemPackages = with pkgs; [
    python311Full
    python311Packages.requests
    python311Packages.libtmux
    python311Packages.pip
    python311Packages.pipx
  ];
}

The tmux plugin tmux-window-name gets installed correctly, but then when I go to reload tmux and use it, it does some odd manual checking with pip to ensure that libtmux is available in their tmux_window_name.tmux file (i.e. like python3 -m pip list). And this is where it’s throwing and exiting.

If I take the generated command out of the final tmux_window_name.tmux file in my store and run it (/nix/store/8k4p193rnzy3rqjkv19abz3b738l25z9-python3-3.11.6/bin/python3 -m pip list ) I get an error message: no module named pip.

I also saw some other packages in tmuxPlugins do a wrapProgram thing where they make a few separate programs available in 1 which sounds kinda like what I’m trying to accomplish here, but I’ve just seen that for the first time and have no idea if it might work here.

Anyway, long story short - does any one have any examples of a tmux plugin derivation which uses python and libtmux and how to wire that all up together with the nix store paths?

1 Like

Something like this should work, hopefully, but I’m not sure how to add it to my nixos/hm config. This is my how far I’ve gotten.

{
  fetchFromGitHub,
  lib,
  tmuxPlugins,
  python3,
  pkgs,
}:
tmuxPlugins.mkTmuxPlugin {
  pluginName = "tmux-window-name";
  version = "2024-03-08";
  src = fetchFromGitHub {
    owner = "ofirgall";
    repo = "tmux-window-name";
    rev = "34026b6f442ceb07628bf25ae1b04a0cd475e9ae";
    sha256 = "sha256-BNgxLk/BkaQkGlB4g2WKVs39y4VHL1Y2TdTEoBy7yo0=";
  };
  nativeBuildInputs = [pkgs.makeWrapper];
  postInstall = ''
    for f in scripts/path_utils.py scripts/rename_session_windows.py; do
      wrapProgram $target/scripts/$f \
        --prefix PATH : ${with pkgs;
      lib.makeBinPath (python3.withPackages (python-pkgs: [
        python-pkgs.libtmux
      ]))}
    done
  '';
  rtpFilePath = "tmux_window_name.tmux";
}
1 Like

@tetov thanks for chiming in, that looks pretty good, two things though:

  • The rtp file, tmux_window_name.tmux also has a check for libtmux and python3 call in it, so I think we’ll need to wrap it or patch it too
  • Running your example as is gives me an error about getting a set but expecting a list, but I can’t figure out what option its referring to haha, everything looks good to me :thinking:

Anyway, to use something liek this, I’ve been doing this:

{ pkgs, lib, unstablePkgs, input, ... }:
let
  tmux-window-name = pkgs.tmuxPlugins.mkTmuxPlugin {
    pluginName = "tmux-window-name";
    version = "2024-03-08";
    src = pkgs.fetchFromGitHub {
      owner = "ofirgall";
      repo = "tmux-window-name";
      rev = "34026b6f442ceb07628bf25ae1b04a0cd475e9ae";
      sha256 = "sha256-BNgxLk/BkaQkGlB4g2WKVs39y4VHL1Y2TdTEoBy7yo0=";
    };
    nativeBuildInputs = [ pkgs.makeWrapper ];
    rtpFilePath = "tmux_window_name.tmux";
    postInstall = ''
      for f in scripts/path_utils.py scripts/rename_session_windows.py; do
        wrapProgram $target/scripts/$f \
          --prefix PATH : ${with pkgs; lib.makeBinPath (
          python3.withPackages (python-pkgs: [
            python-pkgs.libtmux
          ])
        )}
      done
    '';
  };
in
{
  programs.tmux = {
    enable = true;
    package = unstablePkgs.tmux;
    clock24 = true;
    keyMode = "vi";
    newSession = true;
    historyLimit = 10000;
    prefix = "C-a";
    plugins = [
      {
        plugin = tmux-window-name;
      }
    ];
  };
}

@tetov I got it to work! (Mostly :sweat_smile:). It correctly sets directory names when you’re in a split, but the feature where it would rename it to an nvim filename, for example when nvim is focused when switching windows, isn’t working. There’s a fn in the pyhton file which normally strips /usr/bin/.. from the binary name and now its /home/user/.nix-profile/.., etc. so that part needs modified too. Anyway, here’s my basic working version:

{ pkgs, lib, unstablePkgs, input, ... }:
let
  pythonInputs = (pkgs.python3.withPackages (p: with p; [
    libtmux
    pip
  ]));
  tmux-window-name = pkgs.tmuxPlugins.mkTmuxPlugin {
    pluginName = "tmux-window-name";
    version = "2024-03-08";
    src = pkgs.fetchFromGitHub {
      owner = "ofirgall";
      repo = "tmux-window-name";
      rev = "34026b6f442ceb07628bf25ae1b04a0cd475e9ae";
      sha256 = "sha256-BNgxLk/BkaQkGlB4g2WKVs39y4VHL1Y2TdTEoBy7yo0=";
    };
    nativeBuildInputs = [ pkgs.makeWrapper ];
    rtpFilePath = "tmux_window_name.tmux";
    postInstall = ''
      for f in tmux_window_name.tmux scripts/rename_session_windows.py; do
        wrapProgram $target/$f \
          --prefix PATH : ${lib.makeBinPath [pythonInputs]}
      done
    '';
  };
in
{
  programs.tmux = {
    enable = true;
    package = unstablePkgs.tmux;
    clock24 = true;
    keyMode = "vi";
    newSession = true;
    historyLimit = 10000;
    prefix = "C-a";
    plugins = [
      tmux-window-name
    ];
  };
}

So I’ve added a sed line before the for loop doing the wrapping, and it does build, but doesn’t seem to execute the sed replace correctly. Although it worked in testing :thinking:

I’m tryign to replace line 25 in scripts/rename_session_windows.py which is:

USR_BIN_REMOVER = (r'^(/usr)?/bin/(.+)', r'\g<2>')

And basically add another regex to the array, r'^/home/ndo/.nix-profile/(.+)', r'\g<2>')

Here’s the line I added right before the for loop doing the wrapping which doesn’t seem to work…

sed -i "s|^USR_BIN_REMOVER|USR_BIN_REMOVER = (r\'^(/usr)?/bin/(.+)\', r\'^/home/ndo/.nix-profile/(.+)\', r\'\\g<2>\')|" $target/scripts/rename_session_windows.py

Alrightttt, got it! :joy:

{ pkgs, config, lib, unstablePkgs, input, ... }:
let
  pythonInputs = (pkgs.python3.withPackages (p: with p; [
    libtmux
    pip
  ]));
  tmux-window-name = pkgs.tmuxPlugins.mkTmuxPlugin {
    pluginName = "tmux-window-name";
    version = "2024-03-08";
    src = pkgs.fetchFromGitHub {
      owner = "ofirgall";
      repo = "tmux-window-name";
      rev = "34026b6f442ceb07628bf25ae1b04a0cd475e9ae";
      sha256 = "sha256-BNgxLk/BkaQkGlB4g2WKVs39y4VHL1Y2TdTEoBy7yo0=";
    };
    nativeBuildInputs = [ pkgs.makeWrapper ];
    rtpFilePath = "tmux_window_name.tmux";
    postInstall = ''
      # Update USR_BIN_REMOVER with .nix-profile PATH
      sed -i "s|^USR_BIN_REMOVER.*|USR_BIN_REMOVER = (r\'^/home/${config.home.username}/.nix-profile/bin/(.+)( --.*)?\', r\'\\\g<1>\')|" $target/scripts/rename_session_windows.py

      # Update substitute_sets with .nix-profile PATHs
      sed -i "s|^\ssubstitute_sets: List.*|    substitute_sets: List[Tuple] = field(default_factory=lambda: [(\'/home/${config.home.username}/.nix-profile/bin/(.+) --.*\', \'\\\g<1>\'), (r\'.+ipython([32])\', r\'ipython\\\g<1>\'), USR_BIN_REMOVER, (r\'(bash) (.+)/(.+[ $])(.+)\', \'\\\g<3>\\\g<4>\')])|" $target/scripts/rename_session_windows.py

      # Update dir_programs with .nix-profile PATH for applications
      sed -i "s|^\sdir_programs: List.*|    dir_programs: List[str] = field(default_factory=lambda: [['/home/${config.home.username}/.nix-profile/bin/vim', '/home/${config.home.username}/.nix-profile/bin/vi', '/home/${config.home.username}/.nix-profile/bin/git', '/home/${config.home.username}/.nix-profile/bin/nvim']])|" $target/scripts/rename_session_windows.py

      for f in tmux_window_name.tmux scripts/rename_session_windows.py; do
        wrapProgram $target/$f \
          --prefix PATH : ${lib.makeBinPath [pythonInputs]}
      done
    '';
  };
in
{
  programs.tmux = {
    enable = true;
    package = unstablePkgs.tmux;
    clock24 = true;
    keyMode = "vi";
    newSession = true;
    historyLimit = 10000;
    prefix = "C-a";
    plugins = [
      tmux-window-name
    ];
    extraConfig = ''
      # tmux-window-name
      set -g @tmux_window_name_log_level "'DEBUG'"
      # set -g @tmux_window_name_substitute_sets "[('/home/${config.home.username}/.nix-profile/bin/(.+) --.*', '\\g<1>')]"
      # set -g @tmux_window_name_dir_programs "['nvim', 'vim', 'vi', 'git', '/home/${config.home.username}/.nix-profile/bin/nvim']"
    '';
  };
}

So as you can see, there are 3 sed subtitutions in the postInstall step. The first one, for USR_BIN_REMOVER is definitely required afaik. But the following two (substitute_sets and dir_programs) can theoretically be replaced with some @tmux_window_name_* settings. I’ve kept them commented out in the extraConfig option if anyone wants to use those instead of experiemnt with the setup, etc.

Anyway, this seems to do the trick for me! Renames normal shell windows to just the current working directory, and then stuff like nvim windows are renamed to nvim:$(pwd) basically :tada:

I’m going to try and submit this to the tmuxPlugins nixpkg file

EDIT: nixpkgs PR open, it’s my first one so feel free to point out anythign I may have missed haha - tmuxPlugins.tmux-window-name: init at unstable by ndom91 · Pull Request #296174 · NixOS/nixpkgs · GitHub