@@ -1276,11 +1276,16 @@ impl CompletionsMenu {
&None
};
- let highlights = combine_syntax_and_fuzzy_match_highlights(
- &completion.label.text,
- &style.text,
- styled_runs_for_code_label(&completion.label, &style.syntax),
- &mat.positions,
+ let highlights = gpui::combine_highlights(
+ mat.ranges().map(|range| (range, FontWeight::BOLD.into())),
+ styled_runs_for_code_label(&completion.label, &style.syntax).map(
+ |(range, mut highlight)| {
+ // Ignore font weight for syntax highlighting, as we'll use it
+ // for fuzzy matches.
+ highlight.font_weight = None;
+ (range, highlight)
+ },
+ ),
);
let completion_label = StyledText::new(completion.label.text.clone())
.with_runs(text_runs_for_highlights(
@@ -10056,75 +10061,6 @@ pub fn text_runs_for_highlights(
runs
}
-pub fn combine_syntax_and_fuzzy_match_highlights(
- text: &str,
- default_style: &TextStyle,
- syntax_ranges: impl Iterator<Item = (Range<usize>, HighlightStyle)>,
- match_indices: &[usize],
-) -> Vec<(Range<usize>, HighlightStyle)> {
- let mut highlights = Vec::new();
- let mut match_indices = match_indices.iter().copied().peekable();
-
- for (range, mut syntax_highlight) in syntax_ranges.chain([(usize::MAX..0, Default::default())])
- {
- syntax_highlight.font_weight = None;
-
- // Add highlights for any fuzzy match characters before the next
- // syntax highlight range.
- while let Some(&match_index) = match_indices.peek() {
- if match_index >= range.start {
- break;
- }
- match_indices.next();
- let end_index = char_ix_after(match_index, text);
- highlights.push((match_index..end_index, FontWeight::BOLD.into()));
- }
-
- if range.start == usize::MAX {
- break;
- }
-
- // Add highlights for any fuzzy match characters within the
- // syntax highlight range.
- let mut offset = range.start;
- while let Some(&match_index) = match_indices.peek() {
- if match_index >= range.end {
- break;
- }
-
- match_indices.next();
- if match_index > offset {
- highlights.push((offset..match_index, syntax_highlight));
- }
-
- let mut end_index = char_ix_after(match_index, text);
- while let Some(&next_match_index) = match_indices.peek() {
- if next_match_index == end_index && next_match_index < range.end {
- end_index = char_ix_after(next_match_index, text);
- match_indices.next();
- } else {
- break;
- }
- }
-
- let mut match_style = syntax_highlight;
- match_style.font_weight = Some(FontWeight::BOLD);
- highlights.push((match_index..end_index, match_style));
- offset = end_index;
- }
-
- if offset < range.end {
- highlights.push((offset..range.end, syntax_highlight));
- }
- }
-
- fn char_ix_after(ix: usize, text: &str) -> usize {
- ix + text[ix..].chars().next().unwrap().len_utf8()
- }
-
- highlights
-}
-
pub fn styled_runs_for_code_label<'a>(
label: &'a CodeLabel,
syntax_theme: &'a theme::SyntaxTheme,
@@ -6740,75 +6740,6 @@ async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
// );
// }
-#[test]
-fn test_combine_syntax_and_fuzzy_match_highlights() {
- let string = "abcdefghijklmnop";
- let syntax_ranges = [
- (
- 0..3,
- HighlightStyle {
- color: Some(Hsla::red()),
- ..Default::default()
- },
- ),
- (
- 4..8,
- HighlightStyle {
- color: Some(Hsla::green()),
- ..Default::default()
- },
- ),
- ];
- let match_indices = [4, 6, 7, 8];
- assert_eq!(
- combine_syntax_and_fuzzy_match_highlights(
- string,
- &TextStyle::default(),
- syntax_ranges.into_iter(),
- &match_indices,
- ),
- &[
- (
- 0..3,
- HighlightStyle {
- color: Some(Hsla::red()),
- ..Default::default()
- },
- ),
- (
- 4..5,
- HighlightStyle {
- color: Some(Hsla::green()),
- font_weight: Some(gpui::FontWeight::BOLD),
- ..Default::default()
- },
- ),
- (
- 5..6,
- HighlightStyle {
- color: Some(Hsla::green()),
- ..Default::default()
- },
- ),
- (
- 6..8,
- HighlightStyle {
- color: Some(Hsla::green()),
- font_weight: Some(gpui::FontWeight::BOLD),
- ..Default::default()
- },
- ),
- (
- 8..9,
- HighlightStyle {
- font_weight: Some(gpui::FontWeight::BOLD),
- ..Default::default()
- },
- ),
- ]
- );
-}
-
#[gpui::test]
async fn go_to_prev_overlapping_diagnostic(
executor: BackgroundExecutor,
@@ -1,9 +1,12 @@
+use std::{iter, mem, ops::Range};
+
use crate::{
black, phi, point, rems, AbsoluteLength, BorrowAppContext, BorrowWindow, Bounds, ContentMask,
Corners, CornersRefinement, CursorStyle, DefiniteLength, Edges, EdgesRefinement, Font,
FontFeatures, FontStyle, FontWeight, Hsla, Length, Pixels, Point, PointRefinement, Rgba,
SharedString, Size, SizeRefinement, Styled, TextRun, WindowContext,
};
+use collections::HashSet;
use refineable::{Cascade, Refineable};
use smallvec::SmallVec;
pub use taffy::style::{
@@ -512,6 +515,15 @@ impl From<FontWeight> for HighlightStyle {
}
}
+impl From<FontStyle> for HighlightStyle {
+ fn from(font_style: FontStyle) -> Self {
+ Self {
+ font_style: Some(font_style),
+ ..Default::default()
+ }
+ }
+}
+
impl From<Rgba> for HighlightStyle {
fn from(color: Rgba) -> Self {
Self {
@@ -520,3 +532,140 @@ impl From<Rgba> for HighlightStyle {
}
}
}
+
+pub fn combine_highlights(
+ a: impl IntoIterator<Item = (Range<usize>, HighlightStyle)>,
+ b: impl IntoIterator<Item = (Range<usize>, HighlightStyle)>,
+) -> impl Iterator<Item = (Range<usize>, HighlightStyle)> {
+ let mut endpoints = Vec::new();
+ let mut highlights = Vec::new();
+ for (range, highlight) in a.into_iter().chain(b) {
+ if !range.is_empty() {
+ let highlight_id = highlights.len();
+ endpoints.push((range.start, highlight_id, true));
+ endpoints.push((range.end, highlight_id, false));
+ highlights.push(highlight);
+ }
+ }
+ endpoints.sort_unstable_by_key(|(position, _, _)| *position);
+ let mut endpoints = endpoints.into_iter().peekable();
+
+ let mut active_styles = HashSet::default();
+ let mut ix = 0;
+ iter::from_fn(move || {
+ while let Some((endpoint_ix, highlight_id, is_start)) = endpoints.peek() {
+ let prev_index = mem::replace(&mut ix, *endpoint_ix);
+ if ix > prev_index && !active_styles.is_empty() {
+ let mut current_style = HighlightStyle::default();
+ for highlight_id in &active_styles {
+ current_style.highlight(highlights[*highlight_id]);
+ }
+ return Some((prev_index..ix, current_style));
+ }
+
+ if *is_start {
+ active_styles.insert(*highlight_id);
+ } else {
+ active_styles.remove(highlight_id);
+ }
+ endpoints.next();
+ }
+ None
+ })
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::{blue, green, red, yellow};
+
+ use super::*;
+
+ #[test]
+ fn test_combine_highlights() {
+ assert_eq!(
+ combine_highlights(
+ [
+ (0..5, green().into()),
+ (4..10, FontWeight::BOLD.into()),
+ (15..20, yellow().into()),
+ ],
+ [
+ (2..6, FontStyle::Italic.into()),
+ (1..3, blue().into()),
+ (21..23, red().into()),
+ ]
+ )
+ .collect::<Vec<_>>(),
+ [
+ (
+ 0..1,
+ HighlightStyle {
+ color: Some(green()),
+ ..Default::default()
+ }
+ ),
+ (
+ 1..2,
+ HighlightStyle {
+ color: Some(blue()),
+ ..Default::default()
+ }
+ ),
+ (
+ 2..3,
+ HighlightStyle {
+ color: Some(blue()),
+ font_style: Some(FontStyle::Italic),
+ ..Default::default()
+ }
+ ),
+ (
+ 3..4,
+ HighlightStyle {
+ color: Some(green()),
+ font_style: Some(FontStyle::Italic),
+ ..Default::default()
+ }
+ ),
+ (
+ 4..5,
+ HighlightStyle {
+ color: Some(green()),
+ font_weight: Some(FontWeight::BOLD),
+ font_style: Some(FontStyle::Italic),
+ ..Default::default()
+ }
+ ),
+ (
+ 5..6,
+ HighlightStyle {
+ font_weight: Some(FontWeight::BOLD),
+ font_style: Some(FontStyle::Italic),
+ ..Default::default()
+ }
+ ),
+ (
+ 6..10,
+ HighlightStyle {
+ font_weight: Some(FontWeight::BOLD),
+ ..Default::default()
+ }
+ ),
+ (
+ 15..20,
+ HighlightStyle {
+ color: Some(yellow()),
+ ..Default::default()
+ }
+ ),
+ (
+ 21..23,
+ HighlightStyle {
+ color: Some(red()),
+ ..Default::default()
+ }
+ )
+ ]
+ );
+ }
+}