From 5893837feeeb0b6077bb93e6fcafde415ab031f8 Mon Sep 17 00:00:00 2001 From: mkonig Date: Wed, 29 Apr 2026 15:48:47 +0200 Subject: [PATCH 01/11] improvements --- plugin/init.lua | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/plugin/init.lua b/plugin/init.lua index 45a64f1..8a6d8c2 100644 --- a/plugin/init.lua +++ b/plugin/init.lua @@ -380,7 +380,7 @@ end --- Create the workspace switcher action --- @return table: WezTerm action callback -local function switch_workspace_action() +function M.switch_workspace_action() return wezterm.action_callback(function(window, pane) local choices = get_all_workspace_choices() @@ -476,17 +476,8 @@ function M.setup(config, opts) end end ---- Apply default keybindings to WezTerm configuration --- @param config table: WezTerm configuration table ---- @param key string|nil: Key to bind (default: "f") ---- @param mods string|nil: Modifiers for keybinding (default: "LEADER") -function M.apply_to_config(config, key, mods) - config.keys = config.keys or {} - table.insert(config.keys, { - key = key or "f", - mods = mods or "LEADER", - action = switch_workspace_action(), - }) +function M.apply_to_config(config) end return M From 4228aed7c73d4373742a00109ff39b6b968c18db Mon Sep 17 00:00:00 2001 From: mkonig Date: Wed, 29 Apr 2026 15:58:47 +0200 Subject: [PATCH 02/11] Switch to previous workspace --- plugin/init.lua | 84 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/plugin/init.lua b/plugin/init.lua index 8a6d8c2..808fbea 100644 --- a/plugin/init.lua +++ b/plugin/init.lua @@ -24,6 +24,27 @@ local default_options = { --- Current options (merged with defaults) local options = {} +-- Track workspace history for "switch to previous workspace" functionality +local last_active_workspace = nil +local current_active_workspace = nil + +--- Helper to safely get the active workspace name for a window +--- @param window table: WezTerm window object +--- @return string|nil: active workspace name or nil +local function get_window_active_workspace(window) + if not window then return nil end + + -- Try window:active_workspace() if available + local ok, name = pcall(function() return window:active_workspace() end) + if ok and type(name) == "string" and name ~= "" then return name end + + -- Fallback to mux.get_active_workspace() + local ok2, name2 = pcall(function() return mux.get_active_workspace() end) + if ok2 and type(name2) == "string" and name2 ~= "" then return name2 end + + return nil +end + --- Expand tilde (~) in paths to home directory --- @param path string: Path that may contain ~ --- @return string|nil: Expanded path or nil if home directory cannot be determined @@ -317,6 +338,16 @@ end --- @param window table: WezTerm window object --- @param pane table: WezTerm pane object local function create_or_switch_workspace(item, window, pane) + -- update history: record current active workspace as last before switching + local active = get_window_active_workspace(window) + if active then + -- only update if different + if current_active_workspace ~= active then + last_active_workspace = current_active_workspace + current_active_workspace = active + end + end + if item.type == "workspace" then -- just switch in case already existing workspace window:perform_action( @@ -325,6 +356,12 @@ local function create_or_switch_workspace(item, window, pane) }), pane ) + -- after switching, update history references + local new_active = item.id + if new_active and new_active ~= current_active_workspace then + last_active_workspace = current_active_workspace + current_active_workspace = new_active + end else -- For new workspaces (not existing ones), create the window first local cwd, err = expand_home_path(item.id) @@ -355,6 +392,13 @@ local function create_or_switch_workspace(item, window, pane) pane ) + -- after switching/creating workspace, update history + local new_active = item.id + if new_active and new_active ~= current_active_workspace then + last_active_workspace = current_active_workspace + current_active_workspace = new_active + end + -- Create tabs and panes if configured if item.tabs and #item.tabs > 0 then create_tabs_with_panes(new_window, item.tabs) end end @@ -423,6 +467,43 @@ function M.switch_workspace_action() end) end +--- Switch to previously active workspace +--- @return table: WezTerm action callback +function M.switch_to_previous_workspace() + return wezterm.action_callback(function(window, pane) + if not last_active_workspace or last_active_workspace == "" then + wezterm.log_info("No previously active workspace recorded") + return + end + + -- Find matching choice among all known workspaces; if not found, treat as name + local choices = get_all_workspace_choices() + local found = nil + for _, choice in ipairs(choices) do + if choice.id == last_active_workspace then + found = choice + break + end + end + + if found then + create_or_switch_workspace(found, window, pane) + else + -- If choice isn't found, attempt a direct switch by name + window:perform_action( + act.SwitchToWorkspace({ name = last_active_workspace }), + pane + ) + -- update history after switch + local new_active = last_active_workspace + if new_active and new_active ~= current_active_workspace then + last_active_workspace = current_active_workspace + current_active_workspace = new_active + end + end + end) +end + --- Validate a single workspace configuration entry --- @param entry table: Configuration entry to validate --- @return boolean: True if valid @@ -474,6 +555,9 @@ function M.setup(config, opts) end end end + + -- initialize current active workspace if available + current_active_workspace = get_window_active_workspace(wezterm.gui and wezterm.gui.get_window and wezterm.gui.get_window() or nil) or mux.get_active_workspace() end --- @param config table: WezTerm configuration table From 44b4048a0670d23218d8abb954c2581a0a046dab Mon Sep 17 00:00:00 2001 From: mkonig Date: Thu, 14 May 2026 16:38:10 +0200 Subject: [PATCH 03/11] feat: Color for list entry types. --- README.md | 35 ++++++++++++++++++++++--- plugin/init.lua | 69 ++++++++++++++++++++++++++++++------------------- 2 files changed, 75 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index ee35650..4a56ae4 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,20 @@ local workspace_picker = wezterm.plugin.require("https://github.com/bugii/worksp workspace_picker.setup({ { path = "~/projects/my-project", type = "directory" }, { path = "~/projects/worktrees", type = "worktreeroot" }, +}, { + -- Optional: customize icons and colors + icons = { + directory = "📁", + worktree = "🌳", + zoxide = "⚡", + workspace = "🖥️", + }, + colors = { + directory = "#61afef", -- Blue + worktree = "#98c379", -- Green + zoxide = "#e5c07b", -- Yellow + workspace = "#c678dd", -- Purple + } }) -- Apply default keybinding (LEADER + f) @@ -80,11 +94,14 @@ workspace_picker.setup({ worktree = "🌳", zoxide = "⚡", workspace = "🖥️", + }, + colors = { + directory = "#61afef", -- Blue + worktree = "#98c379", -- Green + zoxide = "#e5c07b", -- Yellow + workspace = "#c678dd", -- Purple } }) - --- Apply to config with custom keybinding -workspace_picker.apply_to_config(config, "f", "CTRL") ``` ### Direct Workspace Switching @@ -120,6 +137,18 @@ config.keys = { | `direction` | string | No | Split direction of (child) panes: `"Right"` or `"Bottom"` (default: `"Right"`) | | `panes` | table | No | Array of pane configurations | +### Color Configuration + +You can customize the colors used for different workspace types in the picker: + +| Field | Type | Required | Description | +| ------------- | ------ | -------- | ---------------------------------------------------------------------------------- | +| `colors` | table | No | Object mapping workspace types to hex color strings | +| `colors.directory` | string | No | Color for directory workspaces (default: `"#61afef"` - blue) | +| `colors.worktree` | string | No | Color for git worktree workspaces (default: `"#98c379"` - green) | +| `colors.zoxide` | string | No | Color for zoxide directory workspaces (default: `"#e5c07b"` - yellow) | +| `colors.workspace` | string | No | Color for existing Wezterm workspaces (default: `"#c678dd"` - purple) | + ### Pane Configuration | Field | Type | Required | Description | diff --git a/plugin/init.lua b/plugin/init.lua index 808fbea..424daa5 100644 --- a/plugin/init.lua +++ b/plugin/init.lua @@ -19,6 +19,13 @@ local default_options = { zoxide = "⚡", workspace = "🖥️", }, + --- Default colors for different workspace types + colors = { + directory = "#61afef", -- Blue + worktree = "#98c379", -- Green + zoxide = "#e5c07b", -- Yellow + workspace = "#c678dd", -- Purple + }, } --- Current options (merged with defaults) @@ -83,10 +90,20 @@ end --- Create a formatted label for a workspace entry --- @param path string: Path to format --- @param icon string: Icon to use +--- @param workspace_type string: Type of workspace (directory, worktree, zoxide, workspace) --- @return table: WezTerm formatted text -local function format_workspace_label(path, icon) +local function format_workspace_label(path, icon, workspace_type) local display_path = path:gsub(wezterm.home_dir or "", "~") - return wezterm.format({ { Text = icon .. " " .. display_path } }) + + -- Get color from options, fallback to defaults, then to light gray + local color = options.colors and options.colors[workspace_type] + or default_options.colors[workspace_type] + or "#abb2bf" + + return wezterm.format({ + { Foreground = { Color = color } }, + { Text = icon .. " " .. display_path } + }) end --- Get static and worktree-based workspace entries from user configuration @@ -109,12 +126,12 @@ local function get_config_entries() if line:match("^worktree ") then local worktree_path = line:match("^worktree (.+)$") if worktree_path and worktree_path ~= expanded_path then - table.insert(entries, { - id = worktree_path, - label = format_workspace_label(worktree_path, options.icons.worktree), - type = "worktree", - tabs = entry.tabs, - }) + table.insert(entries, { + id = worktree_path, + label = format_workspace_label(worktree_path, options.icons.worktree, "worktree"), + type = "worktree", + tabs = entry.tabs, + }) end end end @@ -126,12 +143,12 @@ local function get_config_entries() if not expanded_path then wezterm.log_error("Failed to expand path '" .. entry.path .. "': " .. (err or "unknown error")) else - table.insert(entries, { - id = expanded_path, - label = format_workspace_label(expanded_path, options.icons.directory), - type = "directory", - tabs = entry.tabs, - }) + table.insert(entries, { + id = expanded_path, + label = format_workspace_label(expanded_path, options.icons.directory, "directory"), + type = "directory", + tabs = entry.tabs, + }) end end end @@ -152,12 +169,12 @@ local function get_zoxide_sessions() for line in output:gmatch("[^\r\n]+") do if line and line ~= "" then - table.insert(sessions, { - id = line, - label = format_workspace_label(line, options.icons.zoxide), - type = "zoxide", - tabs = nil, - }) + table.insert(sessions, { + id = line, + label = format_workspace_label(line, options.icons.zoxide, "zoxide"), + type = "zoxide", + tabs = nil, + }) end end @@ -171,12 +188,12 @@ local function get_existing_workspaces() local names = mux.get_workspace_names() for _, name in ipairs(names or {}) do - table.insert(workspaces, { - id = name, - label = format_workspace_label(name, options.icons.workspace), - type = "workspace", - tabs = nil, - }) + table.insert(workspaces, { + id = name, + label = format_workspace_label(name, options.icons.workspace, "workspace"), + type = "workspace", + tabs = nil, + }) end return workspaces From dde1f47e23647596353de460134689940de28b7c Mon Sep 17 00:00:00 2001 From: mkonig Date: Thu, 14 May 2026 17:31:20 +0200 Subject: [PATCH 04/11] feat: better sort. --- plugin/init.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/plugin/init.lua b/plugin/init.lua index 424daa5..dc76867 100644 --- a/plugin/init.lua +++ b/plugin/init.lua @@ -94,12 +94,12 @@ end --- @return table: WezTerm formatted text local function format_workspace_label(path, icon, workspace_type) local display_path = path:gsub(wezterm.home_dir or "", "~") - + -- Get color from options, fallback to defaults, then to light gray - local color = options.colors and options.colors[workspace_type] - or default_options.colors[workspace_type] + local color = options.colors and options.colors[workspace_type] + or default_options.colors[workspace_type] or "#abb2bf" - + return wezterm.format({ { Foreground = { Color = color } }, { Text = icon .. " " .. display_path } @@ -458,7 +458,7 @@ function M.switch_workspace_action() act.InputSelector({ title = "Select Workspace", choices = selector_choices, - fuzzy = true, + fuzzy = false, action = wezterm.action_callback(function(win, p, id) if not id then return end From ba4b0dd6bebcff010fa15926d59e04d5d88153d7 Mon Sep 17 00:00:00 2001 From: mkonig Date: Thu, 14 May 2026 17:57:33 +0200 Subject: [PATCH 05/11] feat: Option to enable/disable fuzzy --- README.md | 16 +++++++++++++--- plugin/init.lua | 4 +++- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 4a56ae4..3ae4657 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ workspace_picker.setup({ { path = "~/projects/my-project", type = "directory" }, { path = "~/projects/worktrees", type = "worktreeroot" }, }, { - -- Optional: customize icons and colors + -- Optional: customize icons, colors, and fuzzy matching icons = { directory = "📁", worktree = "🌳", @@ -45,7 +45,8 @@ workspace_picker.setup({ worktree = "#98c379", -- Green zoxide = "#e5c07b", -- Yellow workspace = "#c678dd", -- Purple - } + }, + fuzzy = true, -- Enable/disable fuzzy matching (default: true) }) -- Apply default keybinding (LEADER + f) @@ -100,7 +101,8 @@ workspace_picker.setup({ worktree = "#98c379", -- Green zoxide = "#e5c07b", -- Yellow workspace = "#c678dd", -- Purple - } + }, + fuzzy = true, -- Enable/disable fuzzy matching (default: true) }) ``` @@ -149,6 +151,14 @@ You can customize the colors used for different workspace types in the picker: | `colors.zoxide` | string | No | Color for zoxide directory workspaces (default: `"#e5c07b"` - yellow) | | `colors.workspace` | string | No | Color for existing Wezterm workspaces (default: `"#c678dd"` - purple) | +### Fuzzy Search Configuration + +You can enable or disable fuzzy matching in the workspace selector: + +| Field | Type | Required | Description | +| ------- | ------- | -------- | ---------------------------------------------------------------------------------- | +| `fuzzy` | boolean | No | Enable/disable fuzzy matching in workspace selector (default: `true`) | + ### Pane Configuration | Field | Type | Required | Description | diff --git a/plugin/init.lua b/plugin/init.lua index dc76867..11b9aea 100644 --- a/plugin/init.lua +++ b/plugin/init.lua @@ -26,6 +26,8 @@ local default_options = { zoxide = "#e5c07b", -- Yellow workspace = "#c678dd", -- Purple }, + --- Whether to use fuzzy matching in the workspace selector + fuzzy = true, } --- Current options (merged with defaults) @@ -458,7 +460,7 @@ function M.switch_workspace_action() act.InputSelector({ title = "Select Workspace", choices = selector_choices, - fuzzy = false, + fuzzy = options.fuzzy, action = wezterm.action_callback(function(win, p, id) if not id then return end From 1a236f80b4145bfc8385a857510b7881b6966e46 Mon Sep 17 00:00:00 2001 From: mkonig Date: Thu, 14 May 2026 18:38:05 +0200 Subject: [PATCH 06/11] feat: Current workspace at top in fuzzy mode. --- plugin/init.lua | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/plugin/init.lua b/plugin/init.lua index 11b9aea..bed3f33 100644 --- a/plugin/init.lua +++ b/plugin/init.lua @@ -223,6 +223,31 @@ local function get_all_workspace_choices() add_unique_items(get_config_entries()) add_unique_items(get_zoxide_sessions()) + -- When fuzzy mode is enabled, move current workspace to top and previous to second + if options.fuzzy then + local current_item = nil + local previous_item = nil + + for _, item in ipairs(all_items) do + if current_active_workspace and item.id == current_active_workspace then + current_item = item + elseif last_active_workspace and item.id == last_active_workspace then + previous_item = item + end + end + + local reordered = {} + if current_item then table.insert(reordered, current_item) end + if previous_item then table.insert(reordered, previous_item) end + for _, item in ipairs(all_items) do + if (not current_item or item.id ~= current_item.id) and + (not previous_item or item.id ~= previous_item.id) then + table.insert(reordered, item) + end + end + return reordered + end + return all_items end From 844f2fdbf80a5f860f321528f882b1bb8985e07b Mon Sep 17 00:00:00 2001 From: mkonig Date: Thu, 14 May 2026 19:38:25 +0200 Subject: [PATCH 07/11] chore: Adapted readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3ae4657..5554077 100644 --- a/README.md +++ b/README.md @@ -153,11 +153,11 @@ You can customize the colors used for different workspace types in the picker: ### Fuzzy Search Configuration -You can enable or disable fuzzy matching in the workspace selector: +You can enable or disable fuzzy matching in the workspace selector. When fuzzy mode is enabled, the workspace list is reordered so that the current workspace appears at the top and the previously active workspace appears second, making them faster to access. When fuzzy mode is disabled, the original order is preserved. | Field | Type | Required | Description | | ------- | ------- | -------- | ---------------------------------------------------------------------------------- | -| `fuzzy` | boolean | No | Enable/disable fuzzy matching in workspace selector (default: `true`) | +| `fuzzy` | boolean | No | Enable/disable fuzzy matching and workspace reordering (default: `true`) | ### Pane Configuration From dc814e05686abbefa6ac34b4107b39e794b80882 Mon Sep 17 00:00:00 2001 From: mkonig Date: Fri, 15 May 2026 21:44:44 +0200 Subject: [PATCH 08/11] feat: Option for fuzzy sort order. --- plugin/init.lua | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/plugin/init.lua b/plugin/init.lua index bed3f33..d3d6f61 100644 --- a/plugin/init.lua +++ b/plugin/init.lua @@ -28,6 +28,9 @@ local default_options = { }, --- Whether to use fuzzy matching in the workspace selector fuzzy = true, + --- Sort order in fuzzy mode: "current_first" (current at top, previous second), + --- "previous_first" (previous at top, current second), or "none" (no reordering) + fuzzy_sort = "previous_first", } --- Current options (merged with defaults) @@ -223,8 +226,8 @@ local function get_all_workspace_choices() add_unique_items(get_config_entries()) add_unique_items(get_zoxide_sessions()) - -- When fuzzy mode is enabled, move current workspace to top and previous to second - if options.fuzzy then + -- When fuzzy mode is enabled, optionally reorder current/previous workspace + if options.fuzzy and options.fuzzy_sort ~= "none" then local current_item = nil local previous_item = nil @@ -236,12 +239,19 @@ local function get_all_workspace_choices() end end + local first, second + if options.fuzzy_sort == "previous_first" then + first, second = previous_item, current_item + else + first, second = current_item, previous_item + end + local reordered = {} - if current_item then table.insert(reordered, current_item) end - if previous_item then table.insert(reordered, previous_item) end + if first then table.insert(reordered, first) end + if second then table.insert(reordered, second) end for _, item in ipairs(all_items) do - if (not current_item or item.id ~= current_item.id) and - (not previous_item or item.id ~= previous_item.id) then + if (not first or item.id ~= first.id) and + (not second or item.id ~= second.id) then table.insert(reordered, item) end end From 435622d14c5c85314fe4a27aace527286b5315d3 Mon Sep 17 00:00:00 2001 From: mkonig Date: Fri, 15 May 2026 21:48:24 +0200 Subject: [PATCH 09/11] fix: Onyl workspaces at top of list --- plugin/init.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugin/init.lua b/plugin/init.lua index d3d6f61..0c86ca3 100644 --- a/plugin/init.lua +++ b/plugin/init.lua @@ -232,9 +232,9 @@ local function get_all_workspace_choices() local previous_item = nil for _, item in ipairs(all_items) do - if current_active_workspace and item.id == current_active_workspace then + if current_active_workspace and item.id == current_active_workspace and item.type == "workspace" then current_item = item - elseif last_active_workspace and item.id == last_active_workspace then + elseif last_active_workspace and item.id == last_active_workspace and item.type == "workspace" then previous_item = item end end From 174ea135bb81f9eb055d3b2f5016a027a48045c4 Mon Sep 17 00:00:00 2001 From: mkonig Date: Sun, 17 May 2026 11:45:45 +0200 Subject: [PATCH 10/11] doc: fuzzy_sort option --- README.md | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 5554077..5372681 100644 --- a/README.md +++ b/README.md @@ -153,11 +153,17 @@ You can customize the colors used for different workspace types in the picker: ### Fuzzy Search Configuration -You can enable or disable fuzzy matching in the workspace selector. When fuzzy mode is enabled, the workspace list is reordered so that the current workspace appears at the top and the previously active workspace appears second, making them faster to access. When fuzzy mode is disabled, the original order is preserved. +You can enable or disable fuzzy matching in the workspace selector. When fuzzy mode is enabled, the workspace list can be optionally reordered to prioritize the current and previous workspaces. -| Field | Type | Required | Description | -| ------- | ------- | -------- | ---------------------------------------------------------------------------------- | -| `fuzzy` | boolean | No | Enable/disable fuzzy matching and workspace reordering (default: `true`) | +| Field | Type | Required | Description | +| ------------- | ------- | -------- | ---------------------------------------------------------------------------------- | +| `fuzzy` | boolean | No | Enable/disable fuzzy matching and workspace reordering (default: `true`) | +| `fuzzy_sort` | string | No | Sort order in fuzzy mode: `"previous_first"`, `"current_first"`, or `"none"` (default: `"previous_first"`) | + +**`fuzzy_sort` values:** +- `"previous_first"` — Previous active workspace at top, current second +- `"current_first"` — Current workspace at top, previous second +- `"none"` — No reordering (original priority order preserved) ### Pane Configuration From 6c16b25a8aedc9ae0fa7f743c01e5d3b42aac131 Mon Sep 17 00:00:00 2001 From: mkonig Date: Sun, 17 May 2026 11:48:37 +0200 Subject: [PATCH 11/11] doc: fuzzy_sort option --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 5372681..6bc1c9e 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,7 @@ workspace_picker.setup({ workspace = "#c678dd", -- Purple }, fuzzy = true, -- Enable/disable fuzzy matching (default: true) + fuzzy_sort = "previous_first", }) -- Apply default keybinding (LEADER + f) @@ -103,6 +104,7 @@ workspace_picker.setup({ workspace = "#c678dd", -- Purple }, fuzzy = true, -- Enable/disable fuzzy matching (default: true) + fuzzy_sort = "previous_first", }) ```