From cba5b4ac113bba836be5a9c4d5c5bfce152fda50 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Wed, 6 Jul 2022 13:44:30 -0700 Subject: [PATCH 1/9] Began working on selections, refactored colors --- crates/terminal/src/color_translation.rs | 134 ++++++++++++++++ crates/terminal/src/terminal.rs | 41 +---- crates/terminal/src/terminal_element.rs | 190 ++++++++++------------- 3 files changed, 227 insertions(+), 138 deletions(-) create mode 100644 crates/terminal/src/color_translation.rs diff --git a/crates/terminal/src/color_translation.rs b/crates/terminal/src/color_translation.rs new file mode 100644 index 0000000000..78c2a569db --- /dev/null +++ b/crates/terminal/src/color_translation.rs @@ -0,0 +1,134 @@ +use alacritty_terminal::{ansi::Color as AnsiColor, term::color::Rgb as AlacRgb}; +use gpui::color::Color; +use theme::TerminalStyle; + +///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 compatability 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 compatability 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!(i >= &16 && i <= &231); + 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) +} + +//Convenience method to convert from a GPUI color to an alacritty Rgb +pub fn to_alac_rgb(color: Color) -> AlacRgb { + AlacRgb { + r: color.r, + g: color.g, + b: 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::color_translation::rgb_for_index(&(i as u8)); + assert_eq!(i, 16 + 36 * r + 6 * g + b); + } + } +} diff --git a/crates/terminal/src/terminal.rs b/crates/terminal/src/terminal.rs index eb7f2a0a90..dc10f181e7 100644 --- a/crates/terminal/src/terminal.rs +++ b/crates/terminal/src/terminal.rs @@ -4,18 +4,19 @@ use alacritty_terminal::{ event_loop::{EventLoop, Msg, Notifier}, grid::Scroll, sync::FairMutex, - term::{color::Rgb as AlacRgb, SizeInfo}, + term::SizeInfo, tty::{self, setup_env}, Term, }; +use color_translation::{get_color_at_index, to_alac_rgb}; use futures::{ channel::mpsc::{unbounded, UnboundedSender}, StreamExt, }; use gpui::{ - actions, color::Color, elements::*, impl_internal_actions, platform::CursorStyle, - ClipboardItem, Entity, MutableAppContext, View, ViewContext, + actions, elements::*, impl_internal_actions, platform::CursorStyle, ClipboardItem, Entity, + MutableAppContext, View, ViewContext, }; use project::{Project, ProjectPath}; use settings::Settings; @@ -23,7 +24,7 @@ use smallvec::SmallVec; use std::{collections::HashMap, path::PathBuf, sync::Arc}; use workspace::{Item, Workspace}; -use crate::terminal_element::{get_color_at_index, TerminalEl}; +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' @@ -37,6 +38,7 @@ const UP_SEQ: &str = "\x1b[A"; const DOWN_SEQ: &str = "\x1b[B"; const DEFAULT_TITLE: &str = "Terminal"; +pub mod color_translation; pub mod gpui_func_tools; pub mod terminal_element; @@ -62,7 +64,7 @@ pub fn init(cx: &mut MutableAppContext) { cx.add_action(Terminal::escape); cx.add_action(Terminal::quit); cx.add_action(Terminal::del); - cx.add_action(Terminal::carriage_return); //TODO figure out how to do this properly. Should we be checking the terminal mode? + cx.add_action(Terminal::carriage_return); cx.add_action(Terminal::left); cx.add_action(Terminal::right); cx.add_action(Terminal::up); @@ -129,7 +131,6 @@ impl Terminal { hold: false, }; - //Does this mangle the zed Env? I'm guessing it does... do child processes have a seperate ENV? let mut env: HashMap = HashMap::new(); //TODO: Properly set the current locale, env.insert("LC_ALL".to_string(), "en_US.UTF-8".to_string()); @@ -219,24 +220,7 @@ impl Terminal { AlacTermEvent::ColorRequest(index, format) => { let color = self.term.lock().colors()[index].unwrap_or_else(|| { let term_style = &cx.global::().theme.terminal; - match index { - 0..=255 => to_alac_rgb(get_color_at_index(&(index as u8), term_style)), - //These additional values are required to match the Alacritty Colors object's behavior - 256 => to_alac_rgb(term_style.foreground), - 257 => to_alac_rgb(term_style.background), - 258 => to_alac_rgb(term_style.cursor), - 259 => to_alac_rgb(term_style.dim_black), - 260 => to_alac_rgb(term_style.dim_red), - 261 => to_alac_rgb(term_style.dim_green), - 262 => to_alac_rgb(term_style.dim_yellow), - 263 => to_alac_rgb(term_style.dim_blue), - 264 => to_alac_rgb(term_style.dim_magenta), - 265 => to_alac_rgb(term_style.dim_cyan), - 266 => to_alac_rgb(term_style.dim_white), - 267 => to_alac_rgb(term_style.bright_foreground), - 268 => to_alac_rgb(term_style.black), //Dim Background, non-standard - _ => AlacRgb { r: 0, g: 0, b: 0 }, - } + to_alac_rgb(get_color_at_index(&index, term_style)) }); self.write_to_pty(&Input(format(color)), cx) } @@ -468,15 +452,6 @@ impl Item for Terminal { } } -//Convenience method for less lines -fn to_alac_rgb(color: Color) -> AlacRgb { - AlacRgb { - r: color.r, - g: color.g, - b: color.g, - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/crates/terminal/src/terminal_element.rs b/crates/terminal/src/terminal_element.rs index 408fb0dcec..ad017c011f 100644 --- a/crates/terminal/src/terminal_element.rs +++ b/crates/terminal/src/terminal_element.rs @@ -1,7 +1,6 @@ use alacritty_terminal::{ - ansi::Color as AnsiColor, grid::{Dimensions, GridIterator, Indexed}, - index::Point, + index::{Column as GridCol, Line as GridLine, Point, Side}, term::{ cell::{Cell, Flags}, SizeInfo, @@ -24,10 +23,12 @@ use gpui::{ use itertools::Itertools; use ordered_float::OrderedFloat; use settings::Settings; -use std::rc::Rc; +use std::{cmp::min, rc::Rc}; use theme::TerminalStyle; -use crate::{gpui_func_tools::paint_layer, Input, ScrollTerminal, Terminal}; +use crate::{ + color_translation::convert_color, gpui_func_tools::paint_layer, Input, ScrollTerminal, Terminal, +}; ///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 @@ -74,6 +75,7 @@ pub struct LayoutState { cursor: Option, background_color: Color, cur_size: SizeInfo, + display_offset: usize, } impl TerminalEl { @@ -194,6 +196,7 @@ impl Element for TerminalEl { cur_size, background_rects, background_color: terminal_theme.background, + display_offset: content.display_offset, }, ) } @@ -209,10 +212,50 @@ impl Element for TerminalEl { let clip_bounds = Some(visible_bounds); paint_layer(cx, clip_bounds, |cx| { //Elements are ephemeral, only at paint time do we know what could be clicked by a mouse + + /* + To set a selection, + set the selection variable on the terminal + + CLICK: + Get the grid point associated with this mouse click + And the side????? - TODO - algorithm for calculating this in Processor::cell_side + On single left click -> Clear selection, start empty selection + On double left click -> start semantic selection + On double triple click -> start line selection + + MOUSE MOVED: + Find the new cell the mouse is over + Update the selection by calling terminal.selection.update() + */ + let cur_size = layout.cur_size.clone(); + let display_offset = layout.display_offset.clone(); cx.scene.push_mouse_region(MouseRegion { view_id: self.view.id(), - mouse_down: Some(Rc::new(|_, cx| cx.focus_parent_view())), + mouse_down: Some(Rc::new(move |pos, cx| { + let point = grid_cell(pos, cur_size, display_offset); + let side = cell_side(cur_size, pos.x() as usize); + + //One problem is we need a terminal + //Second problem is that we need # of clicks + //Third problem is that dragging reports deltas, and we need locations. + //Fourth (minor) is need to render the selection + + // if single_click { + // terminal.selection = Some(Selection::new(SelectionType::Simple, point, side)) + // } else if double_click { + // terminal.selection = Some(Selection::new(SelectionType::Semantic, point, side)) + // } else if triple_click { + // terminal.selection = Some(Selection::new(SelectionType::Lines, point, side)) + // } + + cx.focus_parent_view() + })), bounds: visible_bounds, + drag: Some(Rc::new(|delta, cx| { + //Calculate new point from delta + //terminal.selection.update(point, side) + })), ..Default::default() }); @@ -311,6 +354,19 @@ impl Element for TerminalEl { } } +/* +Mouse moved -> WindowEvent::CursorMoved +mouse press -> WindowEvent::MouseInput +update_selection_scrolling + + +copy_selection +start_selection +toggle_selection +update_selection +clear_selection + */ + ///Configures a text style from the current settings. fn make_text_style(font_cache: &FontCache, settings: &Settings) -> TextStyle { TextStyle { @@ -430,98 +486,34 @@ fn cell_style(indexed: &Indexed<&Cell>, style: &TerminalStyle, text_style: &Text } } -///Converts a 2, 8, or 24 bit color ANSI color to the GPUI equivalent -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, style), +///Copied (with modifications) from alacritty/src/input.rs > Processor::cell_side() +fn cell_side(cur_size: SizeInfo, x: usize) -> Side { + 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 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; + + if cell_x > half_cell_width + // Edge case when mouse leaves the window. + || x as f32 >= end_of_grid + { + Side::Right + } else { + Side::Left } } -///Converts an 8 bit ANSI color to it's GPUI equivalent. -pub fn get_color_at_index(index: &u8, 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); //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 - 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 - } - } -} +///Copied (with modifications) from alacritty/src/event.rs > Mouse::point() +fn grid_cell(pos: Vector2F, cur_size: SizeInfo, display_offset: usize) -> Point { + let col = pos.x() - cur_size.cell_width() / cur_size.cell_width(); //TODO: underflow... + let col = min(GridCol(col as usize), cur_size.last_column()); -///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!(i >= &16 && i <= &231); - 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) + let line = pos.y() - cur_size.padding_y() / cur_size.cell_height(); + let line = min(line as usize, cur_size.bottommost_line().0 as usize); + + Point::new(GridLine((line - display_offset) as i32), col) } ///Draws the grid as Alacritty sees it. Useful for checking if there is an inconsistency between @@ -554,15 +546,3 @@ fn draw_debug_grid(bounds: RectF, layout: &mut LayoutState, cx: &mut PaintContex }); } } - -#[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::terminal_element::rgb_for_index(&(i as u8)); - assert_eq!(i, 16 + 36 * r + 6 * g + b); - } - } -} From 778cfd94d873e3750f29a19e296cc482f95b1b9c Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Wed, 6 Jul 2022 17:37:12 -0700 Subject: [PATCH 2/9] Added basic selections --- crates/editor/src/element.rs | 20 +- crates/terminal/src/terminal_element.rs | 309 ++++++++++++++++-------- 2 files changed, 216 insertions(+), 113 deletions(-) diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 1169df3fd1..976f932e58 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -1685,22 +1685,22 @@ impl Cursor { } #[derive(Debug)] -struct HighlightedRange { - start_y: f32, - line_height: f32, - lines: Vec, - color: Color, - corner_radius: f32, +pub struct HighlightedRange { + pub start_y: f32, + pub line_height: f32, + pub lines: Vec, + pub color: Color, + pub corner_radius: f32, } #[derive(Debug)] -struct HighlightedRangeLine { - start_x: f32, - end_x: f32, +pub struct HighlightedRangeLine { + pub start_x: f32, + pub end_x: f32, } impl HighlightedRange { - fn paint(&self, bounds: RectF, scene: &mut Scene) { + pub fn paint(&self, bounds: RectF, scene: &mut Scene) { if self.lines.len() >= 2 && self.lines[0].start_x > self.lines[1].end_x { self.paint_lines(self.start_y, &self.lines[0..1], bounds, scene); self.paint_lines( diff --git a/crates/terminal/src/terminal_element.rs b/crates/terminal/src/terminal_element.rs index ad017c011f..a339c2c6f9 100644 --- a/crates/terminal/src/terminal_element.rs +++ b/crates/terminal/src/terminal_element.rs @@ -1,12 +1,15 @@ use alacritty_terminal::{ grid::{Dimensions, GridIterator, Indexed}, index::{Column as GridCol, Line as GridLine, Point, Side}, + selection::{Selection, SelectionRange, SelectionType}, + sync::FairMutex, term::{ cell::{Cell, Flags}, SizeInfo, }, + Term, }; -use editor::{Cursor, CursorShape}; +use editor::{Cursor, CursorShape, HighlightedRange, HighlightedRangeLine}; use gpui::{ color::Color, elements::*, @@ -23,11 +26,13 @@ use gpui::{ use itertools::Itertools; use ordered_float::OrderedFloat; use settings::Settings; -use std::{cmp::min, rc::Rc}; +use std::{cmp::min, ops::Range, rc::Rc, sync::Arc}; +use std::{fmt::Debug, ops::Sub}; use theme::TerminalStyle; use crate::{ - color_translation::convert_color, gpui_func_tools::paint_layer, Input, ScrollTerminal, Terminal, + color_translation::convert_color, gpui_func_tools::paint_layer, Input, ScrollTerminal, + Terminal, ZedListener, }; ///Scrolling is unbearably sluggish by default. Alacritty supports a configurable @@ -45,14 +50,27 @@ pub struct TerminalEl { view: WeakViewHandle, } -///Helper types so I don't mix these two up +///New type pattern so I don't mix these two up struct CellWidth(f32); struct LineHeight(f32); +struct LayoutLine { + cells: Vec, + highlighted_range: Option>, +} + +///New type pattern to ensure that we use adjusted mouse positions throughout the code base, rather than +struct PaneRelativePos(Vector2F); + +///Functionally the constructor for the PaneRelativePos type, mutates the mouse_position +fn relative_pos(mouse_position: Vector2F, origin: Vector2F) -> PaneRelativePos { + PaneRelativePos(mouse_position.sub(origin)) //Avoid the extra allocation by mutating +} + #[derive(Clone, Debug, Default)] struct LayoutCell { point: Point, - text: Line, + text: Line, //NOTE TO SELF THIS IS BAD PERFORMANCE RN! background_color: Color, } @@ -68,14 +86,15 @@ impl LayoutCell { ///The information generated during layout that is nescessary for painting pub struct LayoutState { - cells: Vec<(Point, Line)>, - background_rects: Vec<(RectF, Color)>, //Vec index == Line index for the LineSpan + layout_lines: Vec, line_height: LineHeight, em_width: CellWidth, cursor: Option, background_color: Color, cur_size: SizeInfo, display_offset: usize, + terminal: Arc>>, + selection_color: Color, } impl TerminalEl { @@ -111,42 +130,31 @@ impl Element for TerminalEl { view_handle.update(cx.app, |view, _cx| view.set_size(cur_size)); //Now that we're done with the mutable portion, grab the immutable settings and view again - let terminal_theme = &(cx.global::()).theme.terminal; - let term = view_handle.read(cx).term.lock(); + let (selection_color, terminal_theme) = { + let theme = &(cx.global::()).theme; + (theme.editor.selection.selection, &theme.terminal) + }; + let terminal_mutex = view_handle.read(cx).term.clone(); + let term = terminal_mutex.lock(); let grid = term.grid(); let cursor_point = grid.cursor.point; let cursor_text = grid[cursor_point.line][cursor_point.column].c.to_string(); let content = term.renderable_content(); - let layout_cells = layout_cells( + //We have a 'SelectionRange' struct to work with, + //Allows us to query start, end, and contains + //content.selection.unwrap() + + let layout_lines = layout_lines( content.display_iter, &text_style, terminal_theme, cx.text_layout_cache, + content.selection, ); - let cells = layout_cells - .iter() - .map(|c| (c.point, c.text.clone())) - .collect::, Line)>>(); - let background_rects = layout_cells - .iter() - .map(|cell| { - ( - RectF::new( - vec2f( - cell.point.column as f32 * cell_width.0, - cell.point.line as f32 * line_height.0, - ), - vec2f(cell_width.0, line_height.0), - ), - cell.background_color, - ) - }) - .collect::>(); - let block_text = cx.text_layout_cache.layout_str( &cursor_text, text_style.font_size, @@ -185,18 +193,21 @@ impl Element for TerminalEl { Some(block_text.clone()), ) }); + let display_offset = content.display_offset; + drop(term); ( constraint.max, LayoutState { - cells, + layout_lines, line_height, em_width: cell_width, cursor, cur_size, - background_rects, background_color: terminal_theme.background, - display_offset: content.display_offset, + display_offset, + terminal: terminal_mutex, + selection_color, }, ) } @@ -210,6 +221,7 @@ impl Element for TerminalEl { ) -> Self::PaintState { //Setup element stuff let clip_bounds = Some(visible_bounds); + paint_layer(cx, clip_bounds, |cx| { //Elements are ephemeral, only at paint time do we know what could be clicked by a mouse @@ -230,37 +242,45 @@ impl Element for TerminalEl { */ let cur_size = layout.cur_size.clone(); let display_offset = layout.display_offset.clone(); + let terminal_mutex = layout.terminal.clone(); + let origin = bounds.origin() + vec2f(layout.em_width.0, 0.); + + //TODO: Better way of doing this? + let mutex1 = terminal_mutex.clone(); + let _mutex2 = terminal_mutex.clone(); + cx.scene.push_mouse_region(MouseRegion { view_id: self.view.id(), - mouse_down: Some(Rc::new(move |pos, cx| { - let point = grid_cell(pos, cur_size, display_offset); - let side = cell_side(cur_size, pos.x() as usize); + click: Some(Rc::new(move |pos, click_count, cx| { + let (point, side) = mouse_to_cell_data(pos, origin, cur_size, display_offset); - //One problem is we need a terminal - //Second problem is that we need # of clicks - //Third problem is that dragging reports deltas, and we need locations. - //Fourth (minor) is need to render the selection + let selection_type = match click_count { + 1 => Some(SelectionType::Simple), + 2 => Some(SelectionType::Semantic), + 3 => Some(SelectionType::Lines), + _ => None, + }; - // if single_click { - // terminal.selection = Some(Selection::new(SelectionType::Simple, point, side)) - // } else if double_click { - // terminal.selection = Some(Selection::new(SelectionType::Semantic, point, side)) - // } else if triple_click { - // terminal.selection = Some(Selection::new(SelectionType::Lines, point, side)) - // } + let selection = selection_type + .map(|selection_type| Selection::new(selection_type, point, side)); + let mut term = mutex1.lock(); + term.selection = selection; cx.focus_parent_view() })), bounds: visible_bounds, - drag: Some(Rc::new(|delta, cx| { - //Calculate new point from delta - //terminal.selection.update(point, side) + drag: Some(Rc::new(move |_delta, _cx| { + // let (point, side) = mouse_to_cell_data(pos, origin, cur_size, display_offset); + + // let mut term = mutex2.lock(); + // if let Some(mut selection) = term.selection.take() { + // selection.update(point, side); + // term.selection = Some(selection); + // } })), ..Default::default() }); - let origin = bounds.origin() + vec2f(layout.em_width.0, 0.); - paint_layer(cx, clip_bounds, |cx| { //Start with a background color cx.scene.push_quad(Quad { @@ -271,25 +291,84 @@ impl Element for TerminalEl { }); //Draw cell backgrounds - for background_rect in &layout.background_rects { - let new_origin = origin + background_rect.0.origin(); - cx.scene.push_quad(Quad { - bounds: RectF::new(new_origin, background_rect.0.size()), - background: Some(background_rect.1), - border: Default::default(), - corner_radius: 0., + for layout_line in &layout.layout_lines { + for layout_cell in &layout_line.cells { + let position = vec2f( + origin.x() + layout_cell.point.column as f32 * layout.em_width.0, + origin.y() + layout_cell.point.line as f32 * layout.line_height.0, + ); + let size = vec2f(layout.em_width.0, layout.line_height.0); + + cx.scene.push_quad(Quad { + bounds: RectF::new(position, size), + background: Some(layout_cell.background_color), + border: Default::default(), + corner_radius: 0., + }) + } + } + }); + + //Draw Selection + paint_layer(cx, clip_bounds, |cx| { + let mut highlight_y = None; + let highlight_lines = layout + .layout_lines + .iter() + .filter_map(|line| { + if let Some(range) = &line.highlighted_range { + if let None = highlight_y { + highlight_y = Some( + origin.y() + + line.cells[0].point.line as f32 * layout.line_height.0, + ); + } + let start_x = origin.x() + + line.cells[range.start].point.column as f32 * layout.em_width.0; + let end_x = origin.x() + //TODO: Why -1? I know switch from count to index... but where... + + line.cells[range.end - 1].point.column as f32 * layout.em_width.0 + + layout.em_width.0; + + return Some(HighlightedRangeLine { start_x, end_x }); + } else { + return None; + } }) + .collect::>(); + + if let Some(y) = highlight_y { + let hr = HighlightedRange { + start_y: y, //Need to change this + line_height: layout.line_height.0, + lines: highlight_lines, + color: layout.selection_color, + //Copied from editor. TODO: move to theme or something + corner_radius: 0.15 * layout.line_height.0, + }; + hr.paint(bounds, cx.scene); } }); //Draw text paint_layer(cx, clip_bounds, |cx| { - for (point, cell) in &layout.cells { - let cell_origin = vec2f( - origin.x() + point.column as f32 * layout.em_width.0, - origin.y() + point.line as f32 * layout.line_height.0, - ); - cell.paint(cell_origin, visible_bounds, layout.line_height.0, cx); + for layout_line in &layout.layout_lines { + for layout_cell in &layout_line.cells { + let point = layout_cell.point; + + //Don't actually know the start_x for a line, until here: + let cell_origin = vec2f( + origin.x() + point.column as f32 * layout.em_width.0, + origin.y() + point.line as f32 * layout.line_height.0, + ); + + layout_cell.text.paint( + cell_origin, + visible_bounds, + layout.line_height.0, + cx, + ); + } } }); @@ -354,18 +433,17 @@ impl Element for TerminalEl { } } -/* -Mouse moved -> WindowEvent::CursorMoved -mouse press -> WindowEvent::MouseInput -update_selection_scrolling - - -copy_selection -start_selection -toggle_selection -update_selection -clear_selection - */ +fn mouse_to_cell_data( + pos: Vector2F, + origin: Vector2F, + cur_size: SizeInfo, + display_offset: usize, +) -> (Point, alacritty_terminal::index::Direction) { + let relative_pos = relative_pos(pos, origin); + let point = grid_cell(&relative_pos, cur_size, display_offset); + let side = cell_side(&relative_pos, cur_size); + (point, side) +} ///Configures a text style from the current settings. fn make_text_style(font_cache: &FontCache, settings: &Settings) -> TextStyle { @@ -399,38 +477,59 @@ fn make_new_size( ) } -fn layout_cells( +//Let's say that calculating the display is correct, that means that either calculating the highlight ranges is incorrect +//OR calculating the click ranges is incorrect + +fn layout_lines( grid: GridIterator, text_style: &TextStyle, terminal_theme: &TerminalStyle, text_layout_cache: &TextLayoutCache, -) -> Vec { - let mut line_count: i32 = 0; + selection_range: Option, +) -> Vec { let lines = grid.group_by(|i| i.point.line); lines .into_iter() - .map(|(_, line)| { - line_count += 1; - line.map(|indexed_cell| { - let cell_text = &indexed_cell.c.to_string(); + .enumerate() + .map(|(line_index, (_, line))| { + let mut highlighted_range = None; + let cells = line + .enumerate() + .map(|(x_index, indexed_cell)| { + if selection_range + .map(|range| range.contains(indexed_cell.point)) + .unwrap_or(false) + { + let mut range = highlighted_range.take().unwrap_or(x_index..x_index + 1); + range.end = range.end.max(x_index + 1); + highlighted_range = Some(range); + } - let cell_style = cell_style(&indexed_cell, terminal_theme, text_style); + let cell_text = &indexed_cell.c.to_string(); - let layout_cell = text_layout_cache.layout_str( - cell_text, - text_style.font_size, - &[(cell_text.len(), cell_style)], - ); - LayoutCell::new( - Point::new(line_count - 1, indexed_cell.point.column.0 as i32), - layout_cell, - convert_color(&indexed_cell.bg, terminal_theme), - ) - }) - .collect::>() + let cell_style = cell_style(&indexed_cell, terminal_theme, text_style); + + //This is where we might be able to get better performance + let layout_cell = text_layout_cache.layout_str( + cell_text, + text_style.font_size, + &[(cell_text.len(), cell_style)], + ); + + LayoutCell::new( + Point::new(line_index as i32, indexed_cell.point.column.0 as i32), + layout_cell, + convert_color(&indexed_cell.bg, terminal_theme), + ) + }) + .collect::>(); + + LayoutLine { + cells, + highlighted_range, + } }) - .flatten() - .collect::>() + .collect::>() } // Compute the cursor position and expected block width, may return a zero width if x_for_index returns @@ -487,7 +586,8 @@ fn cell_style(indexed: &Indexed<&Cell>, style: &TerminalStyle, text_style: &Text } ///Copied (with modifications) from alacritty/src/input.rs > Processor::cell_side() -fn cell_side(cur_size: SizeInfo, x: usize) -> Side { +fn cell_side(pos: &PaneRelativePos, cur_size: SizeInfo) -> Side { + 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; @@ -506,11 +606,14 @@ fn cell_side(cur_size: SizeInfo, x: usize) -> Side { } ///Copied (with modifications) from alacritty/src/event.rs > Mouse::point() -fn grid_cell(pos: Vector2F, cur_size: SizeInfo, display_offset: usize) -> Point { - let col = pos.x() - cur_size.cell_width() / cur_size.cell_width(); //TODO: underflow... +///Position is a pane-relative position. That means the top left corner of the mouse +///Region should be (0,0) +fn grid_cell(pos: &PaneRelativePos, cur_size: SizeInfo, display_offset: usize) -> Point { + let pos = pos.0; + let col = pos.x() / cur_size.cell_width(); //TODO: underflow... let col = min(GridCol(col as usize), cur_size.last_column()); - let line = pos.y() - cur_size.padding_y() / cur_size.cell_height(); + let line = pos.y() / cur_size.cell_height(); let line = min(line as usize, cur_size.bottommost_line().0 as usize); Point::new(GridLine((line - display_offset) as i32), col) From 240f3d8754c9a11c8499cafd7e7aa865932f215d Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Thu, 7 Jul 2022 12:29:49 -0700 Subject: [PATCH 3/9] Fixed default shell --- crates/terminal/src/terminal.rs | 4 ++-- crates/terminal/src/terminal_element.rs | 4 ---- styles/package-lock.json | 1 - 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/crates/terminal/src/terminal.rs b/crates/terminal/src/terminal.rs index 59ff100a7f..1b929b6941 100644 --- a/crates/terminal/src/terminal.rs +++ b/crates/terminal/src/terminal.rs @@ -1,5 +1,5 @@ use alacritty_terminal::{ - config::{Config, Program, PtyConfig}, + config::{Config, PtyConfig}, event::{Event as AlacTermEvent, EventListener, Notify}, event_loop::{EventLoop, Msg, Notifier}, grid::Scroll, @@ -126,7 +126,7 @@ impl Terminal { .detach(); let pty_config = PtyConfig { - shell: Some(Program::Just("zsh".to_string())), + shell: None, //Use the users default shell working_directory, hold: false, }; diff --git a/crates/terminal/src/terminal_element.rs b/crates/terminal/src/terminal_element.rs index a339c2c6f9..4a162cc99c 100644 --- a/crates/terminal/src/terminal_element.rs +++ b/crates/terminal/src/terminal_element.rs @@ -143,10 +143,6 @@ impl Element for TerminalEl { let content = term.renderable_content(); - //We have a 'SelectionRange' struct to work with, - //Allows us to query start, end, and contains - //content.selection.unwrap() - let layout_lines = layout_lines( content.display_iter, &text_style, diff --git a/styles/package-lock.json b/styles/package-lock.json index 49304dc2fa..2eb6d3a1bf 100644 --- a/styles/package-lock.json +++ b/styles/package-lock.json @@ -5,7 +5,6 @@ "requires": true, "packages": { "": { - "name": "styles", "version": "1.0.0", "license": "ISC", "dependencies": { From 4e3c32c2776cc8b77b0cb829fbe6408a577b337a Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Thu, 7 Jul 2022 13:19:38 -0700 Subject: [PATCH 4/9] Added copying --- assets/keymaps/default.json | 3 ++- crates/terminal/src/terminal.rs | 13 ++++++++++++- pbcpoy | 1 + 3 files changed, 15 insertions(+), 2 deletions(-) create mode 100644 pbcpoy diff --git a/assets/keymaps/default.json b/assets/keymaps/default.json index bea53ece45..6cd3660bf5 100644 --- a/assets/keymaps/default.json +++ b/assets/keymaps/default.json @@ -417,7 +417,8 @@ "up": "terminal::Up", "down": "terminal::Down", "tab": "terminal::Tab", - "cmd-v": "terminal::Paste" + "cmd-v": "terminal::Paste", + "cmd-c": "terminal::Copy" } } ] \ No newline at end of file diff --git a/crates/terminal/src/terminal.rs b/crates/terminal/src/terminal.rs index 1b929b6941..f6109572a7 100644 --- a/crates/terminal/src/terminal.rs +++ b/crates/terminal/src/terminal.rs @@ -52,7 +52,7 @@ pub struct ScrollTerminal(pub i32); actions!( terminal, - [Sigint, Escape, Del, Return, Left, Right, Up, Down, Tab, Clear, Paste, Deploy, Quit] + [Sigint, Escape, Del, Return, Left, Right, Up, Down, Tab, Clear, Copy, Paste, Deploy, Quit] ); impl_internal_actions!(terminal, [Input, ScrollTerminal]); @@ -70,6 +70,7 @@ pub fn init(cx: &mut MutableAppContext) { cx.add_action(Terminal::up); cx.add_action(Terminal::down); cx.add_action(Terminal::tab); + cx.add_action(Terminal::copy); cx.add_action(Terminal::paste); cx.add_action(Terminal::scroll_terminal); } @@ -272,6 +273,16 @@ impl Terminal { cx.emit(Event::CloseTerminal); } + ///Attempt to paste the clipboard into the terminal + fn copy(&mut self, _: &Copy, cx: &mut ViewContext) { + let term = self.term.lock(); + let copy_text = term.selection_to_string(); + match copy_text { + Some(s) => cx.write_to_clipboard(ClipboardItem::new(s)), + None => (), + } + } + ///Attempt to paste the clipboard into the terminal fn paste(&mut self, _: &Paste, cx: &mut ViewContext) { if let Some(item) = cx.read_from_clipboard() { diff --git a/pbcpoy b/pbcpoy new file mode 100644 index 0000000000..f70f10e4db --- /dev/null +++ b/pbcpoy @@ -0,0 +1 @@ +A From e3f492e13a3c75c2d80a7c8a62a7a72ec24e2477 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Thu, 7 Jul 2022 13:29:58 -0700 Subject: [PATCH 5/9] Hoisted assert clipboard into TestAppContext --- crates/editor/src/editor.rs | 2 +- crates/editor/src/test.rs | 8 -------- crates/gpui/src/app.rs | 8 ++++++++ crates/vim/src/vim_test_context.rs | 8 -------- 4 files changed, 9 insertions(+), 17 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index c1e2557555..808926ff50 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -8232,7 +8232,7 @@ mod tests { fox ju|mps over the lazy dog"}); cx.update_editor(|e, cx| e.copy(&Copy, cx)); - cx.assert_clipboard_content(Some("fox jumps over\n")); + cx.cx.assert_clipboard_content(Some("fox jumps over\n")); // Paste with three selections, noticing how the copied full-line selection is inserted // before the empty selections but replaces the selection that is non-empty. diff --git a/crates/editor/src/test.rs b/crates/editor/src/test.rs index d1316a85a0..0affe06f64 100644 --- a/crates/editor/src/test.rs +++ b/crates/editor/src/test.rs @@ -404,14 +404,6 @@ impl<'a> EditorTestContext<'a> { editor_text_with_selections } - - pub fn assert_clipboard_content(&mut self, expected_content: Option<&str>) { - self.cx.update(|cx| { - let actual_content = cx.read_from_clipboard().map(|item| item.text().to_owned()); - let expected_content = expected_content.map(|content| content.to_owned()); - assert_eq!(actual_content, expected_content); - }) - } } impl<'a> Deref for EditorTestContext<'a> { diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index b81714e0bc..26c4fe01a8 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -627,6 +627,14 @@ impl TestAppContext { } }) } + + pub fn assert_clipboard_content(&mut self, expected_content: Option<&str>) { + self.update(|cx| { + let actual_content = cx.read_from_clipboard().map(|item| item.text().to_owned()); + let expected_content = expected_content.map(|content| content.to_owned()); + assert_eq!(actual_content, expected_content); + }) + } } impl AsyncAppContext { diff --git a/crates/vim/src/vim_test_context.rs b/crates/vim/src/vim_test_context.rs index 57d0174703..08ec4bd5e9 100644 --- a/crates/vim/src/vim_test_context.rs +++ b/crates/vim/src/vim_test_context.rs @@ -147,14 +147,6 @@ impl<'a> VimTestContext<'a> { let mode = self.mode(); VimBindingTestContext::new(keystrokes, mode, mode, self) } - - pub fn assert_clipboard_content(&mut self, expected_content: Option<&str>) { - self.cx.update(|cx| { - let actual_content = cx.read_from_clipboard().map(|item| item.text().to_owned()); - let expected_content = expected_content.map(|content| content.to_owned()); - assert_eq!(actual_content, expected_content); - }) - } } impl<'a> Deref for VimTestContext<'a> { From 4bd111111576f03c6329a43ffc92e63323643969 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Thu, 7 Jul 2022 13:43:28 -0700 Subject: [PATCH 6/9] Added a small integration test --- crates/terminal/src/terminal.rs | 72 +++++++++++++++++++++++++++------ 1 file changed, 60 insertions(+), 12 deletions(-) diff --git a/crates/terminal/src/terminal.rs b/crates/terminal/src/terminal.rs index 1d508fb491..84f2055857 100644 --- a/crates/terminal/src/terminal.rs +++ b/crates/terminal/src/terminal.rs @@ -37,6 +37,8 @@ const RIGHT_SEQ: &str = "\x1b[C"; const UP_SEQ: &str = "\x1b[A"; const DOWN_SEQ: &str = "\x1b[B"; const DEFAULT_TITLE: &str = "Terminal"; +const DEBUG_TERMINAL_WIDTH: f32 = 300.; +const DEBUG_TERMINAL_HEIGHT: f32 = 200.; pub mod color_translation; pub mod gpui_func_tools; @@ -146,8 +148,15 @@ impl Terminal { setup_env(&config); //The details here don't matter, the terminal will be resized on the first layout - //Set to something small for easier debugging - let size_info = SizeInfo::new(200., 100.0, 5., 5., 0., 0., false); + let size_info = SizeInfo::new( + DEBUG_TERMINAL_WIDTH, + DEBUG_TERMINAL_HEIGHT, + 5., + 5., + 0., + 0., + false, + ); //Set up the terminal... let term = Term::new(&config, size_info, ZedListener(events_tx.clone())); @@ -485,7 +494,12 @@ mod tests { use std::{path::Path, sync::atomic::AtomicUsize, time::Duration}; use super::*; - use alacritty_terminal::{grid::GridIterator, term::cell::Cell}; + use alacritty_terminal::{ + grid::GridIterator, + index::{Column, Line, Point, Side}, + selection::{Selection, SelectionType}, + term::cell::Cell, + }; use gpui::TestAppContext; use itertools::Itertools; use project::{FakeFs, Fs, RealFs, RemoveOptions, Worktree}; @@ -511,15 +525,6 @@ mod tests { .await; } - pub(crate) fn grid_as_str(grid_iterator: GridIterator) -> String { - let lines = grid_iterator.group_by(|i| i.point.line.0); - lines - .into_iter() - .map(|(_, line)| line.map(|i| i.c).collect::()) - .collect::>() - .join("\n") - } - #[gpui::test] async fn single_file_worktree(cx: &mut TestAppContext) { let mut async_cx = cx.to_async(); @@ -600,4 +605,47 @@ mod tests { .ok() .expect("Could not remove test directory"); } + + ///Basic integration test, can we get the terminal to show up, execute a command, + //and produce noticable output? + #[gpui::test] + async fn test_copy(cx: &mut TestAppContext) { + let terminal = cx.add_view(Default::default(), |cx| Terminal::new(cx, None)); + cx.set_condition_duration(Duration::from_secs(2)); + + terminal.update(cx, |terminal, cx| { + terminal.write_to_pty(&Input(("expr 3 + 4".to_string()).to_string()), cx); + terminal.carriage_return(&Return, cx); + }); + + terminal + .condition(cx, |terminal, _cx| { + let term = terminal.term.clone(); + let content = grid_as_str(term.lock().renderable_content().display_iter); + content.contains("7") + }) + .await; + + terminal.update(cx, |terminal, cx| { + let mut term = terminal.term.lock(); + term.selection = Some(Selection::new( + SelectionType::Semantic, + Point::new(Line(3), Column(0)), + Side::Right, + )); + drop(term); + terminal.copy(&Copy, cx) + }); + + cx.assert_clipboard_content(Some(&"7")); + } + + pub(crate) fn grid_as_str(grid_iterator: GridIterator) -> String { + let lines = grid_iterator.group_by(|i| i.point.line.0); + lines + .into_iter() + .map(|(_, line)| line.map(|i| i.c).collect::()) + .collect::>() + .join("\n") + } } From d981f4a3f42c70dfb0db0044db15be7626bfa18c Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Thu, 7 Jul 2022 13:45:27 -0700 Subject: [PATCH 7/9] tidied up magic constants --- crates/terminal/src/terminal.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/crates/terminal/src/terminal.rs b/crates/terminal/src/terminal.rs index 84f2055857..d56864b631 100644 --- a/crates/terminal/src/terminal.rs +++ b/crates/terminal/src/terminal.rs @@ -39,6 +39,8 @@ const DOWN_SEQ: &str = "\x1b[B"; const DEFAULT_TITLE: &str = "Terminal"; const DEBUG_TERMINAL_WIDTH: f32 = 300.; const DEBUG_TERMINAL_HEIGHT: f32 = 200.; +const DEBUG_CELL_WIDTH: f32 = 5.; +const DEBUG_LINE_HEIGHT: f32 = 5.; pub mod color_translation; pub mod gpui_func_tools; @@ -151,8 +153,8 @@ impl Terminal { let size_info = SizeInfo::new( DEBUG_TERMINAL_WIDTH, DEBUG_TERMINAL_HEIGHT, - 5., - 5., + DEBUG_CELL_WIDTH, + DEBUG_LINE_HEIGHT, 0., 0., false, From 28fd1ccbc655c895da316bca3b2b8aa912ea87db Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Thu, 7 Jul 2022 13:55:58 -0700 Subject: [PATCH 8/9] Added another minor test --- crates/terminal/src/terminal_element.rs | 37 +++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/crates/terminal/src/terminal_element.rs b/crates/terminal/src/terminal_element.rs index 4a162cc99c..1856c8ac31 100644 --- a/crates/terminal/src/terminal_element.rs +++ b/crates/terminal/src/terminal_element.rs @@ -645,3 +645,40 @@ fn draw_debug_grid(bounds: RectF, layout: &mut LayoutState, cx: &mut PaintContex }); } } + +mod test { + + #[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 = alacritty_terminal::term::SizeInfo::new( + term_width, + term_height, + cell_width, + line_height, + 0., + 0., + false, + ); + + 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 (point, _) = + crate::terminal_element::mouse_to_cell_data(mouse_pos, origin, 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), + ) + ); + } +} From 49bd51c7c16324887f319db1f3ef146f095b7a36 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Thu, 7 Jul 2022 14:38:21 -0700 Subject: [PATCH 9/9] Fixed integration test --- crates/terminal/src/terminal.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/crates/terminal/src/terminal.rs b/crates/terminal/src/terminal.rs index d56864b631..40b2cba6cb 100644 --- a/crates/terminal/src/terminal.rs +++ b/crates/terminal/src/terminal.rs @@ -37,7 +37,7 @@ const RIGHT_SEQ: &str = "\x1b[C"; const UP_SEQ: &str = "\x1b[A"; const DOWN_SEQ: &str = "\x1b[B"; const DEFAULT_TITLE: &str = "Terminal"; -const DEBUG_TERMINAL_WIDTH: f32 = 300.; +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.; const DEBUG_LINE_HEIGHT: f32 = 5.; @@ -608,8 +608,7 @@ mod tests { .expect("Could not remove test directory"); } - ///Basic integration test, can we get the terminal to show up, execute a command, - //and produce noticable output? + ///If this test is failing for you, check that DEBUG_TERMINAL_WIDTH is wide enough to fit your entire command prompt! #[gpui::test] async fn test_copy(cx: &mut TestAppContext) { let terminal = cx.add_view(Default::default(), |cx| Terminal::new(cx, None)); @@ -632,7 +631,7 @@ mod tests { let mut term = terminal.term.lock(); term.selection = Some(Selection::new( SelectionType::Semantic, - Point::new(Line(3), Column(0)), + Point::new(Line(2), Column(0)), Side::Right, )); drop(term);