mirror of
https://github.com/wez/wezterm.git
synced 2024-11-23 06:54:45 +03:00
Add PromptInputLine and examples of (re)naming tabs/workspaces
refs: https://github.com/wez/wezterm/issues/522 refs: https://github.com/wez/wezterm/issues/1598
This commit is contained in:
parent
b2b5c64aff
commit
336fefb52f
@ -445,6 +445,14 @@ pub struct QuickSelectArguments {
|
||||
pub scope_lines: Option<usize>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, FromDynamic, ToDynamic)]
|
||||
pub struct PromptInputLine {
|
||||
pub action: Box<KeyAssignment>,
|
||||
/// Descriptive text to show ahead of prompt
|
||||
#[dynamic(default)]
|
||||
pub description: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, FromDynamic, ToDynamic)]
|
||||
pub enum KeyAssignment {
|
||||
SpawnTab(SpawnTabDomain),
|
||||
@ -550,6 +558,7 @@ pub enum KeyAssignment {
|
||||
ActivateWindow(usize),
|
||||
ActivateWindowRelative(isize),
|
||||
ActivateWindowRelativeNoWrap(isize),
|
||||
PromptInputLine(PromptInputLine),
|
||||
}
|
||||
impl_lua_conversion_dynamic!(KeyAssignment);
|
||||
|
||||
|
@ -39,6 +39,9 @@ As features stabilize some brief notes about them will accumulate here.
|
||||
expect. #2644
|
||||
|
||||
#### New
|
||||
* [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.
|
||||
* [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
|
||||
|
90
docs/config/lua/keyassignment/PromptInputLine.md
Normal file
90
docs/config/lua/keyassignment/PromptInputLine.md
Normal file
@ -0,0 +1,90 @@
|
||||
# `PromptInputLine`
|
||||
|
||||
{{since('nightly')}}
|
||||
|
||||
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
|
||||
upon the input.
|
||||
|
||||
`PromptInputLine` accepts two fields:
|
||||
|
||||
* `description` - the text to show at the top of the display area. You may
|
||||
embed escape sequences and/or use [wezterm.format](../wezterm/format.md).
|
||||
* `action` - and event callback registerd via `wezterm.action_callback`. The
|
||||
callback's function signature is `(window, pane, line)` where `window` and
|
||||
`pane` are the [Window](../window/index.md) and [Pane](../pane/index.md)
|
||||
objects from the current pane and window, and `line` is the text that the
|
||||
user entered. `line` may be `nil` if they hit Escape without entering
|
||||
anything, or CTRL-C to cancel the input.
|
||||
|
||||
## Example of interactively renaming the current tab
|
||||
|
||||
```lua
|
||||
local wezterm = require 'wezterm'
|
||||
local act = wezterm.action
|
||||
|
||||
local config = wezterm.config_builder()
|
||||
config.keys = {
|
||||
{
|
||||
key = 'E',
|
||||
mods = 'CTRL|SHIFT',
|
||||
action = act.PromptInputLine {
|
||||
description = 'Enter new name for tab',
|
||||
action = wezterm.action_callback(function(window, pane, line)
|
||||
-- line will be `nil` if they hit escape without entering anything
|
||||
-- An empty string if they just hit enter
|
||||
-- Or the actual line of text they wrote
|
||||
if line then
|
||||
window:active_tab():set_title(line)
|
||||
end
|
||||
end),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return config
|
||||
```
|
||||
|
||||
## Example of interactively picking a name and creating a new workspace
|
||||
|
||||
Similar to the above, but prompts for a name prior to creating
|
||||
the workspace.
|
||||
|
||||
This example also shows the use of `wezterm.format` to emit colored text.
|
||||
|
||||
```lua
|
||||
local wezterm = require 'wezterm'
|
||||
local act = wezterm.action
|
||||
|
||||
local config = wezterm.config_builder()
|
||||
config.keys = {
|
||||
{
|
||||
key = 'N',
|
||||
mods = 'CTRL|SHIFT',
|
||||
action = act.PromptInputLine {
|
||||
description = wezterm.format {
|
||||
{ Attribute = { Intensity = 'Bold' } },
|
||||
{ Foreground = { AnsiColor = 'Fuchsia' } },
|
||||
{ Text = 'Enter name for new workspace' },
|
||||
},
|
||||
action = wezterm.action_callback(function(window, pane, line)
|
||||
-- line will be `nil` if they hit escape without entering anything
|
||||
-- An empty string if they just hit enter
|
||||
-- Or the actual line of text they wrote
|
||||
if line then
|
||||
window:perform_action(
|
||||
act.SwitchToWorkspace {
|
||||
name = line,
|
||||
},
|
||||
pane
|
||||
)
|
||||
end
|
||||
end),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return config
|
||||
```
|
@ -48,5 +48,49 @@ config.keys = {
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return config
|
||||
```
|
||||
|
||||
## Prompting for the workspace name
|
||||
|
||||
{{since('nightly')}}
|
||||
|
||||
```lua
|
||||
local act = wezterm.action
|
||||
|
||||
wezterm.on('update-right-status', function(window, pane)
|
||||
window:set_right_status(window:active_workspace())
|
||||
end)
|
||||
|
||||
config.keys = {
|
||||
-- Prompt for a name to use for a new workspace and switch to it.
|
||||
{
|
||||
key = 'W',
|
||||
mods = 'CTRL|SHIFT',
|
||||
action = act.PromptInputLine {
|
||||
description = wezterm.format {
|
||||
{ Attribute = { Intensity = 'Bold' } },
|
||||
{ Foreground = { AnsiColor = 'Fuchsia' } },
|
||||
{ Text = 'Enter name for new workspace' },
|
||||
},
|
||||
action = wezterm.action_callback(function(window, pane, line)
|
||||
-- line will be `nil` if they hit escape without entering anything
|
||||
-- An empty string if they just hit enter
|
||||
-- Or the actual line of text they wrote
|
||||
if line then
|
||||
window:perform_action(
|
||||
act.SwitchToWorkspace {
|
||||
name = line,
|
||||
},
|
||||
pane
|
||||
)
|
||||
end
|
||||
end),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return config
|
||||
```
|
||||
|
||||
|
@ -736,6 +736,14 @@ pub fn derive_command_from_key_assignment(action: &KeyAssignment) -> Option<Comm
|
||||
menubar: &["Help"],
|
||||
icon: Some("cod_debug"),
|
||||
},
|
||||
PromptInputLine(_) => CommandDef {
|
||||
brief: "Prompt the user for a line of text".into(),
|
||||
doc: "Activates the prompt overlay and wait for input".into(),
|
||||
keys: vec![],
|
||||
args: &[ArgType::ActiveWindow],
|
||||
menubar: &[],
|
||||
icon: None,
|
||||
},
|
||||
QuickSelect => CommandDef {
|
||||
brief: "Enter QuickSelect mode".into(),
|
||||
doc: "Activates the quick selection UI for the current pane".into(),
|
||||
|
@ -10,6 +10,7 @@ pub mod confirm_close_pane;
|
||||
pub mod copy;
|
||||
pub mod debug;
|
||||
pub mod launcher;
|
||||
pub mod prompt;
|
||||
pub mod quickselect;
|
||||
|
||||
pub use confirm_close_pane::{
|
||||
|
106
wezterm-gui/src/overlay/prompt.rs
Normal file
106
wezterm-gui/src/overlay/prompt.rs
Normal file
@ -0,0 +1,106 @@
|
||||
use crate::scripting::guiwin::GuiWin;
|
||||
use config::keyassignment::{KeyAssignment, PromptInputLine};
|
||||
use mux::termwiztermtab::TermWizTerminal;
|
||||
use mux_lua::MuxPane;
|
||||
use std::rc::Rc;
|
||||
use termwiz::input::{InputEvent, KeyCode, KeyEvent};
|
||||
use termwiz::lineedit::*;
|
||||
use termwiz::surface::Change;
|
||||
use termwiz::terminal::Terminal;
|
||||
|
||||
struct PromptHost {
|
||||
history: BasicHistory,
|
||||
}
|
||||
|
||||
impl PromptHost {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
history: BasicHistory::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LineEditorHost for PromptHost {
|
||||
fn history(&mut self) -> &mut dyn History {
|
||||
&mut self.history
|
||||
}
|
||||
|
||||
fn resolve_action(
|
||||
&mut self,
|
||||
event: &InputEvent,
|
||||
editor: &mut LineEditor<'_>,
|
||||
) -> Option<Action> {
|
||||
let (line, _cursor) = editor.get_line_and_cursor();
|
||||
if line.is_empty()
|
||||
&& matches!(
|
||||
event,
|
||||
InputEvent::Key(KeyEvent {
|
||||
key: KeyCode::Escape,
|
||||
..
|
||||
})
|
||||
)
|
||||
{
|
||||
Some(Action::Cancel)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn show_line_prompt_overlay(
|
||||
mut term: TermWizTerminal,
|
||||
args: PromptInputLine,
|
||||
window: GuiWin,
|
||||
pane: MuxPane,
|
||||
) -> anyhow::Result<()> {
|
||||
let name = match *args.action {
|
||||
KeyAssignment::EmitEvent(id) => id,
|
||||
_ => anyhow::bail!(
|
||||
"PromptInputLine requires action to be defined by wezterm.action_callback"
|
||||
),
|
||||
};
|
||||
|
||||
term.no_grab_mouse_in_raw_mode();
|
||||
let mut text = args.description.replace("\r\n", "\n").replace("\n", "\r\n");
|
||||
text.push_str("\r\n");
|
||||
term.render(&[Change::Text(text)])?;
|
||||
|
||||
let mut host = PromptHost::new();
|
||||
let mut editor = LineEditor::new(&mut term);
|
||||
editor.set_prompt("> ");
|
||||
let line = editor.read_line(&mut host)?;
|
||||
|
||||
promise::spawn::spawn_into_main_thread(async move {
|
||||
trampoline(name, window, pane, line);
|
||||
anyhow::Result::<()>::Ok(())
|
||||
})
|
||||
.detach();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn trampoline(name: String, window: GuiWin, pane: MuxPane, line: Option<String>) {
|
||||
promise::spawn::spawn(async move {
|
||||
config::with_lua_config_on_main_thread(move |lua| do_event(lua, name, window, pane, line))
|
||||
.await
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
async fn do_event(
|
||||
lua: Option<Rc<mlua::Lua>>,
|
||||
name: String,
|
||||
window: GuiWin,
|
||||
pane: MuxPane,
|
||||
line: Option<String>,
|
||||
) -> anyhow::Result<()> {
|
||||
if let Some(lua) = lua {
|
||||
let args = lua.pack_multi((window, pane, line))?;
|
||||
|
||||
if let Err(err) = config::lua::emit_event(&lua, (name.clone(), args)).await {
|
||||
log::error!("while processing {} event: {:#}", name, err);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
@ -28,8 +28,8 @@ use ::wezterm_term::input::{ClickPosition, MouseButton as TMB};
|
||||
use ::window::*;
|
||||
use anyhow::{anyhow, ensure, Context};
|
||||
use config::keyassignment::{
|
||||
KeyAssignment, PaneDirection, Pattern, QuickSelectArguments, RotationDirection, SpawnCommand,
|
||||
SplitSize,
|
||||
KeyAssignment, PaneDirection, Pattern, PromptInputLine, QuickSelectArguments,
|
||||
RotationDirection, SpawnCommand, SplitSize,
|
||||
};
|
||||
use config::{
|
||||
configuration, AudibleBell, ConfigHandle, Dimension, DimensionContext, FrontEndSelection,
|
||||
@ -2109,6 +2109,30 @@ impl TermWindow {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
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) {
|
||||
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::prompt::show_line_prompt_overlay(term, args, gui_win, pane)
|
||||
});
|
||||
self.assign_overlay(tab.tab_id(), overlay);
|
||||
promise::spawn::spawn(future).detach();
|
||||
}
|
||||
|
||||
fn show_debug_overlay(&mut self) {
|
||||
let mux = Mux::get();
|
||||
let tab = match mux.get_active_tab_for_window(self.mux_window_id) {
|
||||
@ -2862,6 +2886,7 @@ impl TermWindow {
|
||||
let modal = crate::termwindow::palette::CommandPalette::new(self);
|
||||
self.set_modal(Rc::new(modal));
|
||||
}
|
||||
PromptInputLine(args) => self.show_prompt_input_line(args),
|
||||
};
|
||||
Ok(PerformAssignmentResult::Handled)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user