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 4542e283ee..c2df86de61 100644 --- a/crates/terminal/src/terminal.rs +++ b/crates/terminal/src/terminal.rs @@ -4,19 +4,23 @@ 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, }; +<<<<<<< HEAD use dirs::home_dir; +======= +use color_translation::{get_color_at_index, to_alac_rgb}; +>>>>>>> 3fe0d66d (Began working on selections, refactored colors) 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::{LocalWorktree, Project, ProjectPath}; use settings::Settings; @@ -24,7 +28,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' @@ -38,6 +42,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; @@ -63,7 +68,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); @@ -131,7 +136,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()); @@ -222,24 +226,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) } @@ -479,15 +466,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, - } -} - fn get_working_directory(wt: &LocalWorktree) -> Option { Some(wt.abs_path().to_path_buf()) .filter(|path| path.is_dir()) 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); - } - } -}