WezTerm Sessionizer #4796
Replies: 10 comments 28 replies
-
Neat! Consider using https://wezfurlong.org/wezterm/config/lua/wezterm/GLOBAL.html for the cache; read that page for more details on why! |
Beta Was this translation helpful? Give feedback.
-
Thanks for sharing this! If |
Beta Was this translation helpful? Give feedback.
-
@FrancescElies shared a good tip for making Here's the most up to date simplified script I ended up settling with: local wezterm = require("wezterm")
local act = wezterm.action
local M = {}
local fd = "/path/to/fd"
local rootPath = "/full/path/to/search"
M.toggle = function(window, pane)
local projects = {}
local success, stdout, stderr = wezterm.run_child_process({
fd,
"-HI",
"-td",
"^.git$",
"--max-depth=4",
rootPath,
-- add more paths here
})
if not success then
wezterm.log_error("Failed to run fd: " .. stderr)
return
end
for line in stdout:gmatch("([^\n]*)\n?") do
local project = line:gsub("/.git/$", "")
local label = project
local id = project:gsub(".*/", "")
table.insert(projects, { label = tostring(label), id = tostring(id) })
end
window:perform_action(
act.InputSelector({
action = wezterm.action_callback(function(win, _, id, label)
if not id and not label then
wezterm.log_info("Cancelled")
else
wezterm.log_info("Selected " .. label)
win:perform_action(
act.SwitchToWorkspace({ name = id, spawn = { cwd = label } }),
pane
)
end
end),
fuzzy = true,
title = "Select project",
choices = projects,
}),
pane
)
end
return M |
Beta Was this translation helpful? Give feedback.
-
Hey for anyone doing this add --prune to the fd command to improve performance. Behaves the same as long as you don't have a git repo in a .git directory (I don't see why one would have that) |
Beta Was this translation helpful? Give feedback.
-
@keturiosakys I also ended up modifying mine too and changed how I handled bare repositories and some stuff for windows. Below mine in case it helps, feedback appreciated. --platform.lua
local w = require 'wezterm'
local M = {}
return {
is_win = string.find(w.target_triple, 'windows') ~= nil,
is_linux = string.find(w.target_triple, 'linux') ~= nil,
is_mac = string.find(w.target_triple, 'apple') ~= nil,
} --sessionizer.lua
local w = require 'wezterm'
local platform = require 'platform'
local act = w.action
local M = {}
--- Converts Windows backslash to forwardslash
---@param path string
local function normalize_path(path) return platform.is_win and path:gsub('\\', '/') or path end
local home = normalize_path(w.home_dir)
--- If name nil or false print err_message
---@param name string|boolean|nil
---@param err_message string
local function err_if_not(name, err_message)
if not name then
w.log_error(err_message)
end
end
--
--- path if file or directory exists nil otherwise
---@param path string
local function file_exists(path)
if path == nil then
return nil
end
local f = io.open(path, 'r')
-- io.open won't work to check if directories exist,
-- but works for symlinks and regular files
if f ~= nil then
w.log_info(path .. ' file or symlink found')
io.close(f)
return path
end
return nil
end
-------------------------------------------------------
-- PATHS
--
local fd = (
file_exists(home .. '/bin/fd')
or file_exists 'usr/bin/fd'
or file_exists(home .. '/bin/fd.exe')
or file_exists '/ProgramData/chocolatey/bin/fd.exe'
)
err_if_not(fd, 'fd not found')
local git = (file_exists '/usr/bin/git' or file_exists '/Program Files/Git/cmd/git.exe')
err_if_not(git, 'git not found')
local srcPath = home .. '/src'
err_if_not(srcPath, srcPath .. ' not found')
local search_folders = {
srcPath,
srcPath .. '/work',
srcPath .. '/other',
}
-------------------------------------------------------
--- Merge numeric tables
---@param t1 table
---@param t2 table
---@return table
local function merge_tables(t1, t2)
local result = {}
for index, value in ipairs(t1) do
result[index] = value
end
for index, value in ipairs(t2) do
result[#t1 + index] = value
end
return result
end
M.start = function(window, pane)
local projects = {}
-- assumes ~/src/www, ~/src/work to exist
-- ~/src
-- ├──nushell-config # toplevel config stuff
-- ├──wezterm-config
-- ├──work # work stuff
-- ├──work/project.git # git bare clones marked with .git at the end
-- ├──work/project-bugfix # worktree of project.git
-- ├──work/project-feature # worktree of project.git
-- │ └───31 unlisted
-- └──other # 3rd party project
-- └──103 unlisted
local cmd = merge_tables({ fd, '-HI', '-td', '--max-depth=1', '.' }, search_folders)
w.log_info 'cmd: '
w.log_info(cmd)
for _, value in ipairs(cmd) do
w.log_info(value)
end
local success, stdout, stderr = w.run_child_process(cmd)
if not success then
w.log_error('Failed to run fd: ' .. stderr)
return
end
for line in stdout:gmatch '([^\n]*)\n?' do
local project = normalize_path(line)
local label = project
local id = project
table.insert(projects, { label = tostring(label), id = tostring(id) })
end
window:perform_action(
act.InputSelector {
action = w.action_callback(function(win, _, id, label)
if not id and not label then
w.log_info 'Cancelled'
else
w.log_info('Selected ' .. label)
win:perform_action(act.SwitchToWorkspace { name = id, spawn = { cwd = label } }, pane)
end
end),
fuzzy = true,
title = 'Select project',
choices = projects,
},
pane
)
end
return M |
Beta Was this translation helpful? Give feedback.
-
Optimized further to work with regular and bare repos. My edit, searches for files or folders named ".git". This allows This means you can eg,
Additionally a bit of path cleaning, so labels are shown as relative paths from local wezterm = require("wezterm")
local act = wezterm.action
local M = {}
local fd = "/opt/homebrew/bin/fd"
local lsrc = "/Users/tjex/.local/src"
local dev = "/Users/tjex/dev"
-- from https://github.com/wez/wezterm/discussions/4796
M.open = function(window, pane)
local projects = {}
local home = os.getenv("HOME") .. "/"
local success, stdout, stderr = wezterm.run_child_process({
fd,
"-HI",
".git$",
"--max-depth=4",
"--prune",
lsrc,
dev,
-- add more paths here
})
if not success then
wezterm.log_error("Failed to run fd: " .. stderr)
return
end
-- define variables from from file paths extractions and
-- fill table with results
for line in stdout:gmatch("([^\n]*)\n?") do
-- create label from file path
local project = line:gsub("/.git.*", "")
project = project:gsub("/$", "")
local label = project:gsub(home, "")
-- extract id. Used for workspace name
local _, _, id = string.find(project, ".*/(.+)")
id = id:gsub(".git", "") -- bare repo dirs typically end in .git, remove if so.
table.insert(projects, { label = tostring(label), id = tostring(id) })
end
-- update previous_workspace before changing to new workspace.
wezterm.GLOBAL.previous_workspace = window:active_workspace()
window:perform_action(
act.InputSelector({
action = wezterm.action_callback(function(win, _, id, label)
if not id and not label then
wezterm.log_info("Cancelled")
else
wezterm.log_info("Selected " .. label)
win:perform_action(
act.SwitchToWorkspace({
name = id,
spawn = { cwd = home .. label },
}),
pane
)
end
end),
fuzzy = true,
title = "Select project",
choices = projects,
}),
pane
)
end
return M
|
Beta Was this translation helpful? Give feedback.
-
Has anyone managed to then build on the new workspace? I usually open my editor and create a second tab with just a terminal. I can't seem to figure out how to do that with the callback. |
Beta Was this translation helpful? Give feedback.
-
Is there a way to set the active tab of a newly created workspace to a default string value? Additionally how would you update the logic to only do this when creating non-existing workspaces and not when switching to already existing workspaces? |
Beta Was this translation helpful? Give feedback.
-
Hey, for anyone interested I created a small plugin that does the essential part of this over at mikkasendke/sessionizer.wezterm. It works for what I need it to but could probably be significantly extended and improved. Feel free to use or contribute if you want to. |
Beta Was this translation helpful? Give feedback.
-
Anyone able to use this in WSL2 when their wezterm gui/config is in regular windows? |
Beta Was this translation helpful? Give feedback.
-
Since moving from
tmux
to WezTerm native multiplexer the only thing I missed was thetmux-sessionizer
script from ThePrimeagen that I have adopted as core to my workflow. So I rebuilt it in Lua in WezTerm natively.How it works
It's basically a carbon clone of the original
tmux-sessionizer
script with a couple of notable exceptions:fd
as the underlying search engine (it's faster thanfind
)..git
repositories (including worktrees) as opposed to folders at a certain level of nesting. I found that git repositories are my basic unit of work.Installing
In your
~/.config/wezterm
folder create asessionizer.lua
and make sure to update thefd
androotPath
values according to your system.And then in your main
wezterm.lua
config require thesessionizer.lua
and assign keybindings of your preference. Example:Beta Was this translation helpful? Give feedback.
All reactions