@@ -0,0 +1,444 @@
+use alacritty_terminal::term::TermMode;
+use gpui::keymap::Keystroke;
+
+/*
+Connection events still to do:
+- Reporting mouse events correctly.
+- Reporting scrolls
+- Correctly bracketing a paste
+- Storing changed colors
+- Focus change sequence
+*/
+
+#[derive(Debug)]
+pub enum Modifiers {
+ None,
+ Alt,
+ Ctrl,
+ Shift,
+ CtrlShift,
+ Other,
+}
+
+impl Modifiers {
+ fn new(ks: &Keystroke) -> Self {
+ match (ks.alt, ks.ctrl, ks.shift, ks.cmd) {
+ (false, false, false, false) => Modifiers::None,
+ (true, false, false, false) => Modifiers::Alt,
+ (false, true, false, false) => Modifiers::Ctrl,
+ (false, false, true, false) => Modifiers::Shift,
+ (false, true, true, false) => Modifiers::CtrlShift,
+ _ => Modifiers::Other,
+ }
+ }
+
+ fn any(&self) -> bool {
+ match &self {
+ Modifiers::None => false,
+ Modifiers::Alt => true,
+ Modifiers::Ctrl => true,
+ Modifiers::Shift => true,
+ Modifiers::CtrlShift => true,
+ Modifiers::Other => true,
+ }
+ }
+}
+
+pub fn to_esc_str(keystroke: &Keystroke, mode: &TermMode) -> Option<String> {
+ let modifiers = Modifiers::new(&keystroke);
+
+ // Manual Bindings including modifiers
+ let manual_esc_str = match (keystroke.key.as_ref(), &modifiers) {
+ //Basic special keys
+ ("space", Modifiers::None) => Some(" ".to_string()),
+ ("tab", Modifiers::None) => Some("\x09".to_string()),
+ ("escape", Modifiers::None) => Some("\x1b".to_string()),
+ ("enter", Modifiers::None) => Some("\x0d".to_string()),
+ ("backspace", Modifiers::None) => Some("\x7f".to_string()),
+ //Interesting escape codes
+ ("tab", Modifiers::Shift) => Some("\x1b[Z".to_string()),
+ ("backspace", Modifiers::Alt) => Some("\x1b\x7f".to_string()),
+ ("backspace", Modifiers::Shift) => Some("\x7f".to_string()),
+ ("home", Modifiers::Shift) if mode.contains(TermMode::ALT_SCREEN) => {
+ Some("\x1b[1;2H".to_string())
+ }
+ ("end", Modifiers::Shift) if mode.contains(TermMode::ALT_SCREEN) => {
+ Some("\x1b[1;2F".to_string())
+ }
+ ("pageup", Modifiers::Shift) if mode.contains(TermMode::ALT_SCREEN) => {
+ Some("\x1b[5;2~".to_string())
+ }
+ ("pagedown", Modifiers::Shift) if mode.contains(TermMode::ALT_SCREEN) => {
+ Some("\x1b[6;2~".to_string())
+ }
+ ("home", Modifiers::None) if mode.contains(TermMode::APP_CURSOR) => {
+ Some("\x1bOH".to_string())
+ }
+ ("home", Modifiers::None) if !mode.contains(TermMode::APP_CURSOR) => {
+ Some("\x1b[H".to_string())
+ }
+ ("end", Modifiers::None) if mode.contains(TermMode::APP_CURSOR) => {
+ Some("\x1bOF".to_string())
+ }
+ ("end", Modifiers::None) if !mode.contains(TermMode::APP_CURSOR) => {
+ Some("\x1b[F".to_string())
+ }
+ ("up", Modifiers::None) if mode.contains(TermMode::APP_CURSOR) => {
+ Some("\x1bOA".to_string())
+ }
+ ("up", Modifiers::None) if !mode.contains(TermMode::APP_CURSOR) => {
+ Some("\x1b[A".to_string())
+ }
+ ("down", Modifiers::None) if mode.contains(TermMode::APP_CURSOR) => {
+ Some("\x1bOB".to_string())
+ }
+ ("down", Modifiers::None) if !mode.contains(TermMode::APP_CURSOR) => {
+ Some("\x1b[B".to_string())
+ }
+ ("right", Modifiers::None) if mode.contains(TermMode::APP_CURSOR) => {
+ Some("\x1bOC".to_string())
+ }
+ ("right", Modifiers::None) if !mode.contains(TermMode::APP_CURSOR) => {
+ Some("\x1b[C".to_string())
+ }
+ ("left", Modifiers::None) if mode.contains(TermMode::APP_CURSOR) => {
+ Some("\x1bOD".to_string())
+ }
+ ("left", Modifiers::None) if !mode.contains(TermMode::APP_CURSOR) => {
+ Some("\x1b[D".to_string())
+ }
+ ("back", Modifiers::None) => Some("\x7f".to_string()),
+ ("insert", Modifiers::None) => Some("\x1b[2~".to_string()),
+ ("delete", Modifiers::None) => Some("\x1b[3~".to_string()),
+ ("pageup", Modifiers::None) => Some("\x1b[5~".to_string()),
+ ("pagedown", Modifiers::None) => Some("\x1b[6~".to_string()),
+ ("f1", Modifiers::None) => Some("\x1bOP".to_string()),
+ ("f2", Modifiers::None) => Some("\x1bOQ".to_string()),
+ ("f3", Modifiers::None) => Some("\x1bOR".to_string()),
+ ("f4", Modifiers::None) => Some("\x1bOS".to_string()),
+ ("f5", Modifiers::None) => Some("\x1b[15~".to_string()),
+ ("f6", Modifiers::None) => Some("\x1b[17~".to_string()),
+ ("f7", Modifiers::None) => Some("\x1b[18~".to_string()),
+ ("f8", Modifiers::None) => Some("\x1b[19~".to_string()),
+ ("f9", Modifiers::None) => Some("\x1b[20~".to_string()),
+ ("f10", Modifiers::None) => Some("\x1b[21~".to_string()),
+ ("f11", Modifiers::None) => Some("\x1b[23~".to_string()),
+ ("f12", Modifiers::None) => Some("\x1b[24~".to_string()),
+ ("f13", Modifiers::None) => Some("\x1b[25~".to_string()),
+ ("f14", Modifiers::None) => Some("\x1b[26~".to_string()),
+ ("f15", Modifiers::None) => Some("\x1b[28~".to_string()),
+ ("f16", Modifiers::None) => Some("\x1b[29~".to_string()),
+ ("f17", Modifiers::None) => Some("\x1b[31~".to_string()),
+ ("f18", Modifiers::None) => Some("\x1b[32~".to_string()),
+ ("f19", Modifiers::None) => Some("\x1b[33~".to_string()),
+ ("f20", Modifiers::None) => Some("\x1b[34~".to_string()),
+ // NumpadEnter, Action::Esc("\n".into());
+ //Mappings for caret notation keys
+ ("a", Modifiers::Ctrl) => Some("\x01".to_string()), //1
+ ("A", Modifiers::CtrlShift) => Some("\x01".to_string()), //1
+ ("b", Modifiers::Ctrl) => Some("\x02".to_string()), //2
+ ("B", Modifiers::CtrlShift) => Some("\x02".to_string()), //2
+ ("c", Modifiers::Ctrl) => Some("\x03".to_string()), //3
+ ("C", Modifiers::CtrlShift) => Some("\x03".to_string()), //3
+ ("d", Modifiers::Ctrl) => Some("\x04".to_string()), //4
+ ("D", Modifiers::CtrlShift) => Some("\x04".to_string()), //4
+ ("e", Modifiers::Ctrl) => Some("\x05".to_string()), //5
+ ("E", Modifiers::CtrlShift) => Some("\x05".to_string()), //5
+ ("f", Modifiers::Ctrl) => Some("\x06".to_string()), //6
+ ("F", Modifiers::CtrlShift) => Some("\x06".to_string()), //6
+ ("g", Modifiers::Ctrl) => Some("\x07".to_string()), //7
+ ("G", Modifiers::CtrlShift) => Some("\x07".to_string()), //7
+ ("h", Modifiers::Ctrl) => Some("\x08".to_string()), //8
+ ("H", Modifiers::CtrlShift) => Some("\x08".to_string()), //8
+ ("i", Modifiers::Ctrl) => Some("\x09".to_string()), //9
+ ("I", Modifiers::CtrlShift) => Some("\x09".to_string()), //9
+ ("j", Modifiers::Ctrl) => Some("\x0a".to_string()), //10
+ ("J", Modifiers::CtrlShift) => Some("\x0a".to_string()), //10
+ ("k", Modifiers::Ctrl) => Some("\x0b".to_string()), //11
+ ("K", Modifiers::CtrlShift) => Some("\x0b".to_string()), //11
+ ("l", Modifiers::Ctrl) => Some("\x0c".to_string()), //12
+ ("L", Modifiers::CtrlShift) => Some("\x0c".to_string()), //12
+ ("m", Modifiers::Ctrl) => Some("\x0d".to_string()), //13
+ ("M", Modifiers::CtrlShift) => Some("\x0d".to_string()), //13
+ ("n", Modifiers::Ctrl) => Some("\x0e".to_string()), //14
+ ("N", Modifiers::CtrlShift) => Some("\x0e".to_string()), //14
+ ("o", Modifiers::Ctrl) => Some("\x0f".to_string()), //15
+ ("O", Modifiers::CtrlShift) => Some("\x0f".to_string()), //15
+ ("p", Modifiers::Ctrl) => Some("\x10".to_string()), //16
+ ("P", Modifiers::CtrlShift) => Some("\x10".to_string()), //16
+ ("q", Modifiers::Ctrl) => Some("\x11".to_string()), //17
+ ("Q", Modifiers::CtrlShift) => Some("\x11".to_string()), //17
+ ("r", Modifiers::Ctrl) => Some("\x12".to_string()), //18
+ ("R", Modifiers::CtrlShift) => Some("\x12".to_string()), //18
+ ("s", Modifiers::Ctrl) => Some("\x13".to_string()), //19
+ ("S", Modifiers::CtrlShift) => Some("\x13".to_string()), //19
+ ("t", Modifiers::Ctrl) => Some("\x14".to_string()), //20
+ ("T", Modifiers::CtrlShift) => Some("\x14".to_string()), //20
+ ("u", Modifiers::Ctrl) => Some("\x15".to_string()), //21
+ ("U", Modifiers::CtrlShift) => Some("\x15".to_string()), //21
+ ("v", Modifiers::Ctrl) => Some("\x16".to_string()), //22
+ ("V", Modifiers::CtrlShift) => Some("\x16".to_string()), //22
+ ("w", Modifiers::Ctrl) => Some("\x17".to_string()), //23
+ ("W", Modifiers::CtrlShift) => Some("\x17".to_string()), //23
+ ("x", Modifiers::Ctrl) => Some("\x18".to_string()), //24
+ ("X", Modifiers::CtrlShift) => Some("\x18".to_string()), //24
+ ("y", Modifiers::Ctrl) => Some("\x19".to_string()), //25
+ ("Y", Modifiers::CtrlShift) => Some("\x19".to_string()), //25
+ ("z", Modifiers::Ctrl) => Some("\x1a".to_string()), //26
+ ("Z", Modifiers::CtrlShift) => Some("\x1a".to_string()), //26
+ ("@", Modifiers::Ctrl) => Some("\x00".to_string()), //0
+ ("[", Modifiers::Ctrl) => Some("\x1b".to_string()), //27
+ ("\\", Modifiers::Ctrl) => Some("\x1c".to_string()), //28
+ ("]", Modifiers::Ctrl) => Some("\x1d".to_string()), //29
+ ("^", Modifiers::Ctrl) => Some("\x1e".to_string()), //30
+ ("_", Modifiers::Ctrl) => Some("\x1f".to_string()), //31
+ ("?", Modifiers::Ctrl) => Some("\x7f".to_string()), //127
+ _ => None,
+ };
+ if manual_esc_str.is_some() {
+ return manual_esc_str;
+ }
+
+ // Automated bindings applying modifiers
+ if modifiers.any() {
+ let modifier_code = modifier_code(&keystroke);
+ let modified_esc_str = match keystroke.key.as_ref() {
+ "up" => Some(format!("\x1b[1;{}A", modifier_code)),
+ "down" => Some(format!("\x1b[1;{}B", modifier_code)),
+ "right" => Some(format!("\x1b[1;{}C", modifier_code)),
+ "left" => Some(format!("\x1b[1;{}D", modifier_code)),
+ "f1" => Some(format!("\x1b[1;{}P", modifier_code)),
+ "f2" => Some(format!("\x1b[1;{}Q", modifier_code)),
+ "f3" => Some(format!("\x1b[1;{}R", modifier_code)),
+ "f4" => Some(format!("\x1b[1;{}S", modifier_code)),
+ "F5" => Some(format!("\x1b[15;{}~", modifier_code)),
+ "f6" => Some(format!("\x1b[17;{}~", modifier_code)),
+ "f7" => Some(format!("\x1b[18;{}~", modifier_code)),
+ "f8" => Some(format!("\x1b[19;{}~", modifier_code)),
+ "f9" => Some(format!("\x1b[20;{}~", modifier_code)),
+ "f10" => Some(format!("\x1b[21;{}~", modifier_code)),
+ "f11" => Some(format!("\x1b[23;{}~", modifier_code)),
+ "f12" => Some(format!("\x1b[24;{}~", modifier_code)),
+ "f13" => Some(format!("\x1b[25;{}~", modifier_code)),
+ "f14" => Some(format!("\x1b[26;{}~", modifier_code)),
+ "f15" => Some(format!("\x1b[28;{}~", modifier_code)),
+ "f16" => Some(format!("\x1b[29;{}~", modifier_code)),
+ "f17" => Some(format!("\x1b[31;{}~", modifier_code)),
+ "f18" => Some(format!("\x1b[32;{}~", modifier_code)),
+ "f19" => Some(format!("\x1b[33;{}~", modifier_code)),
+ "f20" => Some(format!("\x1b[34;{}~", modifier_code)),
+ _ if modifier_code == 2 => None,
+ "insert" => Some(format!("\x1b[2;{}~", modifier_code)),
+ "pageup" => Some(format!("\x1b[5;{}~", modifier_code)),
+ "pagedown" => Some(format!("\x1b[6;{}~", modifier_code)),
+ "end" => Some(format!("\x1b[1;{}F", modifier_code)),
+ "home" => Some(format!("\x1b[1;{}H", modifier_code)),
+ _ => None,
+ };
+ if modified_esc_str.is_some() {
+ return modified_esc_str;
+ }
+ }
+
+ //Fallback to sending the keystroke input directly
+ //Skin colors in utf8 are implemented as a seperate, invisible character
+ //that modifies the associated emoji. Some languages may have similarly
+ //implemented modifiers, e.g. certain diacritics that can be typed as a single character.
+ //This means that we need to assume some user input can result in multi-byte,
+ //multi-char strings. This is somewhat difficult, as GPUI normalizes all
+ //keys into a string representation. Hence, the check here to filter out GPUI
+ //keys that weren't captured above.
+ if !matches_gpui_key_str(&keystroke.key) {
+ return Some(keystroke.key.clone());
+ } else {
+ None
+ }
+}
+
+///Checks if the given string matches a GPUI key string.
+///Table made from reading the source at gpui/src/platform/mac/event.rs
+fn matches_gpui_key_str(str: &str) -> bool {
+ match str {
+ "backspace" => true,
+ "up" => true,
+ "down" => true,
+ "left" => true,
+ "right" => true,
+ "pageup" => true,
+ "pagedown" => true,
+ "home" => true,
+ "end" => true,
+ "delete" => true,
+ "enter" => true,
+ "escape" => true,
+ "tab" => true,
+ "f1" => true,
+ "f2" => true,
+ "f3" => true,
+ "f4" => true,
+ "f5" => true,
+ "f6" => true,
+ "f7" => true,
+ "f8" => true,
+ "f9" => true,
+ "f10" => true,
+ "f11" => true,
+ "f12" => true,
+ "space" => true,
+ _ => false,
+ }
+}
+
+/// Code Modifiers
+/// ---------+---------------------------
+/// 2 | Shift
+/// 3 | Alt
+/// 4 | Shift + Alt
+/// 5 | Control
+/// 6 | Shift + Control
+/// 7 | Alt + Control
+/// 8 | Shift + Alt + Control
+/// ---------+---------------------------
+/// from: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-PC-Style-Function-Keys
+fn modifier_code(keystroke: &Keystroke) -> u32 {
+ let mut modifier_code = 0;
+ if keystroke.shift {
+ modifier_code |= 1;
+ }
+ if keystroke.alt {
+ modifier_code |= 1 << 1;
+ }
+ if keystroke.ctrl {
+ modifier_code |= 1 << 2;
+ }
+ modifier_code + 1
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ #[test]
+ fn test_scroll_keys() {
+ //These keys should be handled by the scrolling element directly
+ //Need to signify this by returning 'None'
+ let shift_pageup = Keystroke::parse("shift-pageup").unwrap();
+ let shift_pagedown = Keystroke::parse("shift-pagedown").unwrap();
+ let shift_home = Keystroke::parse("shift-home").unwrap();
+ let shift_end = Keystroke::parse("shift-end").unwrap();
+
+ let none = TermMode::NONE;
+ assert_eq!(to_esc_str(&shift_pageup, &none), None);
+ assert_eq!(to_esc_str(&shift_pagedown, &none), None);
+ assert_eq!(to_esc_str(&shift_home, &none), None);
+ assert_eq!(to_esc_str(&shift_end, &none), None);
+
+ let alt_screen = TermMode::ALT_SCREEN;
+ assert_eq!(
+ to_esc_str(&shift_pageup, &alt_screen),
+ Some("\x1b[5;2~".to_string())
+ );
+ assert_eq!(
+ to_esc_str(&shift_pagedown, &alt_screen),
+ Some("\x1b[6;2~".to_string())
+ );
+ assert_eq!(
+ to_esc_str(&shift_home, &alt_screen),
+ Some("\x1b[1;2H".to_string())
+ );
+ assert_eq!(
+ to_esc_str(&shift_end, &alt_screen),
+ Some("\x1b[1;2F".to_string())
+ );
+
+ let pageup = Keystroke::parse("pageup").unwrap();
+ let pagedown = Keystroke::parse("pagedown").unwrap();
+ let any = TermMode::ANY;
+
+ assert_eq!(to_esc_str(&pageup, &any), Some("\x1b[5~".to_string()));
+ assert_eq!(to_esc_str(&pagedown, &any), Some("\x1b[6~".to_string()));
+ }
+
+ #[test]
+ fn test_multi_char_fallthrough() {
+ let ks = Keystroke {
+ ctrl: false,
+ alt: false,
+ shift: false,
+ cmd: false,
+
+ key: "🖖🏻".to_string(), //2 char string
+ };
+
+ assert_eq!(to_esc_str(&ks, &TermMode::NONE), Some("🖖🏻".to_string()));
+ }
+
+ #[test]
+ fn test_application_mode() {
+ let app_cursor = TermMode::APP_CURSOR;
+ let none = TermMode::NONE;
+
+ let up = Keystroke::parse("up").unwrap();
+ let down = Keystroke::parse("down").unwrap();
+ let left = Keystroke::parse("left").unwrap();
+ let right = Keystroke::parse("right").unwrap();
+
+ assert_eq!(to_esc_str(&up, &none), Some("\x1b[A".to_string()));
+ assert_eq!(to_esc_str(&down, &none), Some("\x1b[B".to_string()));
+ assert_eq!(to_esc_str(&right, &none), Some("\x1b[C".to_string()));
+ assert_eq!(to_esc_str(&left, &none), Some("\x1b[D".to_string()));
+
+ assert_eq!(to_esc_str(&up, &app_cursor), Some("\x1bOA".to_string()));
+ assert_eq!(to_esc_str(&down, &app_cursor), Some("\x1bOB".to_string()));
+ assert_eq!(to_esc_str(&right, &app_cursor), Some("\x1bOC".to_string()));
+ assert_eq!(to_esc_str(&left, &app_cursor), Some("\x1bOD".to_string()));
+ }
+
+ #[test]
+ fn test_ctrl_codes() {
+ let letters_lower = 'a'..='z';
+ let letters_upper = 'A'..='Z';
+ let mode = TermMode::ANY;
+
+ for (lower, upper) in letters_lower.zip(letters_upper) {
+ assert_eq!(
+ to_esc_str(
+ &Keystroke::parse(&format!("ctrl-{}", lower)).unwrap(),
+ &mode
+ ),
+ to_esc_str(
+ &Keystroke::parse(&format!("ctrl-shift-{}", upper)).unwrap(),
+ &mode
+ ),
+ "On letter: {}/{}",
+ lower,
+ upper
+ )
+ }
+ }
+
+ #[test]
+ fn test_modifier_code_calc() {
+ // Code Modifiers
+ // ---------+---------------------------
+ // 2 | Shift
+ // 3 | Alt
+ // 4 | Shift + Alt
+ // 5 | Control
+ // 6 | Shift + Control
+ // 7 | Alt + Control
+ // 8 | Shift + Alt + Control
+ // ---------+---------------------------
+ // from: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-PC-Style-Function-Keys
+ assert_eq!(2, modifier_code(&Keystroke::parse("shift-A").unwrap()));
+ assert_eq!(3, modifier_code(&Keystroke::parse("alt-A").unwrap()));
+ assert_eq!(4, modifier_code(&Keystroke::parse("shift-alt-A").unwrap()));
+ assert_eq!(5, modifier_code(&Keystroke::parse("ctrl-A").unwrap()));
+ assert_eq!(6, modifier_code(&Keystroke::parse("shift-ctrl-A").unwrap()));
+ assert_eq!(7, modifier_code(&Keystroke::parse("alt-ctrl-A").unwrap()));
+ assert_eq!(
+ 8,
+ modifier_code(&Keystroke::parse("shift-ctrl-alt-A").unwrap())
+ );
+ }
+}
@@ -5,7 +5,6 @@ pub mod terminal_element;
use alacritty_terminal::{
event::{Event as AlacTermEvent, EventListener},
- grid::Scroll,
term::SizeInfo,
};
@@ -14,7 +13,7 @@ use dirs::home_dir;
use editor::Input;
use futures::channel::mpsc::UnboundedSender;
use gpui::{
- actions, elements::*, impl_internal_actions, AppContext, ClipboardItem, Entity, ModelHandle,
+ actions, elements::*, keymap::Keystroke, AppContext, ClipboardItem, Entity, ModelHandle,
MutableAppContext, View, ViewContext,
};
use modal::deploy_modal;
@@ -27,16 +26,6 @@ use workspace::{Item, Workspace};
use crate::terminal_element::TerminalEl;
-//ASCII Control characters on a keyboard
-const ETX_CHAR: char = 3_u8 as char; //'End of text', the control code for 'ctrl-c'
-const TAB_CHAR: char = 9_u8 as char;
-const CARRIAGE_RETURN_CHAR: char = 13_u8 as char;
-const ESC_CHAR: char = 27_u8 as char; // == \x1b
-const DEL_CHAR: char = 127_u8 as char;
-const LEFT_SEQ: &str = "\x1b[D";
-const RIGHT_SEQ: &str = "\x1b[C";
-const UP_SEQ: &str = "\x1b[A";
-const DOWN_SEQ: &str = "\x1b[B";
const DEBUG_TERMINAL_WIDTH: f32 = 1000.; //This needs to be wide enough that the prompt can fill the whole space.
const DEBUG_TERMINAL_HEIGHT: f32 = 200.;
const DEBUG_CELL_WIDTH: f32 = 5.;
@@ -52,44 +41,34 @@ pub struct ScrollTerminal(pub i32);
actions!(
terminal,
[
- Sigint,
- Escape,
- Del,
- Return,
- Left,
- Right,
+ Deploy,
Up,
Down,
- Tab,
+ CtrlC,
+ Escape,
+ Enter,
Clear,
Copy,
Paste,
- Deploy,
- Quit,
- DeployModal,
+ DeployModal
]
);
-impl_internal_actions!(terminal, [ScrollTerminal]);
///Initialize and register all of our action handlers
pub fn init(cx: &mut MutableAppContext) {
- cx.add_action(Terminal::deploy);
- cx.add_action(Terminal::send_sigint);
- cx.add_action(Terminal::escape);
- cx.add_action(Terminal::quit);
- cx.add_action(Terminal::del);
- cx.add_action(Terminal::carriage_return);
- cx.add_action(Terminal::left);
- cx.add_action(Terminal::right);
+ //Global binding overrrides
+ cx.add_action(Terminal::ctrl_c);
cx.add_action(Terminal::up);
cx.add_action(Terminal::down);
- cx.add_action(Terminal::tab);
+ cx.add_action(Terminal::escape);
+ cx.add_action(Terminal::enter);
+ //Useful terminal actions
+ cx.add_action(Terminal::deploy);
+ cx.add_action(deploy_modal);
cx.add_action(Terminal::copy);
cx.add_action(Terminal::paste);
- cx.add_action(Terminal::scroll_terminal);
cx.add_action(Terminal::input);
cx.add_action(Terminal::clear);
- cx.add_action(deploy_modal);
}
///A translation struct for Alacritty to communicate with us from their event loop
@@ -168,15 +147,6 @@ impl Terminal {
}
}
- ///Scroll the terminal. This locks the terminal
- fn scroll_terminal(&mut self, scroll: &ScrollTerminal, cx: &mut ViewContext<Self>) {
- self.connection
- .read(cx)
- .term
- .lock()
- .scroll_display(Scroll::Delta(scroll.0));
- }
-
fn input(&mut self, Input(text): &Input, cx: &mut ViewContext<Self>) {
self.connection.update(cx, |connection, _| {
//TODO: This is probably not encoding UTF8 correctly (see alacritty/src/input.rs:L825-837)
@@ -200,11 +170,6 @@ impl Terminal {
workspace.add_item(Box::new(cx.add_view(|cx| Terminal::new(wd, false, cx))), cx);
}
- ///Tell Zed to close us
- fn quit(&mut self, _: &Quit, cx: &mut ViewContext<Self>) {
- cx.emit(Event::CloseTerminal);
- }
-
///Attempt to paste the clipboard into the terminal
fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
let term = self.connection.read(cx).term.lock();
@@ -224,66 +189,38 @@ impl Terminal {
}
}
- ///Send the `up` key
+ ///Synthesize the keyboard event corresponding to 'up'
fn up(&mut self, _: &Up, cx: &mut ViewContext<Self>) {
self.connection.update(cx, |connection, _| {
- connection.write_to_pty(UP_SEQ.to_string());
+ connection.try_keystroke(&Keystroke::parse("up").unwrap());
});
}
- ///Send the `down` key
+ ///Synthesize the keyboard event corresponding to 'down'
fn down(&mut self, _: &Down, cx: &mut ViewContext<Self>) {
self.connection.update(cx, |connection, _| {
- connection.write_to_pty(DOWN_SEQ.to_string());
+ connection.try_keystroke(&Keystroke::parse("down").unwrap());
});
}
- ///Send the `tab` key
- fn tab(&mut self, _: &Tab, cx: &mut ViewContext<Self>) {
+ ///Synthesize the keyboard event corresponding to 'ctrl-c'
+ fn ctrl_c(&mut self, _: &CtrlC, cx: &mut ViewContext<Self>) {
self.connection.update(cx, |connection, _| {
- connection.write_to_pty(TAB_CHAR.to_string());
+ connection.try_keystroke(&Keystroke::parse("ctrl-c").unwrap());
});
}
- ///Send `SIGINT` (`ctrl-c`)
- fn send_sigint(&mut self, _: &Sigint, cx: &mut ViewContext<Self>) {
- self.connection.update(cx, |connection, _| {
- connection.write_to_pty(ETX_CHAR.to_string());
- });
- }
-
- ///Send the `escape` key
+ ///Synthesize the keyboard event corresponding to 'escape'
fn escape(&mut self, _: &Escape, cx: &mut ViewContext<Self>) {
self.connection.update(cx, |connection, _| {
- connection.write_to_pty(ESC_CHAR.to_string());
- });
- }
-
- ///Send the `delete` key. TODO: Difference between this and backspace?
- fn del(&mut self, _: &Del, cx: &mut ViewContext<Self>) {
- self.connection.update(cx, |connection, _| {
- connection.write_to_pty(DEL_CHAR.to_string());
- });
- }
-
- ///Send a carriage return. TODO: May need to check the terminal mode.
- fn carriage_return(&mut self, _: &Return, cx: &mut ViewContext<Self>) {
- self.connection.update(cx, |connection, _| {
- connection.write_to_pty(CARRIAGE_RETURN_CHAR.to_string());
- });
- }
-
- //Send the `left` key
- fn left(&mut self, _: &Left, cx: &mut ViewContext<Self>) {
- self.connection.update(cx, |connection, _| {
- connection.write_to_pty(LEFT_SEQ.to_string());
+ connection.try_keystroke(&Keystroke::parse("escape").unwrap());
});
}
- //Send the `right` key
- fn right(&mut self, _: &Right, cx: &mut ViewContext<Self>) {
+ ///Synthesize the keyboard event corresponding to 'enter'
+ fn enter(&mut self, _: &Enter, cx: &mut ViewContext<Self>) {
self.connection.update(cx, |connection, _| {
- connection.write_to_pty(RIGHT_SEQ.to_string());
+ connection.try_keystroke(&Keystroke::parse("enter").unwrap());
});
}
}
@@ -486,8 +423,8 @@ mod tests {
///Integration test for selections, clipboard, and terminal execution
#[gpui::test(retries = 5)]
async fn test_copy(cx: &mut TestAppContext) {
- let mut cx = TerminalTestContext::new(cx);
+ let mut cx = TerminalTestContext::new(cx);
let grid_content = cx
.execute_and_wait("expr 3 + 4", |content, _cx| content.contains("7"))
.await;