Remove 2 suffix for collab, rope, settings, menu

Co-authored-by: Mikayla <mikayla@zed.dev>
This commit is contained in:
Max Brunsfeld 2024-01-03 12:28:45 -08:00
parent 177e3028a9
commit 0cf65223ce
234 changed files with 3765 additions and 42229 deletions

View file

@ -10,10 +10,10 @@ doctest = false
[dependencies]
gpui = { path = "../gpui" }
gpui = { package = "gpui2", path = "../gpui2" }
settings = { path = "../settings" }
db = { path = "../db" }
theme = { path = "../theme" }
theme = { package = "theme2", path = "../theme2" }
util = { path = "../util" }
alacritty_terminal = { git = "https://github.com/zed-industries/alacritty", rev = "33306142195b354ef3485ca2b1d8a85dfc6605ca" }

View file

@ -1,130 +1,12 @@
use alacritty_terminal::{ansi::Color as AnsiColor, term::color::Rgb as AlacRgb};
use gpui::color::Color;
use theme::TerminalStyle;
use alacritty_terminal::term::color::Rgb as AlacRgb;
///Converts a 2, 8, or 24 bit color ANSI color to the GPUI equivalent
pub fn convert_color(alac_color: &AnsiColor, style: &TerminalStyle) -> Color {
match alac_color {
//Named and theme defined colors
alacritty_terminal::ansi::Color::Named(n) => match n {
alacritty_terminal::ansi::NamedColor::Black => style.black,
alacritty_terminal::ansi::NamedColor::Red => style.red,
alacritty_terminal::ansi::NamedColor::Green => style.green,
alacritty_terminal::ansi::NamedColor::Yellow => style.yellow,
alacritty_terminal::ansi::NamedColor::Blue => style.blue,
alacritty_terminal::ansi::NamedColor::Magenta => style.magenta,
alacritty_terminal::ansi::NamedColor::Cyan => style.cyan,
alacritty_terminal::ansi::NamedColor::White => style.white,
alacritty_terminal::ansi::NamedColor::BrightBlack => style.bright_black,
alacritty_terminal::ansi::NamedColor::BrightRed => style.bright_red,
alacritty_terminal::ansi::NamedColor::BrightGreen => style.bright_green,
alacritty_terminal::ansi::NamedColor::BrightYellow => style.bright_yellow,
alacritty_terminal::ansi::NamedColor::BrightBlue => style.bright_blue,
alacritty_terminal::ansi::NamedColor::BrightMagenta => style.bright_magenta,
alacritty_terminal::ansi::NamedColor::BrightCyan => style.bright_cyan,
alacritty_terminal::ansi::NamedColor::BrightWhite => style.bright_white,
alacritty_terminal::ansi::NamedColor::Foreground => style.foreground,
alacritty_terminal::ansi::NamedColor::Background => style.background,
alacritty_terminal::ansi::NamedColor::Cursor => style.cursor,
alacritty_terminal::ansi::NamedColor::DimBlack => style.dim_black,
alacritty_terminal::ansi::NamedColor::DimRed => style.dim_red,
alacritty_terminal::ansi::NamedColor::DimGreen => style.dim_green,
alacritty_terminal::ansi::NamedColor::DimYellow => style.dim_yellow,
alacritty_terminal::ansi::NamedColor::DimBlue => style.dim_blue,
alacritty_terminal::ansi::NamedColor::DimMagenta => style.dim_magenta,
alacritty_terminal::ansi::NamedColor::DimCyan => style.dim_cyan,
alacritty_terminal::ansi::NamedColor::DimWhite => style.dim_white,
alacritty_terminal::ansi::NamedColor::BrightForeground => style.bright_foreground,
alacritty_terminal::ansi::NamedColor::DimForeground => style.dim_foreground,
},
//'True' colors
alacritty_terminal::ansi::Color::Spec(rgb) => Color::new(rgb.r, rgb.g, rgb.b, u8::MAX),
//8 bit, indexed colors
alacritty_terminal::ansi::Color::Indexed(i) => get_color_at_index(&(*i as usize), style),
}
}
///Converts an 8 bit ANSI color to it's GPUI equivalent.
///Accepts usize for compatibility with the alacritty::Colors interface,
///Other than that use case, should only be called with values in the [0,255] range
pub fn get_color_at_index(index: &usize, style: &TerminalStyle) -> Color {
match index {
//0-15 are the same as the named colors above
0 => style.black,
1 => style.red,
2 => style.green,
3 => style.yellow,
4 => style.blue,
5 => style.magenta,
6 => style.cyan,
7 => style.white,
8 => style.bright_black,
9 => style.bright_red,
10 => style.bright_green,
11 => style.bright_yellow,
12 => style.bright_blue,
13 => style.bright_magenta,
14 => style.bright_cyan,
15 => style.bright_white,
//16-231 are mapped to their RGB colors on a 0-5 range per channel
16..=231 => {
let (r, g, b) = rgb_for_index(&(*index as u8)); //Split the index into it's ANSI-RGB components
let step = (u8::MAX as f32 / 5.).floor() as u8; //Split the RGB range into 5 chunks, with floor so no overflow
Color::new(r * step, g * step, b * step, u8::MAX) //Map the ANSI-RGB components to an RGB color
}
//232-255 are a 24 step grayscale from black to white
232..=255 => {
let i = *index as u8 - 232; //Align index to 0..24
let step = (u8::MAX as f32 / 24.).floor() as u8; //Split the RGB grayscale values into 24 chunks
Color::new(i * step, i * step, i * step, u8::MAX) //Map the ANSI-grayscale components to the RGB-grayscale
}
//For compatibility with the alacritty::Colors interface
256 => style.foreground,
257 => style.background,
258 => style.cursor,
259 => style.dim_black,
260 => style.dim_red,
261 => style.dim_green,
262 => style.dim_yellow,
263 => style.dim_blue,
264 => style.dim_magenta,
265 => style.dim_cyan,
266 => style.dim_white,
267 => style.bright_foreground,
268 => style.black, //'Dim Background', non-standard color
_ => Color::new(0, 0, 0, 255),
}
}
///Generates the rgb channels in [0, 5] for a given index into the 6x6x6 ANSI color cube
///See: [8 bit ansi color](https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit).
///
///Wikipedia gives a formula for calculating the index for a given color:
///
///index = 16 + 36 × r + 6 × g + b (0 ≤ r, g, b ≤ 5)
///
///This function does the reverse, calculating the r, g, and b components from a given index.
fn rgb_for_index(i: &u8) -> (u8, u8, u8) {
debug_assert!((&16..=&231).contains(&i));
let i = i - 16;
let r = (i - (i % 36)) / 36;
let g = ((i % 36) - (i % 6)) / 6;
let b = (i % 36) % 6;
(r, g, b)
}
use gpui::Rgba;
//Convenience method to convert from a GPUI color to an alacritty Rgb
pub fn to_alac_rgb(color: Color) -> AlacRgb {
AlacRgb::new(color.r, color.g, color.g)
}
#[cfg(test)]
mod tests {
#[test]
fn test_rgb_for_index() {
//Test every possible value in the color cube
for i in 16..=231 {
let (r, g, b) = crate::mappings::colors::rgb_for_index(&(i as u8));
assert_eq!(i, 16 + 36 * r + 6 * g + b);
}
}
pub fn to_alac_rgb(color: impl Into<Rgba>) -> AlacRgb {
let color = color.into();
let r = ((color.r * color.a) * 255.) as u8;
let g = ((color.g * color.a) * 255.) as u8;
let b = ((color.b * color.a) * 255.) as u8;
AlacRgb::new(r, g, b)
}

View file

@ -1,9 +1,9 @@
/// The mappings defined in this file where created from reading the alacritty source
use alacritty_terminal::term::TermMode;
use gpui::keymap_matcher::Keystroke;
use gpui::Keystroke;
#[derive(Debug, PartialEq, Eq)]
pub enum Modifiers {
enum AlacModifiers {
None,
Alt,
Ctrl,
@ -12,179 +12,184 @@ pub enum Modifiers {
Other,
}
impl Modifiers {
impl AlacModifiers {
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,
match (
ks.modifiers.alt,
ks.modifiers.control,
ks.modifiers.shift,
ks.modifiers.command,
) {
(false, false, false, false) => AlacModifiers::None,
(true, false, false, false) => AlacModifiers::Alt,
(false, true, false, false) => AlacModifiers::Ctrl,
(false, false, true, false) => AlacModifiers::Shift,
(false, true, true, false) => AlacModifiers::CtrlShift,
_ => AlacModifiers::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,
AlacModifiers::None => false,
AlacModifiers::Alt => true,
AlacModifiers::Ctrl => true,
AlacModifiers::Shift => true,
AlacModifiers::CtrlShift => true,
AlacModifiers::Other => true,
}
}
}
pub fn to_esc_str(keystroke: &Keystroke, mode: &TermMode, alt_is_meta: bool) -> Option<String> {
let modifiers = Modifiers::new(keystroke);
let modifiers = AlacModifiers::new(keystroke);
// Manual Bindings including modifiers
let manual_esc_str = match (keystroke.key.as_ref(), &modifiers) {
//Basic special keys
("tab", Modifiers::None) => Some("\x09".to_string()),
("escape", Modifiers::None) => Some("\x1b".to_string()),
("enter", Modifiers::None) => Some("\x0d".to_string()),
("enter", Modifiers::Shift) => Some("\x0d".to_string()),
("backspace", Modifiers::None) => Some("\x7f".to_string()),
("tab", AlacModifiers::None) => Some("\x09".to_string()),
("escape", AlacModifiers::None) => Some("\x1b".to_string()),
("enter", AlacModifiers::None) => Some("\x0d".to_string()),
("enter", AlacModifiers::Shift) => Some("\x0d".to_string()),
("backspace", AlacModifiers::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) => {
("tab", AlacModifiers::Shift) => Some("\x1b[Z".to_string()),
("backspace", AlacModifiers::Alt) => Some("\x1b\x7f".to_string()),
("backspace", AlacModifiers::Shift) => Some("\x7f".to_string()),
("home", AlacModifiers::Shift) if mode.contains(TermMode::ALT_SCREEN) => {
Some("\x1b[1;2H".to_string())
}
("end", Modifiers::Shift) if mode.contains(TermMode::ALT_SCREEN) => {
("end", AlacModifiers::Shift) if mode.contains(TermMode::ALT_SCREEN) => {
Some("\x1b[1;2F".to_string())
}
("pageup", Modifiers::Shift) if mode.contains(TermMode::ALT_SCREEN) => {
("pageup", AlacModifiers::Shift) if mode.contains(TermMode::ALT_SCREEN) => {
Some("\x1b[5;2~".to_string())
}
("pagedown", Modifiers::Shift) if mode.contains(TermMode::ALT_SCREEN) => {
("pagedown", AlacModifiers::Shift) if mode.contains(TermMode::ALT_SCREEN) => {
Some("\x1b[6;2~".to_string())
}
("home", Modifiers::None) if mode.contains(TermMode::APP_CURSOR) => {
("home", AlacModifiers::None) if mode.contains(TermMode::APP_CURSOR) => {
Some("\x1bOH".to_string())
}
("home", Modifiers::None) if !mode.contains(TermMode::APP_CURSOR) => {
("home", AlacModifiers::None) if !mode.contains(TermMode::APP_CURSOR) => {
Some("\x1b[H".to_string())
}
("end", Modifiers::None) if mode.contains(TermMode::APP_CURSOR) => {
("end", AlacModifiers::None) if mode.contains(TermMode::APP_CURSOR) => {
Some("\x1bOF".to_string())
}
("end", Modifiers::None) if !mode.contains(TermMode::APP_CURSOR) => {
("end", AlacModifiers::None) if !mode.contains(TermMode::APP_CURSOR) => {
Some("\x1b[F".to_string())
}
("up", Modifiers::None) if mode.contains(TermMode::APP_CURSOR) => {
("up", AlacModifiers::None) if mode.contains(TermMode::APP_CURSOR) => {
Some("\x1bOA".to_string())
}
("up", Modifiers::None) if !mode.contains(TermMode::APP_CURSOR) => {
("up", AlacModifiers::None) if !mode.contains(TermMode::APP_CURSOR) => {
Some("\x1b[A".to_string())
}
("down", Modifiers::None) if mode.contains(TermMode::APP_CURSOR) => {
("down", AlacModifiers::None) if mode.contains(TermMode::APP_CURSOR) => {
Some("\x1bOB".to_string())
}
("down", Modifiers::None) if !mode.contains(TermMode::APP_CURSOR) => {
("down", AlacModifiers::None) if !mode.contains(TermMode::APP_CURSOR) => {
Some("\x1b[B".to_string())
}
("right", Modifiers::None) if mode.contains(TermMode::APP_CURSOR) => {
("right", AlacModifiers::None) if mode.contains(TermMode::APP_CURSOR) => {
Some("\x1bOC".to_string())
}
("right", Modifiers::None) if !mode.contains(TermMode::APP_CURSOR) => {
("right", AlacModifiers::None) if !mode.contains(TermMode::APP_CURSOR) => {
Some("\x1b[C".to_string())
}
("left", Modifiers::None) if mode.contains(TermMode::APP_CURSOR) => {
("left", AlacModifiers::None) if mode.contains(TermMode::APP_CURSOR) => {
Some("\x1bOD".to_string())
}
("left", Modifiers::None) if !mode.contains(TermMode::APP_CURSOR) => {
("left", AlacModifiers::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()),
("back", AlacModifiers::None) => Some("\x7f".to_string()),
("insert", AlacModifiers::None) => Some("\x1b[2~".to_string()),
("delete", AlacModifiers::None) => Some("\x1b[3~".to_string()),
("pageup", AlacModifiers::None) => Some("\x1b[5~".to_string()),
("pagedown", AlacModifiers::None) => Some("\x1b[6~".to_string()),
("f1", AlacModifiers::None) => Some("\x1bOP".to_string()),
("f2", AlacModifiers::None) => Some("\x1bOQ".to_string()),
("f3", AlacModifiers::None) => Some("\x1bOR".to_string()),
("f4", AlacModifiers::None) => Some("\x1bOS".to_string()),
("f5", AlacModifiers::None) => Some("\x1b[15~".to_string()),
("f6", AlacModifiers::None) => Some("\x1b[17~".to_string()),
("f7", AlacModifiers::None) => Some("\x1b[18~".to_string()),
("f8", AlacModifiers::None) => Some("\x1b[19~".to_string()),
("f9", AlacModifiers::None) => Some("\x1b[20~".to_string()),
("f10", AlacModifiers::None) => Some("\x1b[21~".to_string()),
("f11", AlacModifiers::None) => Some("\x1b[23~".to_string()),
("f12", AlacModifiers::None) => Some("\x1b[24~".to_string()),
("f13", AlacModifiers::None) => Some("\x1b[25~".to_string()),
("f14", AlacModifiers::None) => Some("\x1b[26~".to_string()),
("f15", AlacModifiers::None) => Some("\x1b[28~".to_string()),
("f16", AlacModifiers::None) => Some("\x1b[29~".to_string()),
("f17", AlacModifiers::None) => Some("\x1b[31~".to_string()),
("f18", AlacModifiers::None) => Some("\x1b[32~".to_string()),
("f19", AlacModifiers::None) => Some("\x1b[33~".to_string()),
("f20", AlacModifiers::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
("a", AlacModifiers::Ctrl) => Some("\x01".to_string()), //1
("A", AlacModifiers::CtrlShift) => Some("\x01".to_string()), //1
("b", AlacModifiers::Ctrl) => Some("\x02".to_string()), //2
("B", AlacModifiers::CtrlShift) => Some("\x02".to_string()), //2
("c", AlacModifiers::Ctrl) => Some("\x03".to_string()), //3
("C", AlacModifiers::CtrlShift) => Some("\x03".to_string()), //3
("d", AlacModifiers::Ctrl) => Some("\x04".to_string()), //4
("D", AlacModifiers::CtrlShift) => Some("\x04".to_string()), //4
("e", AlacModifiers::Ctrl) => Some("\x05".to_string()), //5
("E", AlacModifiers::CtrlShift) => Some("\x05".to_string()), //5
("f", AlacModifiers::Ctrl) => Some("\x06".to_string()), //6
("F", AlacModifiers::CtrlShift) => Some("\x06".to_string()), //6
("g", AlacModifiers::Ctrl) => Some("\x07".to_string()), //7
("G", AlacModifiers::CtrlShift) => Some("\x07".to_string()), //7
("h", AlacModifiers::Ctrl) => Some("\x08".to_string()), //8
("H", AlacModifiers::CtrlShift) => Some("\x08".to_string()), //8
("i", AlacModifiers::Ctrl) => Some("\x09".to_string()), //9
("I", AlacModifiers::CtrlShift) => Some("\x09".to_string()), //9
("j", AlacModifiers::Ctrl) => Some("\x0a".to_string()), //10
("J", AlacModifiers::CtrlShift) => Some("\x0a".to_string()), //10
("k", AlacModifiers::Ctrl) => Some("\x0b".to_string()), //11
("K", AlacModifiers::CtrlShift) => Some("\x0b".to_string()), //11
("l", AlacModifiers::Ctrl) => Some("\x0c".to_string()), //12
("L", AlacModifiers::CtrlShift) => Some("\x0c".to_string()), //12
("m", AlacModifiers::Ctrl) => Some("\x0d".to_string()), //13
("M", AlacModifiers::CtrlShift) => Some("\x0d".to_string()), //13
("n", AlacModifiers::Ctrl) => Some("\x0e".to_string()), //14
("N", AlacModifiers::CtrlShift) => Some("\x0e".to_string()), //14
("o", AlacModifiers::Ctrl) => Some("\x0f".to_string()), //15
("O", AlacModifiers::CtrlShift) => Some("\x0f".to_string()), //15
("p", AlacModifiers::Ctrl) => Some("\x10".to_string()), //16
("P", AlacModifiers::CtrlShift) => Some("\x10".to_string()), //16
("q", AlacModifiers::Ctrl) => Some("\x11".to_string()), //17
("Q", AlacModifiers::CtrlShift) => Some("\x11".to_string()), //17
("r", AlacModifiers::Ctrl) => Some("\x12".to_string()), //18
("R", AlacModifiers::CtrlShift) => Some("\x12".to_string()), //18
("s", AlacModifiers::Ctrl) => Some("\x13".to_string()), //19
("S", AlacModifiers::CtrlShift) => Some("\x13".to_string()), //19
("t", AlacModifiers::Ctrl) => Some("\x14".to_string()), //20
("T", AlacModifiers::CtrlShift) => Some("\x14".to_string()), //20
("u", AlacModifiers::Ctrl) => Some("\x15".to_string()), //21
("U", AlacModifiers::CtrlShift) => Some("\x15".to_string()), //21
("v", AlacModifiers::Ctrl) => Some("\x16".to_string()), //22
("V", AlacModifiers::CtrlShift) => Some("\x16".to_string()), //22
("w", AlacModifiers::Ctrl) => Some("\x17".to_string()), //23
("W", AlacModifiers::CtrlShift) => Some("\x17".to_string()), //23
("x", AlacModifiers::Ctrl) => Some("\x18".to_string()), //24
("X", AlacModifiers::CtrlShift) => Some("\x18".to_string()), //24
("y", AlacModifiers::Ctrl) => Some("\x19".to_string()), //25
("Y", AlacModifiers::CtrlShift) => Some("\x19".to_string()), //25
("z", AlacModifiers::Ctrl) => Some("\x1a".to_string()), //26
("Z", AlacModifiers::CtrlShift) => Some("\x1a".to_string()), //26
("@", AlacModifiers::Ctrl) => Some("\x00".to_string()), //0
("[", AlacModifiers::Ctrl) => Some("\x1b".to_string()), //27
("\\", AlacModifiers::Ctrl) => Some("\x1c".to_string()), //28
("]", AlacModifiers::Ctrl) => Some("\x1d".to_string()), //29
("^", AlacModifiers::Ctrl) => Some("\x1e".to_string()), //30
("_", AlacModifiers::Ctrl) => Some("\x1f".to_string()), //31
("?", AlacModifiers::Ctrl) => Some("\x7f".to_string()), //127
_ => None,
};
if manual_esc_str.is_some() {
@ -232,12 +237,12 @@ pub fn to_esc_str(keystroke: &Keystroke, mode: &TermMode, alt_is_meta: bool) ->
}
}
let alt_meta_binding = if alt_is_meta && modifiers == Modifiers::Alt && keystroke.key.is_ascii()
{
Some(format!("\x1b{}", keystroke.key))
} else {
None
};
let alt_meta_binding =
if alt_is_meta && modifiers == AlacModifiers::Alt && keystroke.key.is_ascii() {
Some(format!("\x1b{}", keystroke.key))
} else {
None
};
if alt_meta_binding.is_some() {
return alt_meta_binding;
@ -259,13 +264,13 @@ pub fn to_esc_str(keystroke: &Keystroke, mode: &TermMode, alt_is_meta: bool) ->
/// 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 {
if keystroke.modifiers.shift {
modifier_code |= 1;
}
if keystroke.alt {
if keystroke.modifiers.alt {
modifier_code |= 1 << 1;
}
if keystroke.ctrl {
if keystroke.modifiers.control {
modifier_code |= 1 << 2;
}
modifier_code + 1
@ -273,7 +278,7 @@ fn modifier_code(keystroke: &Keystroke) -> u32 {
#[cfg(test)]
mod test {
use gpui::keymap_matcher::Keystroke;
use gpui::Modifiers;
use super::*;
@ -327,11 +332,13 @@ mod test {
#[test]
fn test_plain_inputs() {
let ks = Keystroke {
ctrl: false,
alt: false,
shift: false,
cmd: false,
function: false,
modifiers: Modifiers {
control: false,
alt: false,
shift: false,
command: false,
function: false,
},
key: "🖖🏻".to_string(), //2 char string
ime_key: None,
};

View file

@ -1,52 +1,15 @@
use std::cmp::{max, min};
use std::cmp::{self, max, min};
use std::iter::repeat;
use alacritty_terminal::grid::Dimensions;
/// Most of the code, and specifically the constants, in this are copied from Alacritty,
/// with modifications for our circumstances
use alacritty_terminal::index::{Column as GridCol, Line as GridLine, Point, Side};
use alacritty_terminal::index::{Column as GridCol, Line as GridLine, Point as AlacPoint, Side};
use alacritty_terminal::term::TermMode;
use gpui::platform;
use gpui::scene::MouseScrollWheel;
use gpui::{
geometry::vector::Vector2F,
platform::{MouseButtonEvent, MouseMovedEvent, ScrollWheelEvent},
};
use gpui::{px, Modifiers, MouseButton, MouseMoveEvent, Pixels, Point, ScrollWheelEvent};
use crate::TerminalSize;
struct Modifiers {
ctrl: bool,
shift: bool,
alt: bool,
}
impl Modifiers {
fn from_moved(e: &MouseMovedEvent) -> Self {
Modifiers {
ctrl: e.ctrl,
shift: e.shift,
alt: e.alt,
}
}
fn from_button(e: &MouseButtonEvent) -> Self {
Modifiers {
ctrl: e.ctrl,
shift: e.shift,
alt: e.alt,
}
}
fn from_scroll(scroll: &ScrollWheelEvent) -> Self {
Modifiers {
ctrl: scroll.ctrl,
shift: scroll.shift,
alt: scroll.alt,
}
}
}
enum MouseFormat {
SGR,
Normal(bool),
@ -65,7 +28,7 @@ impl MouseFormat {
}
#[derive(Debug)]
enum MouseButton {
enum AlacMouseButton {
LeftButton = 0,
MiddleButton = 1,
RightButton = 2,
@ -78,56 +41,61 @@ enum MouseButton {
Other = 99,
}
impl MouseButton {
fn from_move(e: &MouseMovedEvent) -> Self {
impl AlacMouseButton {
fn from_move(e: &MouseMoveEvent) -> Self {
match e.pressed_button {
Some(b) => match b {
platform::MouseButton::Left => MouseButton::LeftMove,
platform::MouseButton::Middle => MouseButton::MiddleMove,
platform::MouseButton::Right => MouseButton::RightMove,
platform::MouseButton::Navigate(_) => MouseButton::Other,
gpui::MouseButton::Left => AlacMouseButton::LeftMove,
gpui::MouseButton::Middle => AlacMouseButton::MiddleMove,
gpui::MouseButton::Right => AlacMouseButton::RightMove,
gpui::MouseButton::Navigate(_) => AlacMouseButton::Other,
},
None => MouseButton::NoneMove,
None => AlacMouseButton::NoneMove,
}
}
fn from_button(e: &MouseButtonEvent) -> Self {
match e.button {
platform::MouseButton::Left => MouseButton::LeftButton,
platform::MouseButton::Right => MouseButton::MiddleButton,
platform::MouseButton::Middle => MouseButton::RightButton,
platform::MouseButton::Navigate(_) => MouseButton::Other,
fn from_button(e: MouseButton) -> Self {
match e {
gpui::MouseButton::Left => AlacMouseButton::LeftButton,
gpui::MouseButton::Right => AlacMouseButton::MiddleButton,
gpui::MouseButton::Middle => AlacMouseButton::RightButton,
gpui::MouseButton::Navigate(_) => AlacMouseButton::Other,
}
}
fn from_scroll(e: &ScrollWheelEvent) -> Self {
if e.delta.raw().y() > 0. {
MouseButton::ScrollUp
let is_positive = match e.delta {
gpui::ScrollDelta::Pixels(pixels) => pixels.y > px(0.),
gpui::ScrollDelta::Lines(lines) => lines.y > 0.,
};
if is_positive {
AlacMouseButton::ScrollUp
} else {
MouseButton::ScrollDown
AlacMouseButton::ScrollDown
}
}
fn is_other(&self) -> bool {
match self {
MouseButton::Other => true,
AlacMouseButton::Other => true,
_ => false,
}
}
}
pub fn scroll_report(
point: Point,
point: AlacPoint,
scroll_lines: i32,
e: &MouseScrollWheel,
e: &ScrollWheelEvent,
mode: TermMode,
) -> Option<impl Iterator<Item = Vec<u8>>> {
if mode.intersects(TermMode::MOUSE_MODE) {
mouse_report(
point,
MouseButton::from_scroll(e),
AlacMouseButton::from_scroll(e),
true,
Modifiers::from_scroll(e),
e.modifiers,
MouseFormat::from_mode(mode),
)
.map(|report| repeat(report).take(max(scroll_lines, 1) as usize))
@ -149,18 +117,19 @@ pub fn alt_scroll(scroll_lines: i32) -> Vec<u8> {
}
pub fn mouse_button_report(
point: Point,
e: &MouseButtonEvent,
point: AlacPoint,
button: gpui::MouseButton,
modifiers: Modifiers,
pressed: bool,
mode: TermMode,
) -> Option<Vec<u8>> {
let button = MouseButton::from_button(e);
let button = AlacMouseButton::from_button(button);
if !button.is_other() && mode.intersects(TermMode::MOUSE_MODE) {
mouse_report(
point,
button,
pressed,
Modifiers::from_button(e),
modifiers,
MouseFormat::from_mode(mode),
)
} else {
@ -168,19 +137,19 @@ pub fn mouse_button_report(
}
}
pub fn mouse_moved_report(point: Point, e: &MouseMovedEvent, mode: TermMode) -> Option<Vec<u8>> {
let button = MouseButton::from_move(e);
pub fn mouse_moved_report(point: AlacPoint, e: &MouseMoveEvent, mode: TermMode) -> Option<Vec<u8>> {
let button = AlacMouseButton::from_move(e);
if !button.is_other() && mode.intersects(TermMode::MOUSE_MOTION | TermMode::MOUSE_DRAG) {
//Only drags are reported in drag mode, so block NoneMove.
if mode.contains(TermMode::MOUSE_DRAG) && matches!(button, MouseButton::NoneMove) {
if mode.contains(TermMode::MOUSE_DRAG) && matches!(button, AlacMouseButton::NoneMove) {
None
} else {
mouse_report(
point,
button,
true,
Modifiers::from_moved(e),
e.modifiers,
MouseFormat::from_mode(mode),
)
}
@ -189,19 +158,26 @@ pub fn mouse_moved_report(point: Point, e: &MouseMovedEvent, mode: TermMode) ->
}
}
pub fn mouse_side(pos: Vector2F, cur_size: TerminalSize) -> alacritty_terminal::index::Direction {
if cur_size.cell_width as usize == 0 {
pub fn mouse_side(
pos: Point<Pixels>,
cur_size: TerminalSize,
) -> alacritty_terminal::index::Direction {
let cell_width = cur_size.cell_width.floor();
if cell_width == px(0.) {
return Side::Right;
}
let x = pos.0.x() as usize;
let cell_x = x.saturating_sub(cur_size.cell_width as usize) % cur_size.cell_width as usize;
let half_cell_width = (cur_size.cell_width / 2.0) as usize;
let x = pos.x.floor();
let cell_x = cmp::max(px(0.), x - cell_width) % cell_width;
let half_cell_width = (cur_size.cell_width / 2.0).floor();
let additional_padding = (cur_size.width() - cur_size.cell_width * 2.) % cur_size.cell_width;
let end_of_grid = cur_size.width() - cur_size.cell_width - additional_padding;
//Width: Pixels or columns?
if cell_x > half_cell_width
// Edge case when mouse leaves the window.
|| x as f32 >= end_of_grid
|| x >= end_of_grid
{
Side::Right
} else {
@ -209,18 +185,18 @@ pub fn mouse_side(pos: Vector2F, cur_size: TerminalSize) -> alacritty_terminal::
}
}
pub fn grid_point(pos: Vector2F, cur_size: TerminalSize, display_offset: usize) -> Point {
let col = pos.x() / cur_size.cell_width;
let col = min(GridCol(col as usize), cur_size.last_column());
let line = pos.y() / cur_size.line_height;
let line = min(line as i32, cur_size.bottommost_line().0);
Point::new(GridLine(line - display_offset as i32), col)
pub fn grid_point(pos: Point<Pixels>, cur_size: TerminalSize, display_offset: usize) -> AlacPoint {
let col = GridCol((pos.x / cur_size.cell_width) as usize);
let col = min(col, cur_size.last_column());
let line = (pos.y / cur_size.line_height) as i32;
let line = min(line, cur_size.bottommost_line().0);
AlacPoint::new(GridLine(line - display_offset as i32), col)
}
///Generate the bytes to send to the terminal, from the cell location, a mouse event, and the terminal mode
fn mouse_report(
point: Point,
button: MouseButton,
point: AlacPoint,
button: AlacMouseButton,
pressed: bool,
modifiers: Modifiers,
format: MouseFormat,
@ -236,7 +212,7 @@ fn mouse_report(
if modifiers.alt {
mods += 8;
}
if modifiers.ctrl {
if modifiers.control {
mods += 16;
}
@ -254,8 +230,8 @@ fn mouse_report(
}
}
fn normal_mouse_report(point: Point, button: u8, utf8: bool) -> Option<Vec<u8>> {
let Point { line, column } = point;
fn normal_mouse_report(point: AlacPoint, button: u8, utf8: bool) -> Option<Vec<u8>> {
let AlacPoint { line, column } = point;
let max_point = if utf8 { 2015 } else { 223 };
if line >= max_point || column >= max_point {
@ -286,7 +262,7 @@ fn normal_mouse_report(point: Point, button: u8, utf8: bool) -> Option<Vec<u8>>
Some(msg)
}
fn sgr_mouse_report(point: Point, button: u8, pressed: bool) -> String {
fn sgr_mouse_report(point: AlacPoint, button: u8, pressed: bool) -> String {
let c = if pressed { 'M' } else { 'm' };
let msg = format!(
@ -299,38 +275,3 @@ fn sgr_mouse_report(point: Point, button: u8, pressed: bool) -> String {
msg
}
#[cfg(test)]
mod test {
use crate::mappings::mouse::grid_point;
#[test]
fn test_mouse_to_selection() {
let term_width = 100.;
let term_height = 200.;
let cell_width = 10.;
let line_height = 20.;
let mouse_pos_x = 100.; //Window relative
let mouse_pos_y = 100.; //Window relative
let origin_x = 10.;
let origin_y = 20.;
let cur_size = crate::TerminalSize::new(
line_height,
cell_width,
gpui::geometry::vector::vec2f(term_width, term_height),
);
let mouse_pos = gpui::geometry::vector::vec2f(mouse_pos_x, mouse_pos_y);
let origin = gpui::geometry::vector::vec2f(origin_x, origin_y); //Position of terminal window, 1 'cell' in
let mouse_pos = mouse_pos - origin;
let point = grid_point(mouse_pos, cur_size, 0);
assert_eq!(
point,
alacritty_terminal::index::Point::new(
alacritty_terminal::index::Line(((mouse_pos_y - origin_y) / line_height) as i32),
alacritty_terminal::index::Column(((mouse_pos_x - origin_x) / cell_width) as usize),
)
);
}
}

View file

@ -8,7 +8,7 @@ use alacritty_terminal::{
event::{Event as AlacTermEvent, EventListener, Notify, WindowSize},
event_loop::{EventLoop, Msg, Notifier},
grid::{Dimensions, Scroll as AlacScroll},
index::{Boundary, Column, Direction as AlacDirection, Line, Point},
index::{Boundary, Column, Direction as AlacDirection, Line, Point as AlacPoint},
selection::{Selection, SelectionRange, SelectionType},
sync::FairMutex,
term::{
@ -33,14 +33,16 @@ use mappings::mouse::{
use procinfo::LocalProcessInfo;
use serde::{Deserialize, Serialize};
use settings::Settings;
use terminal_settings::{AlternateScroll, Shell, TerminalBlink, TerminalSettings};
use theme::{ActiveTheme, Theme};
use util::truncate_and_trailoff;
use std::{
cmp::min,
cmp::{self, min},
collections::{HashMap, VecDeque},
fmt::Display,
ops::{Deref, Index, RangeInclusive, Sub},
ops::{Deref, Index, RangeInclusive},
os::unix::prelude::AsRawFd,
path::PathBuf,
sync::Arc,
@ -49,28 +51,36 @@ use std::{
use thiserror::Error;
use gpui::{
geometry::vector::{vec2f, Vector2F},
keymap_matcher::Keystroke,
platform::{Modifiers, MouseButton, MouseMovedEvent, TouchPhase},
scene::{MouseDown, MouseDrag, MouseScrollWheel, MouseUp},
AnyWindowHandle, AppContext, ClipboardItem, Entity, ModelContext, Task,
actions, black, px, red, AnyWindowHandle, AppContext, Bounds, ClipboardItem, EventEmitter,
Hsla, Keystroke, ModelContext, Modifiers, MouseButton, MouseDownEvent, MouseMoveEvent,
MouseUpEvent, Pixels, Point, Rgba, ScrollWheelEvent, Size, Task, TouchPhase,
};
use crate::mappings::{
colors::{get_color_at_index, to_alac_rgb},
keys::to_esc_str,
};
use crate::mappings::{colors::to_alac_rgb, keys::to_esc_str};
use lazy_static::lazy_static;
actions!(
terminal,
[
Clear,
Copy,
Paste,
ShowCharacterPalette,
SearchTest,
SendText,
SendKeystroke,
]
);
///Scrolling is unbearably sluggish by default. Alacritty supports a configurable
///Scroll multiplier that is set to 3 by default. This will be removed when I
///Implement scroll bars.
const SCROLL_MULTIPLIER: f32 = 4.;
const MAX_SEARCH_LINES: usize = 100;
const DEBUG_TERMINAL_WIDTH: f32 = 500.;
const DEBUG_TERMINAL_HEIGHT: f32 = 30.;
const DEBUG_CELL_WIDTH: f32 = 5.;
const DEBUG_LINE_HEIGHT: f32 = 5.;
const DEBUG_TERMINAL_WIDTH: Pixels = px(500.);
const DEBUG_TERMINAL_HEIGHT: Pixels = px(30.);
const DEBUG_CELL_WIDTH: Pixels = px(5.);
const DEBUG_LINE_HEIGHT: Pixels = px(5.);
lazy_static! {
// Regex Copied from alacritty's ui_config.rs and modified its declaration slightly:
@ -112,11 +122,11 @@ enum InternalEvent {
Clear,
// FocusNextMatch,
Scroll(AlacScroll),
ScrollToPoint(Point),
SetSelection(Option<(Selection, Point)>),
UpdateSelection(Vector2F),
ScrollToAlacPoint(AlacPoint),
SetSelection(Option<(Selection, AlacPoint)>),
UpdateSelection(Point<Pixels>),
// Adjusted mouse position, should open
FindHyperlink(Vector2F, bool),
FindHyperlink(Point<Pixels>, bool),
Copy,
}
@ -131,48 +141,46 @@ impl EventListener for ZedListener {
}
pub fn init(cx: &mut AppContext) {
settings::register::<TerminalSettings>(cx);
TerminalSettings::register(cx);
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub struct TerminalSize {
pub cell_width: f32,
pub line_height: f32,
pub height: f32,
pub width: f32,
pub cell_width: Pixels,
pub line_height: Pixels,
pub size: Size<Pixels>,
}
impl TerminalSize {
pub fn new(line_height: f32, cell_width: f32, size: Vector2F) -> Self {
pub fn new(line_height: Pixels, cell_width: Pixels, size: Size<Pixels>) -> Self {
TerminalSize {
cell_width,
line_height,
width: size.x(),
height: size.y(),
size,
}
}
pub fn num_lines(&self) -> usize {
(self.height / self.line_height).floor() as usize
f32::from((self.size.height / self.line_height).floor()) as usize
}
pub fn num_columns(&self) -> usize {
(self.width / self.cell_width).floor() as usize
f32::from((self.size.width / self.cell_width).floor()) as usize
}
pub fn height(&self) -> f32 {
self.height
pub fn height(&self) -> Pixels {
self.size.height
}
pub fn width(&self) -> f32 {
self.width
pub fn width(&self) -> Pixels {
self.size.width
}
pub fn cell_width(&self) -> f32 {
pub fn cell_width(&self) -> Pixels {
self.cell_width
}
pub fn line_height(&self) -> f32 {
pub fn line_height(&self) -> Pixels {
self.line_height
}
}
@ -181,7 +189,10 @@ impl Default for TerminalSize {
TerminalSize::new(
DEBUG_LINE_HEIGHT,
DEBUG_CELL_WIDTH,
vec2f(DEBUG_TERMINAL_WIDTH, DEBUG_TERMINAL_HEIGHT),
Size {
width: DEBUG_TERMINAL_WIDTH,
height: DEBUG_TERMINAL_HEIGHT,
},
)
}
}
@ -191,8 +202,8 @@ impl From<TerminalSize> for WindowSize {
WindowSize {
num_lines: val.num_lines() as u16,
num_cols: val.num_columns() as u16,
cell_width: val.cell_width() as u16,
cell_height: val.line_height() as u16,
cell_width: f32::from(val.cell_width()) as u16,
cell_height: f32::from(val.line_height()) as u16,
}
}
}
@ -346,7 +357,7 @@ impl TerminalBuilder {
let pty = match tty::new(
&pty_config,
TerminalSize::default().into(),
window.id() as u64,
window.window_id().as_u64(),
) {
Ok(pty) => pty,
Err(error) => {
@ -388,7 +399,7 @@ impl TerminalBuilder {
shell_pid,
foreground_process_info: None,
breadcrumb_text: String::new(),
scroll_px: 0.,
scroll_px: px(0.),
last_mouse_position: None,
next_link_id: 0,
selection_phase: SelectionPhase::Ended,
@ -404,18 +415,21 @@ impl TerminalBuilder {
pub fn subscribe(mut self, cx: &mut ModelContext<Terminal>) -> Terminal {
//Event loop
cx.spawn_weak(|this, mut cx| async move {
cx.spawn(|this, mut cx| async move {
use futures::StreamExt;
while let Some(event) = self.events_rx.next().await {
this.upgrade(&cx)?.update(&mut cx, |this, cx| {
this.update(&mut cx, |this, cx| {
//Process the first event immediately for lowered latency
this.process_event(&event, cx);
});
})?;
'outer: loop {
let mut events = vec![];
let mut timer = cx.background().timer(Duration::from_millis(4)).fuse();
let mut timer = cx
.background_executor()
.timer(Duration::from_millis(4))
.fuse();
let mut wakeup = false;
loop {
futures::select_biased! {
@ -442,7 +456,7 @@ impl TerminalBuilder {
smol::future::yield_now().await;
break 'outer;
} else {
this.upgrade(&cx)?.update(&mut cx, |this, cx| {
this.update(&mut cx, |this, cx| {
if wakeup {
this.process_event(&AlacTermEvent::Wakeup, cx);
}
@ -450,13 +464,13 @@ impl TerminalBuilder {
for event in events {
this.process_event(&event, cx);
}
});
})?;
smol::future::yield_now().await;
}
}
}
Some(())
anyhow::Ok(())
})
.detach();
@ -466,7 +480,7 @@ impl TerminalBuilder {
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct IndexedCell {
pub point: Point,
pub point: AlacPoint,
pub cell: Cell,
}
@ -496,7 +510,7 @@ pub struct TerminalContent {
#[derive(Clone)]
pub struct HoveredWord {
pub word: String,
pub word_match: RangeInclusive<Point>,
pub word_match: RangeInclusive<AlacPoint>,
pub id: usize,
}
@ -510,7 +524,7 @@ impl Default for TerminalContent {
selection: Default::default(),
cursor: RenderableCursor {
shape: alacritty_terminal::ansi::CursorShape::Block,
point: Point::new(Line(0), Column(0)),
point: AlacPoint::new(Line(0), Column(0)),
},
cursor_char: Default::default(),
size: Default::default(),
@ -530,19 +544,19 @@ pub struct Terminal {
term: Arc<FairMutex<Term<ZedListener>>>,
events: VecDeque<InternalEvent>,
/// This is only used for mouse mode cell change detection
last_mouse: Option<(Point, AlacDirection)>,
last_mouse: Option<(AlacPoint, AlacDirection)>,
/// This is only used for terminal hovered word checking
last_mouse_position: Option<Vector2F>,
pub matches: Vec<RangeInclusive<Point>>,
last_mouse_position: Option<Point<Pixels>>,
pub matches: Vec<RangeInclusive<AlacPoint>>,
pub last_content: TerminalContent,
last_synced: Instant,
sync_task: Option<Task<()>>,
pub selection_head: Option<Point>,
pub selection_head: Option<AlacPoint>,
pub breadcrumb_text: String,
shell_pid: u32,
shell_fd: u32,
pub foreground_process_info: Option<LocalProcessInfo>,
scroll_px: f32,
scroll_px: Pixels,
next_link_id: usize,
selection_phase: SelectionPhase,
cmd_pressed: bool,
@ -630,18 +644,17 @@ impl Terminal {
match event {
InternalEvent::ColorRequest(index, format) => {
let color = term.colors()[*index].unwrap_or_else(|| {
let term_style = &theme::current(cx).terminal;
to_alac_rgb(get_color_at_index(index, &term_style))
to_alac_rgb(get_color_at_index(*index, cx.theme().as_ref()))
});
self.write_to_pty(format(color))
}
InternalEvent::Resize(mut new_size) => {
new_size.height = f32::max(new_size.line_height, new_size.height);
new_size.width = f32::max(new_size.cell_width, new_size.width);
new_size.size.height = cmp::max(new_size.line_height, new_size.height());
new_size.size.width = cmp::max(new_size.cell_width, new_size.width());
self.last_content.size = new_size.clone();
self.pty_tx.0.send(Msg::Resize((new_size).into())).ok();
self.pty_tx.0.send(Msg::Resize(new_size.into())).ok();
term.resize(new_size);
}
@ -667,7 +680,7 @@ impl Terminal {
// Reset the cursor
term.grid_mut().cursor.point =
Point::new(Line(0), term.grid_mut().cursor.point.column);
AlacPoint::new(Line(0), term.grid_mut().cursor.point.column);
let new_cursor = term.grid().cursor.point;
// Clear the lines below the new cursor
@ -712,7 +725,7 @@ impl Terminal {
cx.write_to_clipboard(ClipboardItem::new(txt))
}
}
InternalEvent::ScrollToPoint(point) => {
InternalEvent::ScrollToAlacPoint(point) => {
term.scroll_to_point(*point);
self.refresh_hovered_word();
}
@ -825,7 +838,7 @@ impl Terminal {
fn update_selected_word(
&mut self,
prev_word: Option<HoveredWord>,
word_match: RangeInclusive<Point>,
word_match: RangeInclusive<AlacPoint>,
word: String,
is_url: bool,
cx: &mut ModelContext<Self>,
@ -873,11 +886,11 @@ impl Terminal {
self.set_selection(Some((make_selection(&search_match), *search_match.end())));
self.events
.push_back(InternalEvent::ScrollToPoint(*search_match.start()));
.push_back(InternalEvent::ScrollToAlacPoint(*search_match.start()));
}
}
pub fn select_matches(&mut self, matches: Vec<RangeInclusive<Point>>) {
pub fn select_matches(&mut self, matches: Vec<RangeInclusive<AlacPoint>>) {
let matches_to_select = self
.matches
.iter()
@ -894,13 +907,13 @@ impl Terminal {
pub fn select_all(&mut self) {
let term = self.term.lock();
let start = Point::new(term.topmost_line(), Column(0));
let end = Point::new(term.bottommost_line(), term.last_column());
let start = AlacPoint::new(term.topmost_line(), Column(0));
let end = AlacPoint::new(term.bottommost_line(), term.last_column());
drop(term);
self.set_selection(Some((make_selection(&(start..=end)), end)));
}
fn set_selection(&mut self, selection: Option<(Selection, Point)>) {
fn set_selection(&mut self, selection: Option<(Selection, AlacPoint)>) {
self.events
.push_back(InternalEvent::SetSelection(selection));
}
@ -954,11 +967,11 @@ impl Terminal {
}
pub fn try_modifiers_change(&mut self, modifiers: &Modifiers) -> bool {
let changed = self.cmd_pressed != modifiers.cmd;
if !self.cmd_pressed && modifiers.cmd {
let changed = self.cmd_pressed != modifiers.command;
if !self.cmd_pressed && modifiers.command {
self.refresh_hovered_word();
}
self.cmd_pressed = modifiers.cmd;
self.cmd_pressed = modifiers.command;
changed
}
@ -982,17 +995,17 @@ impl Terminal {
term.lock_unfair() //It's been too long, force block
} else if let None = self.sync_task {
//Skip this frame
let delay = cx.background().timer(Duration::from_millis(16));
self.sync_task = Some(cx.spawn_weak(|weak_handle, mut cx| async move {
let delay = cx.background_executor().timer(Duration::from_millis(16));
self.sync_task = Some(cx.spawn(|weak_handle, mut cx| async move {
delay.await;
cx.update(|cx| {
if let Some(handle) = weak_handle.upgrade(cx) {
handle.update(cx, |terminal, cx| {
if let Some(handle) = weak_handle.upgrade() {
handle
.update(&mut cx, |terminal, cx| {
terminal.sync_task.take();
cx.notify();
});
}
});
})
.ok();
}
}));
return;
} else {
@ -1050,7 +1063,7 @@ impl Terminal {
}
}
pub fn mouse_changed(&mut self, point: Point, side: AlacDirection) -> bool {
pub fn mouse_changed(&mut self, point: AlacPoint, side: AlacDirection) -> bool {
match self.last_mouse {
Some((old_point, old_side)) => {
if old_point == point && old_side == side {
@ -1071,10 +1084,10 @@ impl Terminal {
self.last_content.mode.intersects(TermMode::MOUSE_MODE) && !shift
}
pub fn mouse_move(&mut self, e: &MouseMovedEvent, origin: Vector2F) {
let position = e.position.sub(origin);
pub fn mouse_move(&mut self, e: &MouseMoveEvent, origin: Point<Pixels>) {
let position = e.position - origin;
self.last_mouse_position = Some(position);
if self.mouse_mode(e.shift) {
if self.mouse_mode(e.modifiers.shift) {
let point = grid_point(
position,
self.last_content.size,
@ -1092,7 +1105,7 @@ impl Terminal {
}
}
fn word_from_position(&mut self, position: Option<Vector2F>) {
fn word_from_position(&mut self, position: Option<Point<Pixels>>) {
if self.selection_phase == SelectionPhase::Selecting {
self.last_content.last_hovered_word = None;
} else if let Some(position) = position {
@ -1101,11 +1114,16 @@ impl Terminal {
}
}
pub fn mouse_drag(&mut self, e: MouseDrag, origin: Vector2F) {
let position = e.position.sub(origin);
pub fn mouse_drag(
&mut self,
e: &MouseMoveEvent,
origin: Point<Pixels>,
region: Bounds<Pixels>,
) {
let position = e.position - origin;
self.last_mouse_position = Some(position);
if !self.mouse_mode(e.shift) {
if !self.mouse_mode(e.modifiers.shift) {
self.selection_phase = SelectionPhase::Selecting;
// Alacritty has the same ordering, of first updating the selection
// then scrolling 15ms later
@ -1114,7 +1132,7 @@ impl Terminal {
// Doesn't make sense to scroll the alt screen
if !self.last_content.mode.contains(TermMode::ALT_SCREEN) {
let scroll_delta = match self.drag_line_delta(e) {
let scroll_delta = match self.drag_line_delta(e, region) {
Some(value) => value,
None => return,
};
@ -1127,34 +1145,36 @@ impl Terminal {
}
}
fn drag_line_delta(&mut self, e: MouseDrag) -> Option<f32> {
fn drag_line_delta(&mut self, e: &MouseMoveEvent, region: Bounds<Pixels>) -> Option<Pixels> {
//TODO: Why do these need to be doubled? Probably the same problem that the IME has
let top = e.region.origin_y() + (self.last_content.size.line_height * 2.);
let bottom = e.region.lower_left().y() - (self.last_content.size.line_height * 2.);
let scroll_delta = if e.position.y() < top {
(top - e.position.y()).powf(1.1)
} else if e.position.y() > bottom {
-((e.position.y() - bottom).powf(1.1))
let top = region.origin.y + (self.last_content.size.line_height * 2.);
let bottom = region.lower_left().y - (self.last_content.size.line_height * 2.);
let scroll_delta = if e.position.y < top {
(top - e.position.y).pow(1.1)
} else if e.position.y > bottom {
-((e.position.y - bottom).pow(1.1))
} else {
return None; //Nothing to do
};
Some(scroll_delta)
}
pub fn mouse_down(&mut self, e: &MouseDown, origin: Vector2F) {
let position = e.position.sub(origin);
pub fn mouse_down(&mut self, e: &MouseDownEvent, origin: Point<Pixels>) {
let position = e.position - origin;
let point = grid_point(
position,
self.last_content.size,
self.last_content.display_offset,
);
if self.mouse_mode(e.shift) {
if let Some(bytes) = mouse_button_report(point, e, true, self.last_content.mode) {
if self.mouse_mode(e.modifiers.shift) {
if let Some(bytes) =
mouse_button_report(point, e.button, e.modifiers, true, self.last_content.mode)
{
self.pty_tx.notify(bytes);
}
} else if e.button == MouseButton::Left {
let position = e.position.sub(origin);
let position = e.position - origin;
let point = grid_point(
position,
self.last_content.size,
@ -1182,18 +1202,25 @@ impl Terminal {
}
}
pub fn mouse_up(&mut self, e: &MouseUp, origin: Vector2F, cx: &mut ModelContext<Self>) {
let setting = settings::get::<TerminalSettings>(cx);
pub fn mouse_up(
&mut self,
e: &MouseUpEvent,
origin: Point<Pixels>,
cx: &mut ModelContext<Self>,
) {
let setting = TerminalSettings::get_global(cx);
let position = e.position.sub(origin);
if self.mouse_mode(e.shift) {
let position = e.position - origin;
if self.mouse_mode(e.modifiers.shift) {
let point = grid_point(
position,
self.last_content.size,
self.last_content.display_offset,
);
if let Some(bytes) = mouse_button_report(point, e, false, self.last_content.mode) {
if let Some(bytes) =
mouse_button_report(point, e.button, e.modifiers, false, self.last_content.mode)
{
self.pty_tx.notify(bytes);
}
} else {
@ -1205,7 +1232,7 @@ impl Terminal {
if self.selection_phase == SelectionPhase::Ended {
let mouse_cell_index = content_index_for_mouse(position, &self.last_content.size);
if let Some(link) = self.last_content.cells[mouse_cell_index].hyperlink() {
cx.platform().open_url(link.uri());
cx.open_url(link.uri());
} else if self.cmd_pressed {
self.events
.push_back(InternalEvent::FindHyperlink(position, true));
@ -1218,13 +1245,13 @@ impl Terminal {
}
///Scroll the terminal
pub fn scroll_wheel(&mut self, e: MouseScrollWheel, origin: Vector2F) {
pub fn scroll_wheel(&mut self, e: &ScrollWheelEvent, origin: Point<Pixels>) {
let mouse_mode = self.mouse_mode(e.shift);
if let Some(scroll_lines) = self.determine_scroll_lines(&e, mouse_mode) {
if mouse_mode {
let point = grid_point(
e.position.sub(origin),
e.position - origin,
self.last_content.size,
self.last_content.display_offset,
);
@ -1257,34 +1284,30 @@ impl Terminal {
self.word_from_position(self.last_mouse_position);
}
fn determine_scroll_lines(&mut self, e: &MouseScrollWheel, mouse_mode: bool) -> Option<i32> {
fn determine_scroll_lines(&mut self, e: &ScrollWheelEvent, mouse_mode: bool) -> Option<i32> {
let scroll_multiplier = if mouse_mode { 1. } else { SCROLL_MULTIPLIER };
let line_height = self.last_content.size.line_height;
match e.phase {
match e.touch_phase {
/* Reset scroll state on started */
Some(TouchPhase::Started) => {
self.scroll_px = 0.;
TouchPhase::Started => {
self.scroll_px = px(0.);
None
}
/* Calculate the appropriate scroll lines */
Some(gpui::platform::TouchPhase::Moved) => {
TouchPhase::Moved => {
let old_offset = (self.scroll_px / line_height) as i32;
self.scroll_px += e.delta.pixel_delta(line_height).y() * scroll_multiplier;
self.scroll_px += e.delta.pixel_delta(line_height).y * scroll_multiplier;
let new_offset = (self.scroll_px / line_height) as i32;
// Whenever we hit the edges, reset our stored scroll to 0
// so we can respond to changes in direction quickly
self.scroll_px %= self.last_content.size.height;
self.scroll_px %= self.last_content.size.height();
Some(new_offset - old_offset)
}
/* Fall back to delta / line_height */
None => Some(
((e.delta.pixel_delta(line_height).y() * scroll_multiplier) / line_height) as i32,
),
_ => None,
TouchPhase::Ended => None,
}
}
@ -1292,9 +1315,9 @@ impl Terminal {
&mut self,
searcher: RegexSearch,
cx: &mut ModelContext<Self>,
) -> Task<Vec<RangeInclusive<Point>>> {
) -> Task<Vec<RangeInclusive<AlacPoint>>> {
let term = self.term.clone();
cx.background().spawn(async move {
cx.background_executor().spawn(async move {
let term = term.lock();
all_search_matches(&term, &searcher).collect()
@ -1344,13 +1367,11 @@ impl Drop for Terminal {
}
}
impl Entity for Terminal {
type Event = Event;
}
impl EventEmitter<Event> for Terminal {}
/// Based on alacritty/src/display/hint.rs > regex_match_at
/// Retrieve the match, if the specified point is inside the content matching the regex.
fn regex_match_at<T>(term: &Term<T>, point: Point, regex: &RegexSearch) -> Option<Match> {
fn regex_match_at<T>(term: &Term<T>, point: AlacPoint, regex: &RegexSearch) -> Option<Match> {
visible_regex_match_iter(term, regex).find(|rm| rm.contains(&point))
}
@ -1362,8 +1383,8 @@ pub fn visible_regex_match_iter<'a, T>(
) -> impl Iterator<Item = Match> + 'a {
let viewport_start = Line(-(term.grid().display_offset() as i32));
let viewport_end = viewport_start + term.bottommost_line();
let mut start = term.line_search_left(Point::new(viewport_start, Column(0)));
let mut end = term.line_search_right(Point::new(viewport_end, Column(0)));
let mut start = term.line_search_left(AlacPoint::new(viewport_start, Column(0)));
let mut end = term.line_search_right(AlacPoint::new(viewport_end, Column(0)));
start.line = start.line.max(viewport_start - MAX_SEARCH_LINES);
end.line = end.line.min(viewport_end + MAX_SEARCH_LINES);
@ -1372,7 +1393,7 @@ pub fn visible_regex_match_iter<'a, T>(
.take_while(move |rm| rm.start().line <= viewport_end)
}
fn make_selection(range: &RangeInclusive<Point>) -> Selection {
fn make_selection(range: &RangeInclusive<AlacPoint>) -> Selection {
let mut selection = Selection::new(SelectionType::Simple, *range.start(), AlacDirection::Left);
selection.update(*range.end(), AlacDirection::Right);
selection
@ -1382,33 +1403,124 @@ fn all_search_matches<'a, T>(
term: &'a Term<T>,
regex: &'a RegexSearch,
) -> impl Iterator<Item = Match> + 'a {
let start = Point::new(term.grid().topmost_line(), Column(0));
let end = Point::new(term.grid().bottommost_line(), term.grid().last_column());
let start = AlacPoint::new(term.grid().topmost_line(), Column(0));
let end = AlacPoint::new(term.grid().bottommost_line(), term.grid().last_column());
RegexIter::new(start, end, AlacDirection::Right, term, regex)
}
fn content_index_for_mouse(pos: Vector2F, size: &TerminalSize) -> usize {
let col = (pos.x() / size.cell_width()).round() as usize;
fn content_index_for_mouse(pos: Point<Pixels>, size: &TerminalSize) -> usize {
let col = (pos.x / size.cell_width()).round() as usize;
let clamped_col = min(col, size.columns() - 1);
let row = (pos.y() / size.line_height()).round() as usize;
let row = (pos.y / size.line_height()).round() as usize;
let clamped_row = min(row, size.screen_lines() - 1);
clamped_row * size.columns() + clamped_col
}
///Converts an 8 bit ANSI color to it's GPUI equivalent.
///Accepts usize for compatibility with the alacritty::Colors interface,
///Other than that use case, should only be called with values in the [0,255] range
pub fn get_color_at_index(index: usize, theme: &Theme) -> Hsla {
let colors = theme.colors();
match index {
//0-15 are the same as the named colors above
0 => colors.terminal_ansi_black,
1 => colors.terminal_ansi_red,
2 => colors.terminal_ansi_green,
3 => colors.terminal_ansi_yellow,
4 => colors.terminal_ansi_blue,
5 => colors.terminal_ansi_magenta,
6 => colors.terminal_ansi_cyan,
7 => colors.terminal_ansi_white,
8 => colors.terminal_ansi_bright_black,
9 => colors.terminal_ansi_bright_red,
10 => colors.terminal_ansi_bright_green,
11 => colors.terminal_ansi_bright_yellow,
12 => colors.terminal_ansi_bright_blue,
13 => colors.terminal_ansi_bright_magenta,
14 => colors.terminal_ansi_bright_cyan,
15 => colors.terminal_ansi_bright_white,
//16-231 are mapped to their RGB colors on a 0-5 range per channel
16..=231 => {
let (r, g, b) = rgb_for_index(&(index as u8)); //Split the index into it's ANSI-RGB components
let step = (u8::MAX as f32 / 5.).floor() as u8; //Split the RGB range into 5 chunks, with floor so no overflow
rgba_color(r * step, g * step, b * step) //Map the ANSI-RGB components to an RGB color
}
//232-255 are a 24 step grayscale from black to white
232..=255 => {
let i = index as u8 - 232; //Align index to 0..24
let step = (u8::MAX as f32 / 24.).floor() as u8; //Split the RGB grayscale values into 24 chunks
rgba_color(i * step, i * step, i * step) //Map the ANSI-grayscale components to the RGB-grayscale
}
//For compatibility with the alacritty::Colors interface
256 => colors.text,
257 => colors.background,
258 => theme.players().local().cursor,
// todo!(more colors)
259 => red(), //style.dim_black,
260 => red(), //style.dim_red,
261 => red(), //style.dim_green,
262 => red(), //style.dim_yellow,
263 => red(), //style.dim_blue,
264 => red(), //style.dim_magenta,
265 => red(), //style.dim_cyan,
266 => red(), //style.dim_white,
267 => red(), //style.bright_foreground,
268 => colors.terminal_ansi_black, //'Dim Background', non-standard color
_ => black(),
}
}
///Generates the rgb channels in [0, 5] for a given index into the 6x6x6 ANSI color cube
///See: [8 bit ansi color](https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit).
///
///Wikipedia gives a formula for calculating the index for a given color:
///
///index = 16 + 36 × r + 6 × g + b (0 ≤ r, g, b ≤ 5)
///
///This function does the reverse, calculating the r, g, and b components from a given index.
fn rgb_for_index(i: &u8) -> (u8, u8, u8) {
debug_assert!((&16..=&231).contains(&i));
let i = i - 16;
let r = (i - (i % 36)) / 36;
let g = ((i % 36) - (i % 6)) / 6;
let b = (i % 36) % 6;
(r, g, b)
}
pub fn rgba_color(r: u8, g: u8, b: u8) -> Hsla {
Rgba {
r: (r as f32 / 255.) as f32,
g: (g as f32 / 255.) as f32,
b: (b as f32 / 255.) as f32,
a: 1.,
}
.into()
}
#[cfg(test)]
mod tests {
use alacritty_terminal::{
index::{Column, Line, Point},
index::{Column, Line, Point as AlacPoint},
term::cell::Cell,
};
use gpui::geometry::vector::vec2f;
use gpui::{point, size, Pixels};
use rand::{distributions::Alphanumeric, rngs::ThreadRng, thread_rng, Rng};
use crate::{content_index_for_mouse, IndexedCell, TerminalContent, TerminalSize};
use crate::{
content_index_for_mouse, rgb_for_index, IndexedCell, TerminalContent, TerminalSize,
};
#[test]
fn test_rgb_for_index() {
//Test every possible value in the color cube
for i in 16..=231 {
let (r, g, b) = rgb_for_index(&(i as u8));
assert_eq!(i, 16 + 36 * r + 6 * g + b);
}
}
#[test]
fn test_mouse_to_cell_test() {
@ -1421,10 +1533,12 @@ mod tests {
let cell_size = rng.gen_range(5 * PRECISION..20 * PRECISION) as f32 / PRECISION as f32;
let size = crate::TerminalSize {
cell_width: cell_size,
line_height: cell_size,
height: cell_size * (viewport_cells as f32),
width: cell_size * (viewport_cells as f32),
cell_width: Pixels::from(cell_size),
line_height: Pixels::from(cell_size),
size: size(
Pixels::from(cell_size * (viewport_cells as f32)),
Pixels::from(cell_size * (viewport_cells as f32)),
),
};
let cells = get_cells(size, &mut rng);
@ -1438,9 +1552,9 @@ mod tests {
let row_offset = rng.gen_range(0..PRECISION) as f32 / PRECISION as f32;
let col_offset = rng.gen_range(0..PRECISION) as f32 / PRECISION as f32;
let mouse_pos = vec2f(
col as f32 * cell_size + col_offset,
row as f32 * cell_size + row_offset,
let mouse_pos = point(
Pixels::from(col as f32 * cell_size + col_offset),
Pixels::from(row as f32 * cell_size + row_offset),
);
let content_index = content_index_for_mouse(mouse_pos, &content.size);
@ -1458,21 +1572,28 @@ mod tests {
let mut rng = thread_rng();
let size = crate::TerminalSize {
cell_width: 10.,
line_height: 10.,
height: 100.,
width: 100.,
cell_width: Pixels::from(10.),
line_height: Pixels::from(10.),
size: size(Pixels::from(100.), Pixels::from(100.)),
};
let cells = get_cells(size, &mut rng);
let content = convert_cells_to_content(size, &cells);
assert_eq!(
content.cells[content_index_for_mouse(vec2f(-10., -10.), &content.size)].c,
content.cells[content_index_for_mouse(
point(Pixels::from(-10.), Pixels::from(-10.)),
&content.size
)]
.c,
cells[0][0]
);
assert_eq!(
content.cells[content_index_for_mouse(vec2f(1000., 1000.), &content.size)].c,
content.cells[content_index_for_mouse(
point(Pixels::from(1000.), Pixels::from(1000.)),
&content.size
)]
.c,
cells[9][9]
);
}
@ -1480,9 +1601,9 @@ mod tests {
fn get_cells(size: TerminalSize, rng: &mut ThreadRng) -> Vec<Vec<char>> {
let mut cells = Vec::new();
for _ in 0..((size.height() / size.line_height()) as usize) {
for _ in 0..(f32::from(size.height() / size.line_height()) as usize) {
let mut row_vec = Vec::new();
for _ in 0..((size.width() / size.cell_width()) as usize) {
for _ in 0..(f32::from(size.width() / size.cell_width()) as usize) {
let cell_char = rng.sample(Alphanumeric) as char;
row_vec.push(cell_char)
}
@ -1499,7 +1620,7 @@ mod tests {
for col in 0..cells[row].len() {
let cell_char = cells[row][col];
ic.push(IndexedCell {
point: Point::new(Line(row as i32), Column(col)),
point: AlacPoint::new(Line(row as i32), Column(col)),
cell: Cell {
c: cell_char,
..Default::default()

View file

@ -1,8 +1,7 @@
use std::{collections::HashMap, path::PathBuf};
use gpui::{fonts, AppContext};
use gpui::{px, AbsoluteLength, AppContext, FontFeatures, Pixels};
use schemars::JsonSchema;
use serde_derive::{Deserialize, Serialize};
use std::{collections::HashMap, path::PathBuf};
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
@ -16,18 +15,18 @@ pub enum TerminalDockPosition {
pub struct TerminalSettings {
pub shell: Shell,
pub working_directory: WorkingDirectory,
font_size: Option<f32>,
pub font_size: Option<Pixels>,
pub font_family: Option<String>,
pub line_height: TerminalLineHeight,
pub font_features: Option<fonts::Features>,
pub font_features: Option<FontFeatures>,
pub env: HashMap<String, String>,
pub blinking: TerminalBlink,
pub alternate_scroll: AlternateScroll,
pub option_as_meta: bool,
pub copy_on_select: bool,
pub dock: TerminalDockPosition,
pub default_width: f32,
pub default_height: f32,
pub default_width: Pixels,
pub default_height: Pixels,
pub detect_venv: VenvSettings,
}
@ -79,7 +78,7 @@ pub struct TerminalSettingsContent {
pub font_size: Option<f32>,
pub font_family: Option<String>,
pub line_height: Option<TerminalLineHeight>,
pub font_features: Option<fonts::Features>,
pub font_features: Option<FontFeatures>,
pub env: Option<HashMap<String, String>>,
pub blinking: Option<TerminalBlink>,
pub alternate_scroll: Option<AlternateScroll>,
@ -91,14 +90,7 @@ pub struct TerminalSettingsContent {
pub detect_venv: Option<VenvSettings>,
}
impl TerminalSettings {
pub fn font_size(&self, cx: &AppContext) -> Option<f32> {
self.font_size
.map(|size| theme::adjusted_font_size(size, cx))
}
}
impl settings::Setting for TerminalSettings {
impl settings::Settings for TerminalSettings {
const KEY: Option<&'static str> = Some("terminal");
type FileContent = TerminalSettingsContent;
@ -106,7 +98,7 @@ impl settings::Setting for TerminalSettings {
fn load(
default_value: &Self::FileContent,
user_values: &[&Self::FileContent],
_: &AppContext,
_: &mut AppContext,
) -> anyhow::Result<Self> {
Self::load_via_json_merge(default_value, user_values)
}
@ -122,12 +114,13 @@ pub enum TerminalLineHeight {
}
impl TerminalLineHeight {
pub fn value(&self) -> f32 {
match self {
pub fn value(&self) -> AbsoluteLength {
let value = match self {
TerminalLineHeight::Comfortable => 1.618,
TerminalLineHeight::Standard => 1.3,
TerminalLineHeight::Custom(line_height) => f32::max(*line_height, 1.),
}
};
px(value).into()
}
}