diff --git a/src/app/config_sync.rs b/src/app/config_sync.rs index 2fae02d..563bfaa 100644 --- a/src/app/config_sync.rs +++ b/src/app/config_sync.rs @@ -632,7 +632,8 @@ impl Ashell { } pub(crate) fn export_connections(&mut self, window: &mut gpui::Window, cx: &mut Context) { - if self.active_dialog.is_some() { + let parent_dialog = self.active_dialog; + if parent_dialog.is_some() && parent_dialog != Some(DialogKind::Settings) { return; } @@ -695,7 +696,7 @@ impl Ashell { let sessions = sessions_for_export.clone(); view_for_ok.update(cx, |this, cx| { if this.active_dialog == Some(DialogKind::ConnectionExport) { - this.active_dialog = None; + this.active_dialog = parent_dialog; } this.start_connection_csv_export(sessions, window, cx); cx.notify(); @@ -707,7 +708,7 @@ impl Ashell { move |_, _, cx| { view.update(cx, |this, cx| { if this.active_dialog == Some(DialogKind::ConnectionExport) { - this.active_dialog = None; + this.active_dialog = parent_dialog; } cx.notify(); }); diff --git a/src/app/ui.rs b/src/app/ui.rs index 0e1ff29..639d7ba 100644 --- a/src/app/ui.rs +++ b/src/app/ui.rs @@ -3336,7 +3336,6 @@ impl Ashell { active_session_id: Option<&str>, cx: &mut Context, ) -> gpui::AnyElement { - let connect_id = session.id.clone(); let edit_id = session.id.clone(); let delete_id = session.id.clone(); let management_mode = self.connection_management_mode; @@ -3347,8 +3346,6 @@ impl Ashell { let selection_id = session.id.clone(); let row_selection_id = selection_id.clone(); let row_id = ElementId::Name(format!("saved-connect-{}", session.id).into()); - let connect_button_id = - ElementId::Name(format!("connect-saved-session-{}", session.id).into()); div() .id(row_id) @@ -3477,33 +3474,7 @@ impl Ashell { .text_size(ui_rems(0.75)) .text_color(cx.theme().muted_foreground) .child(detail), - ) - .when(management_mode, |this| { - this.child( - h_flex() - .w(px(24.)) - .ml_1() - .flex_none() - .items_center() - .justify_center() - .on_mouse_down(MouseButton::Left, |_, _, cx| { - cx.stop_propagation(); - }) - .on_mouse_down(MouseButton::Right, |_, _, cx| { - cx.stop_propagation(); - }) - .child( - pointer_button(connect_button_id) - .ghost() - .small() - .icon(IconName::ExternalLink) - .tooltip(t!("connect").to_string()) - .on_click(cx.listener(move |this, _, window, cx| { - this.connect_saved_session(connect_id.clone(), window, cx); - })), - ), - ) - }), + ), ) .into_any_element() } diff --git a/src/terminal/element.rs b/src/terminal/element.rs index b777ac3..c0c5810 100644 --- a/src/terminal/element.rs +++ b/src/terminal/element.rs @@ -12,6 +12,7 @@ use gpui_component::ActiveTheme as _; use crate::Ashell; use crate::terminal::custom_blocks::{is_custom_block_supported, paint_custom_block}; +use crate::terminal::highlight::HighlightStyle; use crate::terminal::{RenderSnapshot, ViewportSelection}; #[derive(Clone, Copy)] @@ -338,21 +339,16 @@ impl TerminalElement { } } - fn cell_run_style(&self, cell: &alacritty_terminal::term::cell::Cell, cx: &App) -> TextRun { - let mut fg = color_to_hsla(cell.fg, true, cx); - let mut bg = color_to_hsla(cell.bg, false, cx); - if cell.flags.contains(Flags::INVERSE) { - std::mem::swap(&mut fg, &mut bg); - } - if cell.flags.contains(Flags::DIM) { - fg.a *= 0.7; - } - + fn cell_run_style( + &self, + cell: &alacritty_terminal::term::cell::Cell, + foreground: Hsla, + ) -> TextRun { let underline = cell .flags .intersects(Flags::ALL_UNDERLINES) .then(|| UnderlineStyle { - color: Some(fg), + color: Some(foreground), thickness: px(1.0), wavy: cell.flags.contains(Flags::UNDERCURL), }); @@ -360,7 +356,7 @@ impl TerminalElement { .flags .contains(Flags::STRIKEOUT) .then(|| StrikethroughStyle { - color: Some(fg), + color: Some(foreground), thickness: px(1.0), }); @@ -377,7 +373,7 @@ impl TerminalElement { TextRun { len: cell.c.len_utf8(), - color: fg, + color: foreground, background_color: None, font: Font { family: self.font_family.clone(), @@ -408,12 +404,6 @@ impl TerminalElement { let mut underlines = Vec::new(); let mut current_run: Option = None; - // Retrieve cached keyword highlights and merge with search highlights - let mut highlights = self.snapshot.highlights.clone(); - if let Some(sm) = self.search_highlights.as_ref() { - highlights.extend(sm.iter().map(|(k, v)| (*k, *v))); - } - for render_cell in &self.snapshot.cells { let cell = &render_cell.cell; if cell.flags.intersects( @@ -425,24 +415,54 @@ impl TerminalElement { let selected = self.snapshot.selection.is_some_and(|selection| { selection_contains(selection, render_cell.row, render_cell.col) }); - let bg = color_to_hsla(cell.bg, false, cx); - if selected || !is_default_bg(cell.bg) || cell.flags.contains(Flags::INVERSE) { + let cell_position = (render_cell.row, render_cell.col); + let keyword_highlight = self.snapshot.highlights.get(&cell_position).copied(); + let search_highlight = self + .search_highlights + .as_ref() + .and_then(|highlights| highlights.get(&cell_position)) + .copied(); + let (foreground, background) = resolve_cell_colors( + color_to_hsla(cell.fg, true, cx), + color_to_hsla(cell.bg, false, cx), + keyword_highlight, + cell.flags, + ); + let cell_span = if cell.flags.contains(Flags::WIDE_CHAR) { + 2 + } else { + 1 + }; + + if selected { rects.push(LayoutRect { row: render_cell.row, col: render_cell.col, - cells: if cell.flags.contains(Flags::WIDE_CHAR) { - 2 - } else { - 1 - }, - color: if selected { - cx.theme().selection - } else if cell.flags.contains(Flags::INVERSE) { - color_to_hsla(cell.fg, true, cx) - } else { - bg - }, + cells: cell_span, + color: cx.theme().selection, }); + } else { + let has_highlight_background = + keyword_highlight.is_some_and(|style| style.background.is_some()); + if !is_default_bg(cell.bg) + || cell.flags.contains(Flags::INVERSE) + || has_highlight_background + { + rects.push(LayoutRect { + row: render_cell.row, + col: render_cell.col, + cells: cell_span, + color: background, + }); + } + if let Some(search_background) = search_highlight { + rects.push(LayoutRect { + row: render_cell.row, + col: render_cell.col, + cells: cell_span, + color: search_background, + }); + } } if is_blank(cell) { @@ -452,12 +472,7 @@ impl TerminalElement { continue; } - let mut style = self.cell_run_style(cell, cx); - - // Apply keyword highlight color if this cell was matched. - if let Some(&hl_color) = highlights.get(&(render_cell.row, render_cell.col)) { - style.color = hl_color; - } + let style = self.cell_run_style(cell, foreground); // Apply hover underline if mouse is hovering over this URL if let Some(hu) = &hovered_url { @@ -871,6 +886,31 @@ fn selection_contains(selection: ViewportSelection, row: i32, col: i32) -> bool after_start && before_end } +fn resolve_cell_colors( + mut foreground: Hsla, + mut background: Hsla, + highlight: Option, + flags: Flags, +) -> (Hsla, Hsla) { + if let Some(highlight) = highlight { + if let Some(highlight_foreground) = highlight.foreground { + foreground = highlight_foreground; + } + if let Some(highlight_background) = highlight.background { + background = highlight_background; + } + } + + if flags.contains(Flags::INVERSE) { + std::mem::swap(&mut foreground, &mut background); + } + if flags.contains(Flags::DIM) { + foreground.a *= 0.7; + } + + (foreground, background) +} + fn is_blank(cell: &alacritty_terminal::term::cell::Cell) -> bool { cell.c == ' ' && cell.zerowidth().is_none() @@ -965,7 +1005,14 @@ fn named_color(named: NamedColor, _foreground: bool, cx: &App) -> Hsla { #[cfg(test)] mod tests { - use super::should_render_cursor; + use super::{resolve_cell_colors, should_render_cursor}; + use crate::terminal::highlight::HighlightStyle; + use alacritty_terminal::term::cell::Flags; + use gpui::Hsla; + + fn color(h: f32, s: f32, l: f32) -> Hsla { + Hsla { h, s, l, a: 1.0 } + } #[test] fn renders_cursor_only_for_focused_pane_in_focused_terminal_window() { @@ -974,4 +1021,46 @@ mod tests { assert!(!should_render_cursor(true, false, true)); assert!(!should_render_cursor(true, true, false)); } + + #[test] + fn applies_highlight_channels_before_inverse_terminal_style() { + let original_foreground = color(0.0, 0.0, 0.8); + let original_background = color(0.0, 0.0, 0.1); + let highlight_foreground = color(0.1, 0.8, 0.5); + let highlight_background = color(0.6, 0.8, 0.4); + + let (foreground, background) = resolve_cell_colors( + original_foreground, + original_background, + Some(HighlightStyle { + foreground: Some(highlight_foreground), + background: Some(highlight_background), + }), + Flags::INVERSE, + ); + + assert_eq!(foreground, highlight_background); + assert_eq!(background, highlight_foreground); + } + + #[test] + fn foreground_highlight_preserves_background_and_dim_style() { + let original_foreground = color(0.0, 0.0, 0.8); + let original_background = color(0.0, 0.0, 0.1); + let highlight_foreground = color(0.1, 0.8, 0.5); + + let (foreground, background) = resolve_cell_colors( + original_foreground, + original_background, + Some(HighlightStyle { + foreground: Some(highlight_foreground), + background: None, + }), + Flags::DIM, + ); + + assert_eq!(foreground.alpha(1.0), highlight_foreground); + assert!((foreground.a - 0.7).abs() < f32::EPSILON); + assert_eq!(background, original_background); + } } diff --git a/src/terminal/highlight.rs b/src/terminal/highlight.rs index 87b8877..bd499ba 100644 --- a/src/terminal/highlight.rs +++ b/src/terminal/highlight.rs @@ -1,41 +1,19 @@ use crate::terminal::RenderCell; -use gpui::Hsla; +use gpui::{Hsla, Rgba}; use std::collections::HashMap; -trait HslaExt { - fn into_rgba_like(self, r: u8, g: u8, b: u8) -> Self; +#[derive(Debug, Clone, Copy, Default, PartialEq)] +pub struct HighlightStyle { + pub foreground: Option, + pub background: Option, } -impl HslaExt for Hsla { - fn into_rgba_like(self, r: u8, g: u8, b: u8) -> Self { - let rf = r as f32 / 255.0; - let gf = g as f32 / 255.0; - let bf = b as f32 / 255.0; - let max = rf.max(gf).max(bf); - let min = rf.min(gf).min(bf); - let l = (max + min) / 2.0; - if max == min { - return Hsla { - h: 0.0, - s: 0.0, - l, - a: 1.0, - }; +impl HighlightStyle { + fn from_foreground(color: Hsla) -> Self { + Self { + foreground: Some(color), + background: None, } - let d = max - min; - let s = if l > 0.5 { - d / (2.0 - max - min) - } else { - d / (max + min) - }; - let h = if max == rf { - ((gf - bf) / d + if gf < bf { 6.0 } else { 0.0 }) / 6.0 - } else if max == gf { - ((bf - rf) / d + 2.0) / 6.0 - } else { - ((rf - gf) / d + 4.0) / 6.0 - }; - Hsla { h, s, l, a: 1.0 } } } @@ -97,13 +75,12 @@ struct HighlightColors { } fn hsla(r: u8, g: u8, b: u8) -> Hsla { - Hsla { - h: 0.0, - s: 0.0, - l: 0.0, + Hsla::from(Rgba { + r: r as f32 / 255.0, + g: g as f32 / 255.0, + b: b as f32 / 255.0, a: 1.0, - } - .into_rgba_like(r, g, b) + }) } fn highlight_colors() -> HighlightColors { @@ -168,7 +145,7 @@ fn highlight_colors() -> HighlightColors { struct KeywordMatch { start_col: i32, end_col: i32, - color: Hsla, + style: HighlightStyle, priority: usize, } @@ -184,7 +161,7 @@ impl KeywordMatch { #[derive(Default)] struct KeywordHighlights { - colors: HashMap<(i32, i32), Hsla>, + styles: HashMap<(i32, i32), HighlightStyle>, pending: Vec, next_priority: usize, } @@ -214,7 +191,7 @@ impl KeywordHighlights { for candidate in selected { for col in candidate.start_col..=candidate.end_col { - self.colors.insert((row_i32, col), candidate.color); + self.styles.insert((row_i32, col), candidate.style); } } } @@ -262,7 +239,7 @@ fn highlight_keywords( map.pending.push(KeywordMatch { start_col, end_col, - color, + style: HighlightStyle::from_foreground(color), priority, }); start = abs + kw.len(); @@ -276,7 +253,7 @@ fn highlight_keywords( /// Highlight HTTP status codes (200, 301, 404, 500, etc.) /// Only matches specific common HTTP codes, not all 3-digit numbers. fn highlight_http_codes( - map: &mut HashMap<(i32, i32), Hsla>, + map: &mut HashMap<(i32, i32), HighlightStyle>, text: &str, byte_to_col: &[i32], row_i32: i32, @@ -352,12 +329,13 @@ fn highlight_http_codes( let start_col = byte_to_col[i]; let end_col = byte_to_col[(i + 2).min(byte_to_col.len() - 1)]; for c in start_col..=end_col { - map.entry((row_i32, c)).or_insert(color); + map.entry((row_i32, c)) + .or_insert_with(|| HighlightStyle::from_foreground(color)); } } } -pub fn highlight_cells(cells: &[RenderCell], rows: usize) -> HashMap<(i32, i32), Hsla> { +pub fn highlight_cells(cells: &[RenderCell], rows: usize) -> HashMap<(i32, i32), HighlightStyle> { let colors = highlight_colors(); let mut row_chars: Vec> = vec![Vec::with_capacity(128); rows]; @@ -365,7 +343,9 @@ pub fn highlight_cells(cells: &[RenderCell], rows: usize) -> HashMap<(i32, i32), if rc.row < 0 || (rc.row as usize) >= rows { continue; } - row_chars[rc.row as usize].push((rc.col, rc.cell.c)); + if let Some(character) = rendered_text_character(&rc.cell) { + row_chars[rc.row as usize].push((rc.col, character)); + } } for row in row_chars.iter_mut() { row.sort_by_key(|&(col, _)| col); @@ -852,7 +832,7 @@ pub fn highlight_cells(cells: &[RenderCell], rows: usize) -> HashMap<(i32, i32), map.apply_row(row_i32); // ── 30. HTTP status codes ────────────────────────────── - highlight_http_codes(&mut map.colors, text, &byte_to_col, row_i32, &colors); + highlight_http_codes(&mut map.styles, text, &byte_to_col, row_i32, &colors); // ── 31. IP addresses ─────────────────────────────────── for m in find_ip_addresses(text) { @@ -860,7 +840,9 @@ pub fn highlight_cells(cells: &[RenderCell], rows: usize) -> HashMap<(i32, i32), let start_col = byte_to_col[m]; let end_col = byte_to_col[(m + ip_len - 1).min(byte_to_col.len() - 1)]; for c in start_col..=end_col { - map.colors.entry((row_i32, c)).or_insert(colors.network); + map.styles + .entry((row_i32, c)) + .or_insert_with(|| HighlightStyle::from_foreground(colors.network)); } } @@ -870,7 +852,9 @@ pub fn highlight_cells(cells: &[RenderCell], rows: usize) -> HashMap<(i32, i32), let start_col = byte_to_col[m]; let end_col = byte_to_col[(m + port_len - 1).min(byte_to_col.len() - 1)]; for c in start_col..=end_col { - map.colors.entry((row_i32, c)).or_insert(colors.port); + map.styles + .entry((row_i32, c)) + .or_insert_with(|| HighlightStyle::from_foreground(colors.port)); } } } @@ -884,13 +868,15 @@ pub fn highlight_cells(cells: &[RenderCell], rows: usize) -> HashMap<(i32, i32), let idx = m + i; if idx < line.byte_to_cell.len() { let (r, c) = line.byte_to_cell[idx]; - map.colors.entry((r as i32, c as i32)).or_insert(colors.url); + map.styles + .entry((r as i32, c as i32)) + .or_insert_with(|| HighlightStyle::from_foreground(colors.url)); } } } } - map.colors + map.styles } fn find_ip_len(text: &str) -> usize { @@ -1090,10 +1076,12 @@ pub fn build_logical_lines<'a>(cells: &'a [RenderCell], rows: usize) -> Vec(cells: &'a [RenderCell], rows: usize) -> Vec Option { + use alacritty_terminal::term::cell::Flags; + + if cell + .flags + .intersects(Flags::WIDE_CHAR_SPACER | Flags::LEADING_WIDE_CHAR_SPACER) + { + None + } else if cell.flags.contains(Flags::HIDDEN) { + Some(' ') + } else { + Some(cell.c) + } +} + pub fn find_url_at_cell( cells: &[RenderCell], rows: usize, @@ -1142,7 +1145,7 @@ pub fn find_url_at_cell( #[cfg(test)] mod tests { - use super::{KeywordHighlights, KeywordMatch, find_url_len, hsla}; + use super::{HighlightStyle, KeywordHighlights, KeywordMatch, find_url_len, hsla}; fn detected_url(text: &str) -> &str { &text[..find_url_len(text)] @@ -1181,21 +1184,26 @@ mod tests { KeywordMatch { start_col: 3, end_col: 11, - color: short_color, + style: HighlightStyle::from_foreground(short_color), priority: 0, }, KeywordMatch { start_col: 0, end_col: 11, - color: long_color, + style: HighlightStyle::from_foreground(long_color), priority: 1, }, ]; highlights.apply_row(0); - assert_eq!(highlights.colors.len(), 12); - assert!(highlights.colors.values().all(|color| *color == long_color)); + assert_eq!(highlights.styles.len(), 12); + assert!( + highlights + .styles + .values() + .all(|style| style.foreground == Some(long_color) && style.background.is_none()) + ); } #[test] @@ -1208,8 +1216,13 @@ mod tests { super::highlight_keywords(&mut highlights, text, &byte_to_col, 0, &["BOOT"], color); highlights.apply_row(0); - assert_eq!(highlights.colors.len(), 4); - assert!((0..9).all(|col| !highlights.colors.contains_key(&(0, col)))); - assert!((10..14).all(|col| highlights.colors.get(&(0, col)) == Some(&color))); + assert_eq!(highlights.styles.len(), 4); + assert!((0..9).all(|col| !highlights.styles.contains_key(&(0, col)))); + assert!((10..14).all(|col| { + highlights + .styles + .get(&(0, col)) + .is_some_and(|style| style.foreground == Some(color) && style.background.is_none()) + })); } } diff --git a/src/terminal/input.rs b/src/terminal/input.rs index 1fb2a5e..45671c2 100644 --- a/src/terminal/input.rs +++ b/src/terminal/input.rs @@ -345,7 +345,7 @@ impl Ashell { }; ( session.id.clone(), - tab.cursor_state().map(|cursor| (cursor.row, cursor.col)), + tab.buffer_cursor_position(), tab.is_alternate_screen_active(), ) }; @@ -1284,6 +1284,11 @@ fn terminal_command_text( start: (usize, usize), end: Option<(usize, usize)>, ) -> Option { + let start = buffer_position_in_viewport(snapshot, start)?; + let end = match end { + Some(position) => Some(buffer_position_in_viewport(snapshot, position)?), + None => None, + }; let logical_lines = crate::terminal::highlight::build_logical_lines(&snapshot.cells, snapshot.rows); for line in logical_lines { @@ -1315,42 +1320,38 @@ fn terminal_command_text( None } +fn buffer_position_in_viewport( + snapshot: &crate::terminal::RenderSnapshot, + position: (usize, usize), +) -> Option<(usize, usize)> { + let viewport_start = snapshot + .history_size + .saturating_sub(snapshot.display_offset); + let row = position.0.checked_sub(viewport_start)?; + (row < snapshot.rows && position.1 < snapshot.cols).then_some((row, position.1)) +} + fn command_history_text(rendered: Option<&str>, buffered: &str, input_uncertain: bool) -> String { + let rendered = rendered.unwrap_or_default().trim(); let buffered = buffered.trim(); if !input_uncertain && !buffered.is_empty() { return buffered.to_string(); } - merge_command_text(rendered, buffered) -} - -fn merge_command_text(rendered: Option<&str>, buffered: &str) -> String { - let rendered = rendered.unwrap_or_default().trim(); - let buffered = buffered.trim(); if rendered.is_empty() { return buffered.to_string(); } if buffered.is_empty() { return rendered.to_string(); } - if rendered.starts_with(buffered) || rendered.ends_with(buffered) { - return rendered.to_string(); - } - if buffered.starts_with(rendered) || buffered.ends_with(rendered) { + // Completion extends the current token; a new argument after exact raw input is stale content. + if rendered + .strip_prefix(buffered) + .and_then(|suffix| suffix.chars().next()) + .is_some_and(char::is_whitespace) + { return buffered.to_string(); } - - let overlap = buffered - .char_indices() - .map(|(index, _)| index) - .chain(std::iter::once(buffered.len())) - .filter(|index| *index > 0 && rendered.ends_with(&buffered[..*index])) - .max() - .unwrap_or(0); - if overlap > 0 { - format!("{rendered}{}", &buffered[overlap..]) - } else { - rendered.to_string() - } + rendered.to_string() } #[cfg(test)] @@ -1581,6 +1582,58 @@ mod tests { ); } + #[test] + fn rejects_whitespace_delimited_screen_suffix_when_input_is_uncertain() { + let command = "sh /site/jimureport/jimureport-restart.sh"; + let rendered = format!("{command} /sijiji-r"); + + assert_eq!( + command_history_text(Some(rendered.as_str()), command, true), + command + ); + } + + #[test] + fn does_not_append_tab_completion_keystroke_fragments() { + let command = "sh /site/jimureport/jimureport-restart.sh"; + let buffered = "sh /sijiji-r"; + + assert_eq!(command_history_text(Some(command), buffered, true), command); + } + + #[test] + fn falls_back_to_raw_input_when_uncertain_screen_text_is_unavailable() { + let command = "sh /site/jimureport/jimureport-restart.sh"; + + assert_eq!(command_history_text(None, command, true), command); + } + + #[test] + fn keeps_screen_completion_that_extends_the_current_argument() { + let buffered = "sh /site/jimu"; + let rendered = "sh /site/jimureport/jimureport-restart.sh"; + + assert_eq!( + command_history_text(Some(rendered), buffered, true), + rendered + ); + } + + #[test] + fn excludes_hidden_screen_residue_from_rendered_command() { + let command = "sh /site/jimureport/jimureport-restart.sh"; + let rendered = format!("$ {command} /sijiji-r"); + let mut snapshot = snapshot(&[rendered.as_str()], rendered.len()); + for cell in snapshot.cells.iter_mut().skip(2 + command.chars().count()) { + cell.cell.flags.insert(Flags::HIDDEN); + } + + assert_eq!( + terminal_command_text(&snapshot, (0, 2), None), + Some(command.to_string()) + ); + } + #[test] fn truncates_rendered_command_at_the_submission_cursor() { let command = "sh /site/vocano/vocano-restart.sh"; @@ -1592,4 +1645,26 @@ mod tests { Some(command.to_string()) ); } + + #[test] + fn keeps_command_coordinates_stable_when_wrapping_scrolls_the_viewport() { + let first_row = "$ sh /site/jimureport/jimureport-"; + let second_row = "restart.sh /sijiji-r"; + let mut snapshot = snapshot(&[first_row, second_row], first_row.len()); + snapshot.history_size = 3; + snapshot.display_offset = 2; + snapshot + .cells + .iter_mut() + .find(|cell| cell.row == 0 && cell.col == first_row.len() as i32 - 1) + .unwrap() + .cell + .flags + .insert(Flags::WRAPLINE); + + assert_eq!( + terminal_command_text(&snapshot, (1, 2), Some((2, "restart.sh".len()))), + Some("sh /site/jimureport/jimureport-restart.sh".to_string()) + ); + } } diff --git a/src/terminal/mod.rs b/src/terminal/mod.rs index 2aada89..b79c698 100644 --- a/src/terminal/mod.rs +++ b/src/terminal/mod.rs @@ -887,7 +887,7 @@ pub struct TerminalTab { type HighlightCache = std::cell::RefCell< Option<( Vec, - std::collections::HashMap<(i32, i32), gpui::Hsla>, + std::collections::HashMap<(i32, i32), self::highlight::HighlightStyle>, )>, >; @@ -940,7 +940,7 @@ pub struct RenderSnapshot { pub history_size: usize, pub rows: usize, pub cols: usize, - pub highlights: std::collections::HashMap<(i32, i32), gpui::Hsla>, + pub highlights: std::collections::HashMap<(i32, i32), self::highlight::HighlightStyle>, } #[derive(Clone, Copy)] @@ -2136,7 +2136,7 @@ impl TerminalTab { let _ = self.cursor_state_for_click_at(now); } - fn buffer_cursor_position(&self) -> Option<(usize, usize)> { + pub(crate) fn buffer_cursor_position(&self) -> Option<(usize, usize)> { let grid = self.term.grid(); let row = usize::try_from(grid.cursor.point.line.0).ok()?; Some((