From a77bb47294e0a31cde129007401de6ec28b08b66 Mon Sep 17 00:00:00 2001 From: Daniel Kongsgaard Date: Thu, 31 Aug 2023 03:30:04 +0200 Subject: [PATCH 01/19] First step towards implementing #4226. --- wezterm-gui/src/overlay/selector.rs | 41 ++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/wezterm-gui/src/overlay/selector.rs b/wezterm-gui/src/overlay/selector.rs index 51903a410f5..27ab9068023 100644 --- a/wezterm-gui/src/overlay/selector.rs +++ b/wezterm-gui/src/overlay/selector.rs @@ -26,6 +26,7 @@ struct SelectorState { always_fuzzy: bool, args: InputSelector, event_name: String, + alphabet: String, } impl SelectorState { @@ -88,6 +89,8 @@ impl SelectorState { ]; let max_items = self.max_items; + let alphabet_len = self.alphabet.len(); + let mut alphabet_chars = self.alphabet.chars(); for (row_num, (entry_idx, entry)) in self .filtered_entries @@ -107,8 +110,12 @@ impl SelectorState { attr.set_reverse(true); } - if row_num < 9 && !self.filtering { - changes.push(Change::Text(format!(" {}. ", row_num + 1))); + + // from above we know that row_num <= max_items + if row_num < alphabet_len && !self.filtering { + if let Some(c) = alphabet_chars.next() { + changes.push(Change::Text(format!(" {}. ", c ))); + } } else { changes.push(Change::Text(" ".to_string())); } @@ -178,25 +185,44 @@ impl SelectorState { } fn run_loop(&mut self, term: &mut TermWizTerminal) -> anyhow::Result<()> { + let max_items = self.max_items; + let alphabet = self.alphabet.to_lowercase(); + let alphabet_has_j = alphabet.contains("j"); + let alphabet_has_k = alphabet.contains("k"); + let mut alphabet_chars = alphabet.chars(); while let Ok(Some(event)) = term.poll_input(None) { match event { InputEvent::Key(KeyEvent { key: KeyCode::Char(c), - .. - }) if !self.filtering && c >= '1' && c <= '9' => { - if self.launch(self.top_row + (c as u32 - '1' as u32) as usize) { - break; + modifiers: Modifiers::NONE, + }) if !self.filtering && alphabet.contains(c) => { + if let Some(pos) = alphabet_chars.position(|x| x == c) { + if pos as usize <= max_items && self.launch(self.top_row + pos as usize) { + break; + } } } InputEvent::Key(KeyEvent { key: KeyCode::Char('j'), .. - }) if !self.filtering => { + }) if !self.filtering && !alphabet_has_j => { self.move_down(); } InputEvent::Key(KeyEvent { key: KeyCode::Char('k'), .. + }) if !self.filtering && !alphabet_has_k => { + self.move_up(); + } + InputEvent::Key(KeyEvent { + key: KeyCode::Char('J'), + .. + }) if !self.filtering => { + self.move_down(); + } + InputEvent::Key(KeyEvent { + key: KeyCode::Char('K'), + .. }) if !self.filtering => { self.move_up(); } @@ -368,6 +394,7 @@ pub fn selector( always_fuzzy: args.fuzzy, args, event_name, + alphabet: "1234567890abcdefghijklmnopqrstuvxyz".to_string(), }; term.set_raw_mode()?; From 5974746346b21d257aab3cc0e9ed7106c12503ce Mon Sep 17 00:00:00 2001 From: Daniel Kongsgaard Date: Thu, 31 Aug 2023 16:25:12 +0200 Subject: [PATCH 02/19] Added InputSelector config options. --- config/src/keyassignment.rs | 16 +++++++ .../config/lua/keyassignment/InputSelector.md | 15 +++++- wezterm-gui/src/overlay/selector.rs | 47 ++++++++++++++----- 3 files changed, 64 insertions(+), 14 deletions(-) diff --git a/config/src/keyassignment.rs b/config/src/keyassignment.rs index 3d656479559..b185b358b7e 100644 --- a/config/src/keyassignment.rs +++ b/config/src/keyassignment.rs @@ -477,8 +477,24 @@ pub struct InputSelector { #[dynamic(default)] pub fuzzy: bool, + + // Overrides the main input_select_alphabet config + #[dynamic(default = "default_num_alphabet")] + pub alphabet: String, + + #[dynamic(default = "default_description")] + pub description: String, } +fn default_num_alphabet() -> String { + "1234567890qwertyuiopasdfghjklzxcvbnm".to_string() +} + +fn default_description() -> String { + "Select an item and press Enter = accept, Esc = cancel, / = filter".to_string() +} + + #[derive(Debug, Clone, PartialEq, FromDynamic, ToDynamic)] pub enum KeyAssignment { SpawnTab(SpawnTabDomain), diff --git a/docs/config/lua/keyassignment/InputSelector.md b/docs/config/lua/keyassignment/InputSelector.md index 3576b04cff5..36bc7229c02 100644 --- a/docs/config/lua/keyassignment/InputSelector.md +++ b/docs/config/lua/keyassignment/InputSelector.md @@ -22,6 +22,16 @@ upon the input. objects from the current pane and window, and `id` and `label` hold the corresponding fields from the selected choice. Both will be `nil` if the overlay is cancelled without selecting anything. +* `fuzzy` - a boolean that defaults to `false`. If `true`, InputSelector will start + in its fuzzy finding mode (this is equivalent to starting the InputSelector and + pressing / in the default mode). +* `alphabet` - a string of unique characters. The characters in the string are used + to calculate one or two click shortcuts that can be used to quickly choose from + the InputSelector when not in fuzzy finding mode. Defaults to: + `"1234567890qwertyuiopasdfghjklzxcvbnm"`. +* `fuzzy_description` - a string to display when in fuzzy finding mode. Defaults to: + `"Select an item and press Enter = accept, Esc = cancel, / = filter"`. + ## Example of choosing some canned text to enter into the terminal @@ -44,6 +54,7 @@ config.keys = { end end), title = 'I am title', + fuzzy = true, choices = { -- This is the first entry { @@ -95,7 +106,7 @@ config.keys = { -- could read or compute data from other sources local choices = {} - for n = 1, 10 do + for n = 1, 20 do table.insert(choices, { label = tostring(n) }) end @@ -113,6 +124,8 @@ config.keys = { end), title = 'I am title', choices = choices, + alphabet = '123456789', + description = 'Write the number you want to choose.', }, pane ) diff --git a/wezterm-gui/src/overlay/selector.rs b/wezterm-gui/src/overlay/selector.rs index 27ab9068023..c52f6e1e6e9 100644 --- a/wezterm-gui/src/overlay/selector.rs +++ b/wezterm-gui/src/overlay/selector.rs @@ -11,6 +11,7 @@ use termwiz::input::{InputEvent, KeyCode, KeyEvent, Modifiers, MouseButtons, Mou use termwiz::surface::{Change, Position}; use termwiz::terminal::Terminal; use termwiz_funcs::truncate_right; +use super::quickselect::compute_labels_for_alphabet; const ROW_OVERHEAD: usize = 3; @@ -26,7 +27,7 @@ struct SelectorState { always_fuzzy: bool, args: InputSelector, event_name: String, - alphabet: String, + selection: String, } impl SelectorState { @@ -70,6 +71,7 @@ impl SelectorState { fn render(&mut self, term: &mut TermWizTerminal) -> termwiz::Result<()> { let size = term.get_screen_size()?; let max_width = size.cols.saturating_sub(6); + let desc = &self.args.description; let mut changes = vec![ Change::ClearScreen(ColorAttribute::Default), @@ -80,8 +82,7 @@ impl SelectorState { Change::Text(format!( "{}\r\n", truncate_right( - "Select an item and press Enter=accept \ - Esc=cancel /=filter", + desc, max_width ) )), @@ -89,8 +90,15 @@ impl SelectorState { ]; let max_items = self.max_items; - let alphabet_len = self.alphabet.len(); - let mut alphabet_chars = self.alphabet.chars(); + let alphabet = &self.args.alphabet; + let mut labels = compute_labels_for_alphabet(alphabet, max_items+1) + .into_iter(); + let labels_len = labels.len(); + let max_label_len = labels + .clone() + .map(|s| s.len()) + .max() + .unwrap_or(0); for (row_num, (entry_idx, entry)) in self .filtered_entries @@ -110,12 +118,20 @@ impl SelectorState { attr.set_reverse(true); } - // from above we know that row_num <= max_items - if row_num < alphabet_len && !self.filtering { - if let Some(c) = alphabet_chars.next() { - changes.push(Change::Text(format!(" {}. ", c ))); + // show labels as long as we have more labels left + // and we are not filtering + if !self.filtering && row_num < labels_len { + if let Some(s) = labels.next() { + let ex_spaces = " ".to_string() + .repeat(max_label_len - s.len() + 1); + changes.push(Change::Text(format!("{}{}. ", ex_spaces, s))); } + } else if !self.filtering { + changes.push(Change::Text(format!("{}", + " ".to_string() + .repeat(max_label_len+3) + ))); } else { changes.push(Change::Text(" ".to_string())); } @@ -186,17 +202,19 @@ impl SelectorState { fn run_loop(&mut self, term: &mut TermWizTerminal) -> anyhow::Result<()> { let max_items = self.max_items; - let alphabet = self.alphabet.to_lowercase(); + let alphabet = self.args.alphabet.to_lowercase(); let alphabet_has_j = alphabet.contains("j"); let alphabet_has_k = alphabet.contains("k"); - let mut alphabet_chars = alphabet.chars(); + let labels = compute_labels_for_alphabet(&alphabet, max_items+1); + while let Ok(Some(event)) = term.poll_input(None) { match event { InputEvent::Key(KeyEvent { key: KeyCode::Char(c), modifiers: Modifiers::NONE, }) if !self.filtering && alphabet.contains(c) => { - if let Some(pos) = alphabet_chars.position(|x| x == c) { + self.selection.push(c); + if let Some(pos) = labels.iter().position(|x| *x == self.selection) { if pos as usize <= max_items && self.launch(self.top_row + pos as usize) { break; } @@ -248,6 +266,9 @@ impl SelectorState { key: KeyCode::Backspace, .. }) => { + if !self.filtering && !self.selection.is_empty() { + self.selection.pop(); + } if self.filter_term.pop().is_none() && !self.always_fuzzy { self.filtering = false; } @@ -394,7 +415,7 @@ pub fn selector( always_fuzzy: args.fuzzy, args, event_name, - alphabet: "1234567890abcdefghijklmnopqrstuvxyz".to_string(), + selection: String::new(), }; term.set_raw_mode()?; From 1da48d19b2364abea74fb4fa311b2ac53bd47346 Mon Sep 17 00:00:00 2001 From: Daniel Kongsgaard Date: Thu, 31 Aug 2023 16:36:56 +0200 Subject: [PATCH 03/19] Format + fix to prepare for PR to resolve #4226. --- config/src/keyassignment.rs | 2 -- .../config/lua/keyassignment/InputSelector.md | 2 +- wezterm-gui/src/overlay/selector.rs | 32 ++++++------------- 3 files changed, 11 insertions(+), 25 deletions(-) diff --git a/config/src/keyassignment.rs b/config/src/keyassignment.rs index b185b358b7e..48151fb6a25 100644 --- a/config/src/keyassignment.rs +++ b/config/src/keyassignment.rs @@ -478,7 +478,6 @@ pub struct InputSelector { #[dynamic(default)] pub fuzzy: bool, - // Overrides the main input_select_alphabet config #[dynamic(default = "default_num_alphabet")] pub alphabet: String, @@ -494,7 +493,6 @@ fn default_description() -> String { "Select an item and press Enter = accept, Esc = cancel, / = filter".to_string() } - #[derive(Debug, Clone, PartialEq, FromDynamic, ToDynamic)] pub enum KeyAssignment { SpawnTab(SpawnTabDomain), diff --git a/docs/config/lua/keyassignment/InputSelector.md b/docs/config/lua/keyassignment/InputSelector.md index 36bc7229c02..12031895f1a 100644 --- a/docs/config/lua/keyassignment/InputSelector.md +++ b/docs/config/lua/keyassignment/InputSelector.md @@ -125,7 +125,7 @@ config.keys = { title = 'I am title', choices = choices, alphabet = '123456789', - description = 'Write the number you want to choose.', + description = 'Write the number you want to choose or press / to search.', }, pane ) diff --git a/wezterm-gui/src/overlay/selector.rs b/wezterm-gui/src/overlay/selector.rs index c52f6e1e6e9..bc3c5367c7e 100644 --- a/wezterm-gui/src/overlay/selector.rs +++ b/wezterm-gui/src/overlay/selector.rs @@ -1,3 +1,4 @@ +use super::quickselect::compute_labels_for_alphabet; use crate::scripting::guiwin::GuiWin; use config::keyassignment::{InputSelector, InputSelectorEntry, KeyAssignment}; use fuzzy_matcher::skim::SkimMatcherV2; @@ -11,7 +12,6 @@ use termwiz::input::{InputEvent, KeyCode, KeyEvent, Modifiers, MouseButtons, Mou use termwiz::surface::{Change, Position}; use termwiz::terminal::Terminal; use termwiz_funcs::truncate_right; -use super::quickselect::compute_labels_for_alphabet; const ROW_OVERHEAD: usize = 3; @@ -79,26 +79,15 @@ impl SelectorState { x: Position::Absolute(0), y: Position::Absolute(0), }, - Change::Text(format!( - "{}\r\n", - truncate_right( - desc, - max_width - ) - )), + Change::Text(format!("{}\r\n", truncate_right(desc, max_width))), Change::AllAttributes(CellAttributes::default()), ]; let max_items = self.max_items; let alphabet = &self.args.alphabet; - let mut labels = compute_labels_for_alphabet(alphabet, max_items+1) - .into_iter(); + let mut labels = compute_labels_for_alphabet(alphabet, max_items + 1).into_iter(); let labels_len = labels.len(); - let max_label_len = labels - .clone() - .map(|s| s.len()) - .max() - .unwrap_or(0); + let max_label_len = labels.clone().map(|s| s.len()).max().unwrap_or(0); for (row_num, (entry_idx, entry)) in self .filtered_entries @@ -123,15 +112,14 @@ impl SelectorState { // and we are not filtering if !self.filtering && row_num < labels_len { if let Some(s) = labels.next() { - let ex_spaces = " ".to_string() - .repeat(max_label_len - s.len() + 1); + let ex_spaces = " ".to_string().repeat(max_label_len - s.len() + 1); changes.push(Change::Text(format!("{}{}. ", ex_spaces, s))); } } else if !self.filtering { - changes.push(Change::Text(format!("{}", - " ".to_string() - .repeat(max_label_len+3) - ))); + changes.push(Change::Text(format!( + "{}", + " ".to_string().repeat(max_label_len + 3) + ))); } else { changes.push(Change::Text(" ".to_string())); } @@ -205,7 +193,7 @@ impl SelectorState { let alphabet = self.args.alphabet.to_lowercase(); let alphabet_has_j = alphabet.contains("j"); let alphabet_has_k = alphabet.contains("k"); - let labels = compute_labels_for_alphabet(&alphabet, max_items+1); + let labels = compute_labels_for_alphabet(&alphabet, max_items + 1); while let Ok(Some(event)) = term.poll_input(None) { match event { From dec93d8fbec6e15199257361b82ca98df6dd7dfd Mon Sep 17 00:00:00 2001 From: Danielkonge Date: Thu, 31 Aug 2023 22:31:39 +0200 Subject: [PATCH 04/19] Accept renaming suggestion. Co-authored-by: Wez Furlong --- wezterm-gui/src/overlay/selector.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/wezterm-gui/src/overlay/selector.rs b/wezterm-gui/src/overlay/selector.rs index bc3c5367c7e..d996565d6ea 100644 --- a/wezterm-gui/src/overlay/selector.rs +++ b/wezterm-gui/src/overlay/selector.rs @@ -85,9 +85,10 @@ impl SelectorState { let max_items = self.max_items; let alphabet = &self.args.alphabet; - let mut labels = compute_labels_for_alphabet(alphabet, max_items + 1).into_iter(); - let labels_len = labels.len(); - let max_label_len = labels.clone().map(|s| s.len()).max().unwrap_or(0); + let labels = compute_labels_for_alphabet(alphabet, max_items + 1); + let num_labels = labels.len(); + let max_label_len = labels.iter().map(|s| s.len()).max().unwrap_or(0); + let mut labels_iter = labels.into_iter(); for (row_num, (entry_idx, entry)) in self .filtered_entries From 68d2f2c8a0bba8d148a1c9f31c5e5a724a34def9 Mon Sep 17 00:00:00 2001 From: Danielkonge Date: Thu, 31 Aug 2023 22:35:38 +0200 Subject: [PATCH 05/19] Accept suggestion about pop(). Co-authored-by: Wez Furlong --- wezterm-gui/src/overlay/selector.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wezterm-gui/src/overlay/selector.rs b/wezterm-gui/src/overlay/selector.rs index d996565d6ea..732b3b3e1b8 100644 --- a/wezterm-gui/src/overlay/selector.rs +++ b/wezterm-gui/src/overlay/selector.rs @@ -255,7 +255,7 @@ impl SelectorState { key: KeyCode::Backspace, .. }) => { - if !self.filtering && !self.selection.is_empty() { + if !self.filtering { self.selection.pop(); } if self.filter_term.pop().is_none() && !self.always_fuzzy { From a35b3e603621b14d373ea6741bf36a186ae0156d Mon Sep 17 00:00:00 2001 From: Danielkonge Date: Thu, 31 Aug 2023 22:36:05 +0200 Subject: [PATCH 06/19] Fix typo. Co-authored-by: Wez Furlong --- docs/config/lua/keyassignment/InputSelector.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/config/lua/keyassignment/InputSelector.md b/docs/config/lua/keyassignment/InputSelector.md index 12031895f1a..bd3a249d0c3 100644 --- a/docs/config/lua/keyassignment/InputSelector.md +++ b/docs/config/lua/keyassignment/InputSelector.md @@ -29,7 +29,7 @@ upon the input. to calculate one or two click shortcuts that can be used to quickly choose from the InputSelector when not in fuzzy finding mode. Defaults to: `"1234567890qwertyuiopasdfghjklzxcvbnm"`. -* `fuzzy_description` - a string to display when in fuzzy finding mode. Defaults to: +* `description` - a string to display when in fuzzy finding mode. Defaults to: `"Select an item and press Enter = accept, Esc = cancel, / = filter"`. From 7fe0fba66ec3e5bcf85b39d5812540053a2a1cea Mon Sep 17 00:00:00 2001 From: Daniel Kongsgaard Date: Fri, 1 Sep 2023 02:07:39 +0200 Subject: [PATCH 07/19] Tried fixing stuff from the feedback. --- .../config/lua/keyassignment/InputSelector.md | 59 +++++++++++++- wezterm-gui/src/overlay/selector.rs | 81 +++++++++---------- 2 files changed, 94 insertions(+), 46 deletions(-) diff --git a/docs/config/lua/keyassignment/InputSelector.md b/docs/config/lua/keyassignment/InputSelector.md index bd3a249d0c3..d3179dd8833 100644 --- a/docs/config/lua/keyassignment/InputSelector.md +++ b/docs/config/lua/keyassignment/InputSelector.md @@ -54,7 +54,6 @@ config.keys = { end end), title = 'I am title', - fuzzy = true, choices = { -- This is the first entry { @@ -136,5 +135,63 @@ config.keys = { return config ``` +# Example of switching between a list of workspaces with the InputSelector + +```lua +local wezterm = require 'wezterm' +local act = wezterm.action +local config = wezterm.config_builder() + +config.keys = { + { + key = 'w', + mods = 'LEADER', + action = wezterm.action_callback(function(window, pane) + -- Here you can dynamically construct a longer list if needed + + local home = wezterm.home_dir + local workspaces = { + { id = home .. '/work', label = 'Work' }, + { id = home .. '/personal', label = 'Personal' }, + { id = home .. '/.config', label = 'Config' }, + } + + window:perform_action( + act.InputSelector { + action = wezterm.action_callback(function(inner_window, inner_pane, id, label) + if not id and not label then + wezterm.log_info 'cancelled' + else + wezterm.log_info('id = ' .. id) + wezterm.log_info('label = ' .. label) + inner_window:perform_action( + act.SwitchToWorkspace { + name = label, + spawn = { + label = 'Workspace: ' .. label, + cwd = id, + } + }, + inner_pane + ) + end + end), + title = 'Choose Workspace', + choices = workspaces, + fuzzy = true, + description = "Fuzzy find and/or make a workspace" + }, + pane + ) + end), + }, +} + +return config +``` + + + + See also [PromptInputLine](PromptInputLine.md). diff --git a/wezterm-gui/src/overlay/selector.rs b/wezterm-gui/src/overlay/selector.rs index 732b3b3e1b8..32385b9b485 100644 --- a/wezterm-gui/src/overlay/selector.rs +++ b/wezterm-gui/src/overlay/selector.rs @@ -28,6 +28,7 @@ struct SelectorState { args: InputSelector, event_name: String, selection: String, + labels: Vec, } impl SelectorState { @@ -72,6 +73,11 @@ impl SelectorState { let size = term.get_screen_size()?; let max_width = size.cols.saturating_sub(6); let desc = &self.args.description; + let max_items = size.rows.saturating_sub(ROW_OVERHEAD); + if max_items != self.max_items { + self.labels = compute_labels_for_alphabet(&self.args.alphabet, max_items + 1); + self.max_items = max_items; + } let mut changes = vec![ Change::ClearScreen(ColorAttribute::Default), @@ -83,10 +89,7 @@ impl SelectorState { Change::AllAttributes(CellAttributes::default()), ]; - let max_items = self.max_items; - let alphabet = &self.args.alphabet; - let labels = compute_labels_for_alphabet(alphabet, max_items + 1); - let num_labels = labels.len(); + let labels = &self.labels; let max_label_len = labels.iter().map(|s| s.len()).max().unwrap_or(0); let mut labels_iter = labels.into_iter(); @@ -111,16 +114,12 @@ impl SelectorState { // from above we know that row_num <= max_items // show labels as long as we have more labels left // and we are not filtering - if !self.filtering && row_num < labels_len { - if let Some(s) = labels.next() { - let ex_spaces = " ".to_string().repeat(max_label_len - s.len() + 1); - changes.push(Change::Text(format!("{}{}. ", ex_spaces, s))); + if !self.filtering { + if let Some(label) = labels_iter.next() { + changes.push(Change::Text(format!(" {label:>max_label_len$}. "))); + } else { + changes.push(Change::Text(" ".repeat(max_label_len + 3))); } - } else if !self.filtering { - changes.push(Change::Text(format!( - "{}", - " ".to_string().repeat(max_label_len + 3) - ))); } else { changes.push(Change::Text(" ".to_string())); } @@ -190,11 +189,8 @@ impl SelectorState { } fn run_loop(&mut self, term: &mut TermWizTerminal) -> anyhow::Result<()> { - let max_items = self.max_items; let alphabet = self.args.alphabet.to_lowercase(); - let alphabet_has_j = alphabet.contains("j"); - let alphabet_has_k = alphabet.contains("k"); - let labels = compute_labels_for_alphabet(&alphabet, max_items + 1); + let labels = self.labels.clone(); while let Ok(Some(event)) = term.poll_input(None) { match event { @@ -204,7 +200,11 @@ impl SelectorState { }) if !self.filtering && alphabet.contains(c) => { self.selection.push(c); if let Some(pos) = labels.iter().position(|x| *x == self.selection) { - if pos as usize <= max_items && self.launch(self.top_row + pos as usize) { + // since the number of labels is always <= self.max_items + // by construction, we have pos as usize <= self.max_items + // for free + self.active_idx = self.top_row + pos as usize; + if self.launch(self.active_idx) { break; } } @@ -212,35 +212,23 @@ impl SelectorState { InputEvent::Key(KeyEvent { key: KeyCode::Char('j'), .. - }) if !self.filtering && !alphabet_has_j => { + }) if !self.filtering && !self.args.alphabet.contains("j") => { self.move_down(); } InputEvent::Key(KeyEvent { key: KeyCode::Char('k'), .. - }) if !self.filtering && !alphabet_has_k => { + }) if !self.filtering && !self.args.alphabet.contains("k") => { self.move_up(); } InputEvent::Key(KeyEvent { - key: KeyCode::Char('J'), - .. - }) if !self.filtering => { - self.move_down(); - } - InputEvent::Key(KeyEvent { - key: KeyCode::Char('K'), - .. - }) if !self.filtering => { - self.move_up(); - } - InputEvent::Key(KeyEvent { - key: KeyCode::Char('P'), + key: KeyCode::Char('P' | 'K'), modifiers: Modifiers::CTRL, }) => { self.move_up(); } InputEvent::Key(KeyEvent { - key: KeyCode::Char('N'), + key: KeyCode::Char('N' | 'J'), modifiers: Modifiers::CTRL, }) => { self.move_down(); @@ -257,14 +245,15 @@ impl SelectorState { }) => { if !self.filtering { self.selection.pop(); + } else { + if self.filter_term.pop().is_none() && !self.always_fuzzy { + self.filtering = false; + } + self.update_filter(); } - if self.filter_term.pop().is_none() && !self.always_fuzzy { - self.filtering = false; - } - self.update_filter(); } InputEvent::Key(KeyEvent { - key: KeyCode::Char('G'), + key: KeyCode::Char('G' | 'C'), modifiers: Modifiers::CTRL, }) | InputEvent::Key(KeyEvent { @@ -337,9 +326,11 @@ impl SelectorState { break; } } - InputEvent::Resized { rows, .. } => { - self.max_items = rows.saturating_sub(ROW_OVERHEAD); - } + + // Moved to render + // InputEvent::Resized { rows, .. } => { + // self.max_items = rows.saturating_sub(ROW_OVERHEAD); + // } _ => {} } self.render(term)?; @@ -390,11 +381,10 @@ pub fn selector( anyhow::bail!("InputSelector requires action to be defined by wezterm.action_callback") } }; - let size = term.get_screen_size()?; - let max_items = size.rows.saturating_sub(ROW_OVERHEAD); + // let size = term.get_screen_size()?; let mut state = SelectorState { active_idx: 0, - max_items, + max_items: 0, pane, top_row: 0, filter_term: String::new(), @@ -405,6 +395,7 @@ pub fn selector( args, event_name, selection: String::new(), + labels: vec![], }; term.set_raw_mode()?; From 7c2fdc2765ba64544239cf5c04f02f4683a02e88 Mon Sep 17 00:00:00 2001 From: Daniel Kongsgaard Date: Fri, 1 Sep 2023 02:17:12 +0200 Subject: [PATCH 08/19] Fixed typo. --- wezterm-gui/src/overlay/selector.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/wezterm-gui/src/overlay/selector.rs b/wezterm-gui/src/overlay/selector.rs index 32385b9b485..8be02df4f50 100644 --- a/wezterm-gui/src/overlay/selector.rs +++ b/wezterm-gui/src/overlay/selector.rs @@ -381,7 +381,6 @@ pub fn selector( anyhow::bail!("InputSelector requires action to be defined by wezterm.action_callback") } }; - // let size = term.get_screen_size()?; let mut state = SelectorState { active_idx: 0, max_items: 0, From b441be688f457e7b728473d5fe33218a978ef6fc Mon Sep 17 00:00:00 2001 From: Daniel Kongsgaard Date: Fri, 1 Sep 2023 03:10:45 +0200 Subject: [PATCH 09/19] Fixed small mistake in docs. --- docs/config/lua/keyassignment/InputSelector.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/config/lua/keyassignment/InputSelector.md b/docs/config/lua/keyassignment/InputSelector.md index d3179dd8833..c325b721601 100644 --- a/docs/config/lua/keyassignment/InputSelector.md +++ b/docs/config/lua/keyassignment/InputSelector.md @@ -142,6 +142,8 @@ local wezterm = require 'wezterm' local act = wezterm.action local config = wezterm.config_builder() +config.leader = { key = 'Space', mods = 'CTRL', timeout_milliseconds = 3000 } + config.keys = { { key = 'w', From 3206a41fc7d15741abfa90019474baa2b67e7245 Mon Sep 17 00:00:00 2001 From: Daniel Kongsgaard Date: Fri, 1 Sep 2023 21:40:28 +0200 Subject: [PATCH 10/19] Small fix for label computation. --- wezterm-gui/src/overlay/selector.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/wezterm-gui/src/overlay/selector.rs b/wezterm-gui/src/overlay/selector.rs index 8be02df4f50..6b15dcb7496 100644 --- a/wezterm-gui/src/overlay/selector.rs +++ b/wezterm-gui/src/overlay/selector.rs @@ -75,7 +75,10 @@ impl SelectorState { let desc = &self.args.description; let max_items = size.rows.saturating_sub(ROW_OVERHEAD); if max_items != self.max_items { - self.labels = compute_labels_for_alphabet(&self.args.alphabet, max_items + 1); + self.labels = compute_labels_for_alphabet( + &self.args.alphabet, + self.filtered_entries.len().min(max_items + 1), + ); self.max_items = max_items; } From 38f0896f6cba77de9074e12ff5970631f42c6538 Mon Sep 17 00:00:00 2001 From: Daniel Kongsgaard Date: Mon, 11 Sep 2023 16:47:39 +0200 Subject: [PATCH 11/19] Allow uppercase alphabet + add fuzzy_description. --- config/src/keyassignment.rs | 7 ++++ .../config/lua/keyassignment/InputSelector.md | 10 ++++-- wezterm-gui/src/overlay/quickselect.rs | 35 +++++++++++++------ wezterm-gui/src/overlay/selector.rs | 10 +++--- wezterm-gui/src/termwindow/paneselect.rs | 2 +- 5 files changed, 44 insertions(+), 20 deletions(-) diff --git a/config/src/keyassignment.rs b/config/src/keyassignment.rs index 48151fb6a25..a227edec981 100644 --- a/config/src/keyassignment.rs +++ b/config/src/keyassignment.rs @@ -483,6 +483,9 @@ pub struct InputSelector { #[dynamic(default = "default_description")] pub description: String, + + #[dynamic(default = "default_fuzzy_description")] + pub fuzzy_description: String, } fn default_num_alphabet() -> String { @@ -493,6 +496,10 @@ fn default_description() -> String { "Select an item and press Enter = accept, Esc = cancel, / = filter".to_string() } +fn default_fuzzy_description() -> String { + "Fuzzy matching: ".to_string() +} + #[derive(Debug, Clone, PartialEq, FromDynamic, ToDynamic)] pub enum KeyAssignment { SpawnTab(SpawnTabDomain), diff --git a/docs/config/lua/keyassignment/InputSelector.md b/docs/config/lua/keyassignment/InputSelector.md index c325b721601..2e899452e4e 100644 --- a/docs/config/lua/keyassignment/InputSelector.md +++ b/docs/config/lua/keyassignment/InputSelector.md @@ -29,8 +29,11 @@ upon the input. to calculate one or two click shortcuts that can be used to quickly choose from the InputSelector when not in fuzzy finding mode. Defaults to: `"1234567890qwertyuiopasdfghjklzxcvbnm"`. -* `description` - a string to display when in fuzzy finding mode. Defaults to: +* `description` - a string to display when not in fuzzy finding mode. Defaults to: `"Select an item and press Enter = accept, Esc = cancel, / = filter"`. +* `fuzzy_description` - a string to display when in fuzzy finding mode. Defaults to: + `"Fuzzy matching: "`. + ## Example of choosing some canned text to enter into the terminal @@ -135,7 +138,7 @@ config.keys = { return config ``` -# Example of switching between a list of workspaces with the InputSelector +## Example of switching between a list of workspaces with the InputSelector ```lua local wezterm = require 'wezterm' @@ -153,6 +156,7 @@ config.keys = { local home = wezterm.home_dir local workspaces = { + { id = home, label = 'Home' }, { id = home .. '/work', label = 'Work' }, { id = home .. '/personal', label = 'Personal' }, { id = home .. '/.config', label = 'Config' }, @@ -181,7 +185,7 @@ config.keys = { title = 'Choose Workspace', choices = workspaces, fuzzy = true, - description = "Fuzzy find and/or make a workspace" + fuzzy_description = "Fuzzy find and/or make a workspace" }, pane ) diff --git a/wezterm-gui/src/overlay/quickselect.rs b/wezterm-gui/src/overlay/quickselect.rs index 208fd9b1195..9adcd49c6dc 100644 --- a/wezterm-gui/src/overlay/quickselect.rs +++ b/wezterm-gui/src/overlay/quickselect.rs @@ -56,11 +56,22 @@ const PATTERNS: [&str; 14] = [ /// This function computes a set of labels for a given alphabet. /// It is derived from https://github.com/fcsonline/tmux-thumbs/blob/master/src/alphabets.rs /// which is Copyright (c) 2019 Ferran Basora and provided under the MIT license -pub fn compute_labels_for_alphabet(alphabet: &str, num_matches: usize) -> Vec { - let alphabet = alphabet - .chars() - .map(|c| c.to_lowercase().to_string()) - .collect::>(); +pub fn compute_labels_for_alphabet( + alphabet: &str, + num_matches: usize, + make_lowercase: bool, +) -> Vec { + let alphabet = if make_lowercase { + alphabet + .chars() + .map(|c| c.to_lowercase().to_string()) + .collect::>() + } else { + alphabet + .chars() + .map(|c| c.to_string()) + .collect::>() + }; // Prefer to use single character matches to represent everything let mut primary = alphabet.clone(); let mut secondary = vec![]; @@ -108,13 +119,16 @@ mod alphabet_test { #[test] fn simple_alphabet() { - assert_eq!(compute_labels_for_alphabet("abcd", 3), vec!["a", "b", "c"]); + assert_eq!( + compute_labels_for_alphabet("abcd", 3, true), + vec!["a", "b", "c"] + ); } #[test] fn more_matches_than_alphabet_can_represent() { assert_eq!( - compute_labels_for_alphabet("asdfqwerzxcvjklmiuopghtybn", 792).len(), + compute_labels_for_alphabet("asdfqwerzxcvjklmiuopghtybn", 792, true).len(), 676 ); } @@ -122,7 +136,7 @@ mod alphabet_test { #[test] fn composed_single() { assert_eq!( - compute_labels_for_alphabet("abcd", 6), + compute_labels_for_alphabet("abcd", 6, true), vec!["a", "b", "c", "da", "db", "dc"] ); } @@ -130,7 +144,7 @@ mod alphabet_test { #[test] fn composed_multiple() { assert_eq!( - compute_labels_for_alphabet("abcd", 8), + compute_labels_for_alphabet("abcd", 8, true), vec!["a", "b", "ca", "cb", "da", "db", "dc", "dd"] ); } @@ -140,7 +154,7 @@ mod alphabet_test { // The number of chars in the alphabet limits the potential matches to fewer // than the number of matches that we requested assert_eq!( - compute_labels_for_alphabet("ab", 5), + compute_labels_for_alphabet("ab", 5, true), vec!["aa", "ab", "ba", "bb"] ); } @@ -717,6 +731,7 @@ impl QuickSelectRenderable { &self.config.quick_select_alphabet }, uniq_results.len(), + true, ); self.by_label.clear(); diff --git a/wezterm-gui/src/overlay/selector.rs b/wezterm-gui/src/overlay/selector.rs index 6b15dcb7496..19633513175 100644 --- a/wezterm-gui/src/overlay/selector.rs +++ b/wezterm-gui/src/overlay/selector.rs @@ -78,6 +78,7 @@ impl SelectorState { self.labels = compute_labels_for_alphabet( &self.args.alphabet, self.filtered_entries.len().min(max_items + 1), + false, ); self.max_items = max_items; } @@ -147,7 +148,7 @@ impl SelectorState { }, Change::ClearToEndOfLine(ColorAttribute::Default), Change::Text(truncate_right( - &format!("Fuzzy matching: {}", self.filter_term), + &format!("{}{}", self.args.fuzzy_description, self.filter_term), max_width, )), ]); @@ -192,17 +193,14 @@ impl SelectorState { } fn run_loop(&mut self, term: &mut TermWizTerminal) -> anyhow::Result<()> { - let alphabet = self.args.alphabet.to_lowercase(); - let labels = self.labels.clone(); - while let Ok(Some(event)) = term.poll_input(None) { match event { InputEvent::Key(KeyEvent { key: KeyCode::Char(c), modifiers: Modifiers::NONE, - }) if !self.filtering && alphabet.contains(c) => { + }) if !self.filtering && self.args.alphabet.contains(c) => { self.selection.push(c); - if let Some(pos) = labels.iter().position(|x| *x == self.selection) { + if let Some(pos) = self.labels.iter().position(|x| *x == self.selection) { // since the number of labels is always <= self.max_items // by construction, we have pos as usize <= self.max_items // for free diff --git a/wezterm-gui/src/termwindow/paneselect.rs b/wezterm-gui/src/termwindow/paneselect.rs index a0ca05c0cc1..2429e6c3e57 100644 --- a/wezterm-gui/src/termwindow/paneselect.rs +++ b/wezterm-gui/src/termwindow/paneselect.rs @@ -72,7 +72,7 @@ impl PaneSelector { let panes = term_window.get_panes_to_render(); let labels = - crate::overlay::quickselect::compute_labels_for_alphabet(alphabet, panes.len()); + crate::overlay::quickselect::compute_labels_for_alphabet(alphabet, panes.len(), true); let mut elements = vec![]; for pos in panes { From e19d7da5302ed4b6f3ae424fff3bddc5c8337bd7 Mon Sep 17 00:00:00 2001 From: Daniel Kongsgaard Date: Mon, 11 Sep 2023 23:17:20 +0200 Subject: [PATCH 12/19] Minor cleanup. --- wezterm-gui/src/overlay/selector.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/wezterm-gui/src/overlay/selector.rs b/wezterm-gui/src/overlay/selector.rs index 19633513175..f4c2fe6588a 100644 --- a/wezterm-gui/src/overlay/selector.rs +++ b/wezterm-gui/src/overlay/selector.rs @@ -72,7 +72,6 @@ impl SelectorState { fn render(&mut self, term: &mut TermWizTerminal) -> termwiz::Result<()> { let size = term.get_screen_size()?; let max_width = size.cols.saturating_sub(6); - let desc = &self.args.description; let max_items = size.rows.saturating_sub(ROW_OVERHEAD); if max_items != self.max_items { self.labels = compute_labels_for_alphabet( @@ -89,7 +88,10 @@ impl SelectorState { x: Position::Absolute(0), y: Position::Absolute(0), }, - Change::Text(format!("{}\r\n", truncate_right(desc, max_width))), + Change::Text(format!( + "{}\r\n", + truncate_right(&self.args.description, max_width) + )), Change::AllAttributes(CellAttributes::default()), ]; From 7bc1bdaab5c09a14d32d7baac046fe4dc1a6d0fc Mon Sep 17 00:00:00 2001 From: Daniel Kongsgaard Date: Wed, 13 Sep 2023 19:12:12 +0200 Subject: [PATCH 13/19] Use more standard alphabet (without j/k). --- config/src/keyassignment.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/src/keyassignment.rs b/config/src/keyassignment.rs index a227edec981..034920e571a 100644 --- a/config/src/keyassignment.rs +++ b/config/src/keyassignment.rs @@ -489,7 +489,7 @@ pub struct InputSelector { } fn default_num_alphabet() -> String { - "1234567890qwertyuiopasdfghjklzxcvbnm".to_string() + "1234567890abcdefghilmnopqrstuvwxyz".to_string() } fn default_description() -> String { From 0906e55cf3d46954843de305b2f65ebf3b76dfea Mon Sep 17 00:00:00 2001 From: Daniel Kongsgaard Date: Wed, 13 Sep 2023 20:17:39 +0200 Subject: [PATCH 14/19] Fixed docs after previous commit. --- docs/config/lua/keyassignment/InputSelector.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/config/lua/keyassignment/InputSelector.md b/docs/config/lua/keyassignment/InputSelector.md index 2e899452e4e..1381d6e6254 100644 --- a/docs/config/lua/keyassignment/InputSelector.md +++ b/docs/config/lua/keyassignment/InputSelector.md @@ -28,7 +28,8 @@ upon the input. * `alphabet` - a string of unique characters. The characters in the string are used to calculate one or two click shortcuts that can be used to quickly choose from the InputSelector when not in fuzzy finding mode. Defaults to: - `"1234567890qwertyuiopasdfghjklzxcvbnm"`. + `"1234567890abcdefghilmnopqrstuvwxyz"`. (Without j/k so they can be used for movement + up and down.) * `description` - a string to display when not in fuzzy finding mode. Defaults to: `"Select an item and press Enter = accept, Esc = cancel, / = filter"`. * `fuzzy_description` - a string to display when in fuzzy finding mode. Defaults to: From b75daa63010eed4d9cba01badaf4a9257ddaeec8 Mon Sep 17 00:00:00 2001 From: Daniel Kongsgaard Date: Thu, 21 Sep 2023 23:04:10 +0200 Subject: [PATCH 15/19] Added key assignments to docs. --- .../config/lua/keyassignment/InputSelector.md | 83 ++++++++++++------- wezterm-gui/src/overlay/selector.rs | 4 +- 2 files changed, 57 insertions(+), 30 deletions(-) diff --git a/docs/config/lua/keyassignment/InputSelector.md b/docs/config/lua/keyassignment/InputSelector.md index 1381d6e6254..237bfb7ba53 100644 --- a/docs/config/lua/keyassignment/InputSelector.md +++ b/docs/config/lua/keyassignment/InputSelector.md @@ -8,7 +8,7 @@ to select from. When the user accepts a line, emits an event that allows you to act upon the input. -`InputSelector` accepts three fields: +`InputSelector` accepts the following fields: * `title` - the title that will be set for the overlay pane * `choices` - a lua table consisting of the potential choices. Each entry @@ -27,15 +27,40 @@ upon the input. pressing / in the default mode). * `alphabet` - a string of unique characters. The characters in the string are used to calculate one or two click shortcuts that can be used to quickly choose from - the InputSelector when not in fuzzy finding mode. Defaults to: + the InputSelector when in the default mode. Defaults to: `"1234567890abcdefghilmnopqrstuvwxyz"`. (Without j/k so they can be used for movement up and down.) -* `description` - a string to display when not in fuzzy finding mode. Defaults to: +* `description` - a string to display when in the default mode. Defaults to: `"Select an item and press Enter = accept, Esc = cancel, / = filter"`. * `fuzzy_description` - a string to display when in fuzzy finding mode. Defaults to: `"Fuzzy matching: "`. +### Key Assignments + +The default key assignments in the InputSelector are as follows: + +| Action | Key Assignment | +|---------|-------------------| +| Add to selection string until a match is found (if not in fuzzy finding mode) | Any key in `alphabet` | +| Start fuzzy search (if in the default mode) | / | +| Add to filtering string (if in fuzzy finding mode) | Any key not listed below | +| Remove from selection or filtering string | Backspace | +| Pick currently highlighted line | Enter | +| | LeftClick (with mouse) | +| Move Down | DownArrow | +| | Ctrl + N | +| | Ctrl + J | +| | j (if not in `alphabet`) | +| Move Up | UpArrow | +| | Ctrl + P | +| | Ctrl + K | +| | k | +| Quit | Ctrl + G | +| | Ctrl + C | +| | Escape | + +Note: If the InputSelector is started with `fuzzy` set to `false`, then Backspace can go from fuzzy finding mode back to the default mode when pressed while the filtering string is empty. ## Example of choosing some canned text to enter into the terminal @@ -164,31 +189,33 @@ config.keys = { } window:perform_action( - act.InputSelector { - action = wezterm.action_callback(function(inner_window, inner_pane, id, label) - if not id and not label then - wezterm.log_info 'cancelled' - else - wezterm.log_info('id = ' .. id) - wezterm.log_info('label = ' .. label) - inner_window:perform_action( - act.SwitchToWorkspace { - name = label, - spawn = { - label = 'Workspace: ' .. label, - cwd = id, - } - }, - inner_pane - ) - end - end), - title = 'Choose Workspace', - choices = workspaces, - fuzzy = true, - fuzzy_description = "Fuzzy find and/or make a workspace" - }, - pane + act.InputSelector { + action = wezterm.action_callback( + function(inner_window, inner_pane, id, label) + if not id and not label then + wezterm.log_info 'cancelled' + else + wezterm.log_info('id = ' .. id) + wezterm.log_info('label = ' .. label) + inner_window:perform_action( + act.SwitchToWorkspace { + name = label, + spawn = { + label = 'Workspace: ' .. label, + cwd = id, + }, + }, + inner_pane + ) + end + end + ), + title = 'Choose Workspace', + choices = workspaces, + fuzzy = true, + fuzzy_description = 'Fuzzy find and/or make a workspace', + }, + pane ) end), }, diff --git a/wezterm-gui/src/overlay/selector.rs b/wezterm-gui/src/overlay/selector.rs index f4c2fe6588a..6ec0bbacfa2 100644 --- a/wezterm-gui/src/overlay/selector.rs +++ b/wezterm-gui/src/overlay/selector.rs @@ -1,4 +1,4 @@ -use super::quickselect::compute_labels_for_alphabet; +use super::quickselect; use crate::scripting::guiwin::GuiWin; use config::keyassignment::{InputSelector, InputSelectorEntry, KeyAssignment}; use fuzzy_matcher::skim::SkimMatcherV2; @@ -74,7 +74,7 @@ impl SelectorState { let max_width = size.cols.saturating_sub(6); let max_items = size.rows.saturating_sub(ROW_OVERHEAD); if max_items != self.max_items { - self.labels = compute_labels_for_alphabet( + self.labels = quickselect::compute_labels_for_alphabet( &self.args.alphabet, self.filtered_entries.len().min(max_items + 1), false, From 90d02794cde191985e12d1b456e339a4ca8d4788 Mon Sep 17 00:00:00 2001 From: Danielkonge Date: Fri, 22 Sep 2023 00:43:41 +0200 Subject: [PATCH 16/19] Apply suggestions from code review (2 remaining) Co-authored-by: Wez Furlong --- docs/config/lua/keyassignment/InputSelector.md | 2 +- wezterm-gui/src/overlay/quickselect.rs | 14 ++++++++++++++ wezterm-gui/src/overlay/selector.rs | 8 +------- wezterm-gui/src/termwindow/paneselect.rs | 2 +- 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/docs/config/lua/keyassignment/InputSelector.md b/docs/config/lua/keyassignment/InputSelector.md index 237bfb7ba53..dd4baf29b2f 100644 --- a/docs/config/lua/keyassignment/InputSelector.md +++ b/docs/config/lua/keyassignment/InputSelector.md @@ -55,7 +55,7 @@ The default key assignments in the InputSelector are as follows: | Move Up | UpArrow | | | Ctrl + P | | | Ctrl + K | -| | k | +| | k (if not in `alphabet`) | | Quit | Ctrl + G | | | Ctrl + C | | | Escape | diff --git a/wezterm-gui/src/overlay/quickselect.rs b/wezterm-gui/src/overlay/quickselect.rs index 9adcd49c6dc..682ebef7f4b 100644 --- a/wezterm-gui/src/overlay/quickselect.rs +++ b/wezterm-gui/src/overlay/quickselect.rs @@ -59,6 +59,20 @@ const PATTERNS: [&str; 14] = [ pub fn compute_labels_for_alphabet( alphabet: &str, num_matches: usize, +) -> Vec { + compute_labels_for_alphabet_impl(alphabet, num_matches, true) +} + +pub fn compute_labels_for_alphabet_with_preserved_case( + alphabet: &str, + num_matches: usize, +) -> Vec { + compute_labels_for_alphabet_impl(alphabet, num_matches, false) +} + +fn compute_labels_for_alphabet_impl( + alphabet: &str, + num_matches: usize, make_lowercase: bool, ) -> Vec { let alphabet = if make_lowercase { diff --git a/wezterm-gui/src/overlay/selector.rs b/wezterm-gui/src/overlay/selector.rs index 6ec0bbacfa2..d2bb0f46c18 100644 --- a/wezterm-gui/src/overlay/selector.rs +++ b/wezterm-gui/src/overlay/selector.rs @@ -74,10 +74,9 @@ impl SelectorState { let max_width = size.cols.saturating_sub(6); let max_items = size.rows.saturating_sub(ROW_OVERHEAD); if max_items != self.max_items { - self.labels = quickselect::compute_labels_for_alphabet( + self.labels = quickselect::compute_labels_for_alphabet_with_preserved_case( &self.args.alphabet, self.filtered_entries.len().min(max_items + 1), - false, ); self.max_items = max_items; } @@ -329,11 +328,6 @@ impl SelectorState { break; } } - - // Moved to render - // InputEvent::Resized { rows, .. } => { - // self.max_items = rows.saturating_sub(ROW_OVERHEAD); - // } _ => {} } self.render(term)?; diff --git a/wezterm-gui/src/termwindow/paneselect.rs b/wezterm-gui/src/termwindow/paneselect.rs index 2429e6c3e57..a0ca05c0cc1 100644 --- a/wezterm-gui/src/termwindow/paneselect.rs +++ b/wezterm-gui/src/termwindow/paneselect.rs @@ -72,7 +72,7 @@ impl PaneSelector { let panes = term_window.get_panes_to_render(); let labels = - crate::overlay::quickselect::compute_labels_for_alphabet(alphabet, panes.len(), true); + crate::overlay::quickselect::compute_labels_for_alphabet(alphabet, panes.len()); let mut elements = vec![]; for pos in panes { From 18ae51261c76dc9a356fe87e9424ee57cb84b56a Mon Sep 17 00:00:00 2001 From: Daniel Kongsgaard Date: Fri, 22 Sep 2023 01:22:52 +0200 Subject: [PATCH 17/19] Updated arcording to feedback (added tests). --- .../config/lua/keyassignment/InputSelector.md | 6 +-- wezterm-gui/src/overlay/quickselect.rs | 47 +++++++++++++------ 2 files changed, 34 insertions(+), 19 deletions(-) diff --git a/docs/config/lua/keyassignment/InputSelector.md b/docs/config/lua/keyassignment/InputSelector.md index dd4baf29b2f..985a0fc6d18 100644 --- a/docs/config/lua/keyassignment/InputSelector.md +++ b/docs/config/lua/keyassignment/InputSelector.md @@ -171,12 +171,10 @@ local wezterm = require 'wezterm' local act = wezterm.action local config = wezterm.config_builder() -config.leader = { key = 'Space', mods = 'CTRL', timeout_milliseconds = 3000 } - config.keys = { { - key = 'w', - mods = 'LEADER', + key = 'S', + mods = 'CTRL|SHIFT', action = wezterm.action_callback(function(window, pane) -- Here you can dynamically construct a longer list if needed diff --git a/wezterm-gui/src/overlay/quickselect.rs b/wezterm-gui/src/overlay/quickselect.rs index 682ebef7f4b..fc3d16b48aa 100644 --- a/wezterm-gui/src/overlay/quickselect.rs +++ b/wezterm-gui/src/overlay/quickselect.rs @@ -56,18 +56,15 @@ const PATTERNS: [&str; 14] = [ /// This function computes a set of labels for a given alphabet. /// It is derived from https://github.com/fcsonline/tmux-thumbs/blob/master/src/alphabets.rs /// which is Copyright (c) 2019 Ferran Basora and provided under the MIT license -pub fn compute_labels_for_alphabet( - alphabet: &str, - num_matches: usize, -) -> Vec { - compute_labels_for_alphabet_impl(alphabet, num_matches, true) +pub fn compute_labels_for_alphabet(alphabet: &str, num_matches: usize) -> Vec { + compute_labels_for_alphabet_impl(alphabet, num_matches, true) } pub fn compute_labels_for_alphabet_with_preserved_case( alphabet: &str, num_matches: usize, ) -> Vec { - compute_labels_for_alphabet_impl(alphabet, num_matches, false) + compute_labels_for_alphabet_impl(alphabet, num_matches, false) } fn compute_labels_for_alphabet_impl( @@ -133,16 +130,13 @@ mod alphabet_test { #[test] fn simple_alphabet() { - assert_eq!( - compute_labels_for_alphabet("abcd", 3, true), - vec!["a", "b", "c"] - ); + assert_eq!(compute_labels_for_alphabet("abcd", 3), vec!["a", "b", "c"]); } #[test] fn more_matches_than_alphabet_can_represent() { assert_eq!( - compute_labels_for_alphabet("asdfqwerzxcvjklmiuopghtybn", 792, true).len(), + compute_labels_for_alphabet("asdfqwerzxcvjklmiuopghtybn", 792).len(), 676 ); } @@ -150,7 +144,7 @@ mod alphabet_test { #[test] fn composed_single() { assert_eq!( - compute_labels_for_alphabet("abcd", 6, true), + compute_labels_for_alphabet("abcd", 6), vec!["a", "b", "c", "da", "db", "dc"] ); } @@ -158,7 +152,7 @@ mod alphabet_test { #[test] fn composed_multiple() { assert_eq!( - compute_labels_for_alphabet("abcd", 8, true), + compute_labels_for_alphabet("abcd", 8), vec!["a", "b", "ca", "cb", "da", "db", "dc", "dd"] ); } @@ -168,10 +162,34 @@ mod alphabet_test { // The number of chars in the alphabet limits the potential matches to fewer // than the number of matches that we requested assert_eq!( - compute_labels_for_alphabet("ab", 5, true), + compute_labels_for_alphabet("ab", 5), vec!["aa", "ab", "ba", "bb"] ); } + + #[test] + fn composed_capital() { + assert_eq!( + compute_labels_for_alphabet_with_preserved_case("AB", 4), + vec!["AA", "AB", "BA", "BB"] + ); + } + + #[test] + fn composed_mixed() { + assert_eq!( + compute_labels_for_alphabet_with_preserved_case("aA", 4), + vec!["aa", "aA", "Aa", "AA"] + ); + } + + #[test] + fn lowercase_alphabet_equal() { + assert_eq!( + compute_labels_for_alphabet_with_preserved_case("abc123", 12), + compute_labels_for_alphabet("abc123", 12) + ); + } } pub struct QuickSelectOverlay { @@ -745,7 +763,6 @@ impl QuickSelectRenderable { &self.config.quick_select_alphabet }, uniq_results.len(), - true, ); self.by_label.clear(); From ea7e7c8499493a96fc9ea0207834eea893b4cf94 Mon Sep 17 00:00:00 2001 From: Danielkonge Date: Fri, 22 Sep 2023 13:39:33 +0200 Subject: [PATCH 18/19] Update docs (1 thing left to do) Co-authored-by: Wez Furlong --- docs/config/lua/keyassignment/InputSelector.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/config/lua/keyassignment/InputSelector.md b/docs/config/lua/keyassignment/InputSelector.md index 985a0fc6d18..eab2c793339 100644 --- a/docs/config/lua/keyassignment/InputSelector.md +++ b/docs/config/lua/keyassignment/InputSelector.md @@ -25,6 +25,11 @@ upon the input. * `fuzzy` - a boolean that defaults to `false`. If `true`, InputSelector will start in its fuzzy finding mode (this is equivalent to starting the InputSelector and pressing / in the default mode). + +{{since('nightly')}} + +These additional fields are also available: + * `alphabet` - a string of unique characters. The characters in the string are used to calculate one or two click shortcuts that can be used to quickly choose from the InputSelector when in the default mode. Defaults to: From 93d509cf828caa3cf44f69abc2657085889cf939 Mon Sep 17 00:00:00 2001 From: Daniel Kongsgaard Date: Fri, 22 Sep 2023 14:00:07 +0200 Subject: [PATCH 19/19] Added version details to the key table. --- docs/config/lua/keyassignment/InputSelector.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/config/lua/keyassignment/InputSelector.md b/docs/config/lua/keyassignment/InputSelector.md index eab2c793339..c5a77782be6 100644 --- a/docs/config/lua/keyassignment/InputSelector.md +++ b/docs/config/lua/keyassignment/InputSelector.md @@ -47,7 +47,8 @@ The default key assignments in the InputSelector are as follows: | Action | Key Assignment | |---------|-------------------| -| Add to selection string until a match is found (if not in fuzzy finding mode) | Any key in `alphabet` | +| Add to selection string until a match is found (if in the default mode) | Any key in `alphabet` {{since('nightly', inline=True)}} | +| Select matching number (if in the default mode) | 1 to 9 {{since('20230408-112425-69ae8472', inline=True)}} | | Start fuzzy search (if in the default mode) | / | | Add to filtering string (if in fuzzy finding mode) | Any key not listed below | | Remove from selection or filtering string | Backspace | @@ -55,14 +56,14 @@ The default key assignments in the InputSelector are as follows: | | LeftClick (with mouse) | | Move Down | DownArrow | | | Ctrl + N | -| | Ctrl + J | +| | Ctrl + J {{since('nightly', inline=True)}} | | | j (if not in `alphabet`) | | Move Up | UpArrow | | | Ctrl + P | -| | Ctrl + K | +| | Ctrl + K {{since('nightly', inline=True)}} | | | k (if not in `alphabet`) | | Quit | Ctrl + G | -| | Ctrl + C | +| | Ctrl + C {{since('nightly', inline=True)}} | | | Escape | Note: If the InputSelector is started with `fuzzy` set to `false`, then Backspace can go from fuzzy finding mode back to the default mode when pressed while the filtering string is empty.