From ffc6218349cf49399f515ad5d2f71a6c4e439326 Mon Sep 17 00:00:00 2001 From: Cole Miller Date: Mon, 16 Jun 2025 17:39:53 -0400 Subject: [PATCH] debugger: Process ANSI color escape codes in console (#32817) - [x] foreground highlights - [x] background highlights - [x] advertise support in DAP capabilities Closes #31372 Release Notes: - Debugger Beta: added basic support for highlighting in the console based on ANSI escape codes. --- Cargo.lock | 1 + crates/agent/src/inline_assistant.rs | 17 +- .../src/chat_panel/message_editor.rs | 21 +- crates/debugger_ui/Cargo.toml | 1 + .../src/session/running/console.rs | 264 +++++++++++++++++- crates/debugger_ui/src/tests/console.rs | 6 +- crates/editor/src/display_map.rs | 48 ++-- .../src/display_map/custom_highlights.rs | 31 +- crates/editor/src/display_map/inlay_map.rs | 20 +- crates/editor/src/editor.rs | 257 ++++++++++------- crates/editor/src/editor_tests.rs | 14 +- crates/editor/src/element.rs | 32 ++- .../editor/src/highlight_matching_bracket.rs | 2 +- crates/editor/src/hover_links.rs | 5 +- crates/editor/src/hover_popover.rs | 2 +- crates/editor/src/items.rs | 29 +- crates/editor/src/test/editor_test_context.rs | 14 +- crates/language_tools/src/syntax_tree_view.rs | 2 +- crates/project/src/debugger/dap_command.rs | 2 +- crates/search/src/project_search.rs | 2 +- crates/vim/src/normal/yank.rs | 2 +- crates/vim/src/replace.rs | 6 +- crates/vim/src/test.rs | 14 +- 23 files changed, 558 insertions(+), 234 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7006a99065..8a4cfca517 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4268,6 +4268,7 @@ dependencies = [ name = "debugger_ui" version = "0.1.0" dependencies = [ + "alacritty_terminal", "anyhow", "client", "collections", diff --git a/crates/agent/src/inline_assistant.rs b/crates/agent/src/inline_assistant.rs index 6b7ebb061a..d36b8f9bb9 100644 --- a/crates/agent/src/inline_assistant.rs +++ b/crates/agent/src/inline_assistant.rs @@ -1350,11 +1350,18 @@ impl InlineAssistant { editor.clear_highlights::(cx); } else { editor.highlight_text::( - foreground_ranges, - HighlightStyle { - fade_out: Some(0.6), - ..Default::default() - }, + foreground_ranges + .into_iter() + .map(|range| { + ( + range, + HighlightStyle { + fade_out: Some(0.6), + ..Default::default() + }, + ) + }) + .collect(), cx, ); } diff --git a/crates/collab_ui/src/chat_panel/message_editor.rs b/crates/collab_ui/src/chat_panel/message_editor.rs index a0df4cd536..e61724db14 100644 --- a/crates/collab_ui/src/chat_panel/message_editor.rs +++ b/crates/collab_ui/src/chat_panel/message_editor.rs @@ -193,10 +193,10 @@ impl MessageEditor { let highlights = editor.text_highlights::(cx); let text = editor.text(cx); let snapshot = editor.buffer().read(cx).snapshot(cx); - let mentions = if let Some((_, ranges)) = highlights { + let mentions = if let Some(ranges) = highlights { ranges .iter() - .map(|range| range.to_offset(&snapshot)) + .map(|(range, _)| range.to_offset(&snapshot)) .zip(self.mentions.iter().copied()) .collect() } else { @@ -483,20 +483,19 @@ impl MessageEditor { let end = multi_buffer.anchor_after(range.end); mentioned_user_ids.push(user.id); - anchor_ranges.push(start..end); + anchor_ranges.push(( + start..end, + HighlightStyle { + font_weight: Some(FontWeight::BOLD), + ..Default::default() + }, + )); } } } editor.clear_highlights::(cx); - editor.highlight_text::( - anchor_ranges, - HighlightStyle { - font_weight: Some(FontWeight::BOLD), - ..Default::default() - }, - cx, - ) + editor.highlight_text::(anchor_ranges, cx) }); this.mentions = mentioned_user_ids; diff --git a/crates/debugger_ui/Cargo.toml b/crates/debugger_ui/Cargo.toml index 6fb582a58a..fc38538965 100644 --- a/crates/debugger_ui/Cargo.toml +++ b/crates/debugger_ui/Cargo.toml @@ -26,6 +26,7 @@ test-support = [ ] [dependencies] +alacritty_terminal.workspace = true anyhow.workspace = true client.workspace = true collections.workspace = true diff --git a/crates/debugger_ui/src/session/running/console.rs b/crates/debugger_ui/src/session/running/console.rs index d872f0f636..791baa58ce 100644 --- a/crates/debugger_ui/src/session/running/console.rs +++ b/crates/debugger_ui/src/session/running/console.rs @@ -2,13 +2,17 @@ use super::{ stack_frame_list::{StackFrameList, StackFrameListEvent}, variable_list::VariableList, }; +use alacritty_terminal::vte::ansi; use anyhow::Result; use collections::HashMap; use dap::OutputEvent; -use editor::{Bias, CompletionProvider, Editor, EditorElement, EditorStyle, ExcerptId}; +use editor::{ + BackgroundHighlight, Bias, CompletionProvider, Editor, EditorElement, EditorStyle, ExcerptId, +}; use fuzzy::StringMatchCandidate; use gpui::{ - Context, Entity, FocusHandle, Focusable, Render, Subscription, Task, TextStyle, WeakEntity, + Context, Entity, FocusHandle, Focusable, HighlightStyle, Hsla, Render, Subscription, Task, + TextStyle, WeakEntity, }; use language::{Buffer, CodeLabel, ToOffset}; use menu::Confirm; @@ -17,8 +21,8 @@ use project::{ debugger::session::{CompletionsQuery, OutputToken, Session, SessionEvent}, }; use settings::Settings; -use std::{cell::RefCell, rc::Rc, usize}; -use theme::ThemeSettings; +use std::{cell::RefCell, ops::Range, rc::Rc, usize}; +use theme::{Theme, ThemeSettings}; use ui::{Divider, prelude::*}; pub struct Console { @@ -30,6 +34,8 @@ pub struct Console { stack_frame_list: Entity, last_token: OutputToken, update_output_task: Task<()>, + ansi_handler: ConsoleHandler, + ansi_processor: ansi::Processor, focus_handle: FocusHandle, } @@ -100,6 +106,8 @@ impl Console { stack_frame_list, update_output_task: Task::ready(()), last_token: OutputToken(0), + ansi_handler: Default::default(), + ansi_processor: Default::default(), focus_handle, } } @@ -135,17 +143,185 @@ impl Console { window: &mut Window, cx: &mut App, ) { - self.console.update(cx, |console, cx| { - let mut to_insert = String::default(); - for event in events { - use std::fmt::Write; + let mut to_insert = String::default(); + for event in events { + use std::fmt::Write; - _ = write!(to_insert, "{}\n", event.output.trim_end()); - } + _ = write!(to_insert, "{}\n", event.output.trim_end()); + } + + let len = self.ansi_handler.pos; + self.ansi_processor + .advance(&mut self.ansi_handler, to_insert.as_bytes()); + let output = std::mem::take(&mut self.ansi_handler.output); + let mut spans = std::mem::take(&mut self.ansi_handler.spans); + let mut background_spans = std::mem::take(&mut self.ansi_handler.background_spans); + if self.ansi_handler.current_range_start < len + output.len() { + spans.push(( + self.ansi_handler.current_range_start..len + output.len(), + self.ansi_handler.current_color, + )); + self.ansi_handler.current_range_start = len + output.len(); + } + if self.ansi_handler.current_background_range_start < len + output.len() { + background_spans.push(( + self.ansi_handler.current_background_range_start..len + output.len(), + self.ansi_handler.current_background_color, + )); + self.ansi_handler.current_background_range_start = len + output.len(); + } + + self.console.update(cx, |console, cx| { + struct ConsoleAnsiHighlight; console.set_read_only(false); console.move_to_end(&editor::actions::MoveToEnd, window, cx); - console.insert(&to_insert, window, cx); + console.insert(&output, window, cx); + let buffer = console.buffer().read(cx).snapshot(cx); + + let mut highlights = console + .remove_text_highlights::(cx) + .unwrap_or_default(); + for (range, color) in spans { + let Some(color) = color else { continue }; + let start = range.start + len; + let range = start..range.end + len; + let range = buffer.anchor_after(range.start)..buffer.anchor_before(range.end); + let style = HighlightStyle { + color: Some(terminal_view::terminal_element::convert_color( + &color, + cx.theme(), + )), + ..Default::default() + }; + highlights.push((range, style)); + } + console.highlight_text::(highlights, cx); + + let mut background_highlights = console + .clear_background_highlights::(cx) + .unwrap_or_default(); + for (range, color) in background_spans { + let Some(color) = color else { continue }; + let start = range.start + len; + let range = start..range.end + len; + let range = buffer.anchor_after(range.start)..buffer.anchor_before(range.end); + + let color_fetcher: fn(&Theme) -> Hsla = match color { + // Named and theme defined colors + ansi::Color::Named(n) => match n { + ansi::NamedColor::Black => |theme| theme.colors().terminal_ansi_black, + ansi::NamedColor::Red => |theme| theme.colors().terminal_ansi_red, + ansi::NamedColor::Green => |theme| theme.colors().terminal_ansi_green, + ansi::NamedColor::Yellow => |theme| theme.colors().terminal_ansi_yellow, + ansi::NamedColor::Blue => |theme| theme.colors().terminal_ansi_blue, + ansi::NamedColor::Magenta => |theme| theme.colors().terminal_ansi_magenta, + ansi::NamedColor::Cyan => |theme| theme.colors().terminal_ansi_cyan, + ansi::NamedColor::White => |theme| theme.colors().terminal_ansi_white, + ansi::NamedColor::BrightBlack => { + |theme| theme.colors().terminal_ansi_bright_black + } + ansi::NamedColor::BrightRed => { + |theme| theme.colors().terminal_ansi_bright_red + } + ansi::NamedColor::BrightGreen => { + |theme| theme.colors().terminal_ansi_bright_green + } + ansi::NamedColor::BrightYellow => { + |theme| theme.colors().terminal_ansi_bright_yellow + } + ansi::NamedColor::BrightBlue => { + |theme| theme.colors().terminal_ansi_bright_blue + } + ansi::NamedColor::BrightMagenta => { + |theme| theme.colors().terminal_ansi_bright_magenta + } + ansi::NamedColor::BrightCyan => { + |theme| theme.colors().terminal_ansi_bright_cyan + } + ansi::NamedColor::BrightWhite => { + |theme| theme.colors().terminal_ansi_bright_white + } + ansi::NamedColor::Foreground => |theme| theme.colors().terminal_foreground, + ansi::NamedColor::Background => |theme| theme.colors().terminal_background, + ansi::NamedColor::Cursor => |theme| theme.players().local().cursor, + ansi::NamedColor::DimBlack => { + |theme| theme.colors().terminal_ansi_dim_black + } + ansi::NamedColor::DimRed => |theme| theme.colors().terminal_ansi_dim_red, + ansi::NamedColor::DimGreen => { + |theme| theme.colors().terminal_ansi_dim_green + } + ansi::NamedColor::DimYellow => { + |theme| theme.colors().terminal_ansi_dim_yellow + } + ansi::NamedColor::DimBlue => |theme| theme.colors().terminal_ansi_dim_blue, + ansi::NamedColor::DimMagenta => { + |theme| theme.colors().terminal_ansi_dim_magenta + } + ansi::NamedColor::DimCyan => |theme| theme.colors().terminal_ansi_dim_cyan, + ansi::NamedColor::DimWhite => { + |theme| theme.colors().terminal_ansi_dim_white + } + ansi::NamedColor::BrightForeground => { + |theme| theme.colors().terminal_bright_foreground + } + ansi::NamedColor::DimForeground => { + |theme| theme.colors().terminal_dim_foreground + } + }, + // 'True' colors + ansi::Color::Spec(_) => |theme| theme.colors().editor_background, + // 8 bit, indexed colors + ansi::Color::Indexed(i) => { + match i { + // 0-15 are the same as the named colors above + 0 => |theme| theme.colors().terminal_ansi_black, + 1 => |theme| theme.colors().terminal_ansi_red, + 2 => |theme| theme.colors().terminal_ansi_green, + 3 => |theme| theme.colors().terminal_ansi_yellow, + 4 => |theme| theme.colors().terminal_ansi_blue, + 5 => |theme| theme.colors().terminal_ansi_magenta, + 6 => |theme| theme.colors().terminal_ansi_cyan, + 7 => |theme| theme.colors().terminal_ansi_white, + 8 => |theme| theme.colors().terminal_ansi_bright_black, + 9 => |theme| theme.colors().terminal_ansi_bright_red, + 10 => |theme| theme.colors().terminal_ansi_bright_green, + 11 => |theme| theme.colors().terminal_ansi_bright_yellow, + 12 => |theme| theme.colors().terminal_ansi_bright_blue, + 13 => |theme| theme.colors().terminal_ansi_bright_magenta, + 14 => |theme| theme.colors().terminal_ansi_bright_cyan, + 15 => |theme| theme.colors().terminal_ansi_bright_white, + // 16-231 are a 6x6x6 RGB color cube, mapped to 0-255 using steps defined by XTerm. + // See: https://github.com/xterm-x11/xterm-snapshots/blob/master/256colres.pl + // 16..=231 => { + // let (r, g, b) = rgb_for_index(index as u8); + // rgba_color( + // if r == 0 { 0 } else { r * 40 + 55 }, + // if g == 0 { 0 } else { g * 40 + 55 }, + // if b == 0 { 0 } else { b * 40 + 55 }, + // ) + // } + // 232-255 are a 24-step grayscale ramp from (8, 8, 8) to (238, 238, 238). + // 232..=255 => { + // let i = index as u8 - 232; // Align index to 0..24 + // let value = i * 10 + 8; + // rgba_color(value, value, value) + // } + // For compatibility with the alacritty::Colors interface + // See: https://github.com/alacritty/alacritty/blob/master/alacritty_terminal/src/term/color.rs + _ => |_| gpui::black(), + } + } + }; + + background_highlights.push(BackgroundHighlight { + range, + color_fetcher, + }); + } + console.highlight_background_ranges::(background_highlights, cx); + console.set_read_only(true); cx.notify(); @@ -459,3 +635,69 @@ impl ConsoleQueryBarCompletionProvider { }) } } + +#[derive(Default)] +struct ConsoleHandler { + output: String, + spans: Vec<(Range, Option)>, + background_spans: Vec<(Range, Option)>, + current_range_start: usize, + current_background_range_start: usize, + current_color: Option, + current_background_color: Option, + pos: usize, +} + +impl ConsoleHandler { + fn break_span(&mut self, color: Option) { + self.spans.push(( + self.current_range_start..self.output.len(), + self.current_color, + )); + self.current_color = color; + self.current_range_start = self.pos; + } + + fn break_background_span(&mut self, color: Option) { + self.background_spans.push(( + self.current_background_range_start..self.output.len(), + self.current_background_color, + )); + self.current_background_color = color; + self.current_background_range_start = self.pos; + } +} + +impl ansi::Handler for ConsoleHandler { + fn input(&mut self, c: char) { + self.output.push(c); + self.pos += 1; + } + + fn linefeed(&mut self) { + self.output.push('\n'); + self.pos += 1; + } + + fn put_tab(&mut self, count: u16) { + self.output + .extend(std::iter::repeat('\t').take(count as usize)); + self.pos += count as usize; + } + + fn terminal_attribute(&mut self, attr: ansi::Attr) { + match attr { + ansi::Attr::Foreground(color) => { + self.break_span(Some(color)); + } + ansi::Attr::Background(color) => { + self.break_background_span(Some(color)); + } + ansi::Attr::Reset => { + self.break_span(None); + self.break_background_span(None); + } + _ => {} + } + } +} diff --git a/crates/debugger_ui/src/tests/console.rs b/crates/debugger_ui/src/tests/console.rs index 2c31c87f54..63639fb29a 100644 --- a/crates/debugger_ui/src/tests/console.rs +++ b/crates/debugger_ui/src/tests/console.rs @@ -110,7 +110,7 @@ async fn test_handle_output_event(executor: BackgroundExecutor, cx: &mut TestApp client .fake_event(dap::messages::Events::Output(dap::OutputEvent { category: Some(dap::OutputEventCategory::Stdout), - output: "Second output line after thread stopped!".to_string(), + output: "\tSecond output line after thread stopped!".to_string(), data: None, variables_reference: None, source: None, @@ -124,7 +124,7 @@ async fn test_handle_output_event(executor: BackgroundExecutor, cx: &mut TestApp client .fake_event(dap::messages::Events::Output(dap::OutputEvent { category: Some(dap::OutputEventCategory::Console), - output: "Second console output line after thread stopped!".to_string(), + output: "\tSecond console output line after thread stopped!".to_string(), data: None, variables_reference: None, source: None, @@ -150,7 +150,7 @@ async fn test_handle_output_event(executor: BackgroundExecutor, cx: &mut TestApp .unwrap(); assert_eq!( - "First console output line before thread stopped!\nFirst output line before thread stopped!\nSecond output line after thread stopped!\nSecond console output line after thread stopped!\n", + "First console output line before thread stopped!\nFirst output line before thread stopped!\n\tSecond output line after thread stopped!\n\tSecond console output line after thread stopped!\n", active_session_panel.read(cx).running_state().read(cx).console().read(cx).editor().read(cx).text(cx).as_str() ); }) diff --git a/crates/editor/src/display_map.rs b/crates/editor/src/display_map.rs index a9af8f2ff9..d755086024 100644 --- a/crates/editor/src/display_map.rs +++ b/crates/editor/src/display_map.rs @@ -80,7 +80,7 @@ pub trait ToDisplayPoint { fn to_display_point(&self, map: &DisplaySnapshot) -> DisplayPoint; } -type TextHighlights = TreeMap>)>>; +type TextHighlights = TreeMap, HighlightStyle)>>; type InlayHighlights = TreeMap>; /// Decides how text in a [`MultiBuffer`] should be displayed in a buffer, handling inlay hints, @@ -474,11 +474,9 @@ impl DisplayMap { pub fn highlight_text( &mut self, type_id: TypeId, - ranges: Vec>, - style: HighlightStyle, + ranges: Vec<(Range, HighlightStyle)>, ) { - self.text_highlights - .insert(type_id, Arc::new((style, ranges))); + self.text_highlights.insert(type_id, ranges); } pub(crate) fn highlight_inlays( @@ -500,16 +498,25 @@ impl DisplayMap { } } - pub fn text_highlights(&self, type_id: TypeId) -> Option<(HighlightStyle, &[Range])> { - let highlights = self.text_highlights.get(&type_id)?; - Some((highlights.0, &highlights.1)) + pub fn text_highlights(&self, type_id: TypeId) -> Option<&[(Range, HighlightStyle)]> { + self.text_highlights + .get(&type_id) + .map(|highlights| highlights.as_slice()) } + pub fn clear_highlights(&mut self, type_id: TypeId) -> bool { let mut cleared = self.text_highlights.remove(&type_id).is_some(); cleared |= self.inlay_highlights.remove(&type_id).is_some(); cleared } + pub fn remove_text_highlights( + &mut self, + type_id: TypeId, + ) -> Option, HighlightStyle)>> { + self.text_highlights.remove(&type_id) + } + pub fn set_font(&self, font: Font, font_size: Pixels, cx: &mut Context) -> bool { self.wrap_map .update(cx, |map, cx| map.set_font_with_size(font, font_size, cx)) @@ -1331,7 +1338,7 @@ impl DisplaySnapshot { #[cfg(any(test, feature = "test-support"))] pub fn text_highlight_ranges( &self, - ) -> Option>)>> { + ) -> Option, HighlightStyle)>> { let type_id = TypeId::of::(); self.text_highlights.get(&type_id).cloned() } @@ -2296,12 +2303,17 @@ pub mod tests { map.highlight_text( TypeId::of::(), vec![ - buffer_snapshot.anchor_before(Point::new(3, 9)) - ..buffer_snapshot.anchor_after(Point::new(3, 14)), - buffer_snapshot.anchor_before(Point::new(3, 17)) - ..buffer_snapshot.anchor_after(Point::new(3, 18)), + ( + buffer_snapshot.anchor_before(Point::new(3, 9)) + ..buffer_snapshot.anchor_after(Point::new(3, 14)), + red.into(), + ), + ( + buffer_snapshot.anchor_before(Point::new(3, 17)) + ..buffer_snapshot.anchor_after(Point::new(3, 18)), + red.into(), + ), ], - red.into(), ); map.insert_blocks( [BlockProperties { @@ -2620,11 +2632,13 @@ pub mod tests { highlighted_ranges .into_iter() .map(|range| { - buffer_snapshot.anchor_before(range.start) - ..buffer_snapshot.anchor_before(range.end) + ( + buffer_snapshot.anchor_before(range.start) + ..buffer_snapshot.anchor_before(range.end), + style, + ) }) .collect(), - style, ); }); diff --git a/crates/editor/src/display_map/custom_highlights.rs b/crates/editor/src/display_map/custom_highlights.rs index 11356586eb..6babad9ac9 100644 --- a/crates/editor/src/display_map/custom_highlights.rs +++ b/crates/editor/src/display_map/custom_highlights.rs @@ -1,16 +1,16 @@ use collections::BTreeMap; use gpui::HighlightStyle; use language::Chunk; -use multi_buffer::{Anchor, MultiBufferChunks, MultiBufferSnapshot, ToOffset as _}; +use multi_buffer::{MultiBufferChunks, MultiBufferSnapshot, ToOffset as _}; use std::{ any::TypeId, cmp, iter::{self, Peekable}, ops::Range, - sync::Arc, vec, }; -use sum_tree::TreeMap; + +use crate::display_map::TextHighlights; pub struct CustomHighlightsChunks<'a> { buffer_chunks: MultiBufferChunks<'a>, @@ -19,15 +19,15 @@ pub struct CustomHighlightsChunks<'a> { multibuffer_snapshot: &'a MultiBufferSnapshot, highlight_endpoints: Peekable>, - active_highlights: BTreeMap, - text_highlights: Option<&'a TreeMap>)>>>, + active_highlights: BTreeMap<(TypeId, usize), HighlightStyle>, + text_highlights: Option<&'a TextHighlights>, } #[derive(Debug, Copy, Clone, Eq, PartialEq)] struct HighlightEndpoint { offset: usize, is_start: bool, - tag: TypeId, + tag: (TypeId, usize), style: HighlightStyle, } @@ -35,7 +35,7 @@ impl<'a> CustomHighlightsChunks<'a> { pub fn new( range: Range, language_aware: bool, - text_highlights: Option<&'a TreeMap>)>>>, + text_highlights: Option<&'a TextHighlights>, multibuffer_snapshot: &'a MultiBufferSnapshot, ) -> Self { Self { @@ -66,7 +66,7 @@ impl<'a> CustomHighlightsChunks<'a> { fn create_highlight_endpoints( range: &Range, - text_highlights: Option<&TreeMap>)>>>, + text_highlights: Option<&TextHighlights>, buffer: &MultiBufferSnapshot, ) -> iter::Peekable> { let mut highlight_endpoints = Vec::new(); @@ -74,10 +74,7 @@ fn create_highlight_endpoints( let start = buffer.anchor_after(range.start); let end = buffer.anchor_after(range.end); for (&tag, text_highlights) in text_highlights.iter() { - let style = text_highlights.0; - let ranges = &text_highlights.1; - - let start_ix = match ranges.binary_search_by(|probe| { + let start_ix = match text_highlights.binary_search_by(|(probe, _)| { let cmp = probe.end.cmp(&start, &buffer); if cmp.is_gt() { cmp::Ordering::Greater @@ -88,7 +85,7 @@ fn create_highlight_endpoints( Ok(i) | Err(i) => i, }; - for range in &ranges[start_ix..] { + for (ix, (range, style)) in text_highlights[start_ix..].iter().enumerate() { if range.start.cmp(&end, &buffer).is_ge() { break; } @@ -96,14 +93,14 @@ fn create_highlight_endpoints( highlight_endpoints.push(HighlightEndpoint { offset: range.start.to_offset(&buffer), is_start: true, - tag, - style, + tag: (tag, ix), + style: *style, }); highlight_endpoints.push(HighlightEndpoint { offset: range.end.to_offset(&buffer), is_start: false, - tag, - style, + tag: (tag, ix), + style: *style, }); } } diff --git a/crates/editor/src/display_map/inlay_map.rs b/crates/editor/src/display_map/inlay_map.rs index 3ec0084775..14fa92172f 100644 --- a/crates/editor/src/display_map/inlay_map.rs +++ b/crates/editor/src/display_map/inlay_map.rs @@ -1085,7 +1085,7 @@ mod tests { use project::{InlayHint, InlayHintLabel, ResolveState}; use rand::prelude::*; use settings::SettingsStore; - use std::{any::TypeId, cmp::Reverse, env, sync::Arc}; + use std::{any::TypeId, cmp::Reverse, env}; use sum_tree::TreeMap; use text::Patch; use util::post_inc; @@ -1593,16 +1593,16 @@ mod tests { log::info!("highlighting text ranges {text_highlight_ranges:?}"); text_highlights.insert( TypeId::of::<()>(), - Arc::new(( - HighlightStyle::default(), - text_highlight_ranges - .into_iter() - .map(|range| { + text_highlight_ranges + .into_iter() + .map(|range| { + ( buffer_snapshot.anchor_before(range.start) - ..buffer_snapshot.anchor_after(range.end) - }) - .collect(), - )), + ..buffer_snapshot.anchor_after(range.end), + HighlightStyle::default(), + ) + }) + .collect(), ); let mut inlay_highlights = InlayHighlights::default(); diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 34aef9046d..e77e33a366 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -196,7 +196,7 @@ pub use sum_tree::Bias; use sum_tree::TreeMap; use text::{BufferId, FromAnchor, OffsetUtf16, Rope}; use theme::{ - ActiveTheme, PlayerColor, StatusColors, SyntaxTheme, ThemeColors, ThemeSettings, + ActiveTheme, PlayerColor, StatusColors, SyntaxTheme, Theme, ThemeSettings, observe_buffer_font_size_adjustment, }; use ui::{ @@ -704,7 +704,12 @@ impl EditorActionId { // type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor; // type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option; -type BackgroundHighlight = (fn(&ThemeColors) -> Hsla, Arc<[Range]>); +#[derive(Clone)] +pub struct BackgroundHighlight { + pub range: Range, + pub color_fetcher: fn(&Theme) -> Hsla, +} + type GutterHighlight = (fn(&App) -> Hsla, Vec>); #[derive(Default)] @@ -1013,7 +1018,7 @@ pub struct Editor { placeholder_text: Option>, highlight_order: usize, highlighted_rows: HashMap>, - background_highlights: TreeMap, + background_highlights: TreeMap>, gutter_highlights: TreeMap, scrollbar_marker_state: ScrollbarMarkerState, active_indent_guides_state: ActiveIndentGuidesState, @@ -6141,7 +6146,7 @@ impl Editor { editor.update(cx, |editor, cx| { editor.highlight_background::( &ranges_to_highlight, - |theme| theme.editor_highlighted_line_background, + |theme| theme.colors().editor_highlighted_line_background, cx, ); }); @@ -6496,12 +6501,12 @@ impl Editor { this.highlight_background::( &read_ranges, - |theme| theme.editor_document_highlight_read_background, + |theme| theme.colors().editor_document_highlight_read_background, cx, ); this.highlight_background::( &write_ranges, - |theme| theme.editor_document_highlight_write_background, + |theme| theme.colors().editor_document_highlight_write_background, cx, ); cx.notify(); @@ -6603,7 +6608,7 @@ impl Editor { if !match_ranges.is_empty() { editor.highlight_background::( &match_ranges, - |theme| theme.editor_document_highlight_bracket_background, + |theme| theme.colors().editor_document_highlight_bracket_background, cx, ) } @@ -7479,12 +7484,15 @@ impl Editor { self.splice_inlays(&[], inlays, cx); } else { let background_color = cx.theme().status().deleted_background; + let style = HighlightStyle { + background_color: Some(background_color), + ..Default::default() + }; self.highlight_text::( - edits.iter().map(|(range, _)| range.clone()).collect(), - HighlightStyle { - background_color: Some(background_color), - ..Default::default() - }, + edits + .iter() + .map(|(range, _)| (range.clone(), style)) + .collect(), cx, ); } @@ -15351,7 +15359,7 @@ impl Editor { } editor.highlight_background::( &ranges, - |theme| theme.editor_highlighted_line_background, + |theme| theme.colors().editor_highlighted_line_background, cx, ); } @@ -15506,25 +15514,28 @@ impl Editor { }) .detach(); - let write_highlights = - this.clear_background_highlights::(cx); - let read_highlights = - this.clear_background_highlights::(cx); + let write_highlights = this + .clear_background_highlights::(cx) + .unwrap_or_default(); + let read_highlights = this + .clear_background_highlights::(cx) + .unwrap_or_default(); let ranges = write_highlights .iter() - .flat_map(|(_, ranges)| ranges.iter()) - .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter())) + .chain(read_highlights.iter()) .cloned() + .map(|highlight| { + ( + highlight.range, + HighlightStyle { + fade_out: Some(0.6), + ..Default::default() + }, + ) + }) .collect(); - this.highlight_text::( - ranges, - HighlightStyle { - fade_out: Some(0.6), - ..Default::default() - }, - cx, - ); + this.highlight_text::(ranges, cx); let rename_focus_handle = rename_editor.focus_handle(cx); window.focus(&rename_focus_handle); let block_id = this.insert_blocks( @@ -18500,7 +18511,7 @@ impl Editor { pub fn set_search_within_ranges(&mut self, ranges: &[Range], cx: &mut Context) { self.highlight_background::( ranges, - |colors| colors.editor_document_highlight_read_background, + |theme| theme.colors().editor_document_highlight_read_background, cx, ) } @@ -18516,11 +18527,29 @@ impl Editor { pub fn highlight_background( &mut self, ranges: &[Range], - color_fetcher: fn(&ThemeColors) -> Hsla, + color_fetcher: fn(&Theme) -> Hsla, cx: &mut Context, ) { + let highlights = ranges + .iter() + .map(|range| BackgroundHighlight { + range: range.clone(), + color_fetcher, + }) + .collect(); self.background_highlights - .insert(TypeId::of::(), (color_fetcher, Arc::from(ranges))); + .insert(TypeId::of::(), highlights); + self.scrollbar_marker_state.dirty = true; + cx.notify(); + } + + pub fn highlight_background_ranges( + &mut self, + background_highlights: Vec, + cx: &mut Context<'_, Editor>, + ) { + self.background_highlights + .insert(TypeId::of::(), background_highlights); self.scrollbar_marker_state.dirty = true; cx.notify(); } @@ -18528,9 +18557,9 @@ impl Editor { pub fn clear_background_highlights( &mut self, cx: &mut Context, - ) -> Option { + ) -> Option> { let text_highlights = self.background_highlights.remove(&TypeId::of::())?; - if !text_highlights.1.is_empty() { + if !text_highlights.is_empty() { self.scrollbar_marker_state.dirty = true; cx.notify(); } @@ -18625,7 +18654,7 @@ impl Editor { let buffer = &snapshot.buffer_snapshot; let start = buffer.anchor_before(0); let end = buffer.anchor_after(buffer.len()); - let theme = cx.theme().colors(); + let theme = cx.theme(); self.background_highlights_in_range(start..end, &snapshot, theme) } @@ -18637,10 +18666,13 @@ impl Editor { .background_highlights .get(&TypeId::of::()); - if let Some((_color, ranges)) = highlights { - ranges + if let Some(highlights) = highlights { + highlights .iter() - .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot)) + .map(|highlight| { + highlight.range.start.to_point(&snapshot) + ..highlight.range.end.to_point(&snapshot) + }) .collect_vec() } else { vec![] @@ -18654,20 +18686,18 @@ impl Editor { ) -> impl 'a + Iterator> { let read_highlights = self .background_highlights - .get(&TypeId::of::()) - .map(|h| &h.1); + .get(&TypeId::of::()); let write_highlights = self .background_highlights - .get(&TypeId::of::()) - .map(|h| &h.1); + .get(&TypeId::of::()); let left_position = position.bias_left(buffer); let right_position = position.bias_right(buffer); read_highlights .into_iter() .chain(write_highlights) - .flat_map(move |ranges| { - let start_ix = match ranges.binary_search_by(|probe| { - let cmp = probe.end.cmp(&left_position, buffer); + .flat_map(move |highlights| { + let start_ix = match highlights.binary_search_by(|probe| { + let cmp = probe.range.end.cmp(&left_position, buffer); if cmp.is_ge() { Ordering::Greater } else { @@ -18677,29 +18707,32 @@ impl Editor { Ok(i) | Err(i) => i, }; - ranges[start_ix..] + highlights[start_ix..] .iter() - .take_while(move |range| range.start.cmp(&right_position, buffer).is_le()) + .take_while(move |highlight| { + highlight.range.start.cmp(&right_position, buffer).is_le() + }) + .map(|highlight| &highlight.range) }) } pub fn has_background_highlights(&self) -> bool { self.background_highlights .get(&TypeId::of::()) - .map_or(false, |(_, highlights)| !highlights.is_empty()) + .map_or(false, |highlights| !highlights.is_empty()) } pub fn background_highlights_in_range( &self, search_range: Range, display_snapshot: &DisplaySnapshot, - theme: &ThemeColors, + theme: &Theme, ) -> Vec<(Range, Hsla)> { let mut results = Vec::new(); - for (color_fetcher, ranges) in self.background_highlights.values() { - let color = color_fetcher(theme); - let start_ix = match ranges.binary_search_by(|probe| { + for highlights in self.background_highlights.values() { + let start_ix = match highlights.binary_search_by(|probe| { let cmp = probe + .range .end .cmp(&search_range.start, &display_snapshot.buffer_snapshot); if cmp.is_gt() { @@ -18710,8 +18743,9 @@ impl Editor { }) { Ok(i) | Err(i) => i, }; - for range in &ranges[start_ix..] { - if range + for highlight in &highlights[start_ix..] { + if highlight + .range .start .cmp(&search_range.end, &display_snapshot.buffer_snapshot) .is_ge() @@ -18719,8 +18753,9 @@ impl Editor { break; } - let start = range.start.to_display_point(display_snapshot); - let end = range.end.to_display_point(display_snapshot); + let start = highlight.range.start.to_display_point(display_snapshot); + let end = highlight.range.end.to_display_point(display_snapshot); + let color = (highlight.color_fetcher)(theme); results.push((start..end, color)) } } @@ -18734,12 +18769,13 @@ impl Editor { count: usize, ) -> Vec> { let mut results = Vec::new(); - let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::()) else { + let Some(highlights) = self.background_highlights.get(&TypeId::of::()) else { return vec![]; }; - let start_ix = match ranges.binary_search_by(|probe| { + let start_ix = match highlights.binary_search_by(|probe| { let cmp = probe + .range .end .cmp(&search_range.start, &display_snapshot.buffer_snapshot); if cmp.is_gt() { @@ -18760,24 +18796,31 @@ impl Editor { }; let mut start_row: Option = None; let mut end_row: Option = None; - if ranges.len() > count { + if highlights.len() > count { return Vec::new(); } - for range in &ranges[start_ix..] { - if range + for highlight in &highlights[start_ix..] { + if highlight + .range .start .cmp(&search_range.end, &display_snapshot.buffer_snapshot) .is_ge() { break; } - let end = range.end.to_point(&display_snapshot.buffer_snapshot); + let end = highlight + .range + .end + .to_point(&display_snapshot.buffer_snapshot); if let Some(current_row) = &end_row { if end.row == current_row.row { continue; } } - let start = range.start.to_point(&display_snapshot.buffer_snapshot); + let start = highlight + .range + .start + .to_point(&display_snapshot.buffer_snapshot); if start_row.is_none() { assert_eq!(end_row, None); start_row = Some(start); @@ -18873,13 +18916,11 @@ impl Editor { pub fn highlight_text( &mut self, - ranges: Vec>, - style: HighlightStyle, + ranges: Vec<(Range, HighlightStyle)>, cx: &mut Context, ) { - self.display_map.update(cx, |map, _| { - map.highlight_text(TypeId::of::(), ranges, style) - }); + self.display_map + .update(cx, |map, _| map.highlight_text(TypeId::of::(), ranges)); cx.notify(); } @@ -18898,7 +18939,7 @@ impl Editor { pub fn text_highlights<'a, T: 'static>( &'a self, cx: &'a App, - ) -> Option<(HighlightStyle, &'a [Range])> { + ) -> Option<&'a [(Range, HighlightStyle)]> { self.display_map.read(cx).text_highlights(TypeId::of::()) } @@ -18911,6 +18952,14 @@ impl Editor { } } + pub fn remove_text_highlights( + &mut self, + cx: &mut Context, + ) -> Option, HighlightStyle)>> { + self.display_map + .update(cx, |map, _| map.remove_text_highlights(TypeId::of::())) + } + pub fn show_local_cursors(&self, window: &mut Window, cx: &mut App) -> bool { (self.read_only(cx) || self.blink_manager.read(cx).visible()) && self.focus_handle.is_focused(window) @@ -19573,11 +19622,11 @@ impl Editor { fn marked_text_ranges(&self, cx: &App) -> Option>> { let snapshot = self.buffer.read(cx).read(cx); - let (_, ranges) = self.text_highlights::(cx)?; + let ranges = self.text_highlights::(cx)?; Some( ranges .iter() - .map(move |range| { + .map(move |(range, _)| { range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot) }) .collect(), @@ -19888,9 +19937,12 @@ impl Editor { pending = "".to_string(); } - let existing_pending = self - .text_highlights::(cx) - .map(|(_, ranges)| ranges.iter().cloned().collect::>()); + let existing_pending = self.text_highlights::(cx).map(|ranges| { + ranges + .iter() + .map(|(range, _)| range.clone()) + .collect::>() + }); if existing_pending.is_none() && pending.is_empty() { return; } @@ -19918,28 +19970,27 @@ impl Editor { .all::(cx) .into_iter() .map(|selection| { - snapshot.buffer_snapshot.anchor_after(selection.end) - ..snapshot - .buffer_snapshot - .anchor_before(selection.end + pending.len()) + ( + snapshot.buffer_snapshot.anchor_after(selection.end) + ..snapshot + .buffer_snapshot + .anchor_before(selection.end + pending.len()), + HighlightStyle { + underline: Some(UnderlineStyle { + thickness: px(1.), + color: None, + wavy: false, + }), + ..Default::default() + }, + ) }) .collect(); if pending.is_empty() { self.clear_highlights::(cx); } else { - self.highlight_text::( - ranges, - HighlightStyle { - underline: Some(UnderlineStyle { - thickness: px(1.), - color: None, - wavy: false, - }), - ..Default::default() - }, - cx, - ); + self.highlight_text::(ranges, cx); } self.ime_transaction = self.ime_transaction.or(transaction); @@ -22012,7 +22063,7 @@ impl EntityInputHandler for Editor { fn marked_text_range(&self, _: &mut Window, cx: &mut Context) -> Option> { let snapshot = self.buffer.read(cx).read(cx); - let range = self.text_highlights::(cx)?.1.first()?; + let (range, _) = self.text_highlights::(cx)?.first()?; Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0) } @@ -22149,7 +22200,18 @@ impl EntityInputHandler for Editor { .disjoint_anchors() .iter() .map(|selection| { - selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot) + ( + selection.start.bias_left(&snapshot) + ..selection.end.bias_right(&snapshot), + HighlightStyle { + underline: Some(UnderlineStyle { + thickness: px(1.), + color: None, + wavy: false, + }), + ..Default::default() + }, + ) }) .collect::>() }; @@ -22157,18 +22219,7 @@ impl EntityInputHandler for Editor { if text.is_empty() { this.unmark_text(window, cx); } else { - this.highlight_text::( - marked_ranges.clone(), - HighlightStyle { - underline: Some(UnderlineStyle { - thickness: px(1.), - color: None, - wavy: false, - }), - ..Default::default() - }, - cx, - ); + this.highlight_text::(marked_ranges.clone(), cx); } // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard) @@ -22184,7 +22235,7 @@ impl EntityInputHandler for Editor { let snapshot = this.buffer.read(cx).read(cx); let new_selected_ranges = marked_ranges .into_iter() - .map(|marked_range| { + .map(|(marked_range, _)| { let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0; let new_start = OffsetUtf16(new_selected_range.start + insertion_start); let new_end = OffsetUtf16(new_selected_range.end + insertion_start); diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index 91ccdfa49d..930f81f9d1 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -13697,7 +13697,7 @@ fn test_highlighted_ranges(cx: &mut TestAppContext) { let mut highlighted_ranges = editor.background_highlights_in_range( anchor_range(Point::new(3, 4)..Point::new(7, 4)), &snapshot, - cx.theme().colors(), + cx.theme(), ); // Enforce a consistent ordering based on color without relying on the ordering of the // highlight's `TypeId` which is non-executor. @@ -13727,7 +13727,7 @@ fn test_highlighted_ranges(cx: &mut TestAppContext) { editor.background_highlights_in_range( anchor_range(Point::new(5, 6)..Point::new(6, 4)), &snapshot, - cx.theme().colors(), + cx.theme(), ), &[( DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5), @@ -19392,8 +19392,10 @@ async fn test_folding_buffer_when_multibuffer_has_only_one_excerpt(cx: &mut Test let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx); let highlight_range = selection_range.clone().to_anchors(&multi_buffer_snapshot); editor.highlight_text::( - vec![highlight_range.clone()], - HighlightStyle::color(Hsla::green()), + vec![( + highlight_range.clone(), + HighlightStyle::color(Hsla::green()), + )], cx, ); editor.change_selections(None, window, cx, |s| s.select_ranges(Some(highlight_range))); @@ -20334,7 +20336,7 @@ async fn test_rename_with_duplicate_edits(cx: &mut TestAppContext) { let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx)); editor.highlight_background::( &[highlight_range], - |c| c.editor_document_highlight_read_background, + |c| c.colors().editor_document_highlight_read_background, cx, ); }); @@ -20412,7 +20414,7 @@ async fn test_rename_without_prepare(cx: &mut TestAppContext) { let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx)); editor.highlight_background::( &[highlight_range], - |c| c.editor_document_highlight_read_background, + |c| c.colors().editor_document_highlight_read_background, cx, ); }); diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index a31b6a73c9..7d81d7f4e2 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -6105,7 +6105,7 @@ impl EditorElement { ); } - for (background_highlight_id, (_, background_ranges)) in + for (background_highlight_id, background_highlights) in background_highlights.iter() { let is_search_highlights = *background_highlight_id @@ -6124,18 +6124,22 @@ impl EditorElement { if is_symbol_occurrences { color.fade_out(0.5); } - let marker_row_ranges = background_ranges.iter().map(|range| { - let display_start = range - .start - .to_display_point(&snapshot.display_snapshot); - let display_end = - range.end.to_display_point(&snapshot.display_snapshot); - ColoredRange { - start: display_start.row(), - end: display_end.row(), - color, - } - }); + let marker_row_ranges = + background_highlights.iter().map(|highlight| { + let display_start = highlight + .range + .start + .to_display_point(&snapshot.display_snapshot); + let display_end = highlight + .range + .end + .to_display_point(&snapshot.display_snapshot); + ColoredRange { + start: display_start.row(), + end: display_end.row(), + color, + } + }); marker_quads.extend( scrollbar_layout .marker_quads_for_ranges(marker_row_ranges, Some(1)), @@ -8033,7 +8037,7 @@ impl Element for EditorElement { editor.read(cx).background_highlights_in_range( start_anchor..end_anchor, &snapshot.display_snapshot, - cx.theme().colors(), + cx.theme(), ) }) .unwrap_or_default(); diff --git a/crates/editor/src/highlight_matching_bracket.rs b/crates/editor/src/highlight_matching_bracket.rs index 35c134a885..778f5783e2 100644 --- a/crates/editor/src/highlight_matching_bracket.rs +++ b/crates/editor/src/highlight_matching_bracket.rs @@ -35,7 +35,7 @@ pub fn refresh_matching_bracket_highlights( opening_range.to_anchors(&snapshot.buffer_snapshot), closing_range.to_anchors(&snapshot.buffer_snapshot), ], - |theme| theme.editor_document_highlight_bracket_background, + |theme| theme.colors().editor_document_highlight_bracket_background, cx, ) } diff --git a/crates/editor/src/hover_links.rs b/crates/editor/src/hover_links.rs index a716b2e031..4c9a1b12a3 100644 --- a/crates/editor/src/hover_links.rs +++ b/crates/editor/src/hover_links.rs @@ -635,7 +635,7 @@ pub fn show_link_definition( match highlight_range { RangeInEditor::Text(text_range) => editor - .highlight_text::(vec![text_range], style, cx), + .highlight_text::(vec![(text_range, style)], cx), RangeInEditor::Inlay(highlight) => editor .highlight_inlays::(vec![highlight], style, cx), } @@ -1403,7 +1403,6 @@ mod tests { let snapshot = editor.snapshot(window, cx); let actual_ranges = snapshot .text_highlight_ranges::() - .map(|ranges| ranges.as_ref().clone().1) .unwrap_or_default(); assert!(actual_ranges.is_empty(), "When no cmd is pressed, should have no hint label selected, but got: {actual_ranges:?}"); @@ -1635,7 +1634,6 @@ mod tests { .snapshot(window, cx) .text_highlight_ranges::() .unwrap_or_default() - .1 .is_empty() ); }); @@ -1842,7 +1840,6 @@ mod tests { .snapshot(window, cx) .text_highlight_ranges::() .unwrap_or_default() - .1 .is_empty() ); }); diff --git a/crates/editor/src/hover_popover.rs b/crates/editor/src/hover_popover.rs index 2411b26ff1..b174a3ba62 100644 --- a/crates/editor/src/hover_popover.rs +++ b/crates/editor/src/hover_popover.rs @@ -520,7 +520,7 @@ fn show_hover( // Highlight the selected symbol using a background highlight editor.highlight_background::( &hover_highlights, - |theme| theme.element_hover, // todo update theme + |theme| theme.colors().element_hover, // todo update theme cx, ); } diff --git a/crates/editor/src/items.rs b/crates/editor/src/items.rs index f5b8b0b64d..27251f32d0 100644 --- a/crates/editor/src/items.rs +++ b/crates/editor/src/items.rs @@ -1432,8 +1432,11 @@ impl SearchableItem for Editor { fn get_matches(&self, _window: &mut Window, _: &mut App) -> Vec> { self.background_highlights .get(&TypeId::of::()) - .map_or(Vec::new(), |(_color, ranges)| { - ranges.iter().cloned().collect() + .map_or(Vec::new(), |highlights| { + highlights + .iter() + .map(|highlight| highlight.range.clone()) + .collect() }) } @@ -1452,14 +1455,14 @@ impl SearchableItem for Editor { _: &mut Window, cx: &mut Context, ) { - let existing_range = self + let existing_ranges = self .background_highlights .get(&TypeId::of::()) - .map(|(_, range)| range.as_ref()); - let updated = existing_range != Some(matches); + .map(|highlights| highlights.iter().map(|highlight| &highlight.range)); + let updated = !existing_ranges.is_some_and(|existing_ranges| existing_ranges.eq(matches)); self.highlight_background::( matches, - |theme| theme.search_match_background, + |theme| theme.colors().search_match_background, cx, ); if updated { @@ -1480,7 +1483,12 @@ impl SearchableItem for Editor { if self.has_filtered_search_ranges() { self.previous_search_ranges = self .clear_background_highlights::(cx) - .map(|(_, ranges)| ranges) + .map(|highlights| { + highlights + .iter() + .map(|highlight| highlight.range.clone()) + .collect() + }) } if !enabled { @@ -1702,8 +1710,11 @@ impl SearchableItem for Editor { let search_within_ranges = self .background_highlights .get(&TypeId::of::()) - .map_or(vec![], |(_color, ranges)| { - ranges.iter().cloned().collect::>() + .map_or(vec![], |highlights| { + highlights + .iter() + .map(|highlight| highlight.range.clone()) + .collect::>() }); cx.background_spawn(async move { diff --git a/crates/editor/src/test/editor_test_context.rs b/crates/editor/src/test/editor_test_context.rs index dfb41096cd..8a26889c4e 100644 --- a/crates/editor/src/test/editor_test_context.rs +++ b/crates/editor/src/test/editor_test_context.rs @@ -510,10 +510,9 @@ impl EditorTestContext { editor .background_highlights .get(&TypeId::of::()) - .map(|h| h.1.clone()) - .unwrap_or_default() - .iter() - .map(|range| range.to_offset(&snapshot.buffer_snapshot)) + .into_iter() + .flat_map(|highlights| highlights.as_slice()) + .map(|highlight| highlight.range.to_offset(&snapshot.buffer_snapshot)) .collect() }); assert_set_eq!(actual_ranges, expected_ranges); @@ -525,7 +524,12 @@ impl EditorTestContext { let snapshot = self.update_editor(|editor, window, cx| editor.snapshot(window, cx)); let actual_ranges: Vec> = snapshot .text_highlight_ranges::() - .map(|ranges| ranges.as_ref().clone().1) + .map(|ranges| { + ranges + .iter() + .map(|(range, _)| range.clone()) + .collect::>() + }) .unwrap_or_default() .into_iter() .map(|range| range.to_offset(&snapshot.buffer_snapshot)) diff --git a/crates/language_tools/src/syntax_tree_view.rs b/crates/language_tools/src/syntax_tree_view.rs index e88b3a825b..99132ce452 100644 --- a/crates/language_tools/src/syntax_tree_view.rs +++ b/crates/language_tools/src/syntax_tree_view.rs @@ -358,7 +358,7 @@ impl Render for SyntaxTreeView { editor.clear_background_highlights::( cx); editor.highlight_background::( &[range], - |theme| theme.editor_document_highlight_write_background, + |theme| theme.colors().editor_document_highlight_write_background, cx, ); }); diff --git a/crates/project/src/debugger/dap_command.rs b/crates/project/src/debugger/dap_command.rs index 6bd5746733..735444b3f3 100644 --- a/crates/project/src/debugger/dap_command.rs +++ b/crates/project/src/debugger/dap_command.rs @@ -1547,7 +1547,7 @@ fn dap_client_capabilities(adapter_id: String) -> InitializeRequestArguments { supports_memory_event: Some(false), supports_args_can_be_interpreted_by_shell: Some(false), supports_start_debugging_request: Some(true), - supports_ansistyling: Some(false), + supports_ansistyling: Some(true), } } diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 9bbf6c73e6..3ed7f73ebd 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -1377,7 +1377,7 @@ impl ProjectSearchView { } editor.highlight_background::( &match_ranges, - |theme| theme.search_match_background, + |theme| theme.colors().search_match_background, cx, ); }); diff --git a/crates/vim/src/normal/yank.rs b/crates/vim/src/normal/yank.rs index 6f83b954b2..ba33ce66b3 100644 --- a/crates/vim/src/normal/yank.rs +++ b/crates/vim/src/normal/yank.rs @@ -222,7 +222,7 @@ impl Vim { editor.highlight_background::( &ranges_to_highlight, - |colors| colors.editor_document_highlight_read_background, + |theme| theme.colors().editor_document_highlight_read_background, cx, ); cx.spawn(async move |this, cx| { diff --git a/crates/vim/src/replace.rs b/crates/vim/src/replace.rs index f975aefa33..f3f015894a 100644 --- a/crates/vim/src/replace.rs +++ b/crates/vim/src/replace.rs @@ -217,8 +217,8 @@ impl Vim { window: &mut Window, cx: &mut Context, ) { - if let Some((_, ranges)) = editor.clear_background_highlights::(cx) { - let previous_range = ranges[0].clone(); + if let Some(highlights) = editor.clear_background_highlights::(cx) { + let previous_range = highlights[0].range.clone(); let new_range_start = new_range.start.to_offset(&snapshot.buffer_snapshot); let new_range_end = new_range.end.to_offset(&snapshot.buffer_snapshot); @@ -261,7 +261,7 @@ impl Vim { let ranges = [new_range]; editor.highlight_background::( &ranges, - |theme| theme.editor_document_highlight_read_background, + |theme| theme.colors().editor_document_highlight_read_background, cx, ); } diff --git a/crates/vim/src/test.rs b/crates/vim/src/test.rs index 346f78c1ca..c010e0b0e0 100644 --- a/crates/vim/src/test.rs +++ b/crates/vim/src/test.rs @@ -864,16 +864,13 @@ async fn test_jk(cx: &mut gpui::TestAppContext) { fn assert_pending_input(cx: &mut VimTestContext, expected: &str) { cx.update_editor(|editor, window, cx| { let snapshot = editor.snapshot(window, cx); - let highlights = editor - .text_highlights::(cx) - .unwrap() - .1; + let highlights = editor.text_highlights::(cx).unwrap(); let (_, ranges) = marked_text_ranges(expected, false); assert_eq!( highlights .iter() - .map(|highlight| highlight.to_offset(&snapshot.buffer_snapshot)) + .map(|(highlight, _)| highlight.to_offset(&snapshot.buffer_snapshot)) .collect::>(), ranges ) @@ -923,15 +920,12 @@ async fn test_jk_delay(cx: &mut gpui::TestAppContext) { cx.assert_state("ˇjhello", Mode::Insert); cx.update_editor(|editor, window, cx| { let snapshot = editor.snapshot(window, cx); - let highlights = editor - .text_highlights::(cx) - .unwrap() - .1; + let highlights = editor.text_highlights::(cx).unwrap(); assert_eq!( highlights .iter() - .map(|highlight| highlight.to_offset(&snapshot.buffer_snapshot)) + .map(|(highlight, _)| highlight.to_offset(&snapshot.buffer_snapshot)) .collect::>(), vec![0..1] )