mirror of
https://github.com/wez/wezterm.git
synced 2024-11-22 22:42:48 +03:00
Add InputSelector action
Allows prompting the user to select from a list and then triggering some action on the selected item. This is the guts of the launcher menu hooked up to user-supplied arbitrary entries.
This commit is contained in:
parent
153497d01d
commit
e3e9821c4d
@ -453,6 +453,24 @@ pub struct PromptInputLine {
|
||||
pub description: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, FromDynamic, ToDynamic)]
|
||||
pub struct InputSelectorEntry {
|
||||
pub label: String,
|
||||
pub id: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, FromDynamic, ToDynamic)]
|
||||
pub struct InputSelector {
|
||||
pub action: Box<KeyAssignment>,
|
||||
#[dynamic(default)]
|
||||
pub title: String,
|
||||
|
||||
pub choices: Vec<InputSelectorEntry>,
|
||||
|
||||
#[dynamic(default)]
|
||||
pub fuzzy: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, FromDynamic, ToDynamic)]
|
||||
pub enum KeyAssignment {
|
||||
SpawnTab(SpawnTabDomain),
|
||||
@ -559,6 +577,7 @@ pub enum KeyAssignment {
|
||||
ActivateWindowRelative(isize),
|
||||
ActivateWindowRelativeNoWrap(isize),
|
||||
PromptInputLine(PromptInputLine),
|
||||
InputSelector(InputSelector),
|
||||
}
|
||||
impl_lua_conversion_dynamic!(KeyAssignment);
|
||||
|
||||
|
@ -42,6 +42,9 @@ As features stabilize some brief notes about them will accumulate here.
|
||||
* [PromptInputLine](config/lua/keyassignment/PromptInputLine.md) action for
|
||||
prompting the user for a line of text and then doing something with it.
|
||||
Can be used to prompt for (re)naming new or existing tabs, workspaces and so on.
|
||||
* [InputSelector](config/lua/keyassignment/InputSelector.md) action for
|
||||
prompting the user to select an item from a list and then doing something
|
||||
with it.
|
||||
* [pane:activate()](config/lua/pane/activate.md) and [tab:activate()](config/lua/MuxTab/activate.md). #3217
|
||||
* [ulimit_nofile](config/lua/config/ulimit_nofile.md) and [ulimint_nproc](config/lua/config/ulimit_nproc.md) options. ?3353
|
||||
* [serial_ports](config/lua/config/serial_ports.md) for more convenient access to serial ports
|
||||
|
127
docs/config/lua/keyassignment/InputSelector.md
Normal file
127
docs/config/lua/keyassignment/InputSelector.md
Normal file
@ -0,0 +1,127 @@
|
||||
# `InputSelector`
|
||||
|
||||
{{since('nightly')}}
|
||||
|
||||
Activates an overlay to display a list of choices for the user
|
||||
to select from.
|
||||
|
||||
When the user accepts a line, emits an event that allows you to act
|
||||
upon the input.
|
||||
|
||||
`InputSelector` accepts three fields:
|
||||
|
||||
* `title` - the title that will be set for the overlay pane
|
||||
* `choices` - a lua table consisting of the potential choices. Each entry
|
||||
is itself a table with a `label` field and an optional `id` field.
|
||||
The label will be shown in the list, while the id can be a different
|
||||
string that is meaningful to your action. The label can be used together
|
||||
with [wezterm.format](../wezterm/format.md) to produce styled test.
|
||||
* `action` - and event callback registerd via `wezterm.action_callback`. The
|
||||
callback's function signature is `(window, pane, id, label)` where `window` and
|
||||
`pane` are the [Window](../window/index.md) and [Pane](../pane/index.md)
|
||||
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.
|
||||
|
||||
## Example of choosing some canned text to enter into the terminal
|
||||
|
||||
```lua
|
||||
local wezterm = require 'wezterm'
|
||||
local act = wezterm.action
|
||||
local config = wezterm.config_builder()
|
||||
|
||||
config.keys = {
|
||||
{
|
||||
key = 'E',
|
||||
mods = 'CTRL|SHIFT',
|
||||
action = act.InputSelector {
|
||||
action = wezterm.action_callback(function(window, pane, id, label)
|
||||
if not id and not label then
|
||||
wezterm.log_info 'cancelled'
|
||||
else
|
||||
wezterm.log_info('you selected ', id, label)
|
||||
pane:send_text(id)
|
||||
end
|
||||
end),
|
||||
title = 'I am title',
|
||||
choices = {
|
||||
-- This is the first entry
|
||||
{
|
||||
-- Here we're using wezterm.format to color the text.
|
||||
-- You can just use a string directly if you don't want
|
||||
-- to control the colors
|
||||
label = wezterm.format {
|
||||
{ Foreground = { AnsiColor = 'Red' } },
|
||||
{ Text = 'No' },
|
||||
{ Foreground = { AnsiColor = 'Green' } },
|
||||
{ Text = ' thanks' },
|
||||
},
|
||||
-- This is the text that we'll send to the terminal when
|
||||
-- this entry is selected
|
||||
id = 'Regretfully, I decline this offer.',
|
||||
},
|
||||
-- This is the second entry
|
||||
{
|
||||
label = 'WTF?',
|
||||
id = 'An interesting idea, but I have some questions about it.',
|
||||
},
|
||||
-- This is the third entry
|
||||
{
|
||||
label = 'LGTM',
|
||||
id = 'This sounds like the right choice',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return config
|
||||
```
|
||||
|
||||
## Example of dynamically constructing a list
|
||||
|
||||
```lua
|
||||
local wezterm = require 'wezterm'
|
||||
local act = wezterm.action
|
||||
local config = wezterm.config_builder()
|
||||
|
||||
config.keys = {
|
||||
{
|
||||
key = 'R',
|
||||
mods = 'CTRL|SHIFT',
|
||||
action = wezterm.action_callback(function(window, pane)
|
||||
-- We're going to dynamically construct the list and then
|
||||
-- show it. Here we're just showing some numbers but you
|
||||
-- could read or compute data from other sources
|
||||
|
||||
local choices = {}
|
||||
for n = 1, 10 do
|
||||
table.insert(choices, { label = tostring(n) })
|
||||
end
|
||||
|
||||
window:perform_action(
|
||||
act.InputSelector {
|
||||
action = wezterm.action_callback(function(window, pane, id, label)
|
||||
if not id and not label then
|
||||
wezterm.log_info 'cancelled'
|
||||
else
|
||||
wezterm.log_info('you selected ', id, label)
|
||||
-- Since we didn't set an id in this example, we're
|
||||
-- sending the label
|
||||
pane:send_text(label)
|
||||
end
|
||||
end),
|
||||
title = 'I am title',
|
||||
choices = choices,
|
||||
},
|
||||
pane
|
||||
)
|
||||
end),
|
||||
},
|
||||
}
|
||||
|
||||
return config
|
||||
```
|
||||
|
||||
See also [PromptInputLine](PromptInputLine.md).
|
||||
|
@ -5,7 +5,7 @@
|
||||
Activates an overlay to display a prompt and request a line of input
|
||||
from the user.
|
||||
|
||||
When the user enters the line, emits an event and allows you to act
|
||||
When the user enters the line, emits an event that allows you to act
|
||||
upon the input.
|
||||
|
||||
`PromptInputLine` accepts two fields:
|
||||
@ -88,3 +88,5 @@ config.keys = {
|
||||
|
||||
return config
|
||||
```
|
||||
|
||||
See also [InputSelector](InputSelector.md).
|
||||
|
@ -736,6 +736,14 @@ pub fn derive_command_from_key_assignment(action: &KeyAssignment) -> Option<Comm
|
||||
menubar: &["Help"],
|
||||
icon: Some("cod_debug"),
|
||||
},
|
||||
InputSelector(_) => CommandDef {
|
||||
brief: "Prompt the user to choose from a list".into(),
|
||||
doc: "Activates the selector overlay and wait for input".into(),
|
||||
keys: vec![],
|
||||
args: &[ArgType::ActiveWindow],
|
||||
menubar: &[],
|
||||
icon: None,
|
||||
},
|
||||
PromptInputLine(_) => CommandDef {
|
||||
brief: "Prompt the user for a line of text".into(),
|
||||
doc: "Activates the prompt overlay and wait for input".into(),
|
||||
|
@ -12,6 +12,7 @@ pub mod debug;
|
||||
pub mod launcher;
|
||||
pub mod prompt;
|
||||
pub mod quickselect;
|
||||
pub mod selector;
|
||||
|
||||
pub use confirm_close_pane::{
|
||||
confirm_close_pane, confirm_close_tab, confirm_close_window, confirm_quit_program,
|
||||
|
378
wezterm-gui/src/overlay/selector.rs
Normal file
378
wezterm-gui/src/overlay/selector.rs
Normal file
@ -0,0 +1,378 @@
|
||||
use crate::scripting::guiwin::GuiWin;
|
||||
use config::keyassignment::{InputSelector, InputSelectorEntry, KeyAssignment};
|
||||
use fuzzy_matcher::skim::SkimMatcherV2;
|
||||
use fuzzy_matcher::FuzzyMatcher;
|
||||
use mux::termwiztermtab::TermWizTerminal;
|
||||
use mux_lua::MuxPane;
|
||||
use std::rc::Rc;
|
||||
use termwiz::cell::{AttributeChange, CellAttributes};
|
||||
use termwiz::color::ColorAttribute;
|
||||
use termwiz::input::{InputEvent, KeyCode, KeyEvent, Modifiers, MouseButtons, MouseEvent};
|
||||
use termwiz::surface::{Change, Position};
|
||||
use termwiz::terminal::Terminal;
|
||||
use termwiz_funcs::truncate_right;
|
||||
|
||||
const ROW_OVERHEAD: usize = 3;
|
||||
|
||||
struct SelectorState {
|
||||
active_idx: usize,
|
||||
max_items: usize,
|
||||
top_row: usize,
|
||||
filter_term: String,
|
||||
filtered_entries: Vec<InputSelectorEntry>,
|
||||
pane: MuxPane,
|
||||
window: GuiWin,
|
||||
filtering: bool,
|
||||
always_fuzzy: bool,
|
||||
args: InputSelector,
|
||||
event_name: String,
|
||||
}
|
||||
|
||||
impl SelectorState {
|
||||
fn update_filter(&mut self) {
|
||||
if self.filter_term.is_empty() {
|
||||
self.filtered_entries = self.args.choices.clone();
|
||||
return;
|
||||
}
|
||||
|
||||
self.filtered_entries.clear();
|
||||
|
||||
let matcher = SkimMatcherV2::default();
|
||||
|
||||
struct MatchResult {
|
||||
row_idx: usize,
|
||||
score: i64,
|
||||
}
|
||||
|
||||
let mut scores: Vec<MatchResult> = self
|
||||
.args
|
||||
.choices
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(row_idx, entry)| {
|
||||
let score = matcher.fuzzy_match(&entry.label, &self.filter_term)?;
|
||||
Some(MatchResult { row_idx, score })
|
||||
})
|
||||
.collect();
|
||||
|
||||
scores.sort_by(|a, b| a.score.cmp(&b.score).reverse());
|
||||
|
||||
for result in scores {
|
||||
self.filtered_entries
|
||||
.push(self.args.choices[result.row_idx].clone());
|
||||
}
|
||||
|
||||
self.active_idx = 0;
|
||||
self.top_row = 0;
|
||||
}
|
||||
|
||||
fn render(&mut self, term: &mut TermWizTerminal) -> termwiz::Result<()> {
|
||||
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(format!(
|
||||
"{}\r\n",
|
||||
truncate_right(
|
||||
"Select an item and press Enter=accept \
|
||||
Esc=cancel /=filter",
|
||||
max_width
|
||||
)
|
||||
)),
|
||||
Change::AllAttributes(CellAttributes::default()),
|
||||
];
|
||||
|
||||
let max_items = self.max_items;
|
||||
|
||||
for (row_num, (entry_idx, entry)) in self
|
||||
.filtered_entries
|
||||
.iter()
|
||||
.enumerate()
|
||||
.skip(self.top_row)
|
||||
.enumerate()
|
||||
{
|
||||
if row_num > max_items {
|
||||
break;
|
||||
}
|
||||
|
||||
let mut attr = CellAttributes::blank();
|
||||
|
||||
if entry_idx == self.active_idx {
|
||||
changes.push(AttributeChange::Reverse(true).into());
|
||||
attr.set_reverse(true);
|
||||
}
|
||||
|
||||
if row_num < 9 && !self.filtering {
|
||||
changes.push(Change::Text(format!(" {}. ", row_num + 1)));
|
||||
} else {
|
||||
changes.push(Change::Text(" ".to_string()));
|
||||
}
|
||||
|
||||
let mut line = crate::tabbar::parse_status_text(&entry.label, attr.clone());
|
||||
if line.len() > max_width {
|
||||
line.resize(max_width, termwiz::surface::SEQ_ZERO);
|
||||
}
|
||||
changes.append(&mut line.changes(&attr));
|
||||
if entry_idx == self.active_idx {
|
||||
changes.push(AttributeChange::Reverse(false).into());
|
||||
}
|
||||
changes.push(Change::AllAttributes(CellAttributes::default()));
|
||||
changes.push(Change::Text(" \r\n".to_string()));
|
||||
}
|
||||
|
||||
if self.filtering || !self.filter_term.is_empty() {
|
||||
changes.append(&mut vec![
|
||||
Change::CursorPosition {
|
||||
x: Position::Absolute(0),
|
||||
y: Position::Absolute(0),
|
||||
},
|
||||
Change::ClearToEndOfLine(ColorAttribute::Default),
|
||||
Change::Text(truncate_right(
|
||||
&format!("Fuzzy matching: {}", self.filter_term),
|
||||
max_width,
|
||||
)),
|
||||
]);
|
||||
}
|
||||
|
||||
term.render(&changes)
|
||||
}
|
||||
|
||||
fn trigger_event(&self, entry: Option<InputSelectorEntry>) {
|
||||
let name = self.event_name.clone();
|
||||
let window = self.window.clone();
|
||||
let pane = self.pane.clone();
|
||||
|
||||
promise::spawn::spawn_into_main_thread(async move {
|
||||
trampoline(name, window, pane, entry);
|
||||
anyhow::Result::<()>::Ok(())
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
fn launch(&self, active_idx: usize) -> bool {
|
||||
if let Some(entry) = self.filtered_entries.get(active_idx).cloned() {
|
||||
self.trigger_event(Some(entry));
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn move_up(&mut self) {
|
||||
self.active_idx = self.active_idx.saturating_sub(1);
|
||||
if self.active_idx < self.top_row {
|
||||
self.top_row = self.active_idx;
|
||||
}
|
||||
}
|
||||
|
||||
fn move_down(&mut self) {
|
||||
self.active_idx = (self.active_idx + 1).min(self.filtered_entries.len() - 1);
|
||||
if self.active_idx + self.top_row > self.max_items {
|
||||
self.top_row = self.active_idx.saturating_sub(self.max_items);
|
||||
}
|
||||
}
|
||||
|
||||
fn run_loop(&mut self, term: &mut TermWizTerminal) -> anyhow::Result<()> {
|
||||
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;
|
||||
}
|
||||
}
|
||||
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'),
|
||||
modifiers: Modifiers::CTRL,
|
||||
}) => {
|
||||
self.move_up();
|
||||
}
|
||||
InputEvent::Key(KeyEvent {
|
||||
key: KeyCode::Char('N'),
|
||||
modifiers: Modifiers::CTRL,
|
||||
}) => {
|
||||
self.move_down();
|
||||
}
|
||||
InputEvent::Key(KeyEvent {
|
||||
key: KeyCode::Char('/'),
|
||||
..
|
||||
}) if !self.filtering => {
|
||||
self.filtering = true;
|
||||
}
|
||||
InputEvent::Key(KeyEvent {
|
||||
key: KeyCode::Backspace,
|
||||
..
|
||||
}) => {
|
||||
if self.filter_term.pop().is_none() && !self.always_fuzzy {
|
||||
self.filtering = false;
|
||||
}
|
||||
self.update_filter();
|
||||
}
|
||||
InputEvent::Key(KeyEvent {
|
||||
key: KeyCode::Char('G'),
|
||||
modifiers: Modifiers::CTRL,
|
||||
})
|
||||
| InputEvent::Key(KeyEvent {
|
||||
key: KeyCode::Escape,
|
||||
..
|
||||
}) => {
|
||||
self.trigger_event(None);
|
||||
break;
|
||||
}
|
||||
InputEvent::Key(KeyEvent {
|
||||
key: KeyCode::Char(c),
|
||||
..
|
||||
}) if self.filtering => {
|
||||
self.filter_term.push(c);
|
||||
self.update_filter();
|
||||
}
|
||||
InputEvent::Key(KeyEvent {
|
||||
key: KeyCode::UpArrow,
|
||||
..
|
||||
}) => {
|
||||
self.move_up();
|
||||
}
|
||||
InputEvent::Key(KeyEvent {
|
||||
key: KeyCode::DownArrow,
|
||||
..
|
||||
}) => {
|
||||
self.move_down();
|
||||
}
|
||||
InputEvent::Mouse(MouseEvent {
|
||||
y, mouse_buttons, ..
|
||||
}) if mouse_buttons.contains(MouseButtons::VERT_WHEEL) => {
|
||||
if mouse_buttons.contains(MouseButtons::WHEEL_POSITIVE) {
|
||||
self.top_row = self.top_row.saturating_sub(1);
|
||||
} else {
|
||||
self.top_row += 1;
|
||||
self.top_row = self.top_row.min(
|
||||
self.filtered_entries
|
||||
.len()
|
||||
.saturating_sub(self.max_items)
|
||||
.saturating_sub(1),
|
||||
);
|
||||
}
|
||||
if y > 0 && y as usize <= self.filtered_entries.len() {
|
||||
self.active_idx = self.top_row + y as usize - 1;
|
||||
}
|
||||
}
|
||||
InputEvent::Mouse(MouseEvent {
|
||||
y, mouse_buttons, ..
|
||||
}) => {
|
||||
if y > 0 && y as usize <= self.filtered_entries.len() {
|
||||
self.active_idx = self.top_row + y as usize - 1;
|
||||
|
||||
if mouse_buttons == MouseButtons::LEFT {
|
||||
if self.launch(self.active_idx) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if mouse_buttons != MouseButtons::NONE {
|
||||
// Treat any other mouse button as cancel
|
||||
self.trigger_event(None);
|
||||
break;
|
||||
}
|
||||
}
|
||||
InputEvent::Key(KeyEvent {
|
||||
key: KeyCode::Enter,
|
||||
..
|
||||
}) => {
|
||||
if self.launch(self.active_idx) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
InputEvent::Resized { rows, .. } => {
|
||||
self.max_items = rows.saturating_sub(ROW_OVERHEAD);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
self.render(term)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn trampoline(name: String, window: GuiWin, pane: MuxPane, entry: Option<InputSelectorEntry>) {
|
||||
promise::spawn::spawn(async move {
|
||||
config::with_lua_config_on_main_thread(move |lua| do_event(lua, name, window, pane, entry))
|
||||
.await
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
async fn do_event(
|
||||
lua: Option<Rc<mlua::Lua>>,
|
||||
name: String,
|
||||
window: GuiWin,
|
||||
pane: MuxPane,
|
||||
entry: Option<InputSelectorEntry>,
|
||||
) -> anyhow::Result<()> {
|
||||
if let Some(lua) = lua {
|
||||
let id = entry.as_ref().map(|entry| entry.id.clone());
|
||||
let label = entry.as_ref().map(|entry| entry.label.to_string());
|
||||
|
||||
let args = lua.pack_multi((window, pane, id, label))?;
|
||||
|
||||
if let Err(err) = config::lua::emit_event(&lua, (name.clone(), args)).await {
|
||||
log::error!("while processing {} event: {:#}", name, err);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn selector(
|
||||
mut term: TermWizTerminal,
|
||||
args: InputSelector,
|
||||
window: GuiWin,
|
||||
pane: MuxPane,
|
||||
) -> anyhow::Result<()> {
|
||||
let event_name = match *args.action {
|
||||
KeyAssignment::EmitEvent(ref id) => id.to_string(),
|
||||
_ => {
|
||||
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 mut state = SelectorState {
|
||||
active_idx: 0,
|
||||
max_items,
|
||||
pane,
|
||||
top_row: 0,
|
||||
filter_term: String::new(),
|
||||
filtered_entries: vec![],
|
||||
window,
|
||||
filtering: args.fuzzy,
|
||||
always_fuzzy: args.fuzzy,
|
||||
args,
|
||||
event_name,
|
||||
};
|
||||
|
||||
term.set_raw_mode()?;
|
||||
term.render(&[Change::Title(state.args.title.to_string())])?;
|
||||
state.update_filter();
|
||||
state.render(&mut term)?;
|
||||
state.run_loop(&mut term)
|
||||
}
|
@ -2109,6 +2109,30 @@ impl TermWindow {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn show_input_selector(&mut self, args: &config::keyassignment::InputSelector) {
|
||||
let mux = Mux::get();
|
||||
let tab = match mux.get_active_tab_for_window(self.mux_window_id) {
|
||||
Some(tab) => tab,
|
||||
None => return,
|
||||
};
|
||||
|
||||
let pane = match self.get_active_pane_or_overlay() {
|
||||
Some(pane) => pane,
|
||||
None => return,
|
||||
};
|
||||
|
||||
let args = args.clone();
|
||||
|
||||
let gui_win = GuiWin::new(self);
|
||||
let pane = MuxPane(pane.pane_id());
|
||||
|
||||
let (overlay, future) = start_overlay(self, &tab, move |_tab_id, term| {
|
||||
crate::overlay::selector::selector(term, args, gui_win, pane)
|
||||
});
|
||||
self.assign_overlay(tab.tab_id(), overlay);
|
||||
promise::spawn::spawn(future).detach();
|
||||
}
|
||||
|
||||
fn show_prompt_input_line(&mut self, args: &PromptInputLine) {
|
||||
let mux = Mux::get();
|
||||
let tab = match mux.get_active_tab_for_window(self.mux_window_id) {
|
||||
@ -2887,6 +2911,7 @@ impl TermWindow {
|
||||
self.set_modal(Rc::new(modal));
|
||||
}
|
||||
PromptInputLine(args) => self.show_prompt_input_line(args),
|
||||
InputSelector(args) => self.show_input_selector(args),
|
||||
};
|
||||
Ok(PerformAssignmentResult::Handled)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user