wezterm icon indicating copy to clipboard operation
wezterm copied to clipboard

Add `activate-window` command to wezterm cli

Open faeb5 opened this issue 2 years ago • 7 comments

Is your feature request related to a problem? Please describe. It's currently not possible to switch to a different window via wezterm cli. This makes it difficult to switch workspaces via shell scripts. For example, I have a shell script that pipes a few directories into fzf and let's me choose one of them. If I chose one of those directories, I want to create a new workspace for it, set the appropriate working directory and then immediately switch to the new workspace.

Creating a new workspace is already possible via wezterm cli spawn --workspace <WORKSPACE_NAME> ... but wezterm will not switch to the newly created workspace by default, so I have to use SwitchToWorkspace separately.

Describe the solution you'd like I'd like to be able to call wezterm cli activate-window --window-id <WINDOW_ID> to activate an existing window. If the GUI is already running, then it should immediately switch to the given window.

Additional context I would probably - my specific workflow - prefer the SwitchToWorkspace action to have some kind of hook or event that allows me to do something before I switch to the new workspace (for example choosing a folder that will be the working-directory and use the name of that folder as workspace name) in one swoop. But since the wezterm cli already provides commands to activate panes and tabs, I'd guess it makes sense to also provide a command to activate windows.

faeb5 avatar Apr 17 '23 06:04 faeb5

Activating a window with the intent of switching workspace is a bit thorny because you're really wanting to control the GUI (which is what owns the GUI window -> Mux window mapping), but the CLI operates at the Mux level which doesn't have any GUI concepts. There's no established way to achieve this sort of command. The existing activate-pane and activate-tab commands are simpler because the Mux model has a first class parent object (the mux tab and mux window, respectively) which points to the child object. Workspaces are really just labelled windows; there is no parent object, so plumbing this particular thing via the CLI requires a decent amount of design work.

Putting that aside for the moment and focusing on:

would probably - my specific workflow - prefer the SwitchToWorkspace action to have some kind of hook or event that allows me to do something before I switch to the new workspace (for example choosing a folder that will be the working-directory and use the name of that folder as workspace name) in one swoop

Take a look at:

You could use the result of the prompting to initiate a window:perform_action of SwitchToWorkspace with an appropriate name or other parameters.

wez avatar Apr 17 '23 21:04 wez

Sounds very good! Didn't know about the existence of InputSelector, which makes things a lot easier. I guess I'll simpy write a module and handle stuff inside wezterm (which I'd like to do in the first place, anyways).

Thanks a lot! Should I close as not planned, then?

faeb5 avatar Apr 18 '23 07:04 faeb5

We can keep it open for a bit; I may decide to close it later, or I may figure out a way to make this work nicely.

wez avatar Apr 18 '23 15:04 wez

Just wanted to share this script that does what op is describing, in case anyone stumbles across the same problem:

{
    key = "R",
    mods = "SHIFT|SUPER",
    action = wezterm.action_callback(function(window, pane)
      local cmd = [[
      echo "$({
        echo $HOME;
        echo $HOME/.dotfiles;
        echo $HOME/.config/nvim;
        echo $HOME/Documents/Coding/Notes;
        find $HOME/Documents/Coding -mindepth 2 -maxdepth 2 -type d;
      })"
      ]]
      local file = io.popen(cmd)
      local output = file:read("*a")
      file:close()

      local choices = {}
      for directory in string.gmatch(output, "([^\n]+)") do
        table.insert(choices, { label = directory })
      end

      window:perform_action(
        act.InputSelector {
          title = "Workspaces",
          choices = choices,
          action = wezterm.action_callback(function(window, pane, id, label)
            if label then
              window:perform_action(act.SwitchToWorkspace {
                name = label:match("([^/]+)$"),
                spawn = {
                  cwd = label,
                }
              }, pane)
            end
          end),
        },
        pane
      )
    end),
  }

Change whatever is in the echo to your own liking.

williamhCode avatar Jul 19 '23 07:07 williamhCode

I'd like to voice support for an activate-workspace CLI command because I prefer to use tools like fzf to pick a project to open rather than tying myself to a wezterm specific solution.

As a side question, @wez what does the activate-pane command actually do? If I'm in workspace A and activate a pane in workspace B (not currently visible) then my GUI doesn't automatically switch to workspace B to see the newly activated pane. The same is true for activate-tab. I'm wondering if we could just alter those CLI commands to also change the UI focus and achieve the same result as OP wants?

mikeduminy avatar Nov 06 '23 12:11 mikeduminy

Here's how I got it working using what is available today and keeping only the minimum amount of code inside of my wezterm config.

Create a select-project.sh script

Assumes you have fd and fzf

#!/bin/bash

# This script will open a new terminal for the selected project.
# Terminal used: wezterm

# hacky way to switch workspace via the wezterm cli
# source: https://github.com/wez/wezterm/issues/2979#issuecomment-1447519267
# watch discussion: https://github.com/wez/wezterm/discussions/3534
# watch issue: https://github.com/wez/wezterm/issues/3542
wezterm-switch-workspace() {
	args=$(jq -n --arg workspace "$1" --arg cwd "$2" '{"workspace":$workspace,"cwd":$cwd}' | base64)
	printf "\033]1337;SetUserVar=%s=%s\007" switch-workspace $args
}

# List of project roots, all are assumed to be below $HOME
project_roots=(
	"$HOME/Source"
	"$XDG_CONFIG_HOME"
)

# 1. Get all project folders without trailing slash
project_folders=$(fd --min-depth 1 --max-depth 1 -t directory . ${project_roots[@]} | xargs realpath)

# 2. Select a project using fzf (all paths are relative to $HOME)
selected_folder=$(printf '%s\n' "${project_folders[@]}" | sed "s|$HOME/||" | fzf)

# 3. Switch to the workspace by communicating with wezterm
wezterm-switch-workspace $selected_folder $HOME/$selected_folder

Handle the action in wezterm config

local wezterm = require 'wezterm'
local act = wezterm.action

wezterm.on('user-var-changed', function(window, pane, name, value)
  if name == 'switch-workspace' then
    local cmd_context = wezterm.json_parse(value)
    window:perform_action(
      act.SwitchToWorkspace {
        name = cmd_context.workspace,
        spawn = {
          cwd = cmd_context.cwd,
        },
      },
      pane
    )
  end
end)

mikeduminy avatar Nov 06 '23 13:11 mikeduminy

I also would like a way to switch to an existing workspace from the cli. I ran into this while trying to port one of my tmux scripts to wezterm. It has the same use case of selecting a project with fzf and creating a workspace for it.

I ended up with a similar solution before I found this issue. Here is mine in case it will help anyone:

https://github.com/benelan/git-mux/blob/wezterm/lua/git-mux.lua

I set the user var in the shell script here. It checks if stdin is a tty so I can use InputSelector instead of fzf in keymaps, e.g.

    {
      key = "p",
      mods = "LEADER|CTRL",
      action = wezterm.action_callback(require("git-mux").project),
    },

I was able to do the larger half of the script that handles creating/switching to git worktrees and wezterm tabs via the cli, and it would be nice to get the workspace portion there too!

benelan avatar May 02 '24 07:05 benelan