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.
This commit is contained in:
Cole Miller 2025-06-16 17:39:53 -04:00 committed by GitHub
parent 1f457169ba
commit ffc6218349
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 558 additions and 234 deletions

1
Cargo.lock generated
View file

@ -4268,6 +4268,7 @@ dependencies = [
name = "debugger_ui"
version = "0.1.0"
dependencies = [
"alacritty_terminal",
"anyhow",
"client",
"collections",

View file

@ -1350,11 +1350,18 @@ impl InlineAssistant {
editor.clear_highlights::<InlineAssist>(cx);
} else {
editor.highlight_text::<InlineAssist>(
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,
);
}

View file

@ -193,10 +193,10 @@ impl MessageEditor {
let highlights = editor.text_highlights::<Self>(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::<Self>(cx);
editor.highlight_text::<Self>(
anchor_ranges,
HighlightStyle {
font_weight: Some(FontWeight::BOLD),
..Default::default()
},
cx,
)
editor.highlight_text::<Self>(anchor_ranges, cx)
});
this.mentions = mentioned_user_ids;

View file

@ -26,6 +26,7 @@ test-support = [
]
[dependencies]
alacritty_terminal.workspace = true
anyhow.workspace = true
client.workspace = true
collections.workspace = true

View file

@ -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<StackFrameList>,
last_token: OutputToken,
update_output_task: Task<()>,
ansi_handler: ConsoleHandler,
ansi_processor: ansi::Processor<ansi::StdSyncHandler>,
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::<ConsoleAnsiHighlight>(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::<ConsoleAnsiHighlight>(highlights, cx);
let mut background_highlights = console
.clear_background_highlights::<ConsoleAnsiHighlight>(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::<ConsoleAnsiHighlight>(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<usize>, Option<ansi::Color>)>,
background_spans: Vec<(Range<usize>, Option<ansi::Color>)>,
current_range_start: usize,
current_background_range_start: usize,
current_color: Option<ansi::Color>,
current_background_color: Option<ansi::Color>,
pos: usize,
}
impl ConsoleHandler {
fn break_span(&mut self, color: Option<ansi::Color>) {
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<ansi::Color>) {
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);
}
_ => {}
}
}
}

View file

@ -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()
);
})

View file

@ -80,7 +80,7 @@ pub trait ToDisplayPoint {
fn to_display_point(&self, map: &DisplaySnapshot) -> DisplayPoint;
}
type TextHighlights = TreeMap<TypeId, Arc<(HighlightStyle, Vec<Range<Anchor>>)>>;
type TextHighlights = TreeMap<TypeId, Vec<(Range<Anchor>, HighlightStyle)>>;
type InlayHighlights = TreeMap<TypeId, TreeMap<InlayId, (HighlightStyle, InlayHighlight)>>;
/// 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<Range<Anchor>>,
style: HighlightStyle,
ranges: Vec<(Range<Anchor>, 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<Anchor>])> {
let highlights = self.text_highlights.get(&type_id)?;
Some((highlights.0, &highlights.1))
pub fn text_highlights(&self, type_id: TypeId) -> Option<&[(Range<Anchor>, 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<Vec<(Range<Anchor>, HighlightStyle)>> {
self.text_highlights.remove(&type_id)
}
pub fn set_font(&self, font: Font, font_size: Pixels, cx: &mut Context<Self>) -> 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<Tag: ?Sized + 'static>(
&self,
) -> Option<Arc<(HighlightStyle, Vec<Range<Anchor>>)>> {
) -> Option<Vec<(Range<Anchor>, HighlightStyle)>> {
let type_id = TypeId::of::<Tag>();
self.text_highlights.get(&type_id).cloned()
}
@ -2296,12 +2303,17 @@ pub mod tests {
map.highlight_text(
TypeId::of::<usize>(),
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,
);
});

View file

@ -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<vec::IntoIter<HighlightEndpoint>>,
active_highlights: BTreeMap<TypeId, HighlightStyle>,
text_highlights: Option<&'a TreeMap<TypeId, Arc<(HighlightStyle, Vec<Range<Anchor>>)>>>,
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<usize>,
language_aware: bool,
text_highlights: Option<&'a TreeMap<TypeId, Arc<(HighlightStyle, Vec<Range<Anchor>>)>>>,
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<usize>,
text_highlights: Option<&TreeMap<TypeId, Arc<(HighlightStyle, Vec<Range<Anchor>>)>>>,
text_highlights: Option<&TextHighlights>,
buffer: &MultiBufferSnapshot,
) -> iter::Peekable<vec::IntoIter<HighlightEndpoint>> {
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,
});
}
}

View file

@ -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();

View file

@ -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<HighlightStyle>;
type BackgroundHighlight = (fn(&ThemeColors) -> Hsla, Arc<[Range<Anchor>]>);
#[derive(Clone)]
pub struct BackgroundHighlight {
pub range: Range<Anchor>,
pub color_fetcher: fn(&Theme) -> Hsla,
}
type GutterHighlight = (fn(&App) -> Hsla, Vec<Range<Anchor>>);
#[derive(Default)]
@ -1013,7 +1018,7 @@ pub struct Editor {
placeholder_text: Option<Arc<str>>,
highlight_order: usize,
highlighted_rows: HashMap<TypeId, Vec<RowHighlight>>,
background_highlights: TreeMap<TypeId, BackgroundHighlight>,
background_highlights: TreeMap<TypeId, Vec<BackgroundHighlight>>,
gutter_highlights: TreeMap<TypeId, GutterHighlight>,
scrollbar_marker_state: ScrollbarMarkerState,
active_indent_guides_state: ActiveIndentGuidesState,
@ -6141,7 +6146,7 @@ impl Editor {
editor.update(cx, |editor, cx| {
editor.highlight_background::<Self>(
&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::<DocumentHighlightRead>(
&read_ranges,
|theme| theme.editor_document_highlight_read_background,
|theme| theme.colors().editor_document_highlight_read_background,
cx,
);
this.highlight_background::<DocumentHighlightWrite>(
&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::<SelectedTextHighlight>(
&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::<InlineCompletionHighlight>(
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::<Self>(
&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::<DocumentHighlightWrite>(cx);
let read_highlights =
this.clear_background_highlights::<DocumentHighlightRead>(cx);
let write_highlights = this
.clear_background_highlights::<DocumentHighlightWrite>(cx)
.unwrap_or_default();
let read_highlights = this
.clear_background_highlights::<DocumentHighlightRead>(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::<Rename>(
ranges,
HighlightStyle {
fade_out: Some(0.6),
..Default::default()
},
cx,
);
this.highlight_text::<Rename>(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<Anchor>], cx: &mut Context<Self>) {
self.highlight_background::<SearchWithinRange>(
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<T: 'static>(
&mut self,
ranges: &[Range<Anchor>],
color_fetcher: fn(&ThemeColors) -> Hsla,
color_fetcher: fn(&Theme) -> Hsla,
cx: &mut Context<Self>,
) {
let highlights = ranges
.iter()
.map(|range| BackgroundHighlight {
range: range.clone(),
color_fetcher,
})
.collect();
self.background_highlights
.insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
.insert(TypeId::of::<T>(), highlights);
self.scrollbar_marker_state.dirty = true;
cx.notify();
}
pub fn highlight_background_ranges<T: 'static>(
&mut self,
background_highlights: Vec<BackgroundHighlight>,
cx: &mut Context<'_, Editor>,
) {
self.background_highlights
.insert(TypeId::of::<T>(), background_highlights);
self.scrollbar_marker_state.dirty = true;
cx.notify();
}
@ -18528,9 +18557,9 @@ impl Editor {
pub fn clear_background_highlights<T: 'static>(
&mut self,
cx: &mut Context<Self>,
) -> Option<BackgroundHighlight> {
) -> Option<Vec<BackgroundHighlight>> {
let text_highlights = self.background_highlights.remove(&TypeId::of::<T>())?;
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::<items::BufferSearchHighlights>());
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<Item = &'a Range<Anchor>> {
let read_highlights = self
.background_highlights
.get(&TypeId::of::<DocumentHighlightRead>())
.map(|h| &h.1);
.get(&TypeId::of::<DocumentHighlightRead>());
let write_highlights = self
.background_highlights
.get(&TypeId::of::<DocumentHighlightWrite>())
.map(|h| &h.1);
.get(&TypeId::of::<DocumentHighlightWrite>());
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<T: 'static>(&self) -> bool {
self.background_highlights
.get(&TypeId::of::<T>())
.map_or(false, |(_, highlights)| !highlights.is_empty())
.map_or(false, |highlights| !highlights.is_empty())
}
pub fn background_highlights_in_range(
&self,
search_range: Range<Anchor>,
display_snapshot: &DisplaySnapshot,
theme: &ThemeColors,
theme: &Theme,
) -> Vec<(Range<DisplayPoint>, 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<RangeInclusive<DisplayPoint>> {
let mut results = Vec::new();
let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::<T>()) else {
let Some(highlights) = self.background_highlights.get(&TypeId::of::<T>()) 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<Point> = None;
let mut end_row: Option<Point> = 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<T: 'static>(
&mut self,
ranges: Vec<Range<Anchor>>,
style: HighlightStyle,
ranges: Vec<(Range<Anchor>, HighlightStyle)>,
cx: &mut Context<Self>,
) {
self.display_map.update(cx, |map, _| {
map.highlight_text(TypeId::of::<T>(), ranges, style)
});
self.display_map
.update(cx, |map, _| map.highlight_text(TypeId::of::<T>(), 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<Anchor>])> {
) -> Option<&'a [(Range<Anchor>, HighlightStyle)]> {
self.display_map.read(cx).text_highlights(TypeId::of::<T>())
}
@ -18911,6 +18952,14 @@ impl Editor {
}
}
pub fn remove_text_highlights<T: 'static>(
&mut self,
cx: &mut Context<Self>,
) -> Option<Vec<(Range<Anchor>, HighlightStyle)>> {
self.display_map
.update(cx, |map, _| map.remove_text_highlights(TypeId::of::<T>()))
}
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<Vec<Range<OffsetUtf16>>> {
let snapshot = self.buffer.read(cx).read(cx);
let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
let ranges = self.text_highlights::<InputComposition>(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::<PendingInput>(cx)
.map(|(_, ranges)| ranges.iter().cloned().collect::<Vec<_>>());
let existing_pending = self.text_highlights::<PendingInput>(cx).map(|ranges| {
ranges
.iter()
.map(|(range, _)| range.clone())
.collect::<Vec<_>>()
});
if existing_pending.is_none() && pending.is_empty() {
return;
}
@ -19918,28 +19970,27 @@ impl Editor {
.all::<usize>(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::<PendingInput>(cx);
} else {
self.highlight_text::<PendingInput>(
ranges,
HighlightStyle {
underline: Some(UnderlineStyle {
thickness: px(1.),
color: None,
wavy: false,
}),
..Default::default()
},
cx,
);
self.highlight_text::<PendingInput>(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<Self>) -> Option<Range<usize>> {
let snapshot = self.buffer.read(cx).read(cx);
let range = self.text_highlights::<InputComposition>(cx)?.1.first()?;
let (range, _) = self.text_highlights::<InputComposition>(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::<Vec<_>>()
};
@ -22157,18 +22219,7 @@ impl EntityInputHandler for Editor {
if text.is_empty() {
this.unmark_text(window, cx);
} else {
this.highlight_text::<InputComposition>(
marked_ranges.clone(),
HighlightStyle {
underline: Some(UnderlineStyle {
thickness: px(1.),
color: None,
wavy: false,
}),
..Default::default()
},
cx,
);
this.highlight_text::<InputComposition>(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);

View file

@ -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::<TestHighlight>(
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::<DocumentHighlightRead>(
&[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::<DocumentHighlightRead>(
&[highlight_range],
|c| c.editor_document_highlight_read_background,
|c| c.colors().editor_document_highlight_read_background,
cx,
);
});

View file

@ -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();

View file

@ -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,
)
}

View file

@ -635,7 +635,7 @@ pub fn show_link_definition(
match highlight_range {
RangeInEditor::Text(text_range) => editor
.highlight_text::<HoveredLinkState>(vec![text_range], style, cx),
.highlight_text::<HoveredLinkState>(vec![(text_range, style)], cx),
RangeInEditor::Inlay(highlight) => editor
.highlight_inlays::<HoveredLinkState>(vec![highlight], style, cx),
}
@ -1403,7 +1403,6 @@ mod tests {
let snapshot = editor.snapshot(window, cx);
let actual_ranges = snapshot
.text_highlight_ranges::<HoveredLinkState>()
.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::<HoveredLinkState>()
.unwrap_or_default()
.1
.is_empty()
);
});
@ -1842,7 +1840,6 @@ mod tests {
.snapshot(window, cx)
.text_highlight_ranges::<HoveredLinkState>()
.unwrap_or_default()
.1
.is_empty()
);
});

View file

@ -520,7 +520,7 @@ fn show_hover(
// Highlight the selected symbol using a background highlight
editor.highlight_background::<HoverState>(
&hover_highlights,
|theme| theme.element_hover, // todo update theme
|theme| theme.colors().element_hover, // todo update theme
cx,
);
}

View file

@ -1432,8 +1432,11 @@ impl SearchableItem for Editor {
fn get_matches(&self, _window: &mut Window, _: &mut App) -> Vec<Range<Anchor>> {
self.background_highlights
.get(&TypeId::of::<BufferSearchHighlights>())
.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<Self>,
) {
let existing_range = self
let existing_ranges = self
.background_highlights
.get(&TypeId::of::<BufferSearchHighlights>())
.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::<BufferSearchHighlights>(
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::<SearchWithinRange>(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::<SearchWithinRange>())
.map_or(vec![], |(_color, ranges)| {
ranges.iter().cloned().collect::<Vec<_>>()
.map_or(vec![], |highlights| {
highlights
.iter()
.map(|highlight| highlight.range.clone())
.collect::<Vec<_>>()
});
cx.background_spawn(async move {

View file

@ -510,10 +510,9 @@ impl EditorTestContext {
editor
.background_highlights
.get(&TypeId::of::<Tag>())
.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<Range<usize>> = snapshot
.text_highlight_ranges::<Tag>()
.map(|ranges| ranges.as_ref().clone().1)
.map(|ranges| {
ranges
.iter()
.map(|(range, _)| range.clone())
.collect::<Vec<_>>()
})
.unwrap_or_default()
.into_iter()
.map(|range| range.to_offset(&snapshot.buffer_snapshot))

View file

@ -358,7 +358,7 @@ impl Render for SyntaxTreeView {
editor.clear_background_highlights::<Self>( cx);
editor.highlight_background::<Self>(
&[range],
|theme| theme.editor_document_highlight_write_background,
|theme| theme.colors().editor_document_highlight_write_background,
cx,
);
});

View file

@ -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),
}
}

View file

@ -1377,7 +1377,7 @@ impl ProjectSearchView {
}
editor.highlight_background::<Self>(
&match_ranges,
|theme| theme.search_match_background,
|theme| theme.colors().search_match_background,
cx,
);
});

View file

@ -222,7 +222,7 @@ impl Vim {
editor.highlight_background::<HighlightOnYank>(
&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| {

View file

@ -217,8 +217,8 @@ impl Vim {
window: &mut Window,
cx: &mut Context<Editor>,
) {
if let Some((_, ranges)) = editor.clear_background_highlights::<VimExchange>(cx) {
let previous_range = ranges[0].clone();
if let Some(highlights) = editor.clear_background_highlights::<VimExchange>(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::<VimExchange>(
&ranges,
|theme| theme.editor_document_highlight_read_background,
|theme| theme.colors().editor_document_highlight_read_background,
cx,
);
}

View file

@ -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::<editor::PendingInput>(cx)
.unwrap()
.1;
let highlights = editor.text_highlights::<editor::PendingInput>(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::<Vec<_>>(),
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::<editor::PendingInput>(cx)
.unwrap()
.1;
let highlights = editor.text_highlights::<editor::PendingInput>(cx).unwrap();
assert_eq!(
highlights
.iter()
.map(|highlight| highlight.to_offset(&snapshot.buffer_snapshot))
.map(|(highlight, _)| highlight.to_offset(&snapshot.buffer_snapshot))
.collect::<Vec<_>>(),
vec![0..1]
)