mirror of
https://github.com/alacritty/alacritty.git
synced 2024-11-18 13:55:23 -05:00
Use paste for ESC action and IME commit
Route string terminal input through 'ActionContext::paste' instead of char by char write improving performance by utilizing bracketed paste mode when it's reasonable. Suggested-by: Kirill Chibisov <contact@kchibisov.com>
This commit is contained in:
parent
924a91e3a3
commit
6dd9dd0aa8
2 changed files with 77 additions and 71 deletions
|
@ -218,56 +218,6 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext<T> for ActionCon
|
||||||
self.notifier.notify(val);
|
self.notifier.notify(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn received_char(&mut self, c: char) {
|
|
||||||
// Don't insert chars when we have IME running.
|
|
||||||
if self.display().ime.preedit().is_some() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle hint selection over anything else.
|
|
||||||
if self.display().hint_state.active() && !*self.suppress_chars {
|
|
||||||
self.hint_input(c);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pass keys to search and ignore them during `suppress_chars`.
|
|
||||||
let search_active = self.search_active();
|
|
||||||
if *self.suppress_chars || search_active || self.terminal().mode().contains(TermMode::VI) {
|
|
||||||
if search_active && !*self.suppress_chars {
|
|
||||||
self.search_input(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.on_typing_start();
|
|
||||||
|
|
||||||
if self.terminal().grid().display_offset() != 0 {
|
|
||||||
self.scroll(Scroll::Bottom);
|
|
||||||
}
|
|
||||||
self.clear_selection();
|
|
||||||
|
|
||||||
let utf8_len = c.len_utf8();
|
|
||||||
let mut bytes = vec![0; utf8_len];
|
|
||||||
c.encode_utf8(&mut bytes[..]);
|
|
||||||
|
|
||||||
#[cfg(not(target_os = "macos"))]
|
|
||||||
let alt_send_esc = true;
|
|
||||||
|
|
||||||
// Don't send ESC when `OptionAsAlt` is used. This doesn't handle
|
|
||||||
// `Only{Left,Right}` variants due to inability to distinguish them.
|
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
let alt_send_esc = self.config().window.option_as_alt != OptionAsAlt::None;
|
|
||||||
|
|
||||||
if alt_send_esc && *self.received_count() == 0 && self.modifiers().alt() && utf8_len == 1 {
|
|
||||||
bytes.insert(0, b'\x1b');
|
|
||||||
}
|
|
||||||
|
|
||||||
self.write_to_pty(bytes);
|
|
||||||
|
|
||||||
*self.received_count() += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Request a redraw.
|
/// Request a redraw.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn mark_dirty(&mut self) {
|
fn mark_dirty(&mut self) {
|
||||||
|
@ -774,9 +724,7 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext<T> for ActionCon
|
||||||
self.clipboard.store(ClipboardType::Clipboard, text);
|
self.clipboard.store(ClipboardType::Clipboard, text);
|
||||||
},
|
},
|
||||||
// Write the text to the PTY/search.
|
// Write the text to the PTY/search.
|
||||||
HintAction::Action(HintInternalAction::Paste) => {
|
HintAction::Action(HintInternalAction::Paste) => self.paste(&text, true),
|
||||||
self.paste(&text);
|
|
||||||
},
|
|
||||||
// Select the text.
|
// Select the text.
|
||||||
HintAction::Action(HintInternalAction::Select) => {
|
HintAction::Action(HintInternalAction::Select) => {
|
||||||
self.start_selection(SelectionType::Simple, *hint_bounds.start(), Side::Left);
|
self.start_selection(SelectionType::Simple, *hint_bounds.start(), Side::Left);
|
||||||
|
@ -832,13 +780,25 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext<T> for ActionCon
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Handle beginning of terminal text input.
|
||||||
|
fn on_terminal_input_start(&mut self) {
|
||||||
|
self.on_typing_start();
|
||||||
|
self.clear_selection();
|
||||||
|
|
||||||
|
if self.terminal().grid().display_offset() != 0 {
|
||||||
|
self.scroll(Scroll::Bottom);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Paste a text into the terminal.
|
/// Paste a text into the terminal.
|
||||||
fn paste(&mut self, text: &str) {
|
fn paste(&mut self, text: &str, bracketed: bool) {
|
||||||
if self.search_active() {
|
if self.search_active() {
|
||||||
for c in text.chars() {
|
for c in text.chars() {
|
||||||
self.search_input(c);
|
self.search_input(c);
|
||||||
}
|
}
|
||||||
} else if self.terminal().mode().contains(TermMode::BRACKETED_PASTE) {
|
} else if bracketed && self.terminal().mode().contains(TermMode::BRACKETED_PASTE) {
|
||||||
|
self.on_terminal_input_start();
|
||||||
|
|
||||||
self.write_to_pty(&b"\x1b[200~"[..]);
|
self.write_to_pty(&b"\x1b[200~"[..]);
|
||||||
|
|
||||||
// Write filtered escape sequences.
|
// Write filtered escape sequences.
|
||||||
|
@ -851,6 +811,8 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext<T> for ActionCon
|
||||||
|
|
||||||
self.write_to_pty(&b"\x1b[201~"[..]);
|
self.write_to_pty(&b"\x1b[201~"[..]);
|
||||||
} else {
|
} else {
|
||||||
|
self.on_terminal_input_start();
|
||||||
|
|
||||||
// In non-bracketed (ie: normal) mode, terminal applications cannot distinguish
|
// In non-bracketed (ie: normal) mode, terminal applications cannot distinguish
|
||||||
// pasted data from keystrokes.
|
// pasted data from keystrokes.
|
||||||
// In theory, we should construct the keystrokes needed to produce the data we are
|
// In theory, we should construct the keystrokes needed to produce the data we are
|
||||||
|
@ -1338,7 +1300,7 @@ impl input::Processor<EventProxy, ActionContext<'_, Notifier, EventProxy>> {
|
||||||
self.key_input(input);
|
self.key_input(input);
|
||||||
},
|
},
|
||||||
WindowEvent::ModifiersChanged(modifiers) => self.modifiers_input(modifiers),
|
WindowEvent::ModifiersChanged(modifiers) => self.modifiers_input(modifiers),
|
||||||
WindowEvent::ReceivedCharacter(c) => self.ctx.received_char(c),
|
WindowEvent::ReceivedCharacter(c) => self.received_char(c),
|
||||||
WindowEvent::MouseInput { state, button, .. } => {
|
WindowEvent::MouseInput { state, button, .. } => {
|
||||||
self.ctx.window().set_mouse_visible(true);
|
self.ctx.window().set_mouse_visible(true);
|
||||||
self.mouse_input(state, button);
|
self.mouse_input(state, button);
|
||||||
|
@ -1372,7 +1334,7 @@ impl input::Processor<EventProxy, ActionContext<'_, Notifier, EventProxy>> {
|
||||||
},
|
},
|
||||||
WindowEvent::DroppedFile(path) => {
|
WindowEvent::DroppedFile(path) => {
|
||||||
let path: String = path.to_string_lossy().into();
|
let path: String = path.to_string_lossy().into();
|
||||||
self.ctx.paste(&(path + " "));
|
self.ctx.paste(&(path + " "), true);
|
||||||
},
|
},
|
||||||
WindowEvent::CursorLeft { .. } => {
|
WindowEvent::CursorLeft { .. } => {
|
||||||
self.ctx.mouse.inside_text_area = false;
|
self.ctx.mouse.inside_text_area = false;
|
||||||
|
@ -1384,11 +1346,7 @@ impl input::Processor<EventProxy, ActionContext<'_, Notifier, EventProxy>> {
|
||||||
WindowEvent::Ime(ime) => match ime {
|
WindowEvent::Ime(ime) => match ime {
|
||||||
Ime::Commit(text) => {
|
Ime::Commit(text) => {
|
||||||
*self.ctx.dirty = true;
|
*self.ctx.dirty = true;
|
||||||
|
self.ctx.paste(&text, true);
|
||||||
for ch in text.chars() {
|
|
||||||
self.ctx.received_char(ch);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.ctx.update_cursor_blinking();
|
self.ctx.update_cursor_blinking();
|
||||||
},
|
},
|
||||||
Ime::Preedit(text, cursor_offset) => {
|
Ime::Preedit(text, cursor_offset) => {
|
||||||
|
|
|
@ -73,7 +73,6 @@ pub struct Processor<T: EventListener, A: ActionContext<T>> {
|
||||||
|
|
||||||
pub trait ActionContext<T: EventListener> {
|
pub trait ActionContext<T: EventListener> {
|
||||||
fn write_to_pty<B: Into<Cow<'static, [u8]>>>(&self, _data: B) {}
|
fn write_to_pty<B: Into<Cow<'static, [u8]>>>(&self, _data: B) {}
|
||||||
fn received_char(&mut self, _c: char) {}
|
|
||||||
fn mark_dirty(&mut self) {}
|
fn mark_dirty(&mut self) {}
|
||||||
fn size_info(&self) -> SizeInfo;
|
fn size_info(&self) -> SizeInfo;
|
||||||
fn copy_selection(&mut self, _ty: ClipboardType) {}
|
fn copy_selection(&mut self, _ty: ClipboardType) {}
|
||||||
|
@ -120,7 +119,8 @@ pub trait ActionContext<T: EventListener> {
|
||||||
fn hint_input(&mut self, _character: char) {}
|
fn hint_input(&mut self, _character: char) {}
|
||||||
fn trigger_hint(&mut self, _hint: &HintMatch) {}
|
fn trigger_hint(&mut self, _hint: &HintMatch) {}
|
||||||
fn expand_selection(&mut self) {}
|
fn expand_selection(&mut self) {}
|
||||||
fn paste(&mut self, _text: &str) {}
|
fn on_terminal_input_start(&mut self) {}
|
||||||
|
fn paste(&mut self, _text: &str, _bracketed: bool) {}
|
||||||
fn spawn_daemon<I, S>(&self, _program: &str, _args: I)
|
fn spawn_daemon<I, S>(&self, _program: &str, _args: I)
|
||||||
where
|
where
|
||||||
I: IntoIterator<Item = S> + Debug + Copy,
|
I: IntoIterator<Item = S> + Debug + Copy,
|
||||||
|
@ -152,11 +152,7 @@ impl<T: EventListener> Execute<T> for Action {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn execute<A: ActionContext<T>>(&self, ctx: &mut A) {
|
fn execute<A: ActionContext<T>>(&self, ctx: &mut A) {
|
||||||
match self {
|
match self {
|
||||||
Action::Esc(s) => {
|
Action::Esc(s) => ctx.paste(s, false),
|
||||||
for c in s.chars() {
|
|
||||||
ctx.received_char(c);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Action::Command(program) => ctx.spawn_daemon(program.program(), program.args()),
|
Action::Command(program) => ctx.spawn_daemon(program.program(), program.args()),
|
||||||
Action::Hint(hint) => {
|
Action::Hint(hint) => {
|
||||||
ctx.display().hint_state.start(hint.clone());
|
ctx.display().hint_state.start(hint.clone());
|
||||||
|
@ -276,11 +272,11 @@ impl<T: EventListener> Execute<T> for Action {
|
||||||
Action::ClearSelection => ctx.clear_selection(),
|
Action::ClearSelection => ctx.clear_selection(),
|
||||||
Action::Paste => {
|
Action::Paste => {
|
||||||
let text = ctx.clipboard_mut().load(ClipboardType::Clipboard);
|
let text = ctx.clipboard_mut().load(ClipboardType::Clipboard);
|
||||||
ctx.paste(&text);
|
ctx.paste(&text, true);
|
||||||
},
|
},
|
||||||
Action::PasteSelection => {
|
Action::PasteSelection => {
|
||||||
let text = ctx.clipboard_mut().load(ClipboardType::Selection);
|
let text = ctx.clipboard_mut().load(ClipboardType::Selection);
|
||||||
ctx.paste(&text);
|
ctx.paste(&text, true);
|
||||||
},
|
},
|
||||||
Action::ToggleFullscreen => ctx.window().toggle_fullscreen(),
|
Action::ToggleFullscreen => ctx.window().toggle_fullscreen(),
|
||||||
Action::ToggleMaximized => ctx.window().toggle_maximized(),
|
Action::ToggleMaximized => ctx.window().toggle_maximized(),
|
||||||
|
@ -964,6 +960,58 @@ impl<T: EventListener, A: ActionContext<T>> Processor<T, A> {
|
||||||
self.ctx.window().set_mouse_cursor(mouse_state);
|
self.ctx.window().set_mouse_cursor(mouse_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Process a received character.
|
||||||
|
pub fn received_char(&mut self, c: char) {
|
||||||
|
let suppress_chars = *self.ctx.suppress_chars();
|
||||||
|
|
||||||
|
// Don't insert chars when we have IME running.
|
||||||
|
if self.ctx.display().ime.preedit().is_some() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle hint selection over anything else.
|
||||||
|
if self.ctx.display().hint_state.active() && !suppress_chars {
|
||||||
|
self.ctx.hint_input(c);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pass keys to search and ignore them during `suppress_chars`.
|
||||||
|
let search_active = self.ctx.search_active();
|
||||||
|
if suppress_chars || search_active || self.ctx.terminal().mode().contains(TermMode::VI) {
|
||||||
|
if search_active && !suppress_chars {
|
||||||
|
self.ctx.search_input(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.ctx.on_terminal_input_start();
|
||||||
|
|
||||||
|
let utf8_len = c.len_utf8();
|
||||||
|
let mut bytes = vec![0; utf8_len];
|
||||||
|
c.encode_utf8(&mut bytes[..]);
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "macos"))]
|
||||||
|
let alt_send_esc = true;
|
||||||
|
|
||||||
|
// Don't send ESC when `OptionAsAlt` is used. This doesn't handle
|
||||||
|
// `Only{Left,Right}` variants due to inability to distinguish them.
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
let alt_send_esc = self.ctx.config().window.option_as_alt != OptionAsAlt::None;
|
||||||
|
|
||||||
|
if alt_send_esc
|
||||||
|
&& *self.ctx.received_count() == 0
|
||||||
|
&& self.ctx.modifiers().alt()
|
||||||
|
&& utf8_len == 1
|
||||||
|
{
|
||||||
|
bytes.insert(0, b'\x1b');
|
||||||
|
}
|
||||||
|
|
||||||
|
self.ctx.write_to_pty(bytes);
|
||||||
|
|
||||||
|
*self.ctx.received_count() += 1;
|
||||||
|
}
|
||||||
|
|
||||||
/// Attempt to find a binding and execute its action.
|
/// Attempt to find a binding and execute its action.
|
||||||
///
|
///
|
||||||
/// The provided mode, mods, and key must match what is allowed by a binding
|
/// The provided mode, mods, and key must match what is allowed by a binding
|
||||||
|
|
Loading…
Reference in a new issue