From 4528e9d5822c3741593db3c71852d44a7461eb9e Mon Sep 17 00:00:00 2001 From: Kyle Kelley Date: Sat, 3 Aug 2024 05:48:16 -0700 Subject: [PATCH] repl: Create better terminal output for REPL stdio (#15715) Rely on our implementation of a GPUI powered alacritty terminal to render stdout & stderr from the repl. Release Notes: - Fixed ANSI escape code and carriage return handling in repl outputs (https://github.com/zed-industries/zed/issues/15640, https://github.com/zed-industries/zed/issues/14855) https://github.com/user-attachments/assets/bd3f1584-863a-4afa-b60b-9d222a830ff8 --------- Co-authored-by: Mikayla --- Cargo.lock | 29 +- Cargo.toml | 2 +- crates/editor/src/editor.rs | 2 +- crates/repl/Cargo.toml | 1 + crates/repl/src/outputs.rs | 35 +- crates/repl/src/stdio.rs | 451 +++++-------------- crates/terminal/Cargo.toml | 2 +- crates/terminal/src/terminal.rs | 2 +- crates/terminal_view/src/terminal_element.rs | 36 +- 9 files changed, 165 insertions(+), 395 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 56e8f1911e2d8665606e03005e963a43b31c02ef..ccad27f96ce146eaaf5451ed46252d7a9aefd5ce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -80,30 +80,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "alacritty_terminal" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6d1ea4484c8676f295307a4892d478c70ac8da1dbd8c7c10830a504b7f1022f" -dependencies = [ - "base64 0.22.1", - "bitflags 2.6.0", - "home", - "libc", - "log", - "miow", - "parking_lot", - "piper", - "polling 3.3.2", - "regex-automata 0.4.5", - "rustix-openpty", - "serde", - "signal-hook", - "unicode-width", - "vte", - "windows-sys 0.48.0", -] - [[package]] name = "alacritty_terminal" version = "0.24.1-dev" @@ -8756,7 +8732,7 @@ dependencies = [ name = "repl" version = "0.1.0" dependencies = [ - "alacritty_terminal 0.23.0", + "alacritty_terminal", "anyhow", "async-dispatcher", "base64 0.22.1", @@ -8781,6 +8757,7 @@ dependencies = [ "serde_json", "settings", "smol", + "terminal", "terminal_view", "theme", "tree-sitter-md", @@ -10875,7 +10852,7 @@ dependencies = [ name = "terminal" version = "0.1.0" dependencies = [ - "alacritty_terminal 0.24.1-dev", + "alacritty_terminal", "anyhow", "collections", "dirs 4.0.0", diff --git a/Cargo.toml b/Cargo.toml index df5e02393fc508b9796218fcea5b096222926fcd..49e6247da65871a14710025a8fdc81add4309632 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -298,7 +298,7 @@ zed_actions = { path = "crates/zed_actions" } # aho-corasick = "1.1" -alacritty_terminal = "0.23" +alacritty_terminal = { git = "https://github.com/alacritty/alacritty", rev = "cacdb5bb3b72bad2c729227537979d95af75978f" } any_vec = "0.14" anyhow = "1.0.86" ashpd = "0.9.1" diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 00738093cbdc763a89e55ca261eec0fbd93ade6e..4908fa57eddfe08d2de7bc49ec3aebeff21d64c5 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -10167,7 +10167,7 @@ impl Editor { blocks } - pub(crate) fn resize_blocks( + pub fn resize_blocks( &mut self, heights: HashMap, autoscroll: Option, diff --git a/crates/repl/Cargo.toml b/crates/repl/Cargo.toml index 94530afb882c314ea397cc801f5291454ce6487d..ab2b2bc005c1558ed079beb2a8a35a3c687cecec 100644 --- a/crates/repl/Cargo.toml +++ b/crates/repl/Cargo.toml @@ -34,6 +34,7 @@ serde.workspace = true serde_json.workspace = true settings.workspace = true smol.workspace = true +terminal.workspace = true terminal_view.workspace = true theme.workspace = true ui.workspace = true diff --git a/crates/repl/src/outputs.rs b/crates/repl/src/outputs.rs index 55b65806bc375122986d3987f6495c05ed64787c..f0857a3fd7a921588a247291047f5b520401ff55 100644 --- a/crates/repl/src/outputs.rs +++ b/crates/repl/src/outputs.rs @@ -278,6 +278,23 @@ pub enum OutputType { ClearOutputWaitMarker, } +impl std::fmt::Debug for OutputType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + OutputType::Plain(_) => f.debug_struct("OutputType(Plain)"), + OutputType::Stream(_) => f.debug_struct("OutputType(Stream)"), + OutputType::Image(_) => f.debug_struct("OutputType(Image)"), + OutputType::ErrorOutput(_) => f.debug_struct("OutputType(ErrorOutput)"), + OutputType::Message(_) => f.debug_struct("OutputType(Message)"), + OutputType::Table(_) => f.debug_struct("OutputType(Table)"), + OutputType::ClearOutputWaitMarker => { + f.debug_struct("OutputType(ClearOutputWaitMarker)") + } + } + .finish() + } +} + impl OutputType { fn render(&self, cx: &ViewContext) -> Option { let el = match self { @@ -298,8 +315,8 @@ impl OutputType { pub fn new(data: &MimeBundle, cx: &mut WindowContext) -> Self { match data.richest(rank_mime_type) { - Some(MimeType::Plain(text)) => OutputType::Plain(TerminalOutput::from(text)), - Some(MimeType::Markdown(text)) => OutputType::Plain(TerminalOutput::from(text)), + Some(MimeType::Plain(text)) => OutputType::Plain(TerminalOutput::from(text, cx)), + Some(MimeType::Markdown(text)) => OutputType::Plain(TerminalOutput::from(text, cx)), Some(MimeType::Png(data)) | Some(MimeType::Jpeg(data)) => match ImageView::from(data) { Ok(view) => OutputType::Image(view), Err(error) => OutputType::Message(format!("Failed to load image: {}", error)), @@ -344,15 +361,14 @@ impl ExecutionView { JupyterMessageContent::DisplayData(result) => OutputType::new(&result.data, cx), JupyterMessageContent::StreamContent(result) => { // Previous stream data will combine together, handling colors, carriage returns, etc - if let Some(new_terminal) = self.apply_terminal_text(&result.text) { + if let Some(new_terminal) = self.apply_terminal_text(&result.text, cx) { new_terminal } else { - cx.notify(); return; } } JupyterMessageContent::ErrorOutput(result) => { - let mut terminal = TerminalOutput::new(); + let mut terminal = TerminalOutput::new(cx); terminal.append_text(&result.traceback.join("\n")); OutputType::ErrorOutput(ErrorView { @@ -427,12 +443,17 @@ impl ExecutionView { cx.notify(); } - fn apply_terminal_text(&mut self, text: &str) -> Option { + fn apply_terminal_text( + &mut self, + text: &str, + cx: &mut ViewContext, + ) -> Option { if let Some(last_output) = self.outputs.last_mut() { match last_output { OutputType::Stream(last_stream) => { last_stream.append_text(text); // Don't need to add a new output, we already have a terminal output + cx.notify(); return None; } // Edge case note: a clear output marker @@ -446,7 +467,7 @@ impl ExecutionView { } } - let mut new_terminal = TerminalOutput::new(); + let mut new_terminal = TerminalOutput::new(cx); new_terminal.append_text(text); Some(OutputType::Stream(new_terminal)) } diff --git a/crates/repl/src/stdio.rs b/crates/repl/src/stdio.rs index f38497d5780682acc1bc6ff1435ae1f7e4b3eb36..349ff864a57e1edbee198e0557dad758e59a05d2 100644 --- a/crates/repl/src/stdio.rs +++ b/crates/repl/src/stdio.rs @@ -1,13 +1,10 @@ use crate::outputs::ExecutionView; -use alacritty_terminal::vte::{ - ansi::{Attr, Color, NamedColor, Rgb}, - Params, ParamsIter, Parser, Perform, -}; -use core::iter; -use gpui::{font, prelude::*, AnyElement, StyledText, TextRun}; -use settings::Settings as _; -use theme::ThemeSettings; -use ui::{div, prelude::*, IntoElement, ViewContext}; +use alacritty_terminal::{term::Config, vte::ansi::Processor}; +use gpui::{canvas, size, AnyElement}; +use std::mem; +use terminal::ZedListener; +use terminal_view::terminal_element::TerminalElement; +use ui::{prelude::*, IntoElement, ViewContext}; /// Implements the most basic of terminal output for use by Jupyter outputs /// whether: @@ -17,362 +14,136 @@ use ui::{div, prelude::*, IntoElement, ViewContext}; /// * text/plain /// * traceback from an error output /// -/// Ideally, we would instead use alacritty::vte::Processor to collect the -/// output and then render up to u8::MAX lines of text. However, it's likely -/// overkill for 95% of outputs. -/// -/// Instead, this implementation handles: -/// -/// * ANSI color codes (background, foreground), including 256 color -/// * Carriage returns/line feeds -/// -/// There is no support for cursor movement, clearing the screen, and other text styles pub struct TerminalOutput { - parser: Parser, - handler: TerminalHandler, + parser: Processor, + handler: alacritty_terminal::Term, } -impl TerminalOutput { - pub fn new() -> Self { - Self { - parser: Parser::new(), - handler: TerminalHandler::new(), - } - } +pub fn terminal_size(cx: &mut WindowContext) -> terminal::TerminalSize { + let text_style = cx.text_style(); + let text_system = cx.text_system(); - pub fn from(text: &str) -> Self { - let mut output = Self::new(); - output.append_text(text); - output - } + let line_height = cx.line_height(); - pub fn append_text(&mut self, text: &str) { - for byte in text.as_bytes() { - self.parser.advance(&mut self.handler, *byte); - } - } + let font_pixels = text_style.font_size.to_pixels(cx.rem_size()); + let font_id = text_system.resolve_font(&text_style.font()); - pub fn render(&self, cx: &ViewContext) -> AnyElement { - let theme = cx.theme(); - let buffer_font = ThemeSettings::get_global(cx).buffer_font.family.clone(); - let runs = self - .handler - .text_runs - .iter() - .chain(Some(&self.handler.current_text_run)) - .map(|ansi_run| { - let color = terminal_view::terminal_element::convert_color( - &ansi_run.fg.unwrap_or(Color::Named(NamedColor::Foreground)), - theme, - ); - let background_color = ansi_run - .bg - .map(|bg| terminal_view::terminal_element::convert_color(&bg, theme)); + let cell_width = text_system + .advance(font_id, font_pixels, 'w') + .unwrap() + .width; - TextRun { - len: ansi_run.len, - color, - background_color, - underline: Default::default(), - font: font(buffer_font.clone()), - strikethrough: None, - } - }) - .collect::>(); + let num_lines = 200; + let columns = 120; - // Trim the last trailing newline for visual appeal - let trimmed = self - .handler - .buffer - .strip_suffix('\n') - .unwrap_or(&self.handler.buffer); + // Reversed math from terminal::TerminalSize to get pixel width according to terminal width + let width = columns as f32 * cell_width; + let height = num_lines as f32 * cx.line_height(); - let text = StyledText::new(trimmed.to_string()).with_runs(runs); - div() - .font_family(buffer_font) - .child(text) - .into_any_element() + terminal::TerminalSize { + cell_width, + line_height, + size: size(width, height), } } -#[derive(Clone, Default)] -struct AnsiTextRun { - len: usize, - fg: Option, - bg: Option, -} - -struct TerminalHandler { - text_runs: Vec, - current_text_run: AnsiTextRun, - buffer: String, -} - -impl TerminalHandler { - fn new() -> Self { +impl TerminalOutput { + pub fn new(cx: &mut WindowContext) -> Self { + let (events_tx, events_rx) = futures::channel::mpsc::unbounded(); + let term = alacritty_terminal::Term::new( + Config::default(), + &terminal_size(cx), + terminal::ZedListener(events_tx.clone()), + ); + + mem::forget(events_rx); Self { - text_runs: Vec::new(), - current_text_run: AnsiTextRun::default(), - buffer: String::new(), - } - } - - fn add_text(&mut self, c: char) { - self.buffer.push(c); - self.current_text_run.len += 1; - } - - fn reset(&mut self) { - if self.current_text_run.len > 0 { - self.text_runs.push(self.current_text_run.clone()); + parser: Processor::new(), + handler: term, } - - self.current_text_run = AnsiTextRun::default(); } - fn terminal_attribute(&mut self, attr: Attr) { - // println!("[terminal_attribute] attr={:?}", attr); - if Attr::Reset == attr { - self.reset(); - return; - } - - if self.current_text_run.len > 0 { - self.text_runs.push(self.current_text_run.clone()); - } - - let mut text_run = AnsiTextRun::default(); - - match attr { - Attr::Foreground(color) => text_run.fg = Some(color), - Attr::Background(color) => text_run.bg = Some(color), - _ => {} - } - - self.current_text_run = text_run; + pub fn from(text: &str, cx: &mut WindowContext) -> Self { + let mut output = Self::new(cx); + output.append_text(text); + output } - fn process_carriage_return(&mut self) { - // Find last carriage return's position - let last_cr = self.buffer.rfind('\r').unwrap_or(0); - self.buffer = self.buffer.chars().take(last_cr).collect(); - - // First work through our current text run - let mut total_len = self.current_text_run.len; - if total_len > last_cr { - // We are in the current text run - self.current_text_run.len = self.current_text_run.len - last_cr; - } else { - let mut last_cr_run = 0; - // Find the last run before the last carriage return - for (i, run) in self.text_runs.iter().enumerate() { - total_len += run.len; - if total_len > last_cr { - last_cr_run = i; - break; - } + pub fn append_text(&mut self, text: &str) { + for byte in text.as_bytes() { + if *byte == b'\n' { + // Dirty (?) hack to move the cursor down + self.parser.advance(&mut self.handler, b'\r'); + self.parser.advance(&mut self.handler, b'\n'); + } else { + self.parser.advance(&mut self.handler, *byte); } - self.text_runs = self.text_runs[..last_cr_run].to_vec(); - self.current_text_run = self.text_runs.pop().unwrap_or(AnsiTextRun::default()); - } - - self.buffer.push('\r'); - self.current_text_run.len += 1; - } -} - -impl Perform for TerminalHandler { - fn print(&mut self, c: char) { - // println!("[print] c={:?}", c); - self.add_text(c); - } - fn execute(&mut self, byte: u8) { - match byte { - b'\n' => { - self.add_text('\n'); - } - b'\r' => { - self.process_carriage_return(); - } - _ => { - // Format as hex - // println!("[execute] byte={:02x}", byte); - } + // self.parser.advance(&mut self.handler, *byte); } } - fn hook(&mut self, _params: &Params, _intermediates: &[u8], _ignore: bool, _c: char) { - // noop - // println!( - // "[hook] params={:?}, intermediates={:?}, c={:?}", - // _params, _intermediates, _c - // ); - } - - fn put(&mut self, _byte: u8) { - // noop - // println!("[put] byte={:02x}", _byte); - } - - fn unhook(&mut self) { - // noop - } - - fn osc_dispatch(&mut self, _params: &[&[u8]], _bell_terminated: bool) { - // noop - // println!("[osc_dispatch] params={:?}", _params); - } - - fn csi_dispatch( - &mut self, - params: &alacritty_terminal::vte::Params, - intermediates: &[u8], - _ignore: bool, - action: char, - ) { - // println!( - // "[csi_dispatch] action={:?}, params={:?}, intermediates={:?}", - // action, params, intermediates - // ); + pub fn render(&self, cx: &ViewContext) -> AnyElement { + let text_style = cx.text_style(); + let text_system = cx.text_system(); - let mut params_iter = params.iter(); - // Collect colors - match (action, intermediates) { - ('m', []) => { - if params.is_empty() { - self.terminal_attribute(Attr::Reset); - } else { - for attr in attrs_from_sgr_parameters(&mut params_iter) { - match attr { - Some(attr) => self.terminal_attribute(attr), - None => return, - } - } + let grid = self + .handler + .renderable_content() + .display_iter + .map(|ic| terminal::IndexedCell { + point: ic.point, + cell: ic.cell.clone(), + }); + let (cells, rects) = TerminalElement::layout_grid(grid, &text_style, text_system, None, cx); + + // lines are 0-indexed, so we must add 1 to get the number of lines + let num_lines = cells.iter().map(|c| c.point.line).max().unwrap_or(0) + 1; + let height = num_lines as f32 * cx.line_height(); + + let line_height = cx.line_height(); + + let font_pixels = text_style.font_size.to_pixels(cx.rem_size()); + let font_id = text_system.resolve_font(&text_style.font()); + + let cell_width = text_system + .advance(font_id, font_pixels, 'w') + .map(|advance| advance.width) + .unwrap_or(Pixels(0.0)); + + canvas( + // prepaint + move |_bounds, _| {}, + // paint + move |bounds, _, cx| { + for rect in rects { + rect.paint( + bounds.origin, + &terminal::TerminalSize { + cell_width, + line_height, + size: bounds.size, + }, + cx, + ); } - } - _ => {} - } - } - - fn esc_dispatch(&mut self, _intermediates: &[u8], _ignore: bool, _byte: u8) { - // noop - // println!( - // "[esc_dispatch] intermediates={:?}, byte={:?}", - // _intermediates, _byte - // ); - } -} - -// The following was pulled from vte::ansi -#[inline] -fn attrs_from_sgr_parameters(params: &mut ParamsIter<'_>) -> Vec> { - let mut attrs = Vec::with_capacity(params.size_hint().0); - - while let Some(param) = params.next() { - let attr = match param { - [0] => Some(Attr::Reset), - [1] => Some(Attr::Bold), - [2] => Some(Attr::Dim), - [3] => Some(Attr::Italic), - [4, 0] => Some(Attr::CancelUnderline), - [4, 2] => Some(Attr::DoubleUnderline), - [4, 3] => Some(Attr::Undercurl), - [4, 4] => Some(Attr::DottedUnderline), - [4, 5] => Some(Attr::DashedUnderline), - [4, ..] => Some(Attr::Underline), - [5] => Some(Attr::BlinkSlow), - [6] => Some(Attr::BlinkFast), - [7] => Some(Attr::Reverse), - [8] => Some(Attr::Hidden), - [9] => Some(Attr::Strike), - [21] => Some(Attr::CancelBold), - [22] => Some(Attr::CancelBoldDim), - [23] => Some(Attr::CancelItalic), - [24] => Some(Attr::CancelUnderline), - [25] => Some(Attr::CancelBlink), - [27] => Some(Attr::CancelReverse), - [28] => Some(Attr::CancelHidden), - [29] => Some(Attr::CancelStrike), - [30] => Some(Attr::Foreground(Color::Named(NamedColor::Black))), - [31] => Some(Attr::Foreground(Color::Named(NamedColor::Red))), - [32] => Some(Attr::Foreground(Color::Named(NamedColor::Green))), - [33] => Some(Attr::Foreground(Color::Named(NamedColor::Yellow))), - [34] => Some(Attr::Foreground(Color::Named(NamedColor::Blue))), - [35] => Some(Attr::Foreground(Color::Named(NamedColor::Magenta))), - [36] => Some(Attr::Foreground(Color::Named(NamedColor::Cyan))), - [37] => Some(Attr::Foreground(Color::Named(NamedColor::White))), - [38] => { - let mut iter = params.map(|param| param[0]); - parse_sgr_color(&mut iter).map(Attr::Foreground) - } - [38, params @ ..] => handle_colon_rgb(params).map(Attr::Foreground), - [39] => Some(Attr::Foreground(Color::Named(NamedColor::Foreground))), - [40] => Some(Attr::Background(Color::Named(NamedColor::Black))), - [41] => Some(Attr::Background(Color::Named(NamedColor::Red))), - [42] => Some(Attr::Background(Color::Named(NamedColor::Green))), - [43] => Some(Attr::Background(Color::Named(NamedColor::Yellow))), - [44] => Some(Attr::Background(Color::Named(NamedColor::Blue))), - [45] => Some(Attr::Background(Color::Named(NamedColor::Magenta))), - [46] => Some(Attr::Background(Color::Named(NamedColor::Cyan))), - [47] => Some(Attr::Background(Color::Named(NamedColor::White))), - [48] => { - let mut iter = params.map(|param| param[0]); - parse_sgr_color(&mut iter).map(Attr::Background) - } - [48, params @ ..] => handle_colon_rgb(params).map(Attr::Background), - [49] => Some(Attr::Background(Color::Named(NamedColor::Background))), - [58] => { - let mut iter = params.map(|param| param[0]); - parse_sgr_color(&mut iter).map(|color| Attr::UnderlineColor(Some(color))) - } - [58, params @ ..] => { - handle_colon_rgb(params).map(|color| Attr::UnderlineColor(Some(color))) - } - [59] => Some(Attr::UnderlineColor(None)), - [90] => Some(Attr::Foreground(Color::Named(NamedColor::BrightBlack))), - [91] => Some(Attr::Foreground(Color::Named(NamedColor::BrightRed))), - [92] => Some(Attr::Foreground(Color::Named(NamedColor::BrightGreen))), - [93] => Some(Attr::Foreground(Color::Named(NamedColor::BrightYellow))), - [94] => Some(Attr::Foreground(Color::Named(NamedColor::BrightBlue))), - [95] => Some(Attr::Foreground(Color::Named(NamedColor::BrightMagenta))), - [96] => Some(Attr::Foreground(Color::Named(NamedColor::BrightCyan))), - [97] => Some(Attr::Foreground(Color::Named(NamedColor::BrightWhite))), - [100] => Some(Attr::Background(Color::Named(NamedColor::BrightBlack))), - [101] => Some(Attr::Background(Color::Named(NamedColor::BrightRed))), - [102] => Some(Attr::Background(Color::Named(NamedColor::BrightGreen))), - [103] => Some(Attr::Background(Color::Named(NamedColor::BrightYellow))), - [104] => Some(Attr::Background(Color::Named(NamedColor::BrightBlue))), - [105] => Some(Attr::Background(Color::Named(NamedColor::BrightMagenta))), - [106] => Some(Attr::Background(Color::Named(NamedColor::BrightCyan))), - [107] => Some(Attr::Background(Color::Named(NamedColor::BrightWhite))), - _ => None, - }; - attrs.push(attr); - } - - attrs -} - -/// Handle colon separated rgb color escape sequence. -#[inline] -fn handle_colon_rgb(params: &[u16]) -> Option { - let rgb_start = if params.len() > 4 { 2 } else { 1 }; - let rgb_iter = params[rgb_start..].iter().copied(); - let mut iter = iter::once(params[0]).chain(rgb_iter); - parse_sgr_color(&mut iter) -} - -/// Parse a color specifier from list of attributes. -fn parse_sgr_color(params: &mut dyn Iterator) -> Option { - match params.next() { - Some(2) => Some(Color::Spec(Rgb { - r: u8::try_from(params.next()?).ok()?, - g: u8::try_from(params.next()?).ok()?, - b: u8::try_from(params.next()?).ok()?, - })), - Some(5) => Some(Color::Indexed(u8::try_from(params.next()?).ok()?)), - _ => None, + for cell in cells { + cell.paint( + bounds.origin, + &terminal::TerminalSize { + cell_width, + line_height, + size: bounds.size, + }, + bounds, + cx, + ); + } + }, + ) + // We must set the height explicitly for the editor block to size itself correctly + .h(height) + .into_any_element() } } diff --git a/crates/terminal/Cargo.toml b/crates/terminal/Cargo.toml index 848a22c9f69ad026b6c4d2e6b7709dd322e3d40d..5c64d844335e8eaf8f2c35e6a78beae7dec61fa0 100644 --- a/crates/terminal/Cargo.toml +++ b/crates/terminal/Cargo.toml @@ -13,7 +13,7 @@ path = "src/terminal.rs" doctest = false [dependencies] -alacritty_terminal = { git = "https://github.com/alacritty/alacritty", rev = "cacdb5bb3b72bad2c729227537979d95af75978f" } +alacritty_terminal.workspace = true anyhow.workspace = true collections.workspace = true dirs.workspace = true diff --git a/crates/terminal/src/terminal.rs b/crates/terminal/src/terminal.rs index 8f9b512027316e202eb1c1af6a5b74e214948d46..21b67f2d8349c1af708f970199acd21ba1f7d88d 100644 --- a/crates/terminal/src/terminal.rs +++ b/crates/terminal/src/terminal.rs @@ -142,7 +142,7 @@ enum InternalEvent { ///A translation struct for Alacritty to communicate with us from their event loop #[derive(Clone)] -pub struct ZedListener(UnboundedSender); +pub struct ZedListener(pub UnboundedSender); impl EventListener for ZedListener { fn send_event(&self, event: AlacTermEvent) { diff --git a/crates/terminal_view/src/terminal_element.rs b/crates/terminal_view/src/terminal_element.rs index 9d94d9d129bc67f8234297b8492e9120ffff401a..395fb51cf66702ab298f42f367a9d3f2e3a5f3d6 100644 --- a/crates/terminal_view/src/terminal_element.rs +++ b/crates/terminal_view/src/terminal_element.rs @@ -73,8 +73,8 @@ impl DisplayCursor { } #[derive(Debug, Default)] -struct LayoutCell { - point: AlacPoint, +pub struct LayoutCell { + pub point: AlacPoint, text: gpui::ShapedLine, } @@ -83,10 +83,10 @@ impl LayoutCell { LayoutCell { point, text } } - fn paint( + pub fn paint( &self, origin: Point, - layout: &LayoutState, + dimensions: &TerminalSize, _visible_bounds: Bounds, cx: &mut WindowContext, ) { @@ -94,17 +94,17 @@ impl LayoutCell { let point = self.point; Point::new( - (origin.x + point.column as f32 * layout.dimensions.cell_width).floor(), - origin.y + point.line as f32 * layout.dimensions.line_height, + (origin.x + point.column as f32 * dimensions.cell_width).floor(), + origin.y + point.line as f32 * dimensions.line_height, ) }; - self.text.paint(pos, layout.dimensions.line_height, cx).ok(); + self.text.paint(pos, dimensions.line_height, cx).ok(); } } #[derive(Clone, Debug, Default)] -struct LayoutRect { +pub struct LayoutRect { point: AlacPoint, num_of_cells: usize, color: Hsla, @@ -127,17 +127,17 @@ impl LayoutRect { } } - fn paint(&self, origin: Point, layout: &LayoutState, cx: &mut WindowContext) { + pub fn paint(&self, origin: Point, dimensions: &TerminalSize, cx: &mut WindowContext) { let position = { let alac_point = self.point; point( - (origin.x + alac_point.column as f32 * layout.dimensions.cell_width).floor(), - origin.y + alac_point.line as f32 * layout.dimensions.line_height, + (origin.x + alac_point.column as f32 * dimensions.cell_width).floor(), + origin.y + alac_point.line as f32 * dimensions.line_height, ) }; let size = point( - (layout.dimensions.cell_width * self.num_of_cells as f32).ceil(), - layout.dimensions.line_height, + (dimensions.cell_width * self.num_of_cells as f32).ceil(), + dimensions.line_height, ) .into(); @@ -196,8 +196,8 @@ impl TerminalElement { //Vec> -> Clip out the parts of the ranges - fn layout_grid( - grid: &Vec, + pub fn layout_grid( + grid: impl Iterator, text_style: &TextStyle, // terminal_theme: &TerminalStyle, text_system: &WindowTextSystem, @@ -755,7 +755,7 @@ impl Element for TerminalElement { // then have that representation be converted to the appropriate highlight data structure let (cells, rects) = TerminalElement::layout_grid( - cells, + cells.iter().cloned(), &text_style, &cx.text_system(), last_hovered_word @@ -918,7 +918,7 @@ impl Element for TerminalElement { }); for rect in &layout.rects { - rect.paint(origin, &layout, cx); + rect.paint(origin, &layout.dimensions, cx); } for (relative_highlighted_range, color) in @@ -939,7 +939,7 @@ impl Element for TerminalElement { } for cell in &layout.cells { - cell.paint(origin, &layout, bounds, cx); + cell.paint(origin, &layout.dimensions, bounds, cx); } if self.cursor_visible {