Rework `SuggestionMap` to take highlight style when retrieving chunks

Antonio Scandurra created

Change summary

crates/editor/src/display_map.rs                | 21 ++++-
crates/editor/src/display_map/block_map.rs      |  7 +
crates/editor/src/display_map/suggestion_map.rs | 24 +++---
crates/editor/src/display_map/tab_map.rs        | 18 ++++-
crates/editor/src/display_map/wrap_map.rs       | 12 ++-
crates/editor/src/editor.rs                     | 12 ---
crates/editor/src/element.rs                    | 66 +++++++++---------
crates/theme/src/theme.rs                       |  1 
styles/src/styleTree/editor.ts                  |  3 
9 files changed, 93 insertions(+), 71 deletions(-)

Detailed changes

crates/editor/src/display_map.rs 🔗

@@ -382,7 +382,7 @@ impl DisplaySnapshot {
     /// Returns text chunks starting at the given display row until the end of the file
     pub fn text_chunks(&self, display_row: u32) -> impl Iterator<Item = &str> {
         self.block_snapshot
-            .chunks(display_row..self.max_point().row() + 1, false, None)
+            .chunks(display_row..self.max_point().row() + 1, false, None, None)
             .map(|h| h.text)
     }
 
@@ -390,7 +390,7 @@ impl DisplaySnapshot {
     pub fn reverse_text_chunks(&self, display_row: u32) -> impl Iterator<Item = &str> {
         (0..=display_row).into_iter().rev().flat_map(|row| {
             self.block_snapshot
-                .chunks(row..row + 1, false, None)
+                .chunks(row..row + 1, false, None, None)
                 .map(|h| h.text)
                 .collect::<Vec<_>>()
                 .into_iter()
@@ -398,9 +398,18 @@ impl DisplaySnapshot {
         })
     }
 
-    pub fn chunks(&self, display_rows: Range<u32>, language_aware: bool) -> DisplayChunks<'_> {
-        self.block_snapshot
-            .chunks(display_rows, language_aware, Some(&self.text_highlights))
+    pub fn chunks(
+        &self,
+        display_rows: Range<u32>,
+        language_aware: bool,
+        suggestion_highlight: Option<HighlightStyle>,
+    ) -> DisplayChunks<'_> {
+        self.block_snapshot.chunks(
+            display_rows,
+            language_aware,
+            Some(&self.text_highlights),
+            suggestion_highlight,
+        )
     }
 
     pub fn chars_at(
@@ -1691,7 +1700,7 @@ pub mod tests {
     ) -> Vec<(String, Option<Color>, Option<Color>)> {
         let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
         let mut chunks: Vec<(String, Option<Color>, Option<Color>)> = Vec::new();
-        for chunk in snapshot.chunks(rows, true) {
+        for chunk in snapshot.chunks(rows, true, None) {
             let syntax_color = chunk
                 .syntax_highlight_id
                 .and_then(|id| id.style(theme)?.color);

crates/editor/src/display_map/block_map.rs 🔗

@@ -4,7 +4,7 @@ use super::{
 };
 use crate::{Anchor, ExcerptId, ExcerptRange, ToPoint as _};
 use collections::{Bound, HashMap, HashSet};
-use gpui::{ElementBox, RenderContext};
+use gpui::{fonts::HighlightStyle, ElementBox, RenderContext};
 use language::{BufferSnapshot, Chunk, Patch, Point};
 use parking_lot::Mutex;
 use std::{
@@ -572,7 +572,7 @@ impl<'a> BlockMapWriter<'a> {
 impl BlockSnapshot {
     #[cfg(test)]
     pub fn text(&self) -> String {
-        self.chunks(0..self.transforms.summary().output_rows, false, None)
+        self.chunks(0..self.transforms.summary().output_rows, false, None, None)
             .map(|chunk| chunk.text)
             .collect()
     }
@@ -582,6 +582,7 @@ impl BlockSnapshot {
         rows: Range<u32>,
         language_aware: bool,
         text_highlights: Option<&'a TextHighlights>,
+        suggestion_highlight: Option<HighlightStyle>,
     ) -> BlockChunks<'a> {
         let max_output_row = cmp::min(rows.end, self.transforms.summary().output_rows);
         let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>();
@@ -614,6 +615,7 @@ impl BlockSnapshot {
                 input_start..input_end,
                 language_aware,
                 text_highlights,
+                suggestion_highlight,
             ),
             input_chunk: Default::default(),
             transforms: cursor,
@@ -1498,6 +1500,7 @@ mod tests {
                         start_row as u32..blocks_snapshot.max_point().row + 1,
                         false,
                         None,
+                        None,
                     )
                     .map(|chunk| chunk.text)
                     .collect::<String>();

crates/editor/src/display_map/suggestion_map.rs 🔗

@@ -60,7 +60,6 @@ impl SuggestionPoint {
 pub struct Suggestion<T> {
     pub position: T,
     pub text: Rope,
-    pub highlight_style: HighlightStyle,
 }
 
 pub struct SuggestionMap(Mutex<SuggestionSnapshot>);
@@ -93,7 +92,6 @@ impl SuggestionMap {
             Suggestion {
                 position: fold_offset,
                 text: new_suggestion.text,
-                highlight_style: new_suggestion.highlight_style,
             }
         });
 
@@ -369,7 +367,7 @@ impl SuggestionSnapshot {
 
     pub fn chars_at(&self, start: SuggestionPoint) -> impl '_ + Iterator<Item = char> {
         let start = self.to_offset(start);
-        self.chunks(start..self.len(), false, None)
+        self.chunks(start..self.len(), false, None, None)
             .flat_map(|chunk| chunk.text.chars())
     }
 
@@ -378,6 +376,7 @@ impl SuggestionSnapshot {
         range: Range<SuggestionOffset>,
         language_aware: bool,
         text_highlights: Option<&'a TextHighlights>,
+        suggestion_highlight: Option<HighlightStyle>,
     ) -> SuggestionChunks<'a> {
         if let Some(suggestion) = self.suggestion.as_ref() {
             let suggestion_range =
@@ -421,7 +420,7 @@ impl SuggestionSnapshot {
                 prefix_chunks,
                 suggestion_chunks,
                 suffix_chunks,
-                highlight_style: suggestion.highlight_style,
+                highlight_style: suggestion_highlight,
             }
         } else {
             SuggestionChunks {
@@ -432,7 +431,7 @@ impl SuggestionSnapshot {
                 )),
                 suggestion_chunks: None,
                 suffix_chunks: None,
-                highlight_style: Default::default(),
+                highlight_style: None,
             }
         }
     }
@@ -467,7 +466,7 @@ impl SuggestionSnapshot {
 
     #[cfg(test)]
     pub fn text(&self) -> String {
-        self.chunks(Default::default()..self.len(), false, None)
+        self.chunks(Default::default()..self.len(), false, None, None)
             .map(|chunk| chunk.text)
             .collect()
     }
@@ -477,7 +476,7 @@ pub struct SuggestionChunks<'a> {
     prefix_chunks: Option<FoldChunks<'a>>,
     suggestion_chunks: Option<text::Chunks<'a>>,
     suffix_chunks: Option<FoldChunks<'a>>,
-    highlight_style: HighlightStyle,
+    highlight_style: Option<HighlightStyle>,
 }
 
 impl<'a> Iterator for SuggestionChunks<'a> {
@@ -497,7 +496,7 @@ impl<'a> Iterator for SuggestionChunks<'a> {
                 return Some(Chunk {
                     text: chunk,
                     syntax_highlight_id: None,
-                    highlight_style: Some(self.highlight_style),
+                    highlight_style: self.highlight_style,
                     diagnostic_severity: None,
                     is_unnecessary: false,
                 });
@@ -563,7 +562,6 @@ mod tests {
             Some(Suggestion {
                 position: 3,
                 text: "123\n456".into(),
-                highlight_style: Default::default(),
             }),
             fold_snapshot,
             Default::default(),
@@ -692,7 +690,12 @@ mod tests {
                 start = expected_text.clip_offset(start, Bias::Right);
 
                 let actual_text = suggestion_snapshot
-                    .chunks(SuggestionOffset(start)..SuggestionOffset(end), false, None)
+                    .chunks(
+                        SuggestionOffset(start)..SuggestionOffset(end),
+                        false,
+                        None,
+                        None,
+                    )
                     .map(|chunk| chunk.text)
                     .collect::<String>();
                 assert_eq!(
@@ -816,7 +819,6 @@ mod tests {
                         .collect::<String>()
                         .as_str()
                         .into(),
-                    highlight_style: Default::default(),
                 })
             };
 

crates/editor/src/display_map/tab_map.rs 🔗

@@ -3,6 +3,7 @@ use super::{
     TextHighlights,
 };
 use crate::MultiBufferSnapshot;
+use gpui::fonts::HighlightStyle;
 use language::{Chunk, Point};
 use parking_lot::Mutex;
 use std::{cmp, mem, num::NonZeroU32, ops::Range};
@@ -47,6 +48,7 @@ impl TabMap {
                     suggestion_edit.old.end..old_max_offset,
                     false,
                     None,
+                    None,
                 ) {
                     let patterns: &[_] = &['\t', '\n'];
                     if let Some(ix) = chunk.text.find(patterns) {
@@ -126,6 +128,7 @@ impl TabSnapshot {
                 TabPoint::new(row, 0)..TabPoint::new(row + 1, 0),
                 false,
                 None,
+                None,
             )
             .map(|chunk| chunk.text.len() as u32)
             .sum::<u32>()
@@ -153,7 +156,7 @@ impl TabSnapshot {
             self.max_point()
         };
         for c in self
-            .chunks(range.start..line_end, false, None)
+            .chunks(range.start..line_end, false, None, None)
             .flat_map(|chunk| chunk.text.chars())
         {
             if c == '\n' {
@@ -167,7 +170,12 @@ impl TabSnapshot {
             last_line_chars = first_line_chars;
         } else {
             for _ in self
-                .chunks(TabPoint::new(range.end.row(), 0)..range.end, false, None)
+                .chunks(
+                    TabPoint::new(range.end.row(), 0)..range.end,
+                    false,
+                    None,
+                    None,
+                )
                 .flat_map(|chunk| chunk.text.chars())
             {
                 last_line_chars += 1;
@@ -188,6 +196,7 @@ impl TabSnapshot {
         range: Range<TabPoint>,
         language_aware: bool,
         text_highlights: Option<&'a TextHighlights>,
+        suggestion_highlight: Option<HighlightStyle>,
     ) -> TabChunks<'a> {
         let (input_start, expanded_char_column, to_next_stop) =
             self.to_suggestion_point(range.start, Bias::Left);
@@ -206,6 +215,7 @@ impl TabSnapshot {
                 input_start..input_end,
                 language_aware,
                 text_highlights,
+                suggestion_highlight,
             ),
             column: expanded_char_column,
             output_position: range.start.0,
@@ -225,7 +235,7 @@ impl TabSnapshot {
 
     #[cfg(test)]
     pub fn text(&self) -> String {
-        self.chunks(TabPoint::zero()..self.max_point(), false, None)
+        self.chunks(TabPoint::zero()..self.max_point(), false, None, None)
             .map(|chunk| chunk.text)
             .collect()
     }
@@ -574,7 +584,7 @@ mod tests {
             assert_eq!(
                 expected_text,
                 tabs_snapshot
-                    .chunks(start..end, false, None)
+                    .chunks(start..end, false, None, None)
                     .map(|c| c.text)
                     .collect::<String>(),
                 "chunks({:?}..{:?})",

crates/editor/src/display_map/wrap_map.rs 🔗

@@ -5,8 +5,9 @@ use super::{
 };
 use crate::MultiBufferSnapshot;
 use gpui::{
-    fonts::FontId, text_layout::LineWrapper, Entity, ModelContext, ModelHandle, MutableAppContext,
-    Task,
+    fonts::{FontId, HighlightStyle},
+    text_layout::LineWrapper,
+    Entity, ModelContext, ModelHandle, MutableAppContext, Task,
 };
 use language::{Chunk, Point};
 use lazy_static::lazy_static;
@@ -444,6 +445,7 @@ impl WrapSnapshot {
                     TabPoint::new(edit.new_rows.start, 0)..new_tab_snapshot.max_point(),
                     false,
                     None,
+                    None,
                 );
                 let mut edit_transforms = Vec::<Transform>::new();
                 for _ in edit.new_rows.start..edit.new_rows.end {
@@ -573,6 +575,7 @@ impl WrapSnapshot {
         rows: Range<u32>,
         language_aware: bool,
         text_highlights: Option<&'a TextHighlights>,
+        suggestion_highlight: Option<HighlightStyle>,
     ) -> WrapChunks<'a> {
         let output_start = WrapPoint::new(rows.start, 0);
         let output_end = WrapPoint::new(rows.end, 0);
@@ -590,6 +593,7 @@ impl WrapSnapshot {
                 input_start..input_end,
                 language_aware,
                 text_highlights,
+                suggestion_highlight,
             ),
             input_chunk: Default::default(),
             output_position: output_start,
@@ -1315,7 +1319,7 @@ mod tests {
         }
 
         pub fn text_chunks(&self, wrap_row: u32) -> impl Iterator<Item = &str> {
-            self.chunks(wrap_row..self.max_point().row() + 1, false, None)
+            self.chunks(wrap_row..self.max_point().row() + 1, false, None, None)
                 .map(|h| h.text)
         }
 
@@ -1339,7 +1343,7 @@ mod tests {
                 }
 
                 let actual_text = self
-                    .chunks(start_row..end_row, true, None)
+                    .chunks(start_row..end_row, true, None, None)
                     .map(|c| c.text)
                     .collect::<String>();
                 assert_eq!(

crates/editor/src/editor.rs 🔗

@@ -2753,10 +2753,6 @@ impl Editor {
                                 Some(Suggestion {
                                     position,
                                     text: completion.text.as_str().into(),
-                                    highlight_style: HighlightStyle {
-                                        color: Some(Color::from_u32(0x777777ff)),
-                                        ..Default::default()
-                                    },
                                 }),
                                 cx,
                             )
@@ -2779,10 +2775,6 @@ impl Editor {
                                     Some(Suggestion {
                                         position,
                                         text: completion.text.as_str().into(),
-                                        highlight_style: HighlightStyle {
-                                            color: Some(Color::from_u32(0x777777ff)),
-                                            ..Default::default()
-                                        },
                                     }),
                                     cx,
                                 )
@@ -2813,10 +2805,6 @@ impl Editor {
                     Some(Suggestion {
                         position: self.copilot_state.position,
                         text: completion.text.as_str().into(),
-                        highlight_style: HighlightStyle {
-                            color: Some(Color::from_u32(0x777777ff)),
-                            ..Default::default()
-                        },
                     }),
                     cx,
                 )

crates/editor/src/element.rs 🔗

@@ -1318,45 +1318,47 @@ impl EditorElement {
                 .collect()
         } else {
             let style = &self.style;
-            let chunks = snapshot.chunks(rows.clone(), true).map(|chunk| {
-                let mut highlight_style = chunk
-                    .syntax_highlight_id
-                    .and_then(|id| id.style(&style.syntax));
-
-                if let Some(chunk_highlight) = chunk.highlight_style {
-                    if let Some(highlight_style) = highlight_style.as_mut() {
-                        highlight_style.highlight(chunk_highlight);
-                    } else {
-                        highlight_style = Some(chunk_highlight);
+            let chunks = snapshot
+                .chunks(rows.clone(), true, Some(style.theme.suggestion))
+                .map(|chunk| {
+                    let mut highlight_style = chunk
+                        .syntax_highlight_id
+                        .and_then(|id| id.style(&style.syntax));
+
+                    if let Some(chunk_highlight) = chunk.highlight_style {
+                        if let Some(highlight_style) = highlight_style.as_mut() {
+                            highlight_style.highlight(chunk_highlight);
+                        } else {
+                            highlight_style = Some(chunk_highlight);
+                        }
                     }
-                }
 
-                let mut diagnostic_highlight = HighlightStyle::default();
+                    let mut diagnostic_highlight = HighlightStyle::default();
 
-                if chunk.is_unnecessary {
-                    diagnostic_highlight.fade_out = Some(style.unnecessary_code_fade);
-                }
+                    if chunk.is_unnecessary {
+                        diagnostic_highlight.fade_out = Some(style.unnecessary_code_fade);
+                    }
 
-                if let Some(severity) = chunk.diagnostic_severity {
-                    // Omit underlines for HINT/INFO diagnostics on 'unnecessary' code.
-                    if severity <= DiagnosticSeverity::WARNING || !chunk.is_unnecessary {
-                        let diagnostic_style = super::diagnostic_style(severity, true, style);
-                        diagnostic_highlight.underline = Some(Underline {
-                            color: Some(diagnostic_style.message.text.color),
-                            thickness: 1.0.into(),
-                            squiggly: true,
-                        });
+                    if let Some(severity) = chunk.diagnostic_severity {
+                        // Omit underlines for HINT/INFO diagnostics on 'unnecessary' code.
+                        if severity <= DiagnosticSeverity::WARNING || !chunk.is_unnecessary {
+                            let diagnostic_style = super::diagnostic_style(severity, true, style);
+                            diagnostic_highlight.underline = Some(Underline {
+                                color: Some(diagnostic_style.message.text.color),
+                                thickness: 1.0.into(),
+                                squiggly: true,
+                            });
+                        }
                     }
-                }
 
-                if let Some(highlight_style) = highlight_style.as_mut() {
-                    highlight_style.highlight(diagnostic_highlight);
-                } else {
-                    highlight_style = Some(diagnostic_highlight);
-                }
+                    if let Some(highlight_style) = highlight_style.as_mut() {
+                        highlight_style.highlight(diagnostic_highlight);
+                    } else {
+                        highlight_style = Some(diagnostic_highlight);
+                    }
 
-                (chunk.text, highlight_style)
-            });
+                    (chunk.text, highlight_style)
+                });
             layout_highlighted_chunks(
                 chunks,
                 &style.text,

crates/theme/src/theme.rs 🔗

@@ -573,6 +573,7 @@ pub struct Editor {
     pub line_number_active: Color,
     pub guest_selections: Vec<SelectionStyle>,
     pub syntax: Arc<SyntaxTheme>,
+    pub suggestion: HighlightStyle,
     pub diagnostic_path_header: DiagnosticPathHeader,
     pub diagnostic_header: DiagnosticHeader,
     pub error_diagnostic: DiagnosticStyle,

styles/src/styleTree/editor.ts 🔗

@@ -43,6 +43,9 @@ export default function editor(colorScheme: ColorScheme) {
         background: background(layer),
         activeLineBackground: withOpacity(background(layer, "on"), 0.75),
         highlightedLineBackground: background(layer, "on"),
+        suggestion: {
+          color: foreground(layer, "disabled")
+        },
         codeActions: {
             indicator: {
                 color: foreground(layer, "variant"),