Revert "Show invisibles in editor (#19298)"

Conrad Irwin created

This reverts commit 6dcec47235fa85f0e416b9230e2fedc61de510ee.

Change summary

Cargo.lock                                  |   1 
Cargo.toml                                  |   2 
crates/editor/Cargo.toml                    |   1 
crates/editor/src/display_map.rs            | 118 +++++--------
crates/editor/src/display_map/block_map.rs  |  56 ++---
crates/editor/src/display_map/invisibles.rs | 157 ------------------
crates/editor/src/display_map/tab_map.rs    | 158 +++++++-----------
crates/editor/src/display_map/wrap_map.rs   | 191 +++++++++++-----------
crates/editor/src/element.rs                |  17 +
crates/editor/src/hover_popover.rs          |  45 ----
crates/gpui/src/text_system/line.rs         |  51 -----
crates/language/src/buffer.rs               |   3 
12 files changed, 248 insertions(+), 552 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -3723,7 +3723,6 @@ dependencies = [
  "tree-sitter-rust",
  "tree-sitter-typescript",
  "ui",
- "unicode-segmentation",
  "unindent",
  "url",
  "util",

Cargo.toml 🔗

@@ -468,7 +468,7 @@ tree-sitter-typescript = "0.23"
 tree-sitter-yaml = { git = "https://github.com/zed-industries/tree-sitter-yaml", rev = "baff0b51c64ef6a1fb1f8390f3ad6015b83ec13a" }
 unicase = "2.6"
 unindent = "0.1.7"
-unicode-segmentation = "1.11"
+unicode-segmentation = "1.10"
 url = "2.2"
 uuid = { version = "1.1.2", features = ["v4", "v5", "serde"] }
 wasmparser = "0.215"

crates/editor/Cargo.toml 🔗

@@ -81,7 +81,6 @@ ui.workspace = true
 url.workspace = true
 util.workspace = true
 workspace.workspace = true
-unicode-segmentation.workspace = true
 
 [dev-dependencies]
 ctor.workspace = true

crates/editor/src/display_map.rs 🔗

@@ -8,7 +8,7 @@
 //! of several smaller structures that form a hierarchy (starting at the bottom):
 //! - [`InlayMap`] that decides where the [`Inlay`]s should be displayed.
 //! - [`FoldMap`] that decides where the fold indicators should be; it also tracks parts of a source file that are currently folded.
-//! - [`CharMap`] that replaces tabs and non-printable characters
+//! - [`TabMap`] that keeps track of hard tabs in a buffer.
 //! - [`WrapMap`] that handles soft wrapping.
 //! - [`BlockMap`] that tracks custom blocks such as diagnostics that should be displayed within buffer.
 //! - [`DisplayMap`] that adds background highlights to the regions of text.
@@ -18,11 +18,10 @@
 //! [EditorElement]: crate::element::EditorElement
 
 mod block_map;
-mod char_map;
 mod crease_map;
 mod fold_map;
 mod inlay_map;
-mod invisibles;
+mod tab_map;
 mod wrap_map;
 
 use crate::{
@@ -33,7 +32,6 @@ pub use block_map::{
     BlockMap, BlockPoint, BlockProperties, BlockStyle, CustomBlockId, RenderBlock,
 };
 use block_map::{BlockRow, BlockSnapshot};
-use char_map::{CharMap, CharSnapshot};
 use collections::{HashMap, HashSet};
 pub use crease_map::*;
 pub use fold_map::{Fold, FoldId, FoldPlaceholder, FoldPoint};
@@ -44,7 +42,6 @@ use gpui::{
 pub(crate) use inlay_map::Inlay;
 use inlay_map::{InlayMap, InlaySnapshot};
 pub use inlay_map::{InlayOffset, InlayPoint};
-pub use invisibles::is_invisible;
 use language::{
     language_settings::language_settings, ChunkRenderer, OffsetUtf16, Point,
     Subscription as BufferSubscription,
@@ -64,9 +61,9 @@ use std::{
     sync::Arc,
 };
 use sum_tree::{Bias, TreeMap};
+use tab_map::{TabMap, TabSnapshot};
 use text::LineIndent;
-use ui::{px, WindowContext};
-use unicode_segmentation::UnicodeSegmentation;
+use ui::WindowContext;
 use wrap_map::{WrapMap, WrapSnapshot};
 
 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
@@ -97,7 +94,7 @@ pub struct DisplayMap {
     /// Decides where the fold indicators should be and tracks parts of a source file that are currently folded.
     fold_map: FoldMap,
     /// Keeps track of hard tabs in a buffer.
-    char_map: CharMap,
+    tab_map: TabMap,
     /// Handles soft wrapping.
     wrap_map: Model<WrapMap>,
     /// Tracks custom blocks such as diagnostics that should be displayed within buffer.
@@ -134,7 +131,7 @@ impl DisplayMap {
         let crease_map = CreaseMap::new(&buffer_snapshot);
         let (inlay_map, snapshot) = InlayMap::new(buffer_snapshot);
         let (fold_map, snapshot) = FoldMap::new(snapshot);
-        let (char_map, snapshot) = CharMap::new(snapshot, tab_size);
+        let (tab_map, snapshot) = TabMap::new(snapshot, tab_size);
         let (wrap_map, snapshot) = WrapMap::new(snapshot, font, font_size, wrap_width, cx);
         let block_map = BlockMap::new(
             snapshot,
@@ -151,7 +148,7 @@ impl DisplayMap {
             buffer_subscription,
             fold_map,
             inlay_map,
-            char_map,
+            tab_map,
             wrap_map,
             block_map,
             crease_map,
@@ -169,17 +166,17 @@ impl DisplayMap {
         let (inlay_snapshot, edits) = self.inlay_map.sync(buffer_snapshot, edits);
         let (fold_snapshot, edits) = self.fold_map.read(inlay_snapshot.clone(), edits);
         let tab_size = Self::tab_size(&self.buffer, cx);
-        let (char_snapshot, edits) = self.char_map.sync(fold_snapshot.clone(), edits, tab_size);
+        let (tab_snapshot, edits) = self.tab_map.sync(fold_snapshot.clone(), edits, tab_size);
         let (wrap_snapshot, edits) = self
             .wrap_map
-            .update(cx, |map, cx| map.sync(char_snapshot.clone(), edits, cx));
+            .update(cx, |map, cx| map.sync(tab_snapshot.clone(), edits, cx));
         let block_snapshot = self.block_map.read(wrap_snapshot.clone(), edits).snapshot;
 
         DisplaySnapshot {
             buffer_snapshot: self.buffer.read(cx).snapshot(cx),
             fold_snapshot,
             inlay_snapshot,
-            char_snapshot,
+            tab_snapshot,
             wrap_snapshot,
             block_snapshot,
             crease_snapshot: self.crease_map.snapshot(),
@@ -215,13 +212,13 @@ impl DisplayMap {
         let tab_size = Self::tab_size(&self.buffer, cx);
         let (snapshot, edits) = self.inlay_map.sync(snapshot, edits);
         let (mut fold_map, snapshot, edits) = self.fold_map.write(snapshot, edits);
-        let (snapshot, edits) = self.char_map.sync(snapshot, edits, tab_size);
+        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
         let (snapshot, edits) = self
             .wrap_map
             .update(cx, |map, cx| map.sync(snapshot, edits, cx));
         self.block_map.read(snapshot, edits);
         let (snapshot, edits) = fold_map.fold(ranges);
-        let (snapshot, edits) = self.char_map.sync(snapshot, edits, tab_size);
+        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
         let (snapshot, edits) = self
             .wrap_map
             .update(cx, |map, cx| map.sync(snapshot, edits, cx));
@@ -239,13 +236,13 @@ impl DisplayMap {
         let tab_size = Self::tab_size(&self.buffer, cx);
         let (snapshot, edits) = self.inlay_map.sync(snapshot, edits);
         let (mut fold_map, snapshot, edits) = self.fold_map.write(snapshot, edits);
-        let (snapshot, edits) = self.char_map.sync(snapshot, edits, tab_size);
+        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
         let (snapshot, edits) = self
             .wrap_map
             .update(cx, |map, cx| map.sync(snapshot, edits, cx));
         self.block_map.read(snapshot, edits);
         let (snapshot, edits) = fold_map.unfold(ranges, inclusive);
-        let (snapshot, edits) = self.char_map.sync(snapshot, edits, tab_size);
+        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
         let (snapshot, edits) = self
             .wrap_map
             .update(cx, |map, cx| map.sync(snapshot, edits, cx));
@@ -280,7 +277,7 @@ impl DisplayMap {
         let tab_size = Self::tab_size(&self.buffer, cx);
         let (snapshot, edits) = self.inlay_map.sync(snapshot, edits);
         let (snapshot, edits) = self.fold_map.read(snapshot, edits);
-        let (snapshot, edits) = self.char_map.sync(snapshot, edits, tab_size);
+        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
         let (snapshot, edits) = self
             .wrap_map
             .update(cx, |map, cx| map.sync(snapshot, edits, cx));
@@ -298,7 +295,7 @@ impl DisplayMap {
         let tab_size = Self::tab_size(&self.buffer, cx);
         let (snapshot, edits) = self.inlay_map.sync(snapshot, edits);
         let (snapshot, edits) = self.fold_map.read(snapshot, edits);
-        let (snapshot, edits) = self.char_map.sync(snapshot, edits, tab_size);
+        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
         let (snapshot, edits) = self
             .wrap_map
             .update(cx, |map, cx| map.sync(snapshot, edits, cx));
@@ -316,7 +313,7 @@ impl DisplayMap {
         let tab_size = Self::tab_size(&self.buffer, cx);
         let (snapshot, edits) = self.inlay_map.sync(snapshot, edits);
         let (snapshot, edits) = self.fold_map.read(snapshot, edits);
-        let (snapshot, edits) = self.char_map.sync(snapshot, edits, tab_size);
+        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
         let (snapshot, edits) = self
             .wrap_map
             .update(cx, |map, cx| map.sync(snapshot, edits, cx));
@@ -334,7 +331,7 @@ impl DisplayMap {
         let tab_size = Self::tab_size(&self.buffer, cx);
         let (snapshot, edits) = self.inlay_map.sync(snapshot, edits);
         let (snapshot, edits) = self.fold_map.read(snapshot, edits);
-        let (snapshot, edits) = self.char_map.sync(snapshot, edits, tab_size);
+        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
         let (snapshot, edits) = self
             .wrap_map
             .update(cx, |map, cx| map.sync(snapshot, edits, cx));
@@ -410,7 +407,7 @@ impl DisplayMap {
         let (snapshot, edits) = self.inlay_map.sync(buffer_snapshot, edits);
         let (snapshot, edits) = self.fold_map.read(snapshot, edits);
         let tab_size = Self::tab_size(&self.buffer, cx);
-        let (snapshot, edits) = self.char_map.sync(snapshot, edits, tab_size);
+        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
         let (snapshot, edits) = self
             .wrap_map
             .update(cx, |map, cx| map.sync(snapshot, edits, cx));
@@ -418,7 +415,7 @@ impl DisplayMap {
 
         let (snapshot, edits) = self.inlay_map.splice(to_remove, to_insert);
         let (snapshot, edits) = self.fold_map.read(snapshot, edits);
-        let (snapshot, edits) = self.char_map.sync(snapshot, edits, tab_size);
+        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
         let (snapshot, edits) = self
             .wrap_map
             .update(cx, |map, cx| map.sync(snapshot, edits, cx));
@@ -470,7 +467,7 @@ pub struct DisplaySnapshot {
     pub fold_snapshot: FoldSnapshot,
     pub crease_snapshot: CreaseSnapshot,
     inlay_snapshot: InlaySnapshot,
-    char_snapshot: CharSnapshot,
+    tab_snapshot: TabSnapshot,
     wrap_snapshot: WrapSnapshot,
     block_snapshot: BlockSnapshot,
     text_highlights: TextHighlights,
@@ -570,8 +567,8 @@ impl DisplaySnapshot {
     fn point_to_display_point(&self, point: MultiBufferPoint, bias: Bias) -> DisplayPoint {
         let inlay_point = self.inlay_snapshot.to_inlay_point(point);
         let fold_point = self.fold_snapshot.to_fold_point(inlay_point, bias);
-        let char_point = self.char_snapshot.to_char_point(fold_point);
-        let wrap_point = self.wrap_snapshot.char_point_to_wrap_point(char_point);
+        let tab_point = self.tab_snapshot.to_tab_point(fold_point);
+        let wrap_point = self.wrap_snapshot.tab_point_to_wrap_point(tab_point);
         let block_point = self.block_snapshot.to_block_point(wrap_point);
         DisplayPoint(block_point)
     }
@@ -599,21 +596,21 @@ impl DisplaySnapshot {
     fn display_point_to_inlay_point(&self, point: DisplayPoint, bias: Bias) -> InlayPoint {
         let block_point = point.0;
         let wrap_point = self.block_snapshot.to_wrap_point(block_point);
-        let char_point = self.wrap_snapshot.to_char_point(wrap_point);
-        let fold_point = self.char_snapshot.to_fold_point(char_point, bias).0;
+        let tab_point = self.wrap_snapshot.to_tab_point(wrap_point);
+        let fold_point = self.tab_snapshot.to_fold_point(tab_point, bias).0;
         fold_point.to_inlay_point(&self.fold_snapshot)
     }
 
     pub fn display_point_to_fold_point(&self, point: DisplayPoint, bias: Bias) -> FoldPoint {
         let block_point = point.0;
         let wrap_point = self.block_snapshot.to_wrap_point(block_point);
-        let char_point = self.wrap_snapshot.to_char_point(wrap_point);
-        self.char_snapshot.to_fold_point(char_point, bias).0
+        let tab_point = self.wrap_snapshot.to_tab_point(wrap_point);
+        self.tab_snapshot.to_fold_point(tab_point, bias).0
     }
 
     pub fn fold_point_to_display_point(&self, fold_point: FoldPoint) -> DisplayPoint {
-        let char_point = self.char_snapshot.to_char_point(fold_point);
-        let wrap_point = self.wrap_snapshot.char_point_to_wrap_point(char_point);
+        let tab_point = self.tab_snapshot.to_tab_point(fold_point);
+        let wrap_point = self.wrap_snapshot.tab_point_to_wrap_point(tab_point);
         let block_point = self.block_snapshot.to_block_point(wrap_point);
         DisplayPoint(block_point)
     }
@@ -691,23 +688,6 @@ impl DisplaySnapshot {
                 }
             }
 
-            if chunk.is_invisible {
-                let invisible_highlight = HighlightStyle {
-                    background_color: Some(editor_style.status.hint_background),
-                    underline: Some(UnderlineStyle {
-                        color: Some(editor_style.status.hint),
-                        thickness: px(1.),
-                        wavy: false,
-                    }),
-                    ..Default::default()
-                };
-                if let Some(highlight_style) = highlight_style.as_mut() {
-                    highlight_style.highlight(invisible_highlight);
-                } else {
-                    highlight_style = Some(invisible_highlight);
-                }
-            }
-
             let mut diagnostic_highlight = HighlightStyle::default();
 
             if chunk.is_unnecessary {
@@ -804,11 +784,12 @@ impl DisplaySnapshot {
         layout_line.closest_index_for_x(x) as u32
     }
 
-    pub fn grapheme_at(&self, mut point: DisplayPoint) -> Option<String> {
+    pub fn display_chars_at(
+        &self,
+        mut point: DisplayPoint,
+    ) -> impl Iterator<Item = (char, DisplayPoint)> + '_ {
         point = DisplayPoint(self.block_snapshot.clip_point(point.0, Bias::Left));
-
-        let chars = self
-            .text_chunks(point.row())
+        self.text_chunks(point.row())
             .flat_map(str::chars)
             .skip_while({
                 let mut column = 0;
@@ -818,21 +799,16 @@ impl DisplaySnapshot {
                     !at_point
                 }
             })
-            .take_while({
-                let mut prev = false;
-                move |char| {
-                    let now = char.is_ascii();
-                    let end = char.is_ascii() && (char.is_ascii_whitespace() || prev);
-                    prev = now;
-                    !end
+            .map(move |ch| {
+                let result = (ch, point);
+                if ch == '\n' {
+                    *point.row_mut() += 1;
+                    *point.column_mut() = 0;
+                } else {
+                    *point.column_mut() += ch.len_utf8() as u32;
                 }
-            });
-
-        chars
-            .collect::<String>()
-            .graphemes(true)
-            .next()
-            .map(|s| s.to_owned())
+                result
+            })
     }
 
     pub fn buffer_chars_at(&self, mut offset: usize) -> impl Iterator<Item = (char, usize)> + '_ {
@@ -1144,8 +1120,8 @@ impl DisplayPoint {
 
     pub fn to_offset(self, map: &DisplaySnapshot, bias: Bias) -> usize {
         let wrap_point = map.block_snapshot.to_wrap_point(self.0);
-        let char_point = map.wrap_snapshot.to_char_point(wrap_point);
-        let fold_point = map.char_snapshot.to_fold_point(char_point, bias).0;
+        let tab_point = map.wrap_snapshot.to_tab_point(wrap_point);
+        let fold_point = map.tab_snapshot.to_fold_point(tab_point, bias).0;
         let inlay_point = fold_point.to_inlay_point(&map.fold_snapshot);
         map.inlay_snapshot
             .to_buffer_offset(map.inlay_snapshot.to_offset(inlay_point))
@@ -1252,7 +1228,7 @@ pub mod tests {
         let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
         log::info!("buffer text: {:?}", snapshot.buffer_snapshot.text());
         log::info!("fold text: {:?}", snapshot.fold_snapshot.text());
-        log::info!("char text: {:?}", snapshot.char_snapshot.text());
+        log::info!("tab text: {:?}", snapshot.tab_snapshot.text());
         log::info!("wrap text: {:?}", snapshot.wrap_snapshot.text());
         log::info!("block text: {:?}", snapshot.block_snapshot.text());
         log::info!("display text: {:?}", snapshot.text());
@@ -1369,7 +1345,7 @@ pub mod tests {
             fold_count = snapshot.fold_count();
             log::info!("buffer text: {:?}", snapshot.buffer_snapshot.text());
             log::info!("fold text: {:?}", snapshot.fold_snapshot.text());
-            log::info!("char text: {:?}", snapshot.char_snapshot.text());
+            log::info!("tab text: {:?}", snapshot.tab_snapshot.text());
             log::info!("wrap text: {:?}", snapshot.wrap_snapshot.text());
             log::info!("block text: {:?}", snapshot.block_snapshot.text());
             log::info!("display text: {:?}", snapshot.text());

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

@@ -1421,7 +1421,7 @@ fn offset_for_row(s: &str, target: u32) -> (u32, usize) {
 mod tests {
     use super::*;
     use crate::display_map::{
-        char_map::CharMap, fold_map::FoldMap, inlay_map::InlayMap, wrap_map::WrapMap,
+        fold_map::FoldMap, inlay_map::InlayMap, tab_map::TabMap, wrap_map::WrapMap,
     };
     use gpui::{div, font, px, AppContext, Context as _, Element};
     use language::{Buffer, Capability};
@@ -1456,9 +1456,9 @@ mod tests {
         let subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
         let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
         let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
-        let (mut char_map, char_snapshot) = CharMap::new(fold_snapshot, 1.try_into().unwrap());
+        let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 1.try_into().unwrap());
         let (wrap_map, wraps_snapshot) =
-            cx.update(|cx| WrapMap::new(char_snapshot, font("Helvetica"), px(14.0), None, cx));
+            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
         let mut block_map = BlockMap::new(wraps_snapshot.clone(), true, 1, 1, 1);
 
         let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
@@ -1609,10 +1609,10 @@ mod tests {
         let (inlay_snapshot, inlay_edits) =
             inlay_map.sync(buffer_snapshot, subscription.consume().into_inner());
         let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
-        let (char_snapshot, tab_edits) =
-            char_map.sync(fold_snapshot, fold_edits, 4.try_into().unwrap());
+        let (tab_snapshot, tab_edits) =
+            tab_map.sync(fold_snapshot, fold_edits, 4.try_into().unwrap());
         let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
-            wrap_map.sync(char_snapshot, tab_edits, cx)
+            wrap_map.sync(tab_snapshot, tab_edits, cx)
         });
         let snapshot = block_map.read(wraps_snapshot, wrap_edits);
         assert_eq!(snapshot.text(), "aaa\n\nb!!!\n\n\nbb\nccc\nddd\n\n\n");
@@ -1672,9 +1672,8 @@ mod tests {
         let multi_buffer_snapshot = multi_buffer.read(cx).snapshot(cx);
         let (_, inlay_snapshot) = InlayMap::new(multi_buffer_snapshot.clone());
         let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
-        let (_, char_snapshot) = CharMap::new(fold_snapshot, 4.try_into().unwrap());
-        let (_, wraps_snapshot) =
-            WrapMap::new(char_snapshot, font, font_size, Some(wrap_width), cx);
+        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
+        let (_, wraps_snapshot) = WrapMap::new(tab_snapshot, font, font_size, Some(wrap_width), cx);
 
         let block_map = BlockMap::new(wraps_snapshot.clone(), true, 1, 1, 1);
         let snapshot = block_map.read(wraps_snapshot, Default::default());
@@ -1711,9 +1710,9 @@ mod tests {
         let _subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
         let (_inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
         let (_fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
-        let (_char_map, char_snapshot) = CharMap::new(fold_snapshot, 1.try_into().unwrap());
+        let (_tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 1.try_into().unwrap());
         let (_wrap_map, wraps_snapshot) =
-            cx.update(|cx| WrapMap::new(char_snapshot, font("Helvetica"), px(14.0), None, cx));
+            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
         let mut block_map = BlockMap::new(wraps_snapshot.clone(), false, 1, 1, 0);
 
         let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
@@ -1816,15 +1815,9 @@ mod tests {
         let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
         let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
         let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
-        let (_, char_snapshot) = CharMap::new(fold_snapshot, 4.try_into().unwrap());
+        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
         let (_, wraps_snapshot) = cx.update(|cx| {
-            WrapMap::new(
-                char_snapshot,
-                font("Helvetica"),
-                px(14.0),
-                Some(px(60.)),
-                cx,
-            )
+            WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), Some(px(60.)), cx)
         });
         let mut block_map = BlockMap::new(wraps_snapshot.clone(), true, 1, 1, 0);
 
@@ -1892,9 +1885,9 @@ mod tests {
         let mut buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
         let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
         let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
-        let (mut char_map, char_snapshot) = CharMap::new(fold_snapshot, 4.try_into().unwrap());
+        let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
         let (wrap_map, wraps_snapshot) = cx
-            .update(|cx| WrapMap::new(char_snapshot, font("Helvetica"), font_size, wrap_width, cx));
+            .update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), font_size, wrap_width, cx));
         let mut block_map = BlockMap::new(
             wraps_snapshot,
             true,
@@ -1951,10 +1944,10 @@ mod tests {
                     let (inlay_snapshot, inlay_edits) =
                         inlay_map.sync(buffer_snapshot.clone(), vec![]);
                     let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
-                    let (char_snapshot, tab_edits) =
-                        char_map.sync(fold_snapshot, fold_edits, tab_size);
+                    let (tab_snapshot, tab_edits) =
+                        tab_map.sync(fold_snapshot, fold_edits, tab_size);
                     let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
-                        wrap_map.sync(char_snapshot, tab_edits, cx)
+                        wrap_map.sync(tab_snapshot, tab_edits, cx)
                     });
                     let mut block_map = block_map.write(wraps_snapshot, wrap_edits);
                     let block_ids =
@@ -1983,10 +1976,10 @@ mod tests {
                     let (inlay_snapshot, inlay_edits) =
                         inlay_map.sync(buffer_snapshot.clone(), vec![]);
                     let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
-                    let (char_snapshot, tab_edits) =
-                        char_map.sync(fold_snapshot, fold_edits, tab_size);
+                    let (tab_snapshot, tab_edits) =
+                        tab_map.sync(fold_snapshot, fold_edits, tab_size);
                     let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
-                        wrap_map.sync(char_snapshot, tab_edits, cx)
+                        wrap_map.sync(tab_snapshot, tab_edits, cx)
                     });
                     let mut block_map = block_map.write(wraps_snapshot, wrap_edits);
                     block_map.remove(block_ids_to_remove);
@@ -2006,9 +1999,9 @@ mod tests {
             let (inlay_snapshot, inlay_edits) =
                 inlay_map.sync(buffer_snapshot.clone(), buffer_edits);
             let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
-            let (char_snapshot, tab_edits) = char_map.sync(fold_snapshot, fold_edits, tab_size);
+            let (tab_snapshot, tab_edits) = tab_map.sync(fold_snapshot, fold_edits, tab_size);
             let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
-                wrap_map.sync(char_snapshot, tab_edits, cx)
+                wrap_map.sync(tab_snapshot, tab_edits, cx)
             });
             let blocks_snapshot = block_map.read(wraps_snapshot.clone(), wrap_edits);
             assert_eq!(
@@ -2091,10 +2084,7 @@ mod tests {
                     }
                 }
 
-                let soft_wrapped = wraps_snapshot
-                    .to_char_point(WrapPoint::new(row, 0))
-                    .column()
-                    > 0;
+                let soft_wrapped = wraps_snapshot.to_tab_point(WrapPoint::new(row, 0)).column() > 0;
                 expected_buffer_rows.push(if soft_wrapped { None } else { buffer_row });
                 expected_text.push_str(input_line);
 

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

@@ -1,157 +0,0 @@
-use std::sync::LazyLock;
-
-use collections::HashMap;
-
-// Invisibility in a Unicode context is not well defined, so we have to guess.
-//
-// We highlight all ASCII control codes, and unicode whitespace because they are likely
-// confused with a normal space (U+0020).
-//
-// We also highlight the handful of blank non-space characters:
-//   U+2800 BRAILLE PATTERN BLANK - Category: So
-//   U+115F HANGUL CHOSEONG FILLER - Category: Lo
-//   U+1160 HANGUL CHOSEONG FILLER - Category: Lo
-//   U+3164 HANGUL FILLER - Category: Lo
-//   U+FFA0 HALFWIDTH HANGUL FILLER - Category: Lo
-//   U+FFFC OBJECT REPLACEMENT CHARACTER - Category: So
-//
-// For the rest of Unicode, invisibility happens for two reasons:
-// * A Format character (like a byte order mark or right-to-left override)
-// * An invisible Nonspacing Mark character (like U+034F, or variation selectors)
-//
-// We don't consider unassigned codepoints invisible as the font renderer already shows
-// a replacement character in that case (and there are a *lot* of them)
-//
-// Control characters are mostly fine to highlight; except:
-// * U+E0020..=U+E007F are used in emoji flags. We don't highlight them right now, but we could if we tightened our heuristics.
-// * U+200D is used to join characters. We highlight this but don't replace it. As our font system ignores mid-glyph highlights this mostly works to highlight unexpected uses.
-//
-// Nonspacing marks are handled like U+200D. This means that mid-glyph we ignore them, but
-// probably causes issues with end-of-glyph usage.
-//
-// ref: https://invisible-characters.com
-// ref: https://www.compart.com/en/unicode/category/Cf
-// ref: https://gist.github.com/ConradIrwin/f759e1fc29267143c4c7895aa495dca5?h=1
-// ref: https://unicode.org/Public/emoji/13.0/emoji-test.txt
-// https://github.com/bits/UTF-8-Unicode-Test-Documents/blob/master/UTF-8_sequence_separated/utf8_sequence_0-0x10ffff_assigned_including-unprintable-asis.txt
-pub fn is_invisible(c: char) -> bool {
-    if c <= '\u{1f}' {
-        c != '\t' && c != '\n' && c != '\r'
-    } else if c >= '\u{7f}' {
-        c <= '\u{9f}' || c.is_whitespace() || contains(c, &FORMAT) || contains(c, &OTHER)
-    } else {
-        false
-    }
-}
-
-pub(crate) fn replacement(c: char) -> Option<&'static str> {
-    if !is_invisible(c) {
-        return None;
-    }
-    if c <= '\x7f' {
-        REPLACEMENTS.get(&c).copied()
-    } else if contains(c, &PRESERVE) {
-        None
-    } else {
-        Some(" ")
-    }
-}
-
-const REPLACEMENTS: LazyLock<HashMap<char, &'static str>> = LazyLock::new(|| {
-    [
-        ('\x00', "␀"),
-        ('\x01', "␁"),
-        ('\x02', "␂"),
-        ('\x03', "␃"),
-        ('\x04', "␄"),
-        ('\x05', "␅"),
-        ('\x06', "␆"),
-        ('\x07', "␇"),
-        ('\x08', "␈"),
-        ('\x0B', "␋"),
-        ('\x0C', "␌"),
-        ('\x0D', "␍"),
-        ('\x0E', "␎"),
-        ('\x0F', "␏"),
-        ('\x10', "␐"),
-        ('\x11', "␑"),
-        ('\x12', "␒"),
-        ('\x13', "␓"),
-        ('\x14', "␔"),
-        ('\x15', "␕"),
-        ('\x16', "␖"),
-        ('\x17', "␗"),
-        ('\x18', "␘"),
-        ('\x19', "␙"),
-        ('\x1A', "␚"),
-        ('\x1B', "␛"),
-        ('\x1C', "␜"),
-        ('\x1D', "␝"),
-        ('\x1E', "␞"),
-        ('\x1F', "␟"),
-        ('\u{007F}', "␡"),
-    ]
-    .into_iter()
-    .collect()
-});
-
-// generated using ucd-generate: ucd-generate general-category --include Format --chars ucd-16.0.0
-pub const FORMAT: &'static [(char, char)] = &[
-    ('\u{ad}', '\u{ad}'),
-    ('\u{600}', '\u{605}'),
-    ('\u{61c}', '\u{61c}'),
-    ('\u{6dd}', '\u{6dd}'),
-    ('\u{70f}', '\u{70f}'),
-    ('\u{890}', '\u{891}'),
-    ('\u{8e2}', '\u{8e2}'),
-    ('\u{180e}', '\u{180e}'),
-    ('\u{200b}', '\u{200f}'),
-    ('\u{202a}', '\u{202e}'),
-    ('\u{2060}', '\u{2064}'),
-    ('\u{2066}', '\u{206f}'),
-    ('\u{feff}', '\u{feff}'),
-    ('\u{fff9}', '\u{fffb}'),
-    ('\u{110bd}', '\u{110bd}'),
-    ('\u{110cd}', '\u{110cd}'),
-    ('\u{13430}', '\u{1343f}'),
-    ('\u{1bca0}', '\u{1bca3}'),
-    ('\u{1d173}', '\u{1d17a}'),
-    ('\u{e0001}', '\u{e0001}'),
-    ('\u{e0020}', '\u{e007f}'),
-];
-
-// hand-made base on https://invisible-characters.com (Excluding Cf)
-pub const OTHER: &'static [(char, char)] = &[
-    ('\u{034f}', '\u{034f}'),
-    ('\u{115F}', '\u{1160}'),
-    ('\u{17b4}', '\u{17b5}'),
-    ('\u{180b}', '\u{180d}'),
-    ('\u{2800}', '\u{2800}'),
-    ('\u{3164}', '\u{3164}'),
-    ('\u{fe00}', '\u{fe0d}'),
-    ('\u{ffa0}', '\u{ffa0}'),
-    ('\u{fffc}', '\u{fffc}'),
-    ('\u{e0100}', '\u{e01ef}'),
-];
-
-// a subset of FORMAT/OTHER that may appear within glyphs
-const PRESERVE: &'static [(char, char)] = &[
-    ('\u{034f}', '\u{034f}'),
-    ('\u{200d}', '\u{200d}'),
-    ('\u{17b4}', '\u{17b5}'),
-    ('\u{180b}', '\u{180d}'),
-    ('\u{e0061}', '\u{e007a}'),
-    ('\u{e007f}', '\u{e007f}'),
-];
-
-fn contains(c: char, list: &[(char, char)]) -> bool {
-    for (start, end) in list {
-        if c < *start {
-            return false;
-        }
-        if c <= *end {
-            return true;
-        }
-    }
-    false
-}

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

@@ -1,6 +1,5 @@
 use super::{
     fold_map::{self, FoldChunks, FoldEdit, FoldPoint, FoldSnapshot},
-    invisibles::{is_invisible, replacement},
     Highlights,
 };
 use language::{Chunk, Point};
@@ -10,14 +9,14 @@ use sum_tree::Bias;
 
 const MAX_EXPANSION_COLUMN: u32 = 256;
 
-/// Keeps track of hard tabs and non-printable characters in a text buffer.
+/// Keeps track of hard tabs in a text buffer.
 ///
 /// See the [`display_map` module documentation](crate::display_map) for more information.
-pub struct CharMap(CharSnapshot);
+pub struct TabMap(TabSnapshot);
 
-impl CharMap {
-    pub fn new(fold_snapshot: FoldSnapshot, tab_size: NonZeroU32) -> (Self, CharSnapshot) {
-        let snapshot = CharSnapshot {
+impl TabMap {
+    pub fn new(fold_snapshot: FoldSnapshot, tab_size: NonZeroU32) -> (Self, TabSnapshot) {
+        let snapshot = TabSnapshot {
             fold_snapshot,
             tab_size,
             max_expansion_column: MAX_EXPANSION_COLUMN,
@@ -27,7 +26,7 @@ impl CharMap {
     }
 
     #[cfg(test)]
-    pub fn set_max_expansion_column(&mut self, column: u32) -> CharSnapshot {
+    pub fn set_max_expansion_column(&mut self, column: u32) -> TabSnapshot {
         self.0.max_expansion_column = column;
         self.0.clone()
     }
@@ -37,9 +36,9 @@ impl CharMap {
         fold_snapshot: FoldSnapshot,
         mut fold_edits: Vec<FoldEdit>,
         tab_size: NonZeroU32,
-    ) -> (CharSnapshot, Vec<TabEdit>) {
+    ) -> (TabSnapshot, Vec<TabEdit>) {
         let old_snapshot = &mut self.0;
-        let mut new_snapshot = CharSnapshot {
+        let mut new_snapshot = TabSnapshot {
             fold_snapshot,
             tab_size,
             max_expansion_column: old_snapshot.max_expansion_column,
@@ -138,15 +137,15 @@ impl CharMap {
                 let new_start = fold_edit.new.start.to_point(&new_snapshot.fold_snapshot);
                 let new_end = fold_edit.new.end.to_point(&new_snapshot.fold_snapshot);
                 tab_edits.push(TabEdit {
-                    old: old_snapshot.to_char_point(old_start)..old_snapshot.to_char_point(old_end),
-                    new: new_snapshot.to_char_point(new_start)..new_snapshot.to_char_point(new_end),
+                    old: old_snapshot.to_tab_point(old_start)..old_snapshot.to_tab_point(old_end),
+                    new: new_snapshot.to_tab_point(new_start)..new_snapshot.to_tab_point(new_end),
                 });
             }
         } else {
             new_snapshot.version += 1;
             tab_edits.push(TabEdit {
-                old: CharPoint::zero()..old_snapshot.max_point(),
-                new: CharPoint::zero()..new_snapshot.max_point(),
+                old: TabPoint::zero()..old_snapshot.max_point(),
+                new: TabPoint::zero()..new_snapshot.max_point(),
             });
         }
 
@@ -156,14 +155,14 @@ impl CharMap {
 }
 
 #[derive(Clone)]
-pub struct CharSnapshot {
+pub struct TabSnapshot {
     pub fold_snapshot: FoldSnapshot,
     pub tab_size: NonZeroU32,
     pub max_expansion_column: u32,
     pub version: usize,
 }
 
-impl CharSnapshot {
+impl TabSnapshot {
     pub fn buffer_snapshot(&self) -> &MultiBufferSnapshot {
         &self.fold_snapshot.inlay_snapshot.buffer
     }
@@ -171,7 +170,7 @@ impl CharSnapshot {
     pub fn line_len(&self, row: u32) -> u32 {
         let max_point = self.max_point();
         if row < max_point.row() {
-            self.to_char_point(FoldPoint::new(row, self.fold_snapshot.line_len(row)))
+            self.to_tab_point(FoldPoint::new(row, self.fold_snapshot.line_len(row)))
                 .0
                 .column
         } else {
@@ -180,10 +179,10 @@ impl CharSnapshot {
     }
 
     pub fn text_summary(&self) -> TextSummary {
-        self.text_summary_for_range(CharPoint::zero()..self.max_point())
+        self.text_summary_for_range(TabPoint::zero()..self.max_point())
     }
 
-    pub fn text_summary_for_range(&self, range: Range<CharPoint>) -> TextSummary {
+    pub fn text_summary_for_range(&self, range: Range<TabPoint>) -> TextSummary {
         let input_start = self.to_fold_point(range.start, Bias::Left).0;
         let input_end = self.to_fold_point(range.end, Bias::Right).0;
         let input_summary = self
@@ -212,7 +211,7 @@ impl CharSnapshot {
         } else {
             for _ in self
                 .chunks(
-                    CharPoint::new(range.end.row(), 0)..range.end,
+                    TabPoint::new(range.end.row(), 0)..range.end,
                     false,
                     Highlights::default(),
                 )
@@ -233,7 +232,7 @@ impl CharSnapshot {
 
     pub fn chunks<'a>(
         &'a self,
-        range: Range<CharPoint>,
+        range: Range<TabPoint>,
         language_aware: bool,
         highlights: Highlights<'a>,
     ) -> TabChunks<'a> {
@@ -279,7 +278,7 @@ impl CharSnapshot {
     #[cfg(test)]
     pub fn text(&self) -> String {
         self.chunks(
-            CharPoint::zero()..self.max_point(),
+            TabPoint::zero()..self.max_point(),
             false,
             Highlights::default(),
         )
@@ -287,24 +286,24 @@ impl CharSnapshot {
         .collect()
     }
 
-    pub fn max_point(&self) -> CharPoint {
-        self.to_char_point(self.fold_snapshot.max_point())
+    pub fn max_point(&self) -> TabPoint {
+        self.to_tab_point(self.fold_snapshot.max_point())
     }
 
-    pub fn clip_point(&self, point: CharPoint, bias: Bias) -> CharPoint {
-        self.to_char_point(
+    pub fn clip_point(&self, point: TabPoint, bias: Bias) -> TabPoint {
+        self.to_tab_point(
             self.fold_snapshot
                 .clip_point(self.to_fold_point(point, bias).0, bias),
         )
     }
 
-    pub fn to_char_point(&self, input: FoldPoint) -> CharPoint {
+    pub fn to_tab_point(&self, input: FoldPoint) -> TabPoint {
         let chars = self.fold_snapshot.chars_at(FoldPoint::new(input.row(), 0));
         let expanded = self.expand_tabs(chars, input.column());
-        CharPoint::new(input.row(), expanded)
+        TabPoint::new(input.row(), expanded)
     }
 
-    pub fn to_fold_point(&self, output: CharPoint, bias: Bias) -> (FoldPoint, u32, u32) {
+    pub fn to_fold_point(&self, output: TabPoint, bias: Bias) -> (FoldPoint, u32, u32) {
         let chars = self.fold_snapshot.chars_at(FoldPoint::new(output.row(), 0));
         let expanded = output.column();
         let (collapsed, expanded_char_column, to_next_stop) =
@@ -316,13 +315,13 @@ impl CharSnapshot {
         )
     }
 
-    pub fn make_char_point(&self, point: Point, bias: Bias) -> CharPoint {
+    pub fn make_tab_point(&self, point: Point, bias: Bias) -> TabPoint {
         let inlay_point = self.fold_snapshot.inlay_snapshot.to_inlay_point(point);
         let fold_point = self.fold_snapshot.to_fold_point(inlay_point, bias);
-        self.to_char_point(fold_point)
+        self.to_tab_point(fold_point)
     }
 
-    pub fn to_point(&self, point: CharPoint, bias: Bias) -> Point {
+    pub fn to_point(&self, point: TabPoint, bias: Bias) -> Point {
         let fold_point = self.to_fold_point(point, bias).0;
         let inlay_point = fold_point.to_inlay_point(&self.fold_snapshot);
         self.fold_snapshot
@@ -345,9 +344,6 @@ impl CharSnapshot {
                 let tab_len = tab_size - expanded_chars % tab_size;
                 expanded_bytes += tab_len;
                 expanded_chars += tab_len;
-            } else if let Some(replacement) = replacement(c) {
-                expanded_chars += replacement.chars().count() as u32;
-                expanded_bytes += replacement.len() as u32;
             } else {
                 expanded_bytes += c.len_utf8() as u32;
                 expanded_chars += 1;
@@ -387,9 +383,6 @@ impl CharSnapshot {
                         Bias::Right => (collapsed_bytes + 1, expanded_chars, 0),
                     };
                 }
-            } else if let Some(replacement) = replacement(c) {
-                expanded_chars += replacement.chars().count() as u32;
-                expanded_bytes += replacement.len() as u32;
             } else {
                 expanded_chars += 1;
                 expanded_bytes += c.len_utf8() as u32;
@@ -411,9 +404,9 @@ impl CharSnapshot {
 }
 
 #[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
-pub struct CharPoint(pub Point);
+pub struct TabPoint(pub Point);
 
-impl CharPoint {
+impl TabPoint {
     pub fn new(row: u32, column: u32) -> Self {
         Self(Point::new(row, column))
     }
@@ -431,13 +424,13 @@ impl CharPoint {
     }
 }
 
-impl From<Point> for CharPoint {
+impl From<Point> for TabPoint {
     fn from(point: Point) -> Self {
         Self(point)
     }
 }
 
-pub type TabEdit = text::Edit<CharPoint>;
+pub type TabEdit = text::Edit<TabPoint>;
 
 #[derive(Clone, Debug, Default, Eq, PartialEq)]
 pub struct TextSummary {
@@ -558,37 +551,6 @@ impl<'a> Iterator for TabChunks<'a> {
                     self.input_column = 0;
                     self.output_position += Point::new(1, 0);
                 }
-                _ if is_invisible(c) => {
-                    if ix > 0 {
-                        let (prefix, suffix) = self.chunk.text.split_at(ix);
-                        self.chunk.text = suffix;
-                        return Some(Chunk {
-                            text: prefix,
-                            is_invisible: false,
-                            ..self.chunk.clone()
-                        });
-                    }
-                    let c_len = c.len_utf8();
-                    let replacement = replacement(c).unwrap_or(&self.chunk.text[..c_len]);
-                    if self.chunk.text.len() >= c_len {
-                        self.chunk.text = &self.chunk.text[c_len..];
-                    } else {
-                        self.chunk.text = "";
-                    }
-                    let len = replacement.chars().count() as u32;
-                    let next_output_position = cmp::min(
-                        self.output_position + Point::new(0, len),
-                        self.max_output_position,
-                    );
-                    self.column += len;
-                    self.input_column += 1;
-                    self.output_position = next_output_position;
-                    return Some(Chunk {
-                        text: replacement,
-                        is_invisible: true,
-                        ..self.chunk.clone()
-                    });
-                }
                 _ => {
                     self.column += 1;
                     if !self.inside_leading_tab {
@@ -618,11 +580,11 @@ mod tests {
         let buffer_snapshot = buffer.read(cx).snapshot(cx);
         let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
         let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
-        let (_, char_snapshot) = CharMap::new(fold_snapshot, 4.try_into().unwrap());
+        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
 
-        assert_eq!(char_snapshot.expand_tabs("\t".chars(), 0), 0);
-        assert_eq!(char_snapshot.expand_tabs("\t".chars(), 1), 4);
-        assert_eq!(char_snapshot.expand_tabs("\ta".chars(), 2), 5);
+        assert_eq!(tab_snapshot.expand_tabs("\t".chars(), 0), 0);
+        assert_eq!(tab_snapshot.expand_tabs("\t".chars(), 1), 4);
+        assert_eq!(tab_snapshot.expand_tabs("\ta".chars(), 2), 5);
     }
 
     #[gpui::test]
@@ -635,16 +597,16 @@ mod tests {
         let buffer_snapshot = buffer.read(cx).snapshot(cx);
         let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
         let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
-        let (_, mut char_snapshot) = CharMap::new(fold_snapshot, 4.try_into().unwrap());
+        let (_, mut tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
 
-        char_snapshot.max_expansion_column = max_expansion_column;
-        assert_eq!(char_snapshot.text(), output);
+        tab_snapshot.max_expansion_column = max_expansion_column;
+        assert_eq!(tab_snapshot.text(), output);
 
         for (ix, c) in input.char_indices() {
             assert_eq!(
-                char_snapshot
+                tab_snapshot
                     .chunks(
-                        CharPoint::new(0, ix as u32)..char_snapshot.max_point(),
+                        TabPoint::new(0, ix as u32)..tab_snapshot.max_point(),
                         false,
                         Highlights::default(),
                     )
@@ -658,13 +620,13 @@ mod tests {
                 let input_point = Point::new(0, ix as u32);
                 let output_point = Point::new(0, output.find(c).unwrap() as u32);
                 assert_eq!(
-                    char_snapshot.to_char_point(FoldPoint(input_point)),
-                    CharPoint(output_point),
-                    "to_char_point({input_point:?})"
+                    tab_snapshot.to_tab_point(FoldPoint(input_point)),
+                    TabPoint(output_point),
+                    "to_tab_point({input_point:?})"
                 );
                 assert_eq!(
-                    char_snapshot
-                        .to_fold_point(CharPoint(output_point), Bias::Left)
+                    tab_snapshot
+                        .to_fold_point(TabPoint(output_point), Bias::Left)
                         .0,
                     FoldPoint(input_point),
                     "to_fold_point({output_point:?})"
@@ -682,10 +644,10 @@ mod tests {
         let buffer_snapshot = buffer.read(cx).snapshot(cx);
         let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
         let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
-        let (_, mut char_snapshot) = CharMap::new(fold_snapshot, 4.try_into().unwrap());
+        let (_, mut tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
 
-        char_snapshot.max_expansion_column = max_expansion_column;
-        assert_eq!(char_snapshot.text(), input);
+        tab_snapshot.max_expansion_column = max_expansion_column;
+        assert_eq!(tab_snapshot.text(), input);
     }
 
     #[gpui::test]
@@ -696,10 +658,10 @@ mod tests {
         let buffer_snapshot = buffer.read(cx).snapshot(cx);
         let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
         let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
-        let (_, char_snapshot) = CharMap::new(fold_snapshot, 4.try_into().unwrap());
+        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
 
         assert_eq!(
-            chunks(&char_snapshot, CharPoint::zero()),
+            chunks(&tab_snapshot, TabPoint::zero()),
             vec![
                 ("    ".to_string(), true),
                 (" ".to_string(), false),
@@ -708,7 +670,7 @@ mod tests {
             ]
         );
         assert_eq!(
-            chunks(&char_snapshot, CharPoint::new(0, 2)),
+            chunks(&tab_snapshot, TabPoint::new(0, 2)),
             vec![
                 ("  ".to_string(), true),
                 (" ".to_string(), false),
@@ -717,7 +679,7 @@ mod tests {
             ]
         );
 
-        fn chunks(snapshot: &CharSnapshot, start: CharPoint) -> Vec<(String, bool)> {
+        fn chunks(snapshot: &TabSnapshot, start: TabPoint) -> Vec<(String, bool)> {
             let mut chunks = Vec::new();
             let mut was_tab = false;
             let mut text = String::new();
@@ -763,12 +725,12 @@ mod tests {
         let (inlay_snapshot, _) = inlay_map.randomly_mutate(&mut 0, &mut rng);
         log::info!("InlayMap text: {:?}", inlay_snapshot.text());
 
-        let (mut char_map, _) = CharMap::new(fold_snapshot.clone(), tab_size);
-        let tabs_snapshot = char_map.set_max_expansion_column(32);
+        let (mut tab_map, _) = TabMap::new(fold_snapshot.clone(), tab_size);
+        let tabs_snapshot = tab_map.set_max_expansion_column(32);
 
         let text = text::Rope::from(tabs_snapshot.text().as_str());
         log::info!(
-            "CharMap text (tab size: {}): {:?}",
+            "TabMap text (tab size: {}): {:?}",
             tab_size,
             tabs_snapshot.text(),
         );
@@ -776,11 +738,11 @@ mod tests {
         for _ in 0..5 {
             let end_row = rng.gen_range(0..=text.max_point().row);
             let end_column = rng.gen_range(0..=text.line_len(end_row));
-            let mut end = CharPoint(text.clip_point(Point::new(end_row, end_column), Bias::Right));
+            let mut end = TabPoint(text.clip_point(Point::new(end_row, end_column), Bias::Right));
             let start_row = rng.gen_range(0..=text.max_point().row);
             let start_column = rng.gen_range(0..=text.line_len(start_row));
             let mut start =
-                CharPoint(text.clip_point(Point::new(start_row, start_column), Bias::Left));
+                TabPoint(text.clip_point(Point::new(start_row, start_column), Bias::Left));
             if start > end {
                 mem::swap(&mut start, &mut end);
             }

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

@@ -1,6 +1,6 @@
 use super::{
-    char_map::{self, CharPoint, CharSnapshot, TabEdit},
     fold_map::FoldBufferRows,
+    tab_map::{self, TabEdit, TabPoint, TabSnapshot},
     Highlights,
 };
 use gpui::{AppContext, Context, Font, LineWrapper, Model, ModelContext, Pixels, Task};
@@ -12,7 +12,7 @@ use std::{cmp, collections::VecDeque, mem, ops::Range, time::Duration};
 use sum_tree::{Bias, Cursor, SumTree};
 use text::Patch;
 
-pub use super::char_map::TextSummary;
+pub use super::tab_map::TextSummary;
 pub type WrapEdit = text::Edit<u32>;
 
 /// Handles soft wrapping of text.
@@ -20,7 +20,7 @@ pub type WrapEdit = text::Edit<u32>;
 /// See the [`display_map` module documentation](crate::display_map) for more information.
 pub struct WrapMap {
     snapshot: WrapSnapshot,
-    pending_edits: VecDeque<(CharSnapshot, Vec<TabEdit>)>,
+    pending_edits: VecDeque<(TabSnapshot, Vec<TabEdit>)>,
     interpolated_edits: Patch<u32>,
     edits_since_sync: Patch<u32>,
     wrap_width: Option<Pixels>,
@@ -30,7 +30,7 @@ pub struct WrapMap {
 
 #[derive(Clone)]
 pub struct WrapSnapshot {
-    char_snapshot: CharSnapshot,
+    tab_snapshot: TabSnapshot,
     transforms: SumTree<Transform>,
     interpolated: bool,
 }
@@ -51,11 +51,11 @@ struct TransformSummary {
 pub struct WrapPoint(pub Point);
 
 pub struct WrapChunks<'a> {
-    input_chunks: char_map::TabChunks<'a>,
+    input_chunks: tab_map::TabChunks<'a>,
     input_chunk: Chunk<'a>,
     output_position: WrapPoint,
     max_output_row: u32,
-    transforms: Cursor<'a, Transform, (WrapPoint, CharPoint)>,
+    transforms: Cursor<'a, Transform, (WrapPoint, TabPoint)>,
 }
 
 #[derive(Clone)]
@@ -65,12 +65,12 @@ pub struct WrapBufferRows<'a> {
     output_row: u32,
     soft_wrapped: bool,
     max_output_row: u32,
-    transforms: Cursor<'a, Transform, (WrapPoint, CharPoint)>,
+    transforms: Cursor<'a, Transform, (WrapPoint, TabPoint)>,
 }
 
 impl WrapMap {
     pub fn new(
-        char_snapshot: CharSnapshot,
+        tab_snapshot: TabSnapshot,
         font: Font,
         font_size: Pixels,
         wrap_width: Option<Pixels>,
@@ -83,7 +83,7 @@ impl WrapMap {
                 pending_edits: Default::default(),
                 interpolated_edits: Default::default(),
                 edits_since_sync: Default::default(),
-                snapshot: WrapSnapshot::new(char_snapshot),
+                snapshot: WrapSnapshot::new(tab_snapshot),
                 background_task: None,
             };
             this.set_wrap_width(wrap_width, cx);
@@ -101,17 +101,17 @@ impl WrapMap {
 
     pub fn sync(
         &mut self,
-        char_snapshot: CharSnapshot,
+        tab_snapshot: TabSnapshot,
         edits: Vec<TabEdit>,
         cx: &mut ModelContext<Self>,
     ) -> (WrapSnapshot, Patch<u32>) {
         if self.wrap_width.is_some() {
-            self.pending_edits.push_back((char_snapshot, edits));
+            self.pending_edits.push_back((tab_snapshot, edits));
             self.flush_edits(cx);
         } else {
             self.edits_since_sync = self
                 .edits_since_sync
-                .compose(self.snapshot.interpolate(char_snapshot, &edits));
+                .compose(self.snapshot.interpolate(tab_snapshot, &edits));
             self.snapshot.interpolated = false;
         }
 
@@ -161,11 +161,11 @@ impl WrapMap {
             let (font, font_size) = self.font_with_size.clone();
             let task = cx.background_executor().spawn(async move {
                 let mut line_wrapper = text_system.line_wrapper(font, font_size);
-                let char_snapshot = new_snapshot.char_snapshot.clone();
-                let range = CharPoint::zero()..char_snapshot.max_point();
+                let tab_snapshot = new_snapshot.tab_snapshot.clone();
+                let range = TabPoint::zero()..tab_snapshot.max_point();
                 let edits = new_snapshot
                     .update(
-                        char_snapshot,
+                        tab_snapshot,
                         &[TabEdit {
                             old: range.clone(),
                             new: range.clone(),
@@ -205,7 +205,7 @@ impl WrapMap {
         } else {
             let old_rows = self.snapshot.transforms.summary().output.lines.row + 1;
             self.snapshot.transforms = SumTree::default();
-            let summary = self.snapshot.char_snapshot.text_summary();
+            let summary = self.snapshot.tab_snapshot.text_summary();
             if !summary.lines.is_zero() {
                 self.snapshot
                     .transforms
@@ -223,8 +223,8 @@ impl WrapMap {
     fn flush_edits(&mut self, cx: &mut ModelContext<Self>) {
         if !self.snapshot.interpolated {
             let mut to_remove_len = 0;
-            for (char_snapshot, _) in &self.pending_edits {
-                if char_snapshot.version <= self.snapshot.char_snapshot.version {
+            for (tab_snapshot, _) in &self.pending_edits {
+                if tab_snapshot.version <= self.snapshot.tab_snapshot.version {
                     to_remove_len += 1;
                 } else {
                     break;
@@ -246,9 +246,9 @@ impl WrapMap {
                 let update_task = cx.background_executor().spawn(async move {
                     let mut edits = Patch::default();
                     let mut line_wrapper = text_system.line_wrapper(font, font_size);
-                    for (char_snapshot, tab_edits) in pending_edits {
+                    for (tab_snapshot, tab_edits) in pending_edits {
                         let wrap_edits = snapshot
-                            .update(char_snapshot, &tab_edits, wrap_width, &mut line_wrapper)
+                            .update(tab_snapshot, &tab_edits, wrap_width, &mut line_wrapper)
                             .await;
                         edits = edits.compose(&wrap_edits);
                     }
@@ -285,11 +285,11 @@ impl WrapMap {
 
         let was_interpolated = self.snapshot.interpolated;
         let mut to_remove_len = 0;
-        for (char_snapshot, edits) in &self.pending_edits {
-            if char_snapshot.version <= self.snapshot.char_snapshot.version {
+        for (tab_snapshot, edits) in &self.pending_edits {
+            if tab_snapshot.version <= self.snapshot.tab_snapshot.version {
                 to_remove_len += 1;
             } else {
-                let interpolated_edits = self.snapshot.interpolate(char_snapshot.clone(), edits);
+                let interpolated_edits = self.snapshot.interpolate(tab_snapshot.clone(), edits);
                 self.edits_since_sync = self.edits_since_sync.compose(&interpolated_edits);
                 self.interpolated_edits = self.interpolated_edits.compose(&interpolated_edits);
             }
@@ -302,49 +302,45 @@ impl WrapMap {
 }
 
 impl WrapSnapshot {
-    fn new(char_snapshot: CharSnapshot) -> Self {
+    fn new(tab_snapshot: TabSnapshot) -> Self {
         let mut transforms = SumTree::default();
-        let extent = char_snapshot.text_summary();
+        let extent = tab_snapshot.text_summary();
         if !extent.lines.is_zero() {
             transforms.push(Transform::isomorphic(extent), &());
         }
         Self {
             transforms,
-            char_snapshot,
+            tab_snapshot,
             interpolated: true,
         }
     }
 
     pub fn buffer_snapshot(&self) -> &MultiBufferSnapshot {
-        self.char_snapshot.buffer_snapshot()
+        self.tab_snapshot.buffer_snapshot()
     }
 
-    fn interpolate(
-        &mut self,
-        new_char_snapshot: CharSnapshot,
-        tab_edits: &[TabEdit],
-    ) -> Patch<u32> {
+    fn interpolate(&mut self, new_tab_snapshot: TabSnapshot, tab_edits: &[TabEdit]) -> Patch<u32> {
         let mut new_transforms;
         if tab_edits.is_empty() {
             new_transforms = self.transforms.clone();
         } else {
-            let mut old_cursor = self.transforms.cursor::<CharPoint>(&());
+            let mut old_cursor = self.transforms.cursor::<TabPoint>(&());
 
             let mut tab_edits_iter = tab_edits.iter().peekable();
             new_transforms =
                 old_cursor.slice(&tab_edits_iter.peek().unwrap().old.start, Bias::Right, &());
 
             while let Some(edit) = tab_edits_iter.next() {
-                if edit.new.start > CharPoint::from(new_transforms.summary().input.lines) {
-                    let summary = new_char_snapshot.text_summary_for_range(
-                        CharPoint::from(new_transforms.summary().input.lines)..edit.new.start,
+                if edit.new.start > TabPoint::from(new_transforms.summary().input.lines) {
+                    let summary = new_tab_snapshot.text_summary_for_range(
+                        TabPoint::from(new_transforms.summary().input.lines)..edit.new.start,
                     );
                     new_transforms.push_or_extend(Transform::isomorphic(summary));
                 }
 
                 if !edit.new.is_empty() {
                     new_transforms.push_or_extend(Transform::isomorphic(
-                        new_char_snapshot.text_summary_for_range(edit.new.clone()),
+                        new_tab_snapshot.text_summary_for_range(edit.new.clone()),
                     ));
                 }
 
@@ -353,7 +349,7 @@ impl WrapSnapshot {
                     if next_edit.old.start > old_cursor.end(&()) {
                         if old_cursor.end(&()) > edit.old.end {
                             let summary = self
-                                .char_snapshot
+                                .tab_snapshot
                                 .text_summary_for_range(edit.old.end..old_cursor.end(&()));
                             new_transforms.push_or_extend(Transform::isomorphic(summary));
                         }
@@ -367,7 +363,7 @@ impl WrapSnapshot {
                 } else {
                     if old_cursor.end(&()) > edit.old.end {
                         let summary = self
-                            .char_snapshot
+                            .tab_snapshot
                             .text_summary_for_range(edit.old.end..old_cursor.end(&()));
                         new_transforms.push_or_extend(Transform::isomorphic(summary));
                     }
@@ -380,7 +376,7 @@ impl WrapSnapshot {
         let old_snapshot = mem::replace(
             self,
             WrapSnapshot {
-                char_snapshot: new_char_snapshot,
+                tab_snapshot: new_tab_snapshot,
                 transforms: new_transforms,
                 interpolated: true,
             },
@@ -391,7 +387,7 @@ impl WrapSnapshot {
 
     async fn update(
         &mut self,
-        new_char_snapshot: CharSnapshot,
+        new_tab_snapshot: TabSnapshot,
         tab_edits: &[TabEdit],
         wrap_width: Pixels,
         line_wrapper: &mut LineWrapper,
@@ -428,27 +424,27 @@ impl WrapSnapshot {
             new_transforms = self.transforms.clone();
         } else {
             let mut row_edits = row_edits.into_iter().peekable();
-            let mut old_cursor = self.transforms.cursor::<CharPoint>(&());
+            let mut old_cursor = self.transforms.cursor::<TabPoint>(&());
 
             new_transforms = old_cursor.slice(
-                &CharPoint::new(row_edits.peek().unwrap().old_rows.start, 0),
+                &TabPoint::new(row_edits.peek().unwrap().old_rows.start, 0),
                 Bias::Right,
                 &(),
             );
 
             while let Some(edit) = row_edits.next() {
                 if edit.new_rows.start > new_transforms.summary().input.lines.row {
-                    let summary = new_char_snapshot.text_summary_for_range(
-                        CharPoint(new_transforms.summary().input.lines)
-                            ..CharPoint::new(edit.new_rows.start, 0),
+                    let summary = new_tab_snapshot.text_summary_for_range(
+                        TabPoint(new_transforms.summary().input.lines)
+                            ..TabPoint::new(edit.new_rows.start, 0),
                     );
                     new_transforms.push_or_extend(Transform::isomorphic(summary));
                 }
 
                 let mut line = String::new();
                 let mut remaining = None;
-                let mut chunks = new_char_snapshot.chunks(
-                    CharPoint::new(edit.new_rows.start, 0)..new_char_snapshot.max_point(),
+                let mut chunks = new_tab_snapshot.chunks(
+                    TabPoint::new(edit.new_rows.start, 0)..new_tab_snapshot.max_point(),
                     false,
                     Highlights::default(),
                 );
@@ -495,19 +491,19 @@ impl WrapSnapshot {
                 }
                 new_transforms.extend(edit_transforms, &());
 
-                old_cursor.seek_forward(&CharPoint::new(edit.old_rows.end, 0), Bias::Right, &());
+                old_cursor.seek_forward(&TabPoint::new(edit.old_rows.end, 0), Bias::Right, &());
                 if let Some(next_edit) = row_edits.peek() {
                     if next_edit.old_rows.start > old_cursor.end(&()).row() {
-                        if old_cursor.end(&()) > CharPoint::new(edit.old_rows.end, 0) {
-                            let summary = self.char_snapshot.text_summary_for_range(
-                                CharPoint::new(edit.old_rows.end, 0)..old_cursor.end(&()),
+                        if old_cursor.end(&()) > TabPoint::new(edit.old_rows.end, 0) {
+                            let summary = self.tab_snapshot.text_summary_for_range(
+                                TabPoint::new(edit.old_rows.end, 0)..old_cursor.end(&()),
                             );
                             new_transforms.push_or_extend(Transform::isomorphic(summary));
                         }
                         old_cursor.next(&());
                         new_transforms.append(
                             old_cursor.slice(
-                                &CharPoint::new(next_edit.old_rows.start, 0),
+                                &TabPoint::new(next_edit.old_rows.start, 0),
                                 Bias::Right,
                                 &(),
                             ),
@@ -515,9 +511,9 @@ impl WrapSnapshot {
                         );
                     }
                 } else {
-                    if old_cursor.end(&()) > CharPoint::new(edit.old_rows.end, 0) {
-                        let summary = self.char_snapshot.text_summary_for_range(
-                            CharPoint::new(edit.old_rows.end, 0)..old_cursor.end(&()),
+                    if old_cursor.end(&()) > TabPoint::new(edit.old_rows.end, 0) {
+                        let summary = self.tab_snapshot.text_summary_for_range(
+                            TabPoint::new(edit.old_rows.end, 0)..old_cursor.end(&()),
                         );
                         new_transforms.push_or_extend(Transform::isomorphic(summary));
                     }
@@ -530,7 +526,7 @@ impl WrapSnapshot {
         let old_snapshot = mem::replace(
             self,
             WrapSnapshot {
-                char_snapshot: new_char_snapshot,
+                tab_snapshot: new_tab_snapshot,
                 transforms: new_transforms,
                 interpolated: false,
             },
@@ -583,17 +579,17 @@ impl WrapSnapshot {
     ) -> WrapChunks<'a> {
         let output_start = WrapPoint::new(rows.start, 0);
         let output_end = WrapPoint::new(rows.end, 0);
-        let mut transforms = self.transforms.cursor::<(WrapPoint, CharPoint)>(&());
+        let mut transforms = self.transforms.cursor::<(WrapPoint, TabPoint)>(&());
         transforms.seek(&output_start, Bias::Right, &());
-        let mut input_start = CharPoint(transforms.start().1 .0);
+        let mut input_start = TabPoint(transforms.start().1 .0);
         if transforms.item().map_or(false, |t| t.is_isomorphic()) {
             input_start.0 += output_start.0 - transforms.start().0 .0;
         }
         let input_end = self
-            .to_char_point(output_end)
-            .min(self.char_snapshot.max_point());
+            .to_tab_point(output_end)
+            .min(self.tab_snapshot.max_point());
         WrapChunks {
-            input_chunks: self.char_snapshot.chunks(
+            input_chunks: self.tab_snapshot.chunks(
                 input_start..input_end,
                 language_aware,
                 highlights,
@@ -610,7 +606,7 @@ impl WrapSnapshot {
     }
 
     pub fn line_len(&self, row: u32) -> u32 {
-        let mut cursor = self.transforms.cursor::<(WrapPoint, CharPoint)>(&());
+        let mut cursor = self.transforms.cursor::<(WrapPoint, TabPoint)>(&());
         cursor.seek(&WrapPoint::new(row + 1, 0), Bias::Left, &());
         if cursor
             .item()
@@ -618,7 +614,7 @@ impl WrapSnapshot {
         {
             let overshoot = row - cursor.start().0.row();
             let tab_row = cursor.start().1.row() + overshoot;
-            let tab_line_len = self.char_snapshot.line_len(tab_row);
+            let tab_line_len = self.tab_snapshot.line_len(tab_row);
             if overshoot == 0 {
                 cursor.start().0.column() + (tab_line_len - cursor.start().1.column())
             } else {
@@ -646,14 +642,14 @@ impl WrapSnapshot {
     }
 
     pub fn buffer_rows(&self, start_row: u32) -> WrapBufferRows {
-        let mut transforms = self.transforms.cursor::<(WrapPoint, CharPoint)>(&());
+        let mut transforms = self.transforms.cursor::<(WrapPoint, TabPoint)>(&());
         transforms.seek(&WrapPoint::new(start_row, 0), Bias::Left, &());
         let mut input_row = transforms.start().1.row();
         if transforms.item().map_or(false, |t| t.is_isomorphic()) {
             input_row += start_row - transforms.start().0.row();
         }
         let soft_wrapped = transforms.item().map_or(false, |t| !t.is_isomorphic());
-        let mut input_buffer_rows = self.char_snapshot.buffer_rows(input_row);
+        let mut input_buffer_rows = self.tab_snapshot.buffer_rows(input_row);
         let input_buffer_row = input_buffer_rows.next().unwrap();
         WrapBufferRows {
             transforms,
@@ -665,26 +661,26 @@ impl WrapSnapshot {
         }
     }
 
-    pub fn to_char_point(&self, point: WrapPoint) -> CharPoint {
-        let mut cursor = self.transforms.cursor::<(WrapPoint, CharPoint)>(&());
+    pub fn to_tab_point(&self, point: WrapPoint) -> TabPoint {
+        let mut cursor = self.transforms.cursor::<(WrapPoint, TabPoint)>(&());
         cursor.seek(&point, Bias::Right, &());
-        let mut char_point = cursor.start().1 .0;
+        let mut tab_point = cursor.start().1 .0;
         if cursor.item().map_or(false, |t| t.is_isomorphic()) {
-            char_point += point.0 - cursor.start().0 .0;
+            tab_point += point.0 - cursor.start().0 .0;
         }
-        CharPoint(char_point)
+        TabPoint(tab_point)
     }
 
     pub fn to_point(&self, point: WrapPoint, bias: Bias) -> Point {
-        self.char_snapshot.to_point(self.to_char_point(point), bias)
+        self.tab_snapshot.to_point(self.to_tab_point(point), bias)
     }
 
     pub fn make_wrap_point(&self, point: Point, bias: Bias) -> WrapPoint {
-        self.char_point_to_wrap_point(self.char_snapshot.make_char_point(point, bias))
+        self.tab_point_to_wrap_point(self.tab_snapshot.make_tab_point(point, bias))
     }
 
-    pub fn char_point_to_wrap_point(&self, point: CharPoint) -> WrapPoint {
-        let mut cursor = self.transforms.cursor::<(CharPoint, WrapPoint)>(&());
+    pub fn tab_point_to_wrap_point(&self, point: TabPoint) -> WrapPoint {
+        let mut cursor = self.transforms.cursor::<(TabPoint, WrapPoint)>(&());
         cursor.seek(&point, Bias::Right, &());
         WrapPoint(cursor.start().1 .0 + (point.0 - cursor.start().0 .0))
     }
@@ -699,10 +695,7 @@ impl WrapSnapshot {
             }
         }
 
-        self.char_point_to_wrap_point(
-            self.char_snapshot
-                .clip_point(self.to_char_point(point), bias),
-        )
+        self.tab_point_to_wrap_point(self.tab_snapshot.clip_point(self.to_tab_point(point), bias))
     }
 
     pub fn prev_row_boundary(&self, mut point: WrapPoint) -> u32 {
@@ -712,7 +705,7 @@ impl WrapSnapshot {
 
         *point.column_mut() = 0;
 
-        let mut cursor = self.transforms.cursor::<(WrapPoint, CharPoint)>(&());
+        let mut cursor = self.transforms.cursor::<(WrapPoint, TabPoint)>(&());
         cursor.seek(&point, Bias::Right, &());
         if cursor.item().is_none() {
             cursor.prev(&());
@@ -732,7 +725,7 @@ impl WrapSnapshot {
     pub fn next_row_boundary(&self, mut point: WrapPoint) -> Option<u32> {
         point.0 += Point::new(1, 0);
 
-        let mut cursor = self.transforms.cursor::<(WrapPoint, CharPoint)>(&());
+        let mut cursor = self.transforms.cursor::<(WrapPoint, TabPoint)>(&());
         cursor.seek(&point, Bias::Right, &());
         while let Some(transform) = cursor.item() {
             if transform.is_isomorphic() && cursor.start().1.column() == 0 {
@@ -749,8 +742,8 @@ impl WrapSnapshot {
         #[cfg(test)]
         {
             assert_eq!(
-                CharPoint::from(self.transforms.summary().input.lines),
-                self.char_snapshot.max_point()
+                TabPoint::from(self.transforms.summary().input.lines),
+                self.tab_snapshot.max_point()
             );
 
             {
@@ -763,18 +756,18 @@ impl WrapSnapshot {
             }
 
             let text = language::Rope::from(self.text().as_str());
-            let mut input_buffer_rows = self.char_snapshot.buffer_rows(0);
+            let mut input_buffer_rows = self.tab_snapshot.buffer_rows(0);
             let mut expected_buffer_rows = Vec::new();
             let mut prev_tab_row = 0;
             for display_row in 0..=self.max_point().row() {
-                let char_point = self.to_char_point(WrapPoint::new(display_row, 0));
-                if char_point.row() == prev_tab_row && display_row != 0 {
+                let tab_point = self.to_tab_point(WrapPoint::new(display_row, 0));
+                if tab_point.row() == prev_tab_row && display_row != 0 {
                     expected_buffer_rows.push(None);
                 } else {
                     expected_buffer_rows.push(input_buffer_rows.next().unwrap());
                 }
 
-                prev_tab_row = char_point.row();
+                prev_tab_row = tab_point.row();
                 assert_eq!(self.line_len(display_row), text.line_len(display_row));
             }
 
@@ -838,11 +831,13 @@ impl<'a> Iterator for WrapChunks<'a> {
             } else {
                 *self.output_position.column_mut() += char_len as u32;
             }
+
             if self.output_position >= transform_end {
                 self.transforms.next(&());
                 break;
             }
         }
+
         let (prefix, suffix) = self.input_chunk.text.split_at(input_len);
         self.input_chunk.text = suffix;
         Some(Chunk {
@@ -997,7 +992,7 @@ impl sum_tree::Summary for TransformSummary {
     }
 }
 
-impl<'a> sum_tree::Dimension<'a, TransformSummary> for CharPoint {
+impl<'a> sum_tree::Dimension<'a, TransformSummary> for TabPoint {
     fn zero(_cx: &()) -> Self {
         Default::default()
     }
@@ -1007,7 +1002,7 @@ impl<'a> sum_tree::Dimension<'a, TransformSummary> for CharPoint {
     }
 }
 
-impl<'a> sum_tree::SeekTarget<'a, TransformSummary, TransformSummary> for CharPoint {
+impl<'a> sum_tree::SeekTarget<'a, TransformSummary, TransformSummary> for TabPoint {
     fn cmp(&self, cursor_location: &TransformSummary, _: &()) -> std::cmp::Ordering {
         Ord::cmp(&self.0, &cursor_location.input.lines)
     }
@@ -1055,7 +1050,7 @@ fn consolidate_wrap_edits(edits: Vec<WrapEdit>) -> Vec<WrapEdit> {
 mod tests {
     use super::*;
     use crate::{
-        display_map::{char_map::CharMap, fold_map::FoldMap, inlay_map::InlayMap},
+        display_map::{fold_map::FoldMap, inlay_map::InlayMap, tab_map::TabMap},
         MultiBuffer,
     };
     use gpui::{font, px, test::observe};
@@ -1107,9 +1102,9 @@ mod tests {
         log::info!("InlayMap text: {:?}", inlay_snapshot.text());
         let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot.clone());
         log::info!("FoldMap text: {:?}", fold_snapshot.text());
-        let (mut char_map, _) = CharMap::new(fold_snapshot.clone(), tab_size);
-        let tabs_snapshot = char_map.set_max_expansion_column(32);
-        log::info!("CharMap text: {:?}", tabs_snapshot.text());
+        let (mut tab_map, _) = TabMap::new(fold_snapshot.clone(), tab_size);
+        let tabs_snapshot = tab_map.set_max_expansion_column(32);
+        log::info!("TabMap text: {:?}", tabs_snapshot.text());
 
         let mut line_wrapper = text_system.line_wrapper(font.clone(), font_size);
         let unwrapped_text = tabs_snapshot.text();
@@ -1155,7 +1150,7 @@ mod tests {
                 20..=39 => {
                     for (fold_snapshot, fold_edits) in fold_map.randomly_mutate(&mut rng) {
                         let (tabs_snapshot, tab_edits) =
-                            char_map.sync(fold_snapshot, fold_edits, tab_size);
+                            tab_map.sync(fold_snapshot, fold_edits, tab_size);
                         let (mut snapshot, wrap_edits) =
                             wrap_map.update(cx, |map, cx| map.sync(tabs_snapshot, tab_edits, cx));
                         snapshot.check_invariants();
@@ -1168,7 +1163,7 @@ mod tests {
                         inlay_map.randomly_mutate(&mut next_inlay_id, &mut rng);
                     let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
                     let (tabs_snapshot, tab_edits) =
-                        char_map.sync(fold_snapshot, fold_edits, tab_size);
+                        tab_map.sync(fold_snapshot, fold_edits, tab_size);
                     let (mut snapshot, wrap_edits) =
                         wrap_map.update(cx, |map, cx| map.sync(tabs_snapshot, tab_edits, cx));
                     snapshot.check_invariants();
@@ -1192,8 +1187,8 @@ mod tests {
             log::info!("InlayMap text: {:?}", inlay_snapshot.text());
             let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
             log::info!("FoldMap text: {:?}", fold_snapshot.text());
-            let (tabs_snapshot, tab_edits) = char_map.sync(fold_snapshot, fold_edits, tab_size);
-            log::info!("CharMap text: {:?}", tabs_snapshot.text());
+            let (tabs_snapshot, tab_edits) = tab_map.sync(fold_snapshot, fold_edits, tab_size);
+            log::info!("TabMap text: {:?}", tabs_snapshot.text());
 
             let unwrapped_text = tabs_snapshot.text();
             let expected_text = wrap_text(&unwrapped_text, wrap_width, &mut line_wrapper);
@@ -1239,7 +1234,7 @@ mod tests {
 
                 if tab_size.get() == 1
                     || !wrapped_snapshot
-                        .char_snapshot
+                        .tab_snapshot
                         .fold_snapshot
                         .text()
                         .contains('\t')

crates/editor/src/element.rs 🔗

@@ -68,7 +68,6 @@ use sum_tree::Bias;
 use theme::{ActiveTheme, Appearance, PlayerColor};
 use ui::prelude::*;
 use ui::{h_flex, ButtonLike, ButtonStyle, ContextMenu, Tooltip};
-use unicode_segmentation::UnicodeSegmentation;
 use util::RangeExt;
 use util::ResultExt;
 use workspace::{item::Item, Workspace};
@@ -1026,21 +1025,23 @@ impl EditorElement {
                     }
                     let block_text = if let CursorShape::Block = selection.cursor_shape {
                         snapshot
-                            .grapheme_at(cursor_position)
+                            .display_chars_at(cursor_position)
+                            .next()
                             .or_else(|| {
                                 if cursor_column == 0 {
-                                    snapshot.placeholder_text().and_then(|s| {
-                                        s.graphemes(true).next().map(|s| s.to_owned())
-                                    })
+                                    snapshot
+                                        .placeholder_text()
+                                        .and_then(|s| s.chars().next())
+                                        .map(|c| (c, cursor_position))
                                 } else {
                                     None
                                 }
                             })
-                            .and_then(|grapheme| {
-                                let text = if grapheme == "\n" {
+                            .and_then(|(character, _)| {
+                                let text = if character == '\n' {
                                     SharedString::from(" ")
                                 } else {
-                                    SharedString::from(grapheme)
+                                    SharedString::from(character.to_string())
                                 };
                                 let len = text.len();
 

crates/editor/src/hover_popover.rs 🔗

@@ -1,7 +1,6 @@
 use crate::{
     display_map::{InlayOffset, ToDisplayPoint},
     hover_links::{InlayHighlight, RangeInEditor},
-    is_invisible,
     scroll::ScrollAmount,
     Anchor, AnchorRangeExt, DisplayPoint, DisplayRow, Editor, EditorSettings, EditorSnapshot,
     Hover, RangeToAnchorExt,
@@ -12,7 +11,7 @@ use gpui::{
     StyleRefinement, Styled, Task, TextStyleRefinement, View, ViewContext,
 };
 use itertools::Itertools;
-use language::{Diagnostic, DiagnosticEntry, Language, LanguageRegistry};
+use language::{DiagnosticEntry, Language, LanguageRegistry};
 use lsp::DiagnosticSeverity;
 use markdown::{Markdown, MarkdownStyle};
 use multi_buffer::ToOffset;
@@ -200,6 +199,7 @@ fn show_hover(
     if editor.pending_rename.is_some() {
         return None;
     }
+
     let snapshot = editor.snapshot(cx);
 
     let (buffer, buffer_position) = editor
@@ -259,7 +259,7 @@ fn show_hover(
             }
 
             // If there's a diagnostic, assign it on the hover state and notify
-            let mut local_diagnostic = snapshot
+            let local_diagnostic = snapshot
                 .buffer_snapshot
                 .diagnostics_in_range::<_, usize>(anchor..anchor, false)
                 // Find the entry with the most specific range
@@ -281,42 +281,6 @@ fn show_hover(
                     })
             });
 
-            if let Some(invisible) = snapshot
-                .buffer_snapshot
-                .chars_at(anchor)
-                .next()
-                .filter(|&c| is_invisible(c))
-            {
-                let after = snapshot.buffer_snapshot.anchor_after(
-                    anchor.to_offset(&snapshot.buffer_snapshot) + invisible.len_utf8(),
-                );
-                local_diagnostic = Some(DiagnosticEntry {
-                    diagnostic: Diagnostic {
-                        severity: DiagnosticSeverity::HINT,
-                        message: format!("Unicode character U+{:02X}", invisible as u32),
-                        ..Default::default()
-                    },
-                    range: anchor..after,
-                })
-            } else if let Some(invisible) = snapshot
-                .buffer_snapshot
-                .reversed_chars_at(anchor)
-                .next()
-                .filter(|&c| is_invisible(c))
-            {
-                let before = snapshot.buffer_snapshot.anchor_before(
-                    anchor.to_offset(&snapshot.buffer_snapshot) - invisible.len_utf8(),
-                );
-                local_diagnostic = Some(DiagnosticEntry {
-                    diagnostic: Diagnostic {
-                        severity: DiagnosticSeverity::HINT,
-                        message: format!("Unicode character U+{:02X}", invisible as u32),
-                        ..Default::default()
-                    },
-                    range: before..anchor,
-                })
-            }
-
             let diagnostic_popover = if let Some(local_diagnostic) = local_diagnostic {
                 let text = match local_diagnostic.diagnostic.source {
                     Some(ref source) => {
@@ -324,6 +288,7 @@ fn show_hover(
                     }
                     None => local_diagnostic.diagnostic.message.clone(),
                 };
+
                 let mut border_color: Option<Hsla> = None;
                 let mut background_color: Option<Hsla> = None;
 
@@ -379,6 +344,7 @@ fn show_hover(
                         Markdown::new_text(text, markdown_style.clone(), None, cx, None)
                     })
                     .ok();
+
                 Some(DiagnosticPopover {
                     local_diagnostic,
                     primary_diagnostic,
@@ -466,6 +432,7 @@ fn show_hover(
                 cx.notify();
                 cx.refresh();
             })?;
+
             anyhow::Ok(())
         }
         .log_err()

crates/gpui/src/text_system/line.rs 🔗

@@ -1,7 +1,6 @@
 use crate::{
-    black, fill, point, px, size, Bounds, Half, Hsla, LineLayout, Pixels, Point, Result,
-    SharedString, StrikethroughStyle, UnderlineStyle, WindowContext, WrapBoundary,
-    WrappedLineLayout,
+    black, fill, point, px, size, Bounds, Hsla, LineLayout, Pixels, Point, Result, SharedString,
+    StrikethroughStyle, UnderlineStyle, WindowContext, WrapBoundary, WrappedLineLayout,
 };
 use derive_more::{Deref, DerefMut};
 use smallvec::SmallVec;
@@ -130,9 +129,8 @@ fn paint_line(
         let text_system = cx.text_system().clone();
         let mut glyph_origin = origin;
         let mut prev_glyph_position = Point::default();
-        let mut max_glyph_size = size(px(0.), px(0.));
         for (run_ix, run) in layout.runs.iter().enumerate() {
-            max_glyph_size = text_system.bounding_box(run.font_id, layout.font_size).size;
+            let max_glyph_size = text_system.bounding_box(run.font_id, layout.font_size).size;
 
             for (glyph_ix, glyph) in run.glyphs.iter().enumerate() {
                 glyph_origin.x += glyph.position.x - prev_glyph_position.x;
@@ -141,9 +139,6 @@ fn paint_line(
                     wraps.next();
                     if let Some((background_origin, background_color)) = current_background.as_mut()
                     {
-                        if glyph_origin.x == background_origin.x {
-                            background_origin.x -= max_glyph_size.width.half()
-                        }
                         cx.paint_quad(fill(
                             Bounds {
                                 origin: *background_origin,
@@ -155,9 +150,6 @@ fn paint_line(
                         background_origin.y += line_height;
                     }
                     if let Some((underline_origin, underline_style)) = current_underline.as_mut() {
-                        if glyph_origin.x == underline_origin.x {
-                            underline_origin.x -= max_glyph_size.width.half();
-                        };
                         cx.paint_underline(
                             *underline_origin,
                             glyph_origin.x - underline_origin.x,
@@ -169,9 +161,6 @@ fn paint_line(
                     if let Some((strikethrough_origin, strikethrough_style)) =
                         current_strikethrough.as_mut()
                     {
-                        if glyph_origin.x == strikethrough_origin.x {
-                            strikethrough_origin.x -= max_glyph_size.width.half();
-                        };
                         cx.paint_strikethrough(
                             *strikethrough_origin,
                             glyph_origin.x - strikethrough_origin.x,
@@ -190,18 +179,7 @@ fn paint_line(
                 let mut finished_underline: Option<(Point<Pixels>, UnderlineStyle)> = None;
                 let mut finished_strikethrough: Option<(Point<Pixels>, StrikethroughStyle)> = None;
                 if glyph.index >= run_end {
-                    let mut style_run = decoration_runs.next();
-
-                    // ignore style runs that apply to a partial glyph
-                    while let Some(run) = style_run {
-                        if glyph.index < run_end + (run.len as usize) {
-                            break;
-                        }
-                        run_end += run.len as usize;
-                        style_run = decoration_runs.next();
-                    }
-
-                    if let Some(style_run) = style_run {
+                    if let Some(style_run) = decoration_runs.next() {
                         if let Some((_, background_color)) = &mut current_background {
                             if style_run.background_color.as_ref() != Some(background_color) {
                                 finished_background = current_background.take();
@@ -262,14 +240,10 @@ fn paint_line(
                 }
 
                 if let Some((background_origin, background_color)) = finished_background {
-                    let mut width = glyph_origin.x - background_origin.x;
-                    if width == px(0.) {
-                        width = px(5.)
-                    };
                     cx.paint_quad(fill(
                         Bounds {
                             origin: background_origin,
-                            size: size(width, line_height),
+                            size: size(glyph_origin.x - background_origin.x, line_height),
                         },
                         background_color,
                     ));
@@ -325,10 +299,7 @@ fn paint_line(
             last_line_end_x -= glyph.position.x;
         }
 
-        if let Some((mut background_origin, background_color)) = current_background.take() {
-            if last_line_end_x == background_origin.x {
-                background_origin.x -= max_glyph_size.width.half()
-            };
+        if let Some((background_origin, background_color)) = current_background.take() {
             cx.paint_quad(fill(
                 Bounds {
                     origin: background_origin,
@@ -338,10 +309,7 @@ fn paint_line(
             ));
         }
 
-        if let Some((mut underline_start, underline_style)) = current_underline.take() {
-            if last_line_end_x == underline_start.x {
-                underline_start.x -= max_glyph_size.width.half()
-            };
+        if let Some((underline_start, underline_style)) = current_underline.take() {
             cx.paint_underline(
                 underline_start,
                 last_line_end_x - underline_start.x,
@@ -349,10 +317,7 @@ fn paint_line(
             );
         }
 
-        if let Some((mut strikethrough_start, strikethrough_style)) = current_strikethrough.take() {
-            if last_line_end_x == strikethrough_start.x {
-                strikethrough_start.x -= max_glyph_size.width.half()
-            };
+        if let Some((strikethrough_start, strikethrough_style)) = current_strikethrough.take() {
             cx.paint_strikethrough(
                 strikethrough_start,
                 last_line_end_x - strikethrough_start.x,

crates/language/src/buffer.rs 🔗

@@ -501,8 +501,6 @@ pub struct Chunk<'a> {
     pub is_unnecessary: bool,
     /// Whether this chunk of text was originally a tab character.
     pub is_tab: bool,
-    /// Whether this chunk of text is an invisible character.
-    pub is_invisible: bool,
     /// An optional recipe for how the chunk should be presented.
     pub renderer: Option<ChunkRenderer>,
 }
@@ -4213,6 +4211,7 @@ impl<'a> Iterator for BufferChunks<'a> {
             if self.range.start == self.chunks.offset() + chunk.len() {
                 self.chunks.next().unwrap();
             }
+
             Some(Chunk {
                 text: slice,
                 syntax_highlight_id: highlight_id,