mirror of
https://github.com/wez/wezterm.git
synced 2024-11-10 15:04:32 +03:00
Improve launcher menu
* Allow selecting the first few rows by number * Allow scrolling through long lists of items * Add actions from key assignments to list refs: #1485 refs: #1413
This commit is contained in:
parent
3ab78f7fac
commit
509122c152
@ -232,8 +232,8 @@ pub enum KeyAssignment {
|
||||
impl_lua_conversion!(KeyAssignment);
|
||||
|
||||
pub struct InputMap {
|
||||
keys: HashMap<(KeyCode, Modifiers), KeyAssignment>,
|
||||
mouse: HashMap<(MouseEventTrigger, Modifiers), KeyAssignment>,
|
||||
pub keys: HashMap<(KeyCode, Modifiers), KeyAssignment>,
|
||||
pub mouse: HashMap<(MouseEventTrigger, Modifiers), KeyAssignment>,
|
||||
leader: Option<LeaderKey>,
|
||||
}
|
||||
|
||||
|
@ -175,64 +175,25 @@ pub fn make_lua_context(config_file: &Path) -> anyhow::Result<Lua> {
|
||||
|
||||
wezterm_mod.set(
|
||||
"pad_right",
|
||||
lua.create_function(|_, (mut result, width): (String, usize)| {
|
||||
let mut len = unicode_column_width(&result, None);
|
||||
while len < width {
|
||||
result.push(' ');
|
||||
len += 1;
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
})?,
|
||||
lua.create_function(|_, (s, width): (String, usize)| Ok(pad_right(s, width)))?,
|
||||
)?;
|
||||
|
||||
wezterm_mod.set(
|
||||
"pad_left",
|
||||
lua.create_function(|_, (mut result, width): (String, usize)| {
|
||||
let mut len = unicode_column_width(&result, None);
|
||||
while len < width {
|
||||
result.insert(0, ' ');
|
||||
len += 1;
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
})?,
|
||||
lua.create_function(|_, (s, width): (String, usize)| Ok(pad_left(s, width)))?,
|
||||
)?;
|
||||
|
||||
wezterm_mod.set(
|
||||
"truncate_right",
|
||||
lua.create_function(|_, (s, max_width): (String, usize)| {
|
||||
let mut result = String::new();
|
||||
let mut len = 0;
|
||||
for g in s.graphemes(true) {
|
||||
let g_len = grapheme_column_width(g, None);
|
||||
if g_len + len > max_width {
|
||||
break;
|
||||
}
|
||||
result.push_str(g);
|
||||
len += g_len;
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
Ok(truncate_right(&s, max_width))
|
||||
})?,
|
||||
)?;
|
||||
|
||||
wezterm_mod.set(
|
||||
"truncate_left",
|
||||
lua.create_function(|_, (s, max_width): (String, usize)| {
|
||||
let mut result = vec![];
|
||||
let mut len = 0;
|
||||
for g in s.graphemes(true).rev() {
|
||||
let g_len = grapheme_column_width(g, None);
|
||||
if g_len + len > max_width {
|
||||
break;
|
||||
}
|
||||
result.push(g);
|
||||
len += g_len;
|
||||
}
|
||||
|
||||
result.reverse();
|
||||
Ok(result.join(""))
|
||||
Ok(truncate_left(&s, max_width))
|
||||
})?,
|
||||
)?;
|
||||
|
||||
@ -1017,3 +978,53 @@ assert(wezterm.emit('bar', 42, 'woot') == true)
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pad_right(mut result: String, width: usize) -> String {
|
||||
let mut len = unicode_column_width(&result, None);
|
||||
while len < width {
|
||||
result.push(' ');
|
||||
len += 1;
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
pub fn pad_left(mut result: String, width: usize) -> String {
|
||||
let mut len = unicode_column_width(&result, None);
|
||||
while len < width {
|
||||
result.insert(0, ' ');
|
||||
len += 1;
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
pub fn truncate_left(s: &str, max_width: usize) -> String {
|
||||
let mut result = vec![];
|
||||
let mut len = 0;
|
||||
for g in s.graphemes(true).rev() {
|
||||
let g_len = grapheme_column_width(g, None);
|
||||
if g_len + len > max_width {
|
||||
break;
|
||||
}
|
||||
result.push(g);
|
||||
len += g_len;
|
||||
}
|
||||
|
||||
result.reverse();
|
||||
result.join("")
|
||||
}
|
||||
|
||||
pub fn truncate_right(s: &str, max_width: usize) -> String {
|
||||
let mut result = String::new();
|
||||
let mut len = 0;
|
||||
for g in s.graphemes(true) {
|
||||
let g_len = grapheme_column_width(g, None);
|
||||
if g_len + len > max_width {
|
||||
break;
|
||||
}
|
||||
result.push_str(g);
|
||||
len += g_len;
|
||||
}
|
||||
result
|
||||
}
|
||||
|
@ -7,11 +7,13 @@
|
||||
//! menus.
|
||||
use crate::termwindow::clipboard::ClipboardHelper;
|
||||
use crate::termwindow::spawn::SpawnWhere;
|
||||
use crate::termwindow::TermWindow;
|
||||
use crate::termwindow::{TermWindow, TermWindowNotif};
|
||||
use anyhow::anyhow;
|
||||
use config::keyassignment::{SpawnCommand, SpawnTabDomain};
|
||||
use config::keyassignment::{InputMap, KeyAssignment, SpawnCommand, SpawnTabDomain};
|
||||
use config::lua::truncate_right;
|
||||
use config::{configuration, TermConfig};
|
||||
use mux::domain::{DomainId, DomainState};
|
||||
use mux::pane::PaneId;
|
||||
use mux::tab::TabId;
|
||||
use mux::termwiztermtab::TermWizTerminal;
|
||||
use mux::window::WindowId;
|
||||
@ -24,6 +26,7 @@ use termwiz::color::ColorAttribute;
|
||||
use termwiz::input::{InputEvent, KeyCode, KeyEvent, MouseButtons, MouseEvent};
|
||||
use termwiz::surface::{Change, Position};
|
||||
use termwiz::terminal::Terminal;
|
||||
use window::WindowOps;
|
||||
|
||||
#[derive(Clone)]
|
||||
enum EntryKind {
|
||||
@ -34,6 +37,7 @@ enum EntryKind {
|
||||
Attach {
|
||||
domain: DomainId,
|
||||
},
|
||||
KeyAssignment(KeyAssignment),
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -230,6 +234,7 @@ fn enumerate_wsl_entries(entries: &mut Vec<Entry>) -> anyhow::Result<()> {
|
||||
|
||||
pub fn launcher(
|
||||
_tab_id: TabId,
|
||||
pane_id: PaneId,
|
||||
domain_id_of_current_tab: DomainId,
|
||||
mut term: TermWizTerminal,
|
||||
mux_window_id: WindowId,
|
||||
@ -237,8 +242,10 @@ pub fn launcher(
|
||||
clipboard: ClipboardHelper,
|
||||
size: PtySize,
|
||||
term_config: Arc<TermConfig>,
|
||||
window: ::window::Window,
|
||||
) -> anyhow::Result<()> {
|
||||
let mut active_idx = 0;
|
||||
let mut top_row;
|
||||
let mut entries = vec![];
|
||||
|
||||
term.set_raw_mode()?;
|
||||
@ -298,41 +305,82 @@ pub fn launcher(
|
||||
entries.push(entry);
|
||||
}
|
||||
|
||||
// Grab interestig key assignments and show those as a kind of command palette
|
||||
let input_map = InputMap::new(&config);
|
||||
for ((keycode, mods), assignment) in input_map.keys {
|
||||
if matches!(
|
||||
&assignment,
|
||||
KeyAssignment::ActivateTabRelative(_) | KeyAssignment::ActivateTab(_)
|
||||
) {
|
||||
// Filter out some noisy, repetitive entries
|
||||
continue;
|
||||
}
|
||||
entries.push(Entry {
|
||||
label: format!("{:?} ({:?} {:?})", assignment, keycode, mods),
|
||||
kind: EntryKind::KeyAssignment(assignment),
|
||||
});
|
||||
}
|
||||
|
||||
fn render(
|
||||
active_idx: usize,
|
||||
entries: &[Entry],
|
||||
term: &mut TermWizTerminal,
|
||||
) -> termwiz::Result<()> {
|
||||
) -> termwiz::Result<usize> {
|
||||
let size = term.get_screen_size()?;
|
||||
let max_width = size.cols.saturating_sub(6);
|
||||
|
||||
let mut changes = vec![
|
||||
Change::ClearScreen(ColorAttribute::Default),
|
||||
Change::CursorPosition {
|
||||
x: Position::Absolute(0),
|
||||
y: Position::Absolute(0),
|
||||
},
|
||||
Change::Text(
|
||||
"Select an item and press Enter to launch it. \
|
||||
Press Escape to cancel\r\n"
|
||||
.to_string(),
|
||||
),
|
||||
Change::Text(format!(
|
||||
"{}\r\n",
|
||||
truncate_right(
|
||||
"Select an item and press Enter to launch it. \
|
||||
Press Escape to cancel",
|
||||
max_width
|
||||
)
|
||||
)),
|
||||
Change::AllAttributes(CellAttributes::default()),
|
||||
];
|
||||
|
||||
for (idx, entry) in entries.iter().enumerate() {
|
||||
if idx == active_idx {
|
||||
let max_items = size.rows - 3;
|
||||
let num_items = entries.len();
|
||||
|
||||
let skip = if num_items - active_idx < max_items {
|
||||
// Align to bottom
|
||||
(num_items - max_items) - 1
|
||||
} else {
|
||||
active_idx.saturating_sub(2)
|
||||
};
|
||||
|
||||
for (row_num, (entry_idx, entry)) in entries.iter().enumerate().skip(skip).enumerate() {
|
||||
if row_num > max_items {
|
||||
break;
|
||||
}
|
||||
if entry_idx == active_idx {
|
||||
changes.push(AttributeChange::Reverse(true).into());
|
||||
}
|
||||
|
||||
changes.push(Change::Text(format!(" {} \r\n", entry.label)));
|
||||
let label = truncate_right(&entry.label, max_width);
|
||||
if row_num < 9 {
|
||||
changes.push(Change::Text(format!(" {}. {} \r\n", row_num + 1, label)));
|
||||
} else {
|
||||
changes.push(Change::Text(format!(" {} \r\n", label)));
|
||||
}
|
||||
|
||||
if idx == active_idx {
|
||||
if entry_idx == active_idx {
|
||||
changes.push(AttributeChange::Reverse(false).into());
|
||||
}
|
||||
}
|
||||
term.render(&changes)
|
||||
term.render(&changes)?;
|
||||
Ok(skip)
|
||||
}
|
||||
|
||||
term.render(&[Change::Title("Launcher".to_string())])?;
|
||||
render(active_idx, &entries, &mut term)?;
|
||||
top_row = render(active_idx, &entries, &mut term)?;
|
||||
|
||||
fn launch(
|
||||
active_idx: usize,
|
||||
@ -341,6 +389,8 @@ pub fn launcher(
|
||||
mux_window_id: WindowId,
|
||||
clipboard: ClipboardHelper,
|
||||
term_config: Arc<TermConfig>,
|
||||
window: &::window::Window,
|
||||
pane_id: PaneId,
|
||||
) {
|
||||
match entries[active_idx].clone().kind {
|
||||
EntryKind::Spawn {
|
||||
@ -368,6 +418,12 @@ pub fn launcher(
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
EntryKind::KeyAssignment(assignment) => {
|
||||
window.notify(TermWindowNotif::PerformAssignment {
|
||||
pane_id,
|
||||
assignment,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -403,7 +459,7 @@ pub fn launcher(
|
||||
y, mouse_buttons, ..
|
||||
}) => {
|
||||
if y > 0 && y as usize <= entries.len() {
|
||||
active_idx = y as usize - 1;
|
||||
active_idx = top_row + y as usize - 1;
|
||||
|
||||
if mouse_buttons == MouseButtons::LEFT {
|
||||
launch(
|
||||
@ -413,6 +469,8 @@ pub fn launcher(
|
||||
mux_window_id,
|
||||
clipboard,
|
||||
term_config,
|
||||
&window,
|
||||
pane_id,
|
||||
);
|
||||
break;
|
||||
}
|
||||
@ -422,6 +480,22 @@ pub fn launcher(
|
||||
break;
|
||||
}
|
||||
}
|
||||
InputEvent::Key(KeyEvent {
|
||||
key: KeyCode::Char(c),
|
||||
..
|
||||
}) if c >= '1' && c <= '9' => {
|
||||
launch(
|
||||
top_row + (c as u32 - '1' as u32) as usize,
|
||||
&entries,
|
||||
size,
|
||||
mux_window_id,
|
||||
clipboard,
|
||||
term_config,
|
||||
&window,
|
||||
pane_id,
|
||||
);
|
||||
break;
|
||||
}
|
||||
InputEvent::Key(KeyEvent {
|
||||
key: KeyCode::Enter,
|
||||
..
|
||||
@ -433,12 +507,14 @@ pub fn launcher(
|
||||
mux_window_id,
|
||||
clipboard,
|
||||
term_config,
|
||||
&window,
|
||||
pane_id,
|
||||
);
|
||||
break;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
render(active_idx, &entries, &mut term)?;
|
||||
top_row = render(active_idx, &entries, &mut term)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -1699,10 +1699,16 @@ impl TermWindow {
|
||||
None => return,
|
||||
};
|
||||
|
||||
let pane = match self.get_active_pane_or_overlay() {
|
||||
Some(pane) => pane,
|
||||
None => return,
|
||||
};
|
||||
|
||||
let mux_window_id = self.mux_window_id;
|
||||
|
||||
let window = self.window.as_ref().unwrap().clone();
|
||||
let clipboard = ClipboardHelper {
|
||||
window: self.window.as_ref().unwrap().clone(),
|
||||
window: window.clone(),
|
||||
};
|
||||
|
||||
let mut domains = mux.iter_domains();
|
||||
@ -1741,9 +1747,11 @@ impl TermWindow {
|
||||
let size = self.terminal_size;
|
||||
let term_config = Arc::new(TermConfig::with_config(self.config.clone()));
|
||||
|
||||
let pane_id = pane.pane_id();
|
||||
let (overlay, future) = start_overlay(self, &tab, move |tab_id, term| {
|
||||
launcher(
|
||||
tab_id,
|
||||
pane_id,
|
||||
domain_id_of_current_pane,
|
||||
term,
|
||||
mux_window_id,
|
||||
@ -1751,6 +1759,7 @@ impl TermWindow {
|
||||
clipboard,
|
||||
size,
|
||||
term_config,
|
||||
window,
|
||||
)
|
||||
});
|
||||
self.assign_overlay(tab.tab_id(), overlay);
|
||||
|
Loading…
Reference in New Issue
Block a user