highlighter: Fix #1204 changes cause to incorrect highlight result. (#1218)

This PR for fix some incorrect changes in #1204 

## Before

<img width="854" height="864" alt="image"
src="https://github.com/user-attachments/assets/eebd07e5-3437-444d-a55d-2941228e3b08"
/>

## After

<img width="954" height="962" alt="image"
src="https://github.com/user-attachments/assets/60761848-8d4f-4fe9-88b9-f0b0f0985d2f"
/>
<img width="1515" height="867" alt="image"
src="https://github.com/user-attachments/assets/3d4654a7-f977-4919-9c9e-f013fcdd8fde"
/>
This commit is contained in:
Jason Lee 2025-09-08 16:14:07 +08:00 committed by GitHub
parent 78dccd4d79
commit a9e6b49a15
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 99 additions and 50 deletions

View file

@ -49,11 +49,19 @@ impl Lang {
} }
} }
const LANGUAGES: [(Lang, &'static str); 10] = [ const LANGUAGES: [(Lang, &'static str); 12] = [
( (
Lang::BuiltIn(Language::Rust), Lang::BuiltIn(Language::Rust),
include_str!("./fixtures/test.rs"), include_str!("./fixtures/test.rs"),
), ),
(
Lang::BuiltIn(Language::Markdown),
include_str!("./fixtures/test.md"),
),
(
Lang::BuiltIn(Language::Html),
include_str!("./fixtures/test.html"),
),
( (
Lang::BuiltIn(Language::JavaScript), Lang::BuiltIn(Language::JavaScript),
include_str!("./fixtures/test.js"), include_str!("./fixtures/test.js"),
@ -90,8 +98,17 @@ const LANGUAGES: [(Lang, &'static str); 10] = [
]; ];
impl Example { impl Example {
pub fn new(window: &mut Window, cx: &mut Context<Self>) -> Self { pub fn new(default: Option<String>, window: &mut Window, cx: &mut Context<Self>) -> Self {
let default_language = LANGUAGES[0].clone(); let default_language = if let Some(name) = default {
LANGUAGES
.iter()
.find(|s| s.0.name().starts_with(name.trim()))
.cloned()
.unwrap_or(LANGUAGES[0].clone())
} else {
LANGUAGES[0].clone()
};
let editor = cx.new(|cx| { let editor = cx.new(|cx| {
InputState::new(window, cx) InputState::new(window, cx)
.code_editor(default_language.0.name().to_string()) .code_editor(default_language.0.name().to_string())
@ -147,10 +164,6 @@ impl Example {
} }
} }
fn view(window: &mut Window, cx: &mut App) -> Entity<Self> {
cx.new(|cx| Self::new(window, cx))
}
fn set_markers(&mut self, window: &mut Window, cx: &mut Context<Self>) { fn set_markers(&mut self, window: &mut Window, cx: &mut Context<Self>) {
if self.language.name() != "rust" { if self.language.name() != "rust" {
return; return;
@ -310,11 +323,18 @@ impl Render for Example {
fn main() { fn main() {
let app = Application::new().with_assets(Assets); let app = Application::new().with_assets(Assets);
// Parse `cargo run -- <story_name>`
let name = std::env::args().nth(1);
app.run(move |cx| { app.run(move |cx| {
story::init(cx); story::init(cx);
init(cx); init(cx);
cx.activate(true); cx.activate(true);
story::create_new_window("Code Editor", Example::view, cx); story::create_new_window(
"Code Editor",
|window, cx| cx.new(|cx| Example::new(name, window, cx)),
cx,
);
}); });
} }

View file

@ -579,7 +579,6 @@ impl SyntaxHighlighter {
}); });
filter.next(); filter.next();
let mut last_range = start_offset..start_offset;
// let mut iter_count = 0; // let mut iter_count = 0;
while let Some(item) = filter.item() { while let Some(item) = filter.item() {
// iter_count += 1; // iter_count += 1;
@ -592,18 +591,7 @@ impl SyntaxHighlighter {
node_range.end = node_range.start; node_range.end = node_range.start;
} }
// Ensure every range is connected. styles.push((node_range, theme.style(name.as_ref()).unwrap_or_default()));
if last_range.end < node_range.start {
styles.push((last_range.end..node_range.start, HighlightStyle::default()));
}
let start = node_range.start.max(last_range.end);
styles.push((
start..node_range.end,
theme.style(name.as_ref()).unwrap_or_default(),
));
last_range = node_range;
filter.next(); filter.next();
} }
// dbg!(iter_count); // dbg!(iter_count);
@ -613,12 +601,7 @@ impl SyntaxHighlighter {
return vec![(start_offset..range.end, HighlightStyle::default())]; return vec![(start_offset..range.end, HighlightStyle::default())];
} }
// Ensure the last range is connected to the end of the line. let styles = unique_styles(&range, styles);
if last_range.end < range.end {
styles.push((last_range.end..range.end, HighlightStyle::default()));
}
let styles = unique_styles(styles);
// NOTE: DO NOT remove this comment, it is used for debugging. // NOTE: DO NOT remove this comment, it is used for debugging.
// for style in &styles { // for style in &styles {
@ -645,44 +628,58 @@ impl SyntaxHighlighter {
/// ///
/// AABCCDDCEEEE /// AABCCDDCEEEE
pub(crate) fn unique_styles( pub(crate) fn unique_styles(
total_range: &Range<usize>,
styles: Vec<(Range<usize>, HighlightStyle)>, styles: Vec<(Range<usize>, HighlightStyle)>,
) -> Vec<(Range<usize>, HighlightStyle)> { ) -> Vec<(Range<usize>, HighlightStyle)> {
if styles.is_empty() { if styles.is_empty() {
return styles; return styles;
} }
// Collect all boundary points and track which are "significant" (range endpoints) let mut intervals = BTreeSet::new();
let mut boundaries = BTreeSet::new(); let mut significant_intervals = BTreeSet::new();
let mut significant_boundaries = BTreeSet::new();
// For example
//
// from: [(6..11), (6..11), (11..17), (17..25), (16..19), (25..59))]
// to: [6, 11, 16, 17, 19, 25, 59]
intervals.insert(total_range.start);
intervals.insert(total_range.end);
for (range, _) in &styles { for (range, _) in &styles {
boundaries.insert(range.start); intervals.insert(range.start);
boundaries.insert(range.end); intervals.insert(range.end);
significant_boundaries.insert(range.end); // End points are significant for merging decisions significant_intervals.insert(range.end); // End points are significant for merging decisions
} }
let boundaries: Vec<usize> = boundaries.into_iter().collect(); let intervals: Vec<usize> = intervals.into_iter().collect();
let mut result = Vec::with_capacity(boundaries.len().saturating_sub(1)); let mut result = Vec::with_capacity(intervals.len().saturating_sub(1));
// For each interval between boundaries, find the top-most style // For each interval between boundaries, find the top-most style
for i in 0..boundaries.len().saturating_sub(1) { //
let interval_start = boundaries[i]; // Result e.g.:
let interval_end = boundaries[i + 1]; //
// [(6..11, red), (11..16, green), (16..17, blue), (17..19, red), (19..25, clean), (25..59, blue)]
if interval_start >= interval_end { for i in 0..intervals.len().saturating_sub(1) {
let interval = intervals[i]..intervals[i + 1];
if interval.start >= interval.end {
continue; continue;
} }
// Find the last (top-most) style that covers this interval // Find the last (top-most) style that covers this interval
let mut top_style: Option<&HighlightStyle> = None; let mut top_style: Option<HighlightStyle> = None;
for (range, style) in &styles { for (range, style) in &styles {
if range.start <= interval_start && interval_end <= range.end { if range.start <= interval.start && interval.end <= range.end {
top_style = Some(style); if let Some(top_style) = &mut top_style {
merge_highlight_style(top_style, style);
} else {
top_style = Some(*style);
}
} }
} }
if let Some(style) = top_style { if let Some(style) = top_style {
result.push((interval_start..interval_end, *style)); result.push((interval, style));
} else {
result.push((interval, HighlightStyle::default()));
} }
} }
@ -692,7 +689,7 @@ pub(crate) fn unique_styles(
if let Some((last_range, last_style)) = merged.last_mut() { if let Some((last_range, last_style)) = merged.last_mut() {
if last_range.end == range.start if last_range.end == range.start
&& *last_style == style && *last_style == style
&& !significant_boundaries.contains(&range.start) && !significant_intervals.contains(&range.start)
{ {
// Merge adjacent ranges with same style, but not across significant boundaries // Merge adjacent ranges with same style, but not across significant boundaries
last_range.end = range.end; last_range.end = range.end;
@ -705,6 +702,31 @@ pub(crate) fn unique_styles(
merged merged
} }
/// Merge other style (Other on top)
fn merge_highlight_style(style: &mut HighlightStyle, other: &HighlightStyle) {
if let Some(color) = other.color {
style.color = Some(color);
}
if let Some(font_weight) = other.font_weight {
style.font_weight = Some(font_weight);
}
if let Some(font_style) = other.font_style {
style.font_style = Some(font_style);
}
if let Some(background_color) = other.background_color {
style.background_color = Some(background_color);
}
if let Some(underline) = other.underline {
style.underline = Some(underline);
}
if let Some(strikethrough) = other.strikethrough {
style.strikethrough = Some(strikethrough);
}
if let Some(fade_out) = other.fade_out {
style.fade_out = Some(fade_out);
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use gpui::Hsla; use gpui::Hsla;
@ -720,6 +742,7 @@ mod tests {
#[track_caller] #[track_caller]
fn assert_unique_styles( fn assert_unique_styles(
range: Range<usize>,
left: Vec<(Range<usize>, HighlightStyle)>, left: Vec<(Range<usize>, HighlightStyle)>,
right: Vec<(Range<usize>, HighlightStyle)>, right: Vec<(Range<usize>, HighlightStyle)>,
) { ) {
@ -740,7 +763,7 @@ mod tests {
} }
} }
let left = unique_styles(left); let left = unique_styles(&range, left);
if left.len() != right.len() { if left.len() != right.len() {
println!("\n---------------------------------------------"); println!("\n---------------------------------------------");
for (range, style) in left.iter() { for (range, style) in left.iter() {
@ -770,18 +793,21 @@ mod tests {
let clean = HighlightStyle::default(); let clean = HighlightStyle::default();
assert_unique_styles( assert_unique_styles(
0..65,
vec![ vec![
(0..10, clean), (2..10, clean),
(0..10, clean), (2..10, clean),
(5..11, red), (5..11, red),
(0..6, clean), (2..6, clean),
(10..15, green), (10..15, green),
(15..30, clean), (15..30, clean),
(29..35, blue), (29..35, blue),
(35..40, green), (35..40, green),
(45..60, blue),
], ],
vec![ vec![
(0..6, clean), (0..5, clean),
(5..6, red),
(6..10, red), (6..10, red),
(10..11, green), (10..11, green),
(11..15, green), (11..15, green),
@ -789,6 +815,9 @@ mod tests {
(29..30, blue), (29..30, blue),
(30..35, blue), (30..35, blue),
(35..40, green), (35..40, green),
(40..45, clean),
(45..60, blue),
(60..65, clean),
], ],
); );
} }