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:
parent
1f457169ba
commit
ffc6218349
23 changed files with 558 additions and 234 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -4268,6 +4268,7 @@ dependencies = [
|
||||||
name = "debugger_ui"
|
name = "debugger_ui"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"alacritty_terminal",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"client",
|
"client",
|
||||||
"collections",
|
"collections",
|
||||||
|
|
|
@ -1350,11 +1350,18 @@ impl InlineAssistant {
|
||||||
editor.clear_highlights::<InlineAssist>(cx);
|
editor.clear_highlights::<InlineAssist>(cx);
|
||||||
} else {
|
} else {
|
||||||
editor.highlight_text::<InlineAssist>(
|
editor.highlight_text::<InlineAssist>(
|
||||||
foreground_ranges,
|
foreground_ranges
|
||||||
HighlightStyle {
|
.into_iter()
|
||||||
fade_out: Some(0.6),
|
.map(|range| {
|
||||||
..Default::default()
|
(
|
||||||
},
|
range,
|
||||||
|
HighlightStyle {
|
||||||
|
fade_out: Some(0.6),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -193,10 +193,10 @@ impl MessageEditor {
|
||||||
let highlights = editor.text_highlights::<Self>(cx);
|
let highlights = editor.text_highlights::<Self>(cx);
|
||||||
let text = editor.text(cx);
|
let text = editor.text(cx);
|
||||||
let snapshot = editor.buffer().read(cx).snapshot(cx);
|
let snapshot = editor.buffer().read(cx).snapshot(cx);
|
||||||
let mentions = if let Some((_, ranges)) = highlights {
|
let mentions = if let Some(ranges) = highlights {
|
||||||
ranges
|
ranges
|
||||||
.iter()
|
.iter()
|
||||||
.map(|range| range.to_offset(&snapshot))
|
.map(|(range, _)| range.to_offset(&snapshot))
|
||||||
.zip(self.mentions.iter().copied())
|
.zip(self.mentions.iter().copied())
|
||||||
.collect()
|
.collect()
|
||||||
} else {
|
} else {
|
||||||
|
@ -483,20 +483,19 @@ impl MessageEditor {
|
||||||
let end = multi_buffer.anchor_after(range.end);
|
let end = multi_buffer.anchor_after(range.end);
|
||||||
|
|
||||||
mentioned_user_ids.push(user.id);
|
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.clear_highlights::<Self>(cx);
|
||||||
editor.highlight_text::<Self>(
|
editor.highlight_text::<Self>(anchor_ranges, cx)
|
||||||
anchor_ranges,
|
|
||||||
HighlightStyle {
|
|
||||||
font_weight: Some(FontWeight::BOLD),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
cx,
|
|
||||||
)
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.mentions = mentioned_user_ids;
|
this.mentions = mentioned_user_ids;
|
||||||
|
|
|
@ -26,6 +26,7 @@ test-support = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
alacritty_terminal.workspace = true
|
||||||
anyhow.workspace = true
|
anyhow.workspace = true
|
||||||
client.workspace = true
|
client.workspace = true
|
||||||
collections.workspace = true
|
collections.workspace = true
|
||||||
|
|
|
@ -2,13 +2,17 @@ use super::{
|
||||||
stack_frame_list::{StackFrameList, StackFrameListEvent},
|
stack_frame_list::{StackFrameList, StackFrameListEvent},
|
||||||
variable_list::VariableList,
|
variable_list::VariableList,
|
||||||
};
|
};
|
||||||
|
use alacritty_terminal::vte::ansi;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
use dap::OutputEvent;
|
use dap::OutputEvent;
|
||||||
use editor::{Bias, CompletionProvider, Editor, EditorElement, EditorStyle, ExcerptId};
|
use editor::{
|
||||||
|
BackgroundHighlight, Bias, CompletionProvider, Editor, EditorElement, EditorStyle, ExcerptId,
|
||||||
|
};
|
||||||
use fuzzy::StringMatchCandidate;
|
use fuzzy::StringMatchCandidate;
|
||||||
use gpui::{
|
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 language::{Buffer, CodeLabel, ToOffset};
|
||||||
use menu::Confirm;
|
use menu::Confirm;
|
||||||
|
@ -17,8 +21,8 @@ use project::{
|
||||||
debugger::session::{CompletionsQuery, OutputToken, Session, SessionEvent},
|
debugger::session::{CompletionsQuery, OutputToken, Session, SessionEvent},
|
||||||
};
|
};
|
||||||
use settings::Settings;
|
use settings::Settings;
|
||||||
use std::{cell::RefCell, rc::Rc, usize};
|
use std::{cell::RefCell, ops::Range, rc::Rc, usize};
|
||||||
use theme::ThemeSettings;
|
use theme::{Theme, ThemeSettings};
|
||||||
use ui::{Divider, prelude::*};
|
use ui::{Divider, prelude::*};
|
||||||
|
|
||||||
pub struct Console {
|
pub struct Console {
|
||||||
|
@ -30,6 +34,8 @@ pub struct Console {
|
||||||
stack_frame_list: Entity<StackFrameList>,
|
stack_frame_list: Entity<StackFrameList>,
|
||||||
last_token: OutputToken,
|
last_token: OutputToken,
|
||||||
update_output_task: Task<()>,
|
update_output_task: Task<()>,
|
||||||
|
ansi_handler: ConsoleHandler,
|
||||||
|
ansi_processor: ansi::Processor<ansi::StdSyncHandler>,
|
||||||
focus_handle: FocusHandle,
|
focus_handle: FocusHandle,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,6 +106,8 @@ impl Console {
|
||||||
stack_frame_list,
|
stack_frame_list,
|
||||||
update_output_task: Task::ready(()),
|
update_output_task: Task::ready(()),
|
||||||
last_token: OutputToken(0),
|
last_token: OutputToken(0),
|
||||||
|
ansi_handler: Default::default(),
|
||||||
|
ansi_processor: Default::default(),
|
||||||
focus_handle,
|
focus_handle,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -135,17 +143,185 @@ impl Console {
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) {
|
) {
|
||||||
self.console.update(cx, |console, cx| {
|
let mut to_insert = String::default();
|
||||||
let mut to_insert = String::default();
|
for event in events {
|
||||||
for event in events {
|
use std::fmt::Write;
|
||||||
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.set_read_only(false);
|
||||||
console.move_to_end(&editor::actions::MoveToEnd, window, cx);
|
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);
|
console.set_read_only(true);
|
||||||
|
|
||||||
cx.notify();
|
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);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -110,7 +110,7 @@ async fn test_handle_output_event(executor: BackgroundExecutor, cx: &mut TestApp
|
||||||
client
|
client
|
||||||
.fake_event(dap::messages::Events::Output(dap::OutputEvent {
|
.fake_event(dap::messages::Events::Output(dap::OutputEvent {
|
||||||
category: Some(dap::OutputEventCategory::Stdout),
|
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,
|
data: None,
|
||||||
variables_reference: None,
|
variables_reference: None,
|
||||||
source: None,
|
source: None,
|
||||||
|
@ -124,7 +124,7 @@ async fn test_handle_output_event(executor: BackgroundExecutor, cx: &mut TestApp
|
||||||
client
|
client
|
||||||
.fake_event(dap::messages::Events::Output(dap::OutputEvent {
|
.fake_event(dap::messages::Events::Output(dap::OutputEvent {
|
||||||
category: Some(dap::OutputEventCategory::Console),
|
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,
|
data: None,
|
||||||
variables_reference: None,
|
variables_reference: None,
|
||||||
source: None,
|
source: None,
|
||||||
|
@ -150,7 +150,7 @@ async fn test_handle_output_event(executor: BackgroundExecutor, cx: &mut TestApp
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(
|
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()
|
active_session_panel.read(cx).running_state().read(cx).console().read(cx).editor().read(cx).text(cx).as_str()
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
|
|
|
@ -80,7 +80,7 @@ pub trait ToDisplayPoint {
|
||||||
fn to_display_point(&self, map: &DisplaySnapshot) -> DisplayPoint;
|
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)>>;
|
type InlayHighlights = TreeMap<TypeId, TreeMap<InlayId, (HighlightStyle, InlayHighlight)>>;
|
||||||
|
|
||||||
/// Decides how text in a [`MultiBuffer`] should be displayed in a buffer, handling inlay hints,
|
/// 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(
|
pub fn highlight_text(
|
||||||
&mut self,
|
&mut self,
|
||||||
type_id: TypeId,
|
type_id: TypeId,
|
||||||
ranges: Vec<Range<Anchor>>,
|
ranges: Vec<(Range<Anchor>, HighlightStyle)>,
|
||||||
style: HighlightStyle,
|
|
||||||
) {
|
) {
|
||||||
self.text_highlights
|
self.text_highlights.insert(type_id, ranges);
|
||||||
.insert(type_id, Arc::new((style, ranges)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn highlight_inlays(
|
pub(crate) fn highlight_inlays(
|
||||||
|
@ -500,16 +498,25 @@ impl DisplayMap {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn text_highlights(&self, type_id: TypeId) -> Option<(HighlightStyle, &[Range<Anchor>])> {
|
pub fn text_highlights(&self, type_id: TypeId) -> Option<&[(Range<Anchor>, HighlightStyle)]> {
|
||||||
let highlights = self.text_highlights.get(&type_id)?;
|
self.text_highlights
|
||||||
Some((highlights.0, &highlights.1))
|
.get(&type_id)
|
||||||
|
.map(|highlights| highlights.as_slice())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear_highlights(&mut self, type_id: TypeId) -> bool {
|
pub fn clear_highlights(&mut self, type_id: TypeId) -> bool {
|
||||||
let mut cleared = self.text_highlights.remove(&type_id).is_some();
|
let mut cleared = self.text_highlights.remove(&type_id).is_some();
|
||||||
cleared |= self.inlay_highlights.remove(&type_id).is_some();
|
cleared |= self.inlay_highlights.remove(&type_id).is_some();
|
||||||
cleared
|
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 {
|
pub fn set_font(&self, font: Font, font_size: Pixels, cx: &mut Context<Self>) -> bool {
|
||||||
self.wrap_map
|
self.wrap_map
|
||||||
.update(cx, |map, cx| map.set_font_with_size(font, font_size, cx))
|
.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"))]
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
pub fn text_highlight_ranges<Tag: ?Sized + 'static>(
|
pub fn text_highlight_ranges<Tag: ?Sized + 'static>(
|
||||||
&self,
|
&self,
|
||||||
) -> Option<Arc<(HighlightStyle, Vec<Range<Anchor>>)>> {
|
) -> Option<Vec<(Range<Anchor>, HighlightStyle)>> {
|
||||||
let type_id = TypeId::of::<Tag>();
|
let type_id = TypeId::of::<Tag>();
|
||||||
self.text_highlights.get(&type_id).cloned()
|
self.text_highlights.get(&type_id).cloned()
|
||||||
}
|
}
|
||||||
|
@ -2296,12 +2303,17 @@ pub mod tests {
|
||||||
map.highlight_text(
|
map.highlight_text(
|
||||||
TypeId::of::<usize>(),
|
TypeId::of::<usize>(),
|
||||||
vec![
|
vec![
|
||||||
buffer_snapshot.anchor_before(Point::new(3, 9))
|
(
|
||||||
..buffer_snapshot.anchor_after(Point::new(3, 14)),
|
buffer_snapshot.anchor_before(Point::new(3, 9))
|
||||||
buffer_snapshot.anchor_before(Point::new(3, 17))
|
..buffer_snapshot.anchor_after(Point::new(3, 14)),
|
||||||
..buffer_snapshot.anchor_after(Point::new(3, 18)),
|
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(
|
map.insert_blocks(
|
||||||
[BlockProperties {
|
[BlockProperties {
|
||||||
|
@ -2620,11 +2632,13 @@ pub mod tests {
|
||||||
highlighted_ranges
|
highlighted_ranges
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|range| {
|
.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(),
|
.collect(),
|
||||||
style,
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
use collections::BTreeMap;
|
use collections::BTreeMap;
|
||||||
use gpui::HighlightStyle;
|
use gpui::HighlightStyle;
|
||||||
use language::Chunk;
|
use language::Chunk;
|
||||||
use multi_buffer::{Anchor, MultiBufferChunks, MultiBufferSnapshot, ToOffset as _};
|
use multi_buffer::{MultiBufferChunks, MultiBufferSnapshot, ToOffset as _};
|
||||||
use std::{
|
use std::{
|
||||||
any::TypeId,
|
any::TypeId,
|
||||||
cmp,
|
cmp,
|
||||||
iter::{self, Peekable},
|
iter::{self, Peekable},
|
||||||
ops::Range,
|
ops::Range,
|
||||||
sync::Arc,
|
|
||||||
vec,
|
vec,
|
||||||
};
|
};
|
||||||
use sum_tree::TreeMap;
|
|
||||||
|
use crate::display_map::TextHighlights;
|
||||||
|
|
||||||
pub struct CustomHighlightsChunks<'a> {
|
pub struct CustomHighlightsChunks<'a> {
|
||||||
buffer_chunks: MultiBufferChunks<'a>,
|
buffer_chunks: MultiBufferChunks<'a>,
|
||||||
|
@ -19,15 +19,15 @@ pub struct CustomHighlightsChunks<'a> {
|
||||||
multibuffer_snapshot: &'a MultiBufferSnapshot,
|
multibuffer_snapshot: &'a MultiBufferSnapshot,
|
||||||
|
|
||||||
highlight_endpoints: Peekable<vec::IntoIter<HighlightEndpoint>>,
|
highlight_endpoints: Peekable<vec::IntoIter<HighlightEndpoint>>,
|
||||||
active_highlights: BTreeMap<TypeId, HighlightStyle>,
|
active_highlights: BTreeMap<(TypeId, usize), HighlightStyle>,
|
||||||
text_highlights: Option<&'a TreeMap<TypeId, Arc<(HighlightStyle, Vec<Range<Anchor>>)>>>,
|
text_highlights: Option<&'a TextHighlights>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
struct HighlightEndpoint {
|
struct HighlightEndpoint {
|
||||||
offset: usize,
|
offset: usize,
|
||||||
is_start: bool,
|
is_start: bool,
|
||||||
tag: TypeId,
|
tag: (TypeId, usize),
|
||||||
style: HighlightStyle,
|
style: HighlightStyle,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ impl<'a> CustomHighlightsChunks<'a> {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
range: Range<usize>,
|
range: Range<usize>,
|
||||||
language_aware: bool,
|
language_aware: bool,
|
||||||
text_highlights: Option<&'a TreeMap<TypeId, Arc<(HighlightStyle, Vec<Range<Anchor>>)>>>,
|
text_highlights: Option<&'a TextHighlights>,
|
||||||
multibuffer_snapshot: &'a MultiBufferSnapshot,
|
multibuffer_snapshot: &'a MultiBufferSnapshot,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -66,7 +66,7 @@ impl<'a> CustomHighlightsChunks<'a> {
|
||||||
|
|
||||||
fn create_highlight_endpoints(
|
fn create_highlight_endpoints(
|
||||||
range: &Range<usize>,
|
range: &Range<usize>,
|
||||||
text_highlights: Option<&TreeMap<TypeId, Arc<(HighlightStyle, Vec<Range<Anchor>>)>>>,
|
text_highlights: Option<&TextHighlights>,
|
||||||
buffer: &MultiBufferSnapshot,
|
buffer: &MultiBufferSnapshot,
|
||||||
) -> iter::Peekable<vec::IntoIter<HighlightEndpoint>> {
|
) -> iter::Peekable<vec::IntoIter<HighlightEndpoint>> {
|
||||||
let mut highlight_endpoints = Vec::new();
|
let mut highlight_endpoints = Vec::new();
|
||||||
|
@ -74,10 +74,7 @@ fn create_highlight_endpoints(
|
||||||
let start = buffer.anchor_after(range.start);
|
let start = buffer.anchor_after(range.start);
|
||||||
let end = buffer.anchor_after(range.end);
|
let end = buffer.anchor_after(range.end);
|
||||||
for (&tag, text_highlights) in text_highlights.iter() {
|
for (&tag, text_highlights) in text_highlights.iter() {
|
||||||
let style = text_highlights.0;
|
let start_ix = match text_highlights.binary_search_by(|(probe, _)| {
|
||||||
let ranges = &text_highlights.1;
|
|
||||||
|
|
||||||
let start_ix = match ranges.binary_search_by(|probe| {
|
|
||||||
let cmp = probe.end.cmp(&start, &buffer);
|
let cmp = probe.end.cmp(&start, &buffer);
|
||||||
if cmp.is_gt() {
|
if cmp.is_gt() {
|
||||||
cmp::Ordering::Greater
|
cmp::Ordering::Greater
|
||||||
|
@ -88,7 +85,7 @@ fn create_highlight_endpoints(
|
||||||
Ok(i) | Err(i) => i,
|
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() {
|
if range.start.cmp(&end, &buffer).is_ge() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -96,14 +93,14 @@ fn create_highlight_endpoints(
|
||||||
highlight_endpoints.push(HighlightEndpoint {
|
highlight_endpoints.push(HighlightEndpoint {
|
||||||
offset: range.start.to_offset(&buffer),
|
offset: range.start.to_offset(&buffer),
|
||||||
is_start: true,
|
is_start: true,
|
||||||
tag,
|
tag: (tag, ix),
|
||||||
style,
|
style: *style,
|
||||||
});
|
});
|
||||||
highlight_endpoints.push(HighlightEndpoint {
|
highlight_endpoints.push(HighlightEndpoint {
|
||||||
offset: range.end.to_offset(&buffer),
|
offset: range.end.to_offset(&buffer),
|
||||||
is_start: false,
|
is_start: false,
|
||||||
tag,
|
tag: (tag, ix),
|
||||||
style,
|
style: *style,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1085,7 +1085,7 @@ mod tests {
|
||||||
use project::{InlayHint, InlayHintLabel, ResolveState};
|
use project::{InlayHint, InlayHintLabel, ResolveState};
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
use settings::SettingsStore;
|
use settings::SettingsStore;
|
||||||
use std::{any::TypeId, cmp::Reverse, env, sync::Arc};
|
use std::{any::TypeId, cmp::Reverse, env};
|
||||||
use sum_tree::TreeMap;
|
use sum_tree::TreeMap;
|
||||||
use text::Patch;
|
use text::Patch;
|
||||||
use util::post_inc;
|
use util::post_inc;
|
||||||
|
@ -1593,16 +1593,16 @@ mod tests {
|
||||||
log::info!("highlighting text ranges {text_highlight_ranges:?}");
|
log::info!("highlighting text ranges {text_highlight_ranges:?}");
|
||||||
text_highlights.insert(
|
text_highlights.insert(
|
||||||
TypeId::of::<()>(),
|
TypeId::of::<()>(),
|
||||||
Arc::new((
|
text_highlight_ranges
|
||||||
HighlightStyle::default(),
|
.into_iter()
|
||||||
text_highlight_ranges
|
.map(|range| {
|
||||||
.into_iter()
|
(
|
||||||
.map(|range| {
|
|
||||||
buffer_snapshot.anchor_before(range.start)
|
buffer_snapshot.anchor_before(range.start)
|
||||||
..buffer_snapshot.anchor_after(range.end)
|
..buffer_snapshot.anchor_after(range.end),
|
||||||
})
|
HighlightStyle::default(),
|
||||||
.collect(),
|
)
|
||||||
)),
|
})
|
||||||
|
.collect(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut inlay_highlights = InlayHighlights::default();
|
let mut inlay_highlights = InlayHighlights::default();
|
||||||
|
|
|
@ -196,7 +196,7 @@ pub use sum_tree::Bias;
|
||||||
use sum_tree::TreeMap;
|
use sum_tree::TreeMap;
|
||||||
use text::{BufferId, FromAnchor, OffsetUtf16, Rope};
|
use text::{BufferId, FromAnchor, OffsetUtf16, Rope};
|
||||||
use theme::{
|
use theme::{
|
||||||
ActiveTheme, PlayerColor, StatusColors, SyntaxTheme, ThemeColors, ThemeSettings,
|
ActiveTheme, PlayerColor, StatusColors, SyntaxTheme, Theme, ThemeSettings,
|
||||||
observe_buffer_font_size_adjustment,
|
observe_buffer_font_size_adjustment,
|
||||||
};
|
};
|
||||||
use ui::{
|
use ui::{
|
||||||
|
@ -704,7 +704,12 @@ impl EditorActionId {
|
||||||
// type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
|
// type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
|
||||||
// type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
|
// 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>>);
|
type GutterHighlight = (fn(&App) -> Hsla, Vec<Range<Anchor>>);
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
@ -1013,7 +1018,7 @@ pub struct Editor {
|
||||||
placeholder_text: Option<Arc<str>>,
|
placeholder_text: Option<Arc<str>>,
|
||||||
highlight_order: usize,
|
highlight_order: usize,
|
||||||
highlighted_rows: HashMap<TypeId, Vec<RowHighlight>>,
|
highlighted_rows: HashMap<TypeId, Vec<RowHighlight>>,
|
||||||
background_highlights: TreeMap<TypeId, BackgroundHighlight>,
|
background_highlights: TreeMap<TypeId, Vec<BackgroundHighlight>>,
|
||||||
gutter_highlights: TreeMap<TypeId, GutterHighlight>,
|
gutter_highlights: TreeMap<TypeId, GutterHighlight>,
|
||||||
scrollbar_marker_state: ScrollbarMarkerState,
|
scrollbar_marker_state: ScrollbarMarkerState,
|
||||||
active_indent_guides_state: ActiveIndentGuidesState,
|
active_indent_guides_state: ActiveIndentGuidesState,
|
||||||
|
@ -6141,7 +6146,7 @@ impl Editor {
|
||||||
editor.update(cx, |editor, cx| {
|
editor.update(cx, |editor, cx| {
|
||||||
editor.highlight_background::<Self>(
|
editor.highlight_background::<Self>(
|
||||||
&ranges_to_highlight,
|
&ranges_to_highlight,
|
||||||
|theme| theme.editor_highlighted_line_background,
|
|theme| theme.colors().editor_highlighted_line_background,
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -6496,12 +6501,12 @@ impl Editor {
|
||||||
|
|
||||||
this.highlight_background::<DocumentHighlightRead>(
|
this.highlight_background::<DocumentHighlightRead>(
|
||||||
&read_ranges,
|
&read_ranges,
|
||||||
|theme| theme.editor_document_highlight_read_background,
|
|theme| theme.colors().editor_document_highlight_read_background,
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
this.highlight_background::<DocumentHighlightWrite>(
|
this.highlight_background::<DocumentHighlightWrite>(
|
||||||
&write_ranges,
|
&write_ranges,
|
||||||
|theme| theme.editor_document_highlight_write_background,
|
|theme| theme.colors().editor_document_highlight_write_background,
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
cx.notify();
|
cx.notify();
|
||||||
|
@ -6603,7 +6608,7 @@ impl Editor {
|
||||||
if !match_ranges.is_empty() {
|
if !match_ranges.is_empty() {
|
||||||
editor.highlight_background::<SelectedTextHighlight>(
|
editor.highlight_background::<SelectedTextHighlight>(
|
||||||
&match_ranges,
|
&match_ranges,
|
||||||
|theme| theme.editor_document_highlight_bracket_background,
|
|theme| theme.colors().editor_document_highlight_bracket_background,
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -7479,12 +7484,15 @@ impl Editor {
|
||||||
self.splice_inlays(&[], inlays, cx);
|
self.splice_inlays(&[], inlays, cx);
|
||||||
} else {
|
} else {
|
||||||
let background_color = cx.theme().status().deleted_background;
|
let background_color = cx.theme().status().deleted_background;
|
||||||
|
let style = HighlightStyle {
|
||||||
|
background_color: Some(background_color),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
self.highlight_text::<InlineCompletionHighlight>(
|
self.highlight_text::<InlineCompletionHighlight>(
|
||||||
edits.iter().map(|(range, _)| range.clone()).collect(),
|
edits
|
||||||
HighlightStyle {
|
.iter()
|
||||||
background_color: Some(background_color),
|
.map(|(range, _)| (range.clone(), style))
|
||||||
..Default::default()
|
.collect(),
|
||||||
},
|
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -15351,7 +15359,7 @@ impl Editor {
|
||||||
}
|
}
|
||||||
editor.highlight_background::<Self>(
|
editor.highlight_background::<Self>(
|
||||||
&ranges,
|
&ranges,
|
||||||
|theme| theme.editor_highlighted_line_background,
|
|theme| theme.colors().editor_highlighted_line_background,
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -15506,25 +15514,28 @@ impl Editor {
|
||||||
})
|
})
|
||||||
.detach();
|
.detach();
|
||||||
|
|
||||||
let write_highlights =
|
let write_highlights = this
|
||||||
this.clear_background_highlights::<DocumentHighlightWrite>(cx);
|
.clear_background_highlights::<DocumentHighlightWrite>(cx)
|
||||||
let read_highlights =
|
.unwrap_or_default();
|
||||||
this.clear_background_highlights::<DocumentHighlightRead>(cx);
|
let read_highlights = this
|
||||||
|
.clear_background_highlights::<DocumentHighlightRead>(cx)
|
||||||
|
.unwrap_or_default();
|
||||||
let ranges = write_highlights
|
let ranges = write_highlights
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(|(_, ranges)| ranges.iter())
|
.chain(read_highlights.iter())
|
||||||
.chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
|
|
||||||
.cloned()
|
.cloned()
|
||||||
|
.map(|highlight| {
|
||||||
|
(
|
||||||
|
highlight.range,
|
||||||
|
HighlightStyle {
|
||||||
|
fade_out: Some(0.6),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
this.highlight_text::<Rename>(
|
this.highlight_text::<Rename>(ranges, cx);
|
||||||
ranges,
|
|
||||||
HighlightStyle {
|
|
||||||
fade_out: Some(0.6),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
cx,
|
|
||||||
);
|
|
||||||
let rename_focus_handle = rename_editor.focus_handle(cx);
|
let rename_focus_handle = rename_editor.focus_handle(cx);
|
||||||
window.focus(&rename_focus_handle);
|
window.focus(&rename_focus_handle);
|
||||||
let block_id = this.insert_blocks(
|
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>) {
|
pub fn set_search_within_ranges(&mut self, ranges: &[Range<Anchor>], cx: &mut Context<Self>) {
|
||||||
self.highlight_background::<SearchWithinRange>(
|
self.highlight_background::<SearchWithinRange>(
|
||||||
ranges,
|
ranges,
|
||||||
|colors| colors.editor_document_highlight_read_background,
|
|theme| theme.colors().editor_document_highlight_read_background,
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -18516,11 +18527,29 @@ impl Editor {
|
||||||
pub fn highlight_background<T: 'static>(
|
pub fn highlight_background<T: 'static>(
|
||||||
&mut self,
|
&mut self,
|
||||||
ranges: &[Range<Anchor>],
|
ranges: &[Range<Anchor>],
|
||||||
color_fetcher: fn(&ThemeColors) -> Hsla,
|
color_fetcher: fn(&Theme) -> Hsla,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) {
|
) {
|
||||||
|
let highlights = ranges
|
||||||
|
.iter()
|
||||||
|
.map(|range| BackgroundHighlight {
|
||||||
|
range: range.clone(),
|
||||||
|
color_fetcher,
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
self.background_highlights
|
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;
|
self.scrollbar_marker_state.dirty = true;
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
@ -18528,9 +18557,9 @@ impl Editor {
|
||||||
pub fn clear_background_highlights<T: 'static>(
|
pub fn clear_background_highlights<T: 'static>(
|
||||||
&mut self,
|
&mut self,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> Option<BackgroundHighlight> {
|
) -> Option<Vec<BackgroundHighlight>> {
|
||||||
let text_highlights = self.background_highlights.remove(&TypeId::of::<T>())?;
|
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;
|
self.scrollbar_marker_state.dirty = true;
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
@ -18625,7 +18654,7 @@ impl Editor {
|
||||||
let buffer = &snapshot.buffer_snapshot;
|
let buffer = &snapshot.buffer_snapshot;
|
||||||
let start = buffer.anchor_before(0);
|
let start = buffer.anchor_before(0);
|
||||||
let end = buffer.anchor_after(buffer.len());
|
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)
|
self.background_highlights_in_range(start..end, &snapshot, theme)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18637,10 +18666,13 @@ impl Editor {
|
||||||
.background_highlights
|
.background_highlights
|
||||||
.get(&TypeId::of::<items::BufferSearchHighlights>());
|
.get(&TypeId::of::<items::BufferSearchHighlights>());
|
||||||
|
|
||||||
if let Some((_color, ranges)) = highlights {
|
if let Some(highlights) = highlights {
|
||||||
ranges
|
highlights
|
||||||
.iter()
|
.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()
|
.collect_vec()
|
||||||
} else {
|
} else {
|
||||||
vec![]
|
vec![]
|
||||||
|
@ -18654,20 +18686,18 @@ impl Editor {
|
||||||
) -> impl 'a + Iterator<Item = &'a Range<Anchor>> {
|
) -> impl 'a + Iterator<Item = &'a Range<Anchor>> {
|
||||||
let read_highlights = self
|
let read_highlights = self
|
||||||
.background_highlights
|
.background_highlights
|
||||||
.get(&TypeId::of::<DocumentHighlightRead>())
|
.get(&TypeId::of::<DocumentHighlightRead>());
|
||||||
.map(|h| &h.1);
|
|
||||||
let write_highlights = self
|
let write_highlights = self
|
||||||
.background_highlights
|
.background_highlights
|
||||||
.get(&TypeId::of::<DocumentHighlightWrite>())
|
.get(&TypeId::of::<DocumentHighlightWrite>());
|
||||||
.map(|h| &h.1);
|
|
||||||
let left_position = position.bias_left(buffer);
|
let left_position = position.bias_left(buffer);
|
||||||
let right_position = position.bias_right(buffer);
|
let right_position = position.bias_right(buffer);
|
||||||
read_highlights
|
read_highlights
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.chain(write_highlights)
|
.chain(write_highlights)
|
||||||
.flat_map(move |ranges| {
|
.flat_map(move |highlights| {
|
||||||
let start_ix = match ranges.binary_search_by(|probe| {
|
let start_ix = match highlights.binary_search_by(|probe| {
|
||||||
let cmp = probe.end.cmp(&left_position, buffer);
|
let cmp = probe.range.end.cmp(&left_position, buffer);
|
||||||
if cmp.is_ge() {
|
if cmp.is_ge() {
|
||||||
Ordering::Greater
|
Ordering::Greater
|
||||||
} else {
|
} else {
|
||||||
|
@ -18677,29 +18707,32 @@ impl Editor {
|
||||||
Ok(i) | Err(i) => i,
|
Ok(i) | Err(i) => i,
|
||||||
};
|
};
|
||||||
|
|
||||||
ranges[start_ix..]
|
highlights[start_ix..]
|
||||||
.iter()
|
.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 {
|
pub fn has_background_highlights<T: 'static>(&self) -> bool {
|
||||||
self.background_highlights
|
self.background_highlights
|
||||||
.get(&TypeId::of::<T>())
|
.get(&TypeId::of::<T>())
|
||||||
.map_or(false, |(_, highlights)| !highlights.is_empty())
|
.map_or(false, |highlights| !highlights.is_empty())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn background_highlights_in_range(
|
pub fn background_highlights_in_range(
|
||||||
&self,
|
&self,
|
||||||
search_range: Range<Anchor>,
|
search_range: Range<Anchor>,
|
||||||
display_snapshot: &DisplaySnapshot,
|
display_snapshot: &DisplaySnapshot,
|
||||||
theme: &ThemeColors,
|
theme: &Theme,
|
||||||
) -> Vec<(Range<DisplayPoint>, Hsla)> {
|
) -> Vec<(Range<DisplayPoint>, Hsla)> {
|
||||||
let mut results = Vec::new();
|
let mut results = Vec::new();
|
||||||
for (color_fetcher, ranges) in self.background_highlights.values() {
|
for highlights in self.background_highlights.values() {
|
||||||
let color = color_fetcher(theme);
|
let start_ix = match highlights.binary_search_by(|probe| {
|
||||||
let start_ix = match ranges.binary_search_by(|probe| {
|
|
||||||
let cmp = probe
|
let cmp = probe
|
||||||
|
.range
|
||||||
.end
|
.end
|
||||||
.cmp(&search_range.start, &display_snapshot.buffer_snapshot);
|
.cmp(&search_range.start, &display_snapshot.buffer_snapshot);
|
||||||
if cmp.is_gt() {
|
if cmp.is_gt() {
|
||||||
|
@ -18710,8 +18743,9 @@ impl Editor {
|
||||||
}) {
|
}) {
|
||||||
Ok(i) | Err(i) => i,
|
Ok(i) | Err(i) => i,
|
||||||
};
|
};
|
||||||
for range in &ranges[start_ix..] {
|
for highlight in &highlights[start_ix..] {
|
||||||
if range
|
if highlight
|
||||||
|
.range
|
||||||
.start
|
.start
|
||||||
.cmp(&search_range.end, &display_snapshot.buffer_snapshot)
|
.cmp(&search_range.end, &display_snapshot.buffer_snapshot)
|
||||||
.is_ge()
|
.is_ge()
|
||||||
|
@ -18719,8 +18753,9 @@ impl Editor {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
let start = range.start.to_display_point(display_snapshot);
|
let start = highlight.range.start.to_display_point(display_snapshot);
|
||||||
let end = range.end.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))
|
results.push((start..end, color))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18734,12 +18769,13 @@ impl Editor {
|
||||||
count: usize,
|
count: usize,
|
||||||
) -> Vec<RangeInclusive<DisplayPoint>> {
|
) -> Vec<RangeInclusive<DisplayPoint>> {
|
||||||
let mut results = Vec::new();
|
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![];
|
return vec![];
|
||||||
};
|
};
|
||||||
|
|
||||||
let start_ix = match ranges.binary_search_by(|probe| {
|
let start_ix = match highlights.binary_search_by(|probe| {
|
||||||
let cmp = probe
|
let cmp = probe
|
||||||
|
.range
|
||||||
.end
|
.end
|
||||||
.cmp(&search_range.start, &display_snapshot.buffer_snapshot);
|
.cmp(&search_range.start, &display_snapshot.buffer_snapshot);
|
||||||
if cmp.is_gt() {
|
if cmp.is_gt() {
|
||||||
|
@ -18760,24 +18796,31 @@ impl Editor {
|
||||||
};
|
};
|
||||||
let mut start_row: Option<Point> = None;
|
let mut start_row: Option<Point> = None;
|
||||||
let mut end_row: Option<Point> = None;
|
let mut end_row: Option<Point> = None;
|
||||||
if ranges.len() > count {
|
if highlights.len() > count {
|
||||||
return Vec::new();
|
return Vec::new();
|
||||||
}
|
}
|
||||||
for range in &ranges[start_ix..] {
|
for highlight in &highlights[start_ix..] {
|
||||||
if range
|
if highlight
|
||||||
|
.range
|
||||||
.start
|
.start
|
||||||
.cmp(&search_range.end, &display_snapshot.buffer_snapshot)
|
.cmp(&search_range.end, &display_snapshot.buffer_snapshot)
|
||||||
.is_ge()
|
.is_ge()
|
||||||
{
|
{
|
||||||
break;
|
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 let Some(current_row) = &end_row {
|
||||||
if end.row == current_row.row {
|
if end.row == current_row.row {
|
||||||
continue;
|
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() {
|
if start_row.is_none() {
|
||||||
assert_eq!(end_row, None);
|
assert_eq!(end_row, None);
|
||||||
start_row = Some(start);
|
start_row = Some(start);
|
||||||
|
@ -18873,13 +18916,11 @@ impl Editor {
|
||||||
|
|
||||||
pub fn highlight_text<T: 'static>(
|
pub fn highlight_text<T: 'static>(
|
||||||
&mut self,
|
&mut self,
|
||||||
ranges: Vec<Range<Anchor>>,
|
ranges: Vec<(Range<Anchor>, HighlightStyle)>,
|
||||||
style: HighlightStyle,
|
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) {
|
) {
|
||||||
self.display_map.update(cx, |map, _| {
|
self.display_map
|
||||||
map.highlight_text(TypeId::of::<T>(), ranges, style)
|
.update(cx, |map, _| map.highlight_text(TypeId::of::<T>(), ranges));
|
||||||
});
|
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18898,7 +18939,7 @@ impl Editor {
|
||||||
pub fn text_highlights<'a, T: 'static>(
|
pub fn text_highlights<'a, T: 'static>(
|
||||||
&'a self,
|
&'a self,
|
||||||
cx: &'a App,
|
cx: &'a App,
|
||||||
) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
|
) -> Option<&'a [(Range<Anchor>, HighlightStyle)]> {
|
||||||
self.display_map.read(cx).text_highlights(TypeId::of::<T>())
|
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 {
|
pub fn show_local_cursors(&self, window: &mut Window, cx: &mut App) -> bool {
|
||||||
(self.read_only(cx) || self.blink_manager.read(cx).visible())
|
(self.read_only(cx) || self.blink_manager.read(cx).visible())
|
||||||
&& self.focus_handle.is_focused(window)
|
&& self.focus_handle.is_focused(window)
|
||||||
|
@ -19573,11 +19622,11 @@ impl Editor {
|
||||||
|
|
||||||
fn marked_text_ranges(&self, cx: &App) -> Option<Vec<Range<OffsetUtf16>>> {
|
fn marked_text_ranges(&self, cx: &App) -> Option<Vec<Range<OffsetUtf16>>> {
|
||||||
let snapshot = self.buffer.read(cx).read(cx);
|
let snapshot = self.buffer.read(cx).read(cx);
|
||||||
let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
|
let ranges = self.text_highlights::<InputComposition>(cx)?;
|
||||||
Some(
|
Some(
|
||||||
ranges
|
ranges
|
||||||
.iter()
|
.iter()
|
||||||
.map(move |range| {
|
.map(move |(range, _)| {
|
||||||
range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
|
range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
|
@ -19888,9 +19937,12 @@ impl Editor {
|
||||||
pending = "".to_string();
|
pending = "".to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
let existing_pending = self
|
let existing_pending = self.text_highlights::<PendingInput>(cx).map(|ranges| {
|
||||||
.text_highlights::<PendingInput>(cx)
|
ranges
|
||||||
.map(|(_, ranges)| ranges.iter().cloned().collect::<Vec<_>>());
|
.iter()
|
||||||
|
.map(|(range, _)| range.clone())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
});
|
||||||
if existing_pending.is_none() && pending.is_empty() {
|
if existing_pending.is_none() && pending.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -19918,28 +19970,27 @@ impl Editor {
|
||||||
.all::<usize>(cx)
|
.all::<usize>(cx)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|selection| {
|
.map(|selection| {
|
||||||
snapshot.buffer_snapshot.anchor_after(selection.end)
|
(
|
||||||
..snapshot
|
snapshot.buffer_snapshot.anchor_after(selection.end)
|
||||||
.buffer_snapshot
|
..snapshot
|
||||||
.anchor_before(selection.end + pending.len())
|
.buffer_snapshot
|
||||||
|
.anchor_before(selection.end + pending.len()),
|
||||||
|
HighlightStyle {
|
||||||
|
underline: Some(UnderlineStyle {
|
||||||
|
thickness: px(1.),
|
||||||
|
color: None,
|
||||||
|
wavy: false,
|
||||||
|
}),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
if pending.is_empty() {
|
if pending.is_empty() {
|
||||||
self.clear_highlights::<PendingInput>(cx);
|
self.clear_highlights::<PendingInput>(cx);
|
||||||
} else {
|
} else {
|
||||||
self.highlight_text::<PendingInput>(
|
self.highlight_text::<PendingInput>(ranges, cx);
|
||||||
ranges,
|
|
||||||
HighlightStyle {
|
|
||||||
underline: Some(UnderlineStyle {
|
|
||||||
thickness: px(1.),
|
|
||||||
color: None,
|
|
||||||
wavy: false,
|
|
||||||
}),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
cx,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.ime_transaction = self.ime_transaction.or(transaction);
|
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>> {
|
fn marked_text_range(&self, _: &mut Window, cx: &mut Context<Self>) -> Option<Range<usize>> {
|
||||||
let snapshot = self.buffer.read(cx).read(cx);
|
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)
|
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()
|
.disjoint_anchors()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|selection| {
|
.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<_>>()
|
.collect::<Vec<_>>()
|
||||||
};
|
};
|
||||||
|
@ -22157,18 +22219,7 @@ impl EntityInputHandler for Editor {
|
||||||
if text.is_empty() {
|
if text.is_empty() {
|
||||||
this.unmark_text(window, cx);
|
this.unmark_text(window, cx);
|
||||||
} else {
|
} else {
|
||||||
this.highlight_text::<InputComposition>(
|
this.highlight_text::<InputComposition>(marked_ranges.clone(), cx);
|
||||||
marked_ranges.clone(),
|
|
||||||
HighlightStyle {
|
|
||||||
underline: Some(UnderlineStyle {
|
|
||||||
thickness: px(1.),
|
|
||||||
color: None,
|
|
||||||
wavy: false,
|
|
||||||
}),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
cx,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
|
// 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 snapshot = this.buffer.read(cx).read(cx);
|
||||||
let new_selected_ranges = marked_ranges
|
let new_selected_ranges = marked_ranges
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|marked_range| {
|
.map(|(marked_range, _)| {
|
||||||
let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
|
let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
|
||||||
let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
|
let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
|
||||||
let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
|
let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
|
||||||
|
|
|
@ -13697,7 +13697,7 @@ fn test_highlighted_ranges(cx: &mut TestAppContext) {
|
||||||
let mut highlighted_ranges = editor.background_highlights_in_range(
|
let mut highlighted_ranges = editor.background_highlights_in_range(
|
||||||
anchor_range(Point::new(3, 4)..Point::new(7, 4)),
|
anchor_range(Point::new(3, 4)..Point::new(7, 4)),
|
||||||
&snapshot,
|
&snapshot,
|
||||||
cx.theme().colors(),
|
cx.theme(),
|
||||||
);
|
);
|
||||||
// Enforce a consistent ordering based on color without relying on the ordering of the
|
// Enforce a consistent ordering based on color without relying on the ordering of the
|
||||||
// highlight's `TypeId` which is non-executor.
|
// highlight's `TypeId` which is non-executor.
|
||||||
|
@ -13727,7 +13727,7 @@ fn test_highlighted_ranges(cx: &mut TestAppContext) {
|
||||||
editor.background_highlights_in_range(
|
editor.background_highlights_in_range(
|
||||||
anchor_range(Point::new(5, 6)..Point::new(6, 4)),
|
anchor_range(Point::new(5, 6)..Point::new(6, 4)),
|
||||||
&snapshot,
|
&snapshot,
|
||||||
cx.theme().colors(),
|
cx.theme(),
|
||||||
),
|
),
|
||||||
&[(
|
&[(
|
||||||
DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
|
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 multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
|
||||||
let highlight_range = selection_range.clone().to_anchors(&multi_buffer_snapshot);
|
let highlight_range = selection_range.clone().to_anchors(&multi_buffer_snapshot);
|
||||||
editor.highlight_text::<TestHighlight>(
|
editor.highlight_text::<TestHighlight>(
|
||||||
vec![highlight_range.clone()],
|
vec![(
|
||||||
HighlightStyle::color(Hsla::green()),
|
highlight_range.clone(),
|
||||||
|
HighlightStyle::color(Hsla::green()),
|
||||||
|
)],
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
editor.change_selections(None, window, cx, |s| s.select_ranges(Some(highlight_range)));
|
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));
|
let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
|
||||||
editor.highlight_background::<DocumentHighlightRead>(
|
editor.highlight_background::<DocumentHighlightRead>(
|
||||||
&[highlight_range],
|
&[highlight_range],
|
||||||
|c| c.editor_document_highlight_read_background,
|
|c| c.colors().editor_document_highlight_read_background,
|
||||||
cx,
|
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));
|
let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
|
||||||
editor.highlight_background::<DocumentHighlightRead>(
|
editor.highlight_background::<DocumentHighlightRead>(
|
||||||
&[highlight_range],
|
&[highlight_range],
|
||||||
|c| c.editor_document_highlight_read_background,
|
|c| c.colors().editor_document_highlight_read_background,
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -6105,7 +6105,7 @@ impl EditorElement {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (background_highlight_id, (_, background_ranges)) in
|
for (background_highlight_id, background_highlights) in
|
||||||
background_highlights.iter()
|
background_highlights.iter()
|
||||||
{
|
{
|
||||||
let is_search_highlights = *background_highlight_id
|
let is_search_highlights = *background_highlight_id
|
||||||
|
@ -6124,18 +6124,22 @@ impl EditorElement {
|
||||||
if is_symbol_occurrences {
|
if is_symbol_occurrences {
|
||||||
color.fade_out(0.5);
|
color.fade_out(0.5);
|
||||||
}
|
}
|
||||||
let marker_row_ranges = background_ranges.iter().map(|range| {
|
let marker_row_ranges =
|
||||||
let display_start = range
|
background_highlights.iter().map(|highlight| {
|
||||||
.start
|
let display_start = highlight
|
||||||
.to_display_point(&snapshot.display_snapshot);
|
.range
|
||||||
let display_end =
|
.start
|
||||||
range.end.to_display_point(&snapshot.display_snapshot);
|
.to_display_point(&snapshot.display_snapshot);
|
||||||
ColoredRange {
|
let display_end = highlight
|
||||||
start: display_start.row(),
|
.range
|
||||||
end: display_end.row(),
|
.end
|
||||||
color,
|
.to_display_point(&snapshot.display_snapshot);
|
||||||
}
|
ColoredRange {
|
||||||
});
|
start: display_start.row(),
|
||||||
|
end: display_end.row(),
|
||||||
|
color,
|
||||||
|
}
|
||||||
|
});
|
||||||
marker_quads.extend(
|
marker_quads.extend(
|
||||||
scrollbar_layout
|
scrollbar_layout
|
||||||
.marker_quads_for_ranges(marker_row_ranges, Some(1)),
|
.marker_quads_for_ranges(marker_row_ranges, Some(1)),
|
||||||
|
@ -8033,7 +8037,7 @@ impl Element for EditorElement {
|
||||||
editor.read(cx).background_highlights_in_range(
|
editor.read(cx).background_highlights_in_range(
|
||||||
start_anchor..end_anchor,
|
start_anchor..end_anchor,
|
||||||
&snapshot.display_snapshot,
|
&snapshot.display_snapshot,
|
||||||
cx.theme().colors(),
|
cx.theme(),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
|
@ -35,7 +35,7 @@ pub fn refresh_matching_bracket_highlights(
|
||||||
opening_range.to_anchors(&snapshot.buffer_snapshot),
|
opening_range.to_anchors(&snapshot.buffer_snapshot),
|
||||||
closing_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,
|
cx,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -635,7 +635,7 @@ pub fn show_link_definition(
|
||||||
|
|
||||||
match highlight_range {
|
match highlight_range {
|
||||||
RangeInEditor::Text(text_range) => editor
|
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
|
RangeInEditor::Inlay(highlight) => editor
|
||||||
.highlight_inlays::<HoveredLinkState>(vec![highlight], style, cx),
|
.highlight_inlays::<HoveredLinkState>(vec![highlight], style, cx),
|
||||||
}
|
}
|
||||||
|
@ -1403,7 +1403,6 @@ mod tests {
|
||||||
let snapshot = editor.snapshot(window, cx);
|
let snapshot = editor.snapshot(window, cx);
|
||||||
let actual_ranges = snapshot
|
let actual_ranges = snapshot
|
||||||
.text_highlight_ranges::<HoveredLinkState>()
|
.text_highlight_ranges::<HoveredLinkState>()
|
||||||
.map(|ranges| ranges.as_ref().clone().1)
|
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
assert!(actual_ranges.is_empty(), "When no cmd is pressed, should have no hint label selected, but got: {actual_ranges:?}");
|
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)
|
.snapshot(window, cx)
|
||||||
.text_highlight_ranges::<HoveredLinkState>()
|
.text_highlight_ranges::<HoveredLinkState>()
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
.1
|
|
||||||
.is_empty()
|
.is_empty()
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -1842,7 +1840,6 @@ mod tests {
|
||||||
.snapshot(window, cx)
|
.snapshot(window, cx)
|
||||||
.text_highlight_ranges::<HoveredLinkState>()
|
.text_highlight_ranges::<HoveredLinkState>()
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
.1
|
|
||||||
.is_empty()
|
.is_empty()
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -520,7 +520,7 @@ fn show_hover(
|
||||||
// Highlight the selected symbol using a background highlight
|
// Highlight the selected symbol using a background highlight
|
||||||
editor.highlight_background::<HoverState>(
|
editor.highlight_background::<HoverState>(
|
||||||
&hover_highlights,
|
&hover_highlights,
|
||||||
|theme| theme.element_hover, // todo update theme
|
|theme| theme.colors().element_hover, // todo update theme
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1432,8 +1432,11 @@ impl SearchableItem for Editor {
|
||||||
fn get_matches(&self, _window: &mut Window, _: &mut App) -> Vec<Range<Anchor>> {
|
fn get_matches(&self, _window: &mut Window, _: &mut App) -> Vec<Range<Anchor>> {
|
||||||
self.background_highlights
|
self.background_highlights
|
||||||
.get(&TypeId::of::<BufferSearchHighlights>())
|
.get(&TypeId::of::<BufferSearchHighlights>())
|
||||||
.map_or(Vec::new(), |(_color, ranges)| {
|
.map_or(Vec::new(), |highlights| {
|
||||||
ranges.iter().cloned().collect()
|
highlights
|
||||||
|
.iter()
|
||||||
|
.map(|highlight| highlight.range.clone())
|
||||||
|
.collect()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1452,14 +1455,14 @@ impl SearchableItem for Editor {
|
||||||
_: &mut Window,
|
_: &mut Window,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) {
|
) {
|
||||||
let existing_range = self
|
let existing_ranges = self
|
||||||
.background_highlights
|
.background_highlights
|
||||||
.get(&TypeId::of::<BufferSearchHighlights>())
|
.get(&TypeId::of::<BufferSearchHighlights>())
|
||||||
.map(|(_, range)| range.as_ref());
|
.map(|highlights| highlights.iter().map(|highlight| &highlight.range));
|
||||||
let updated = existing_range != Some(matches);
|
let updated = !existing_ranges.is_some_and(|existing_ranges| existing_ranges.eq(matches));
|
||||||
self.highlight_background::<BufferSearchHighlights>(
|
self.highlight_background::<BufferSearchHighlights>(
|
||||||
matches,
|
matches,
|
||||||
|theme| theme.search_match_background,
|
|theme| theme.colors().search_match_background,
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
if updated {
|
if updated {
|
||||||
|
@ -1480,7 +1483,12 @@ impl SearchableItem for Editor {
|
||||||
if self.has_filtered_search_ranges() {
|
if self.has_filtered_search_ranges() {
|
||||||
self.previous_search_ranges = self
|
self.previous_search_ranges = self
|
||||||
.clear_background_highlights::<SearchWithinRange>(cx)
|
.clear_background_highlights::<SearchWithinRange>(cx)
|
||||||
.map(|(_, ranges)| ranges)
|
.map(|highlights| {
|
||||||
|
highlights
|
||||||
|
.iter()
|
||||||
|
.map(|highlight| highlight.range.clone())
|
||||||
|
.collect()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if !enabled {
|
if !enabled {
|
||||||
|
@ -1702,8 +1710,11 @@ impl SearchableItem for Editor {
|
||||||
let search_within_ranges = self
|
let search_within_ranges = self
|
||||||
.background_highlights
|
.background_highlights
|
||||||
.get(&TypeId::of::<SearchWithinRange>())
|
.get(&TypeId::of::<SearchWithinRange>())
|
||||||
.map_or(vec![], |(_color, ranges)| {
|
.map_or(vec![], |highlights| {
|
||||||
ranges.iter().cloned().collect::<Vec<_>>()
|
highlights
|
||||||
|
.iter()
|
||||||
|
.map(|highlight| highlight.range.clone())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
});
|
});
|
||||||
|
|
||||||
cx.background_spawn(async move {
|
cx.background_spawn(async move {
|
||||||
|
|
|
@ -510,10 +510,9 @@ impl EditorTestContext {
|
||||||
editor
|
editor
|
||||||
.background_highlights
|
.background_highlights
|
||||||
.get(&TypeId::of::<Tag>())
|
.get(&TypeId::of::<Tag>())
|
||||||
.map(|h| h.1.clone())
|
.into_iter()
|
||||||
.unwrap_or_default()
|
.flat_map(|highlights| highlights.as_slice())
|
||||||
.iter()
|
.map(|highlight| highlight.range.to_offset(&snapshot.buffer_snapshot))
|
||||||
.map(|range| range.to_offset(&snapshot.buffer_snapshot))
|
|
||||||
.collect()
|
.collect()
|
||||||
});
|
});
|
||||||
assert_set_eq!(actual_ranges, expected_ranges);
|
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 snapshot = self.update_editor(|editor, window, cx| editor.snapshot(window, cx));
|
||||||
let actual_ranges: Vec<Range<usize>> = snapshot
|
let actual_ranges: Vec<Range<usize>> = snapshot
|
||||||
.text_highlight_ranges::<Tag>()
|
.text_highlight_ranges::<Tag>()
|
||||||
.map(|ranges| ranges.as_ref().clone().1)
|
.map(|ranges| {
|
||||||
|
ranges
|
||||||
|
.iter()
|
||||||
|
.map(|(range, _)| range.clone())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
})
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|range| range.to_offset(&snapshot.buffer_snapshot))
|
.map(|range| range.to_offset(&snapshot.buffer_snapshot))
|
||||||
|
|
|
@ -358,7 +358,7 @@ impl Render for SyntaxTreeView {
|
||||||
editor.clear_background_highlights::<Self>( cx);
|
editor.clear_background_highlights::<Self>( cx);
|
||||||
editor.highlight_background::<Self>(
|
editor.highlight_background::<Self>(
|
||||||
&[range],
|
&[range],
|
||||||
|theme| theme.editor_document_highlight_write_background,
|
|theme| theme.colors().editor_document_highlight_write_background,
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -1547,7 +1547,7 @@ fn dap_client_capabilities(adapter_id: String) -> InitializeRequestArguments {
|
||||||
supports_memory_event: Some(false),
|
supports_memory_event: Some(false),
|
||||||
supports_args_can_be_interpreted_by_shell: Some(false),
|
supports_args_can_be_interpreted_by_shell: Some(false),
|
||||||
supports_start_debugging_request: Some(true),
|
supports_start_debugging_request: Some(true),
|
||||||
supports_ansistyling: Some(false),
|
supports_ansistyling: Some(true),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1377,7 +1377,7 @@ impl ProjectSearchView {
|
||||||
}
|
}
|
||||||
editor.highlight_background::<Self>(
|
editor.highlight_background::<Self>(
|
||||||
&match_ranges,
|
&match_ranges,
|
||||||
|theme| theme.search_match_background,
|
|theme| theme.colors().search_match_background,
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -222,7 +222,7 @@ impl Vim {
|
||||||
|
|
||||||
editor.highlight_background::<HighlightOnYank>(
|
editor.highlight_background::<HighlightOnYank>(
|
||||||
&ranges_to_highlight,
|
&ranges_to_highlight,
|
||||||
|colors| colors.editor_document_highlight_read_background,
|
|theme| theme.colors().editor_document_highlight_read_background,
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
cx.spawn(async move |this, cx| {
|
cx.spawn(async move |this, cx| {
|
||||||
|
|
|
@ -217,8 +217,8 @@ impl Vim {
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut Context<Editor>,
|
cx: &mut Context<Editor>,
|
||||||
) {
|
) {
|
||||||
if let Some((_, ranges)) = editor.clear_background_highlights::<VimExchange>(cx) {
|
if let Some(highlights) = editor.clear_background_highlights::<VimExchange>(cx) {
|
||||||
let previous_range = ranges[0].clone();
|
let previous_range = highlights[0].range.clone();
|
||||||
|
|
||||||
let new_range_start = new_range.start.to_offset(&snapshot.buffer_snapshot);
|
let new_range_start = new_range.start.to_offset(&snapshot.buffer_snapshot);
|
||||||
let new_range_end = new_range.end.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];
|
let ranges = [new_range];
|
||||||
editor.highlight_background::<VimExchange>(
|
editor.highlight_background::<VimExchange>(
|
||||||
&ranges,
|
&ranges,
|
||||||
|theme| theme.editor_document_highlight_read_background,
|
|theme| theme.colors().editor_document_highlight_read_background,
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -864,16 +864,13 @@ async fn test_jk(cx: &mut gpui::TestAppContext) {
|
||||||
fn assert_pending_input(cx: &mut VimTestContext, expected: &str) {
|
fn assert_pending_input(cx: &mut VimTestContext, expected: &str) {
|
||||||
cx.update_editor(|editor, window, cx| {
|
cx.update_editor(|editor, window, cx| {
|
||||||
let snapshot = editor.snapshot(window, cx);
|
let snapshot = editor.snapshot(window, cx);
|
||||||
let highlights = editor
|
let highlights = editor.text_highlights::<editor::PendingInput>(cx).unwrap();
|
||||||
.text_highlights::<editor::PendingInput>(cx)
|
|
||||||
.unwrap()
|
|
||||||
.1;
|
|
||||||
let (_, ranges) = marked_text_ranges(expected, false);
|
let (_, ranges) = marked_text_ranges(expected, false);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
highlights
|
highlights
|
||||||
.iter()
|
.iter()
|
||||||
.map(|highlight| highlight.to_offset(&snapshot.buffer_snapshot))
|
.map(|(highlight, _)| highlight.to_offset(&snapshot.buffer_snapshot))
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
ranges
|
ranges
|
||||||
)
|
)
|
||||||
|
@ -923,15 +920,12 @@ async fn test_jk_delay(cx: &mut gpui::TestAppContext) {
|
||||||
cx.assert_state("ˇjhello", Mode::Insert);
|
cx.assert_state("ˇjhello", Mode::Insert);
|
||||||
cx.update_editor(|editor, window, cx| {
|
cx.update_editor(|editor, window, cx| {
|
||||||
let snapshot = editor.snapshot(window, cx);
|
let snapshot = editor.snapshot(window, cx);
|
||||||
let highlights = editor
|
let highlights = editor.text_highlights::<editor::PendingInput>(cx).unwrap();
|
||||||
.text_highlights::<editor::PendingInput>(cx)
|
|
||||||
.unwrap()
|
|
||||||
.1;
|
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
highlights
|
highlights
|
||||||
.iter()
|
.iter()
|
||||||
.map(|highlight| highlight.to_offset(&snapshot.buffer_snapshot))
|
.map(|(highlight, _)| highlight.to_offset(&snapshot.buffer_snapshot))
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
vec![0..1]
|
vec![0..1]
|
||||||
)
|
)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue