Revert "Show invisibles in editor (#19298)" (#19752)

Closes: #19714

This reverts commit 6dcec47235.

Release Notes:

- (preview only) Fixes a crash when rendering invisibles
This commit is contained in:
Conrad Irwin 2024-10-25 11:59:22 -06:00 committed by GitHub
parent d40ea8fc81
commit 85bdd9329b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 260 additions and 563 deletions

1
Cargo.lock generated
View file

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

View file

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

View file

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

View file

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

View file

@ -1666,7 +1666,7 @@ fn offset_for_row(s: &str, target: u32) -> (u32, usize) {
mod tests { mod tests {
use super::*; use super::*;
use crate::display_map::{ 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 gpui::{div, font, px, AppContext, Context as _, Element};
use language::{Buffer, Capability}; use language::{Buffer, Capability};
@ -1701,9 +1701,9 @@ mod tests {
let subscription = buffer.update(cx, |buffer, _| buffer.subscribe()); let subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone()); let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot); 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) = 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 block_map = BlockMap::new(wraps_snapshot.clone(), true, 1, 1, 1);
let mut writer = block_map.write(wraps_snapshot.clone(), Default::default()); let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
@ -1851,10 +1851,10 @@ mod tests {
let (inlay_snapshot, inlay_edits) = let (inlay_snapshot, inlay_edits) =
inlay_map.sync(buffer_snapshot, subscription.consume().into_inner()); inlay_map.sync(buffer_snapshot, subscription.consume().into_inner());
let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits); let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
let (char_snapshot, tab_edits) = let (tab_snapshot, tab_edits) =
char_map.sync(fold_snapshot, fold_edits, 4.try_into().unwrap()); tab_map.sync(fold_snapshot, fold_edits, 4.try_into().unwrap());
let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| { 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); let snapshot = block_map.read(wraps_snapshot, wrap_edits);
assert_eq!(snapshot.text(), "aaa\n\nb!!!\n\n\nbb\nccc\nddd\n\n\n"); assert_eq!(snapshot.text(), "aaa\n\nb!!!\n\n\nbb\nccc\nddd\n\n\n");
@ -1914,9 +1914,8 @@ mod tests {
let multi_buffer_snapshot = multi_buffer.read(cx).snapshot(cx); let multi_buffer_snapshot = multi_buffer.read(cx).snapshot(cx);
let (_, inlay_snapshot) = InlayMap::new(multi_buffer_snapshot.clone()); let (_, inlay_snapshot) = InlayMap::new(multi_buffer_snapshot.clone());
let (_, fold_snapshot) = FoldMap::new(inlay_snapshot); 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) = let (_, wraps_snapshot) = WrapMap::new(tab_snapshot, font, font_size, Some(wrap_width), cx);
WrapMap::new(char_snapshot, font, font_size, Some(wrap_width), cx);
let block_map = BlockMap::new(wraps_snapshot.clone(), true, 1, 1, 1); let block_map = BlockMap::new(wraps_snapshot.clone(), true, 1, 1, 1);
let snapshot = block_map.read(wraps_snapshot, Default::default()); let snapshot = block_map.read(wraps_snapshot, Default::default());
@ -1953,9 +1952,9 @@ mod tests {
let _subscription = buffer.update(cx, |buffer, _| buffer.subscribe()); let _subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
let (_inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone()); let (_inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
let (_fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot); 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) = 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 block_map = BlockMap::new(wraps_snapshot.clone(), false, 1, 1, 0);
let mut writer = block_map.write(wraps_snapshot.clone(), Default::default()); let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
@ -2055,15 +2054,9 @@ mod tests {
let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx)); let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone()); let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
let (_, fold_snapshot) = FoldMap::new(inlay_snapshot); 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| { let (_, wraps_snapshot) = cx.update(|cx| {
WrapMap::new( WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), Some(px(60.)), cx)
char_snapshot,
font("Helvetica"),
px(14.0),
Some(px(60.)),
cx,
)
}); });
let mut block_map = BlockMap::new(wraps_snapshot.clone(), true, 1, 1, 0); let mut block_map = BlockMap::new(wraps_snapshot.clone(), true, 1, 1, 0);
@ -2106,7 +2099,7 @@ mod tests {
let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone()); let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot); let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
let tab_size = 1.try_into().unwrap(); let tab_size = 1.try_into().unwrap();
let (mut tab_map, tab_snapshot) = CharMap::new(fold_snapshot, tab_size); let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, tab_size);
let (wrap_map, wraps_snapshot) = let (wrap_map, wraps_snapshot) =
cx.update(|cx| WrapMap::new(tab_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 block_map = BlockMap::new(wraps_snapshot.clone(), false, 1, 1, 0);
@ -2257,9 +2250,9 @@ mod tests {
let mut buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx)); 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 inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot); 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 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( let mut block_map = BlockMap::new(
wraps_snapshot, wraps_snapshot,
true, true,
@ -2321,10 +2314,10 @@ mod tests {
let (inlay_snapshot, inlay_edits) = let (inlay_snapshot, inlay_edits) =
inlay_map.sync(buffer_snapshot.clone(), vec![]); inlay_map.sync(buffer_snapshot.clone(), vec![]);
let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits); let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
let (char_snapshot, tab_edits) = let (tab_snapshot, tab_edits) =
char_map.sync(fold_snapshot, fold_edits, tab_size); tab_map.sync(fold_snapshot, fold_edits, tab_size);
let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| { 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 mut block_map = block_map.write(wraps_snapshot, wrap_edits);
block_map.insert(block_properties.iter().map(|props| BlockProperties { block_map.insert(block_properties.iter().map(|props| BlockProperties {
@ -2346,10 +2339,10 @@ mod tests {
let (inlay_snapshot, inlay_edits) = let (inlay_snapshot, inlay_edits) =
inlay_map.sync(buffer_snapshot.clone(), vec![]); inlay_map.sync(buffer_snapshot.clone(), vec![]);
let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits); let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
let (char_snapshot, tab_edits) = let (tab_snapshot, tab_edits) =
char_map.sync(fold_snapshot, fold_edits, tab_size); tab_map.sync(fold_snapshot, fold_edits, tab_size);
let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| { 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 mut block_map = block_map.write(wraps_snapshot, wrap_edits);
block_map.remove(block_ids_to_remove); block_map.remove(block_ids_to_remove);
@ -2369,9 +2362,9 @@ mod tests {
let (inlay_snapshot, inlay_edits) = let (inlay_snapshot, inlay_edits) =
inlay_map.sync(buffer_snapshot.clone(), buffer_edits); inlay_map.sync(buffer_snapshot.clone(), buffer_edits);
let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_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| { 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); let blocks_snapshot = block_map.read(wraps_snapshot.clone(), wrap_edits);
assert_eq!( assert_eq!(
@ -2486,7 +2479,7 @@ mod tests {
.row as usize]; .row as usize];
let soft_wrapped = wraps_snapshot let soft_wrapped = wraps_snapshot
.to_char_point(WrapPoint::new(wrap_row, 0)) .to_tab_point(WrapPoint::new(wrap_row, 0))
.column() .column()
> 0; > 0;
expected_buffer_rows.push(if soft_wrapped { None } else { buffer_row }); expected_buffer_rows.push(if soft_wrapped { None } else { buffer_row });

View file

@ -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
}

View file

@ -1,6 +1,5 @@
use super::{ use super::{
fold_map::{self, FoldChunks, FoldEdit, FoldPoint, FoldSnapshot}, fold_map::{self, FoldChunks, FoldEdit, FoldPoint, FoldSnapshot},
invisibles::{is_invisible, replacement},
Highlights, Highlights,
}; };
use language::{Chunk, Point}; use language::{Chunk, Point};
@ -10,14 +9,14 @@ use sum_tree::Bias;
const MAX_EXPANSION_COLUMN: u32 = 256; 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. /// See the [`display_map` module documentation](crate::display_map) for more information.
pub struct CharMap(CharSnapshot); pub struct TabMap(TabSnapshot);
impl CharMap { impl TabMap {
pub fn new(fold_snapshot: FoldSnapshot, tab_size: NonZeroU32) -> (Self, CharSnapshot) { pub fn new(fold_snapshot: FoldSnapshot, tab_size: NonZeroU32) -> (Self, TabSnapshot) {
let snapshot = CharSnapshot { let snapshot = TabSnapshot {
fold_snapshot, fold_snapshot,
tab_size, tab_size,
max_expansion_column: MAX_EXPANSION_COLUMN, max_expansion_column: MAX_EXPANSION_COLUMN,
@ -27,7 +26,7 @@ impl CharMap {
} }
#[cfg(test)] #[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.max_expansion_column = column;
self.0.clone() self.0.clone()
} }
@ -37,9 +36,9 @@ impl CharMap {
fold_snapshot: FoldSnapshot, fold_snapshot: FoldSnapshot,
mut fold_edits: Vec<FoldEdit>, mut fold_edits: Vec<FoldEdit>,
tab_size: NonZeroU32, tab_size: NonZeroU32,
) -> (CharSnapshot, Vec<TabEdit>) { ) -> (TabSnapshot, Vec<TabEdit>) {
let old_snapshot = &mut self.0; let old_snapshot = &mut self.0;
let mut new_snapshot = CharSnapshot { let mut new_snapshot = TabSnapshot {
fold_snapshot, fold_snapshot,
tab_size, tab_size,
max_expansion_column: old_snapshot.max_expansion_column, 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_start = fold_edit.new.start.to_point(&new_snapshot.fold_snapshot);
let new_end = fold_edit.new.end.to_point(&new_snapshot.fold_snapshot); let new_end = fold_edit.new.end.to_point(&new_snapshot.fold_snapshot);
tab_edits.push(TabEdit { tab_edits.push(TabEdit {
old: old_snapshot.to_char_point(old_start)..old_snapshot.to_char_point(old_end), old: old_snapshot.to_tab_point(old_start)..old_snapshot.to_tab_point(old_end),
new: new_snapshot.to_char_point(new_start)..new_snapshot.to_char_point(new_end), new: new_snapshot.to_tab_point(new_start)..new_snapshot.to_tab_point(new_end),
}); });
} }
} else { } else {
new_snapshot.version += 1; new_snapshot.version += 1;
tab_edits.push(TabEdit { tab_edits.push(TabEdit {
old: CharPoint::zero()..old_snapshot.max_point(), old: TabPoint::zero()..old_snapshot.max_point(),
new: CharPoint::zero()..new_snapshot.max_point(), new: TabPoint::zero()..new_snapshot.max_point(),
}); });
} }
@ -156,14 +155,14 @@ impl CharMap {
} }
#[derive(Clone)] #[derive(Clone)]
pub struct CharSnapshot { pub struct TabSnapshot {
pub fold_snapshot: FoldSnapshot, pub fold_snapshot: FoldSnapshot,
pub tab_size: NonZeroU32, pub tab_size: NonZeroU32,
pub max_expansion_column: u32, pub max_expansion_column: u32,
pub version: usize, pub version: usize,
} }
impl CharSnapshot { impl TabSnapshot {
pub fn buffer_snapshot(&self) -> &MultiBufferSnapshot { pub fn buffer_snapshot(&self) -> &MultiBufferSnapshot {
&self.fold_snapshot.inlay_snapshot.buffer &self.fold_snapshot.inlay_snapshot.buffer
} }
@ -171,7 +170,7 @@ impl CharSnapshot {
pub fn line_len(&self, row: u32) -> u32 { pub fn line_len(&self, row: u32) -> u32 {
let max_point = self.max_point(); let max_point = self.max_point();
if row < max_point.row() { 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 .0
.column .column
} else { } else {
@ -180,10 +179,10 @@ impl CharSnapshot {
} }
pub fn text_summary(&self) -> TextSummary { 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_start = self.to_fold_point(range.start, Bias::Left).0;
let input_end = self.to_fold_point(range.end, Bias::Right).0; let input_end = self.to_fold_point(range.end, Bias::Right).0;
let input_summary = self let input_summary = self
@ -212,7 +211,7 @@ impl CharSnapshot {
} else { } else {
for _ in self for _ in self
.chunks( .chunks(
CharPoint::new(range.end.row(), 0)..range.end, TabPoint::new(range.end.row(), 0)..range.end,
false, false,
Highlights::default(), Highlights::default(),
) )
@ -233,7 +232,7 @@ impl CharSnapshot {
pub fn chunks<'a>( pub fn chunks<'a>(
&'a self, &'a self,
range: Range<CharPoint>, range: Range<TabPoint>,
language_aware: bool, language_aware: bool,
highlights: Highlights<'a>, highlights: Highlights<'a>,
) -> TabChunks<'a> { ) -> TabChunks<'a> {
@ -280,7 +279,7 @@ impl CharSnapshot {
#[cfg(test)] #[cfg(test)]
pub fn text(&self) -> String { pub fn text(&self) -> String {
self.chunks( self.chunks(
CharPoint::zero()..self.max_point(), TabPoint::zero()..self.max_point(),
false, false,
Highlights::default(), Highlights::default(),
) )
@ -288,24 +287,24 @@ impl CharSnapshot {
.collect() .collect()
} }
pub fn max_point(&self) -> CharPoint { pub fn max_point(&self) -> TabPoint {
self.to_char_point(self.fold_snapshot.max_point()) self.to_tab_point(self.fold_snapshot.max_point())
} }
pub fn clip_point(&self, point: CharPoint, bias: Bias) -> CharPoint { pub fn clip_point(&self, point: TabPoint, bias: Bias) -> TabPoint {
self.to_char_point( self.to_tab_point(
self.fold_snapshot self.fold_snapshot
.clip_point(self.to_fold_point(point, bias).0, bias), .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 chars = self.fold_snapshot.chars_at(FoldPoint::new(input.row(), 0));
let expanded = self.expand_tabs(chars, input.column()); 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 chars = self.fold_snapshot.chars_at(FoldPoint::new(output.row(), 0));
let expanded = output.column(); let expanded = output.column();
let (collapsed, expanded_char_column, to_next_stop) = let (collapsed, expanded_char_column, to_next_stop) =
@ -317,13 +316,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 inlay_point = self.fold_snapshot.inlay_snapshot.to_inlay_point(point);
let fold_point = self.fold_snapshot.to_fold_point(inlay_point, bias); 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 fold_point = self.to_fold_point(point, bias).0;
let inlay_point = fold_point.to_inlay_point(&self.fold_snapshot); let inlay_point = fold_point.to_inlay_point(&self.fold_snapshot);
self.fold_snapshot self.fold_snapshot
@ -346,9 +345,6 @@ impl CharSnapshot {
let tab_len = tab_size - expanded_chars % tab_size; let tab_len = tab_size - expanded_chars % tab_size;
expanded_bytes += tab_len; expanded_bytes += tab_len;
expanded_chars += 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 { } else {
expanded_bytes += c.len_utf8() as u32; expanded_bytes += c.len_utf8() as u32;
expanded_chars += 1; expanded_chars += 1;
@ -388,9 +384,6 @@ impl CharSnapshot {
Bias::Right => (collapsed_bytes + 1, expanded_chars, 0), 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 { } else {
expanded_chars += 1; expanded_chars += 1;
expanded_bytes += c.len_utf8() as u32; expanded_bytes += c.len_utf8() as u32;
@ -412,9 +405,9 @@ impl CharSnapshot {
} }
#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)] #[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 { pub fn new(row: u32, column: u32) -> Self {
Self(Point::new(row, column)) Self(Point::new(row, column))
} }
@ -432,13 +425,13 @@ impl CharPoint {
} }
} }
impl From<Point> for CharPoint { impl From<Point> for TabPoint {
fn from(point: Point) -> Self { fn from(point: Point) -> Self {
Self(point) Self(point)
} }
} }
pub type TabEdit = text::Edit<CharPoint>; pub type TabEdit = text::Edit<TabPoint>;
#[derive(Clone, Debug, Default, Eq, PartialEq)] #[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct TextSummary { pub struct TextSummary {
@ -493,7 +486,7 @@ impl<'a> std::ops::AddAssign<&'a Self> for TextSummary {
const SPACES: &str = " "; const SPACES: &str = " ";
pub struct TabChunks<'a> { pub struct TabChunks<'a> {
snapshot: &'a CharSnapshot, snapshot: &'a TabSnapshot,
fold_chunks: FoldChunks<'a>, fold_chunks: FoldChunks<'a>,
chunk: Chunk<'a>, chunk: Chunk<'a>,
column: u32, column: u32,
@ -506,7 +499,7 @@ pub struct TabChunks<'a> {
} }
impl<'a> TabChunks<'a> { impl<'a> TabChunks<'a> {
pub(crate) fn seek(&mut self, range: Range<CharPoint>) { pub(crate) fn seek(&mut self, range: Range<TabPoint>) {
let (input_start, expanded_char_column, to_next_stop) = let (input_start, expanded_char_column, to_next_stop) =
self.snapshot.to_fold_point(range.start, Bias::Left); self.snapshot.to_fold_point(range.start, Bias::Left);
let input_column = input_start.column(); let input_column = input_start.column();
@ -591,37 +584,6 @@ impl<'a> Iterator for TabChunks<'a> {
self.input_column = 0; self.input_column = 0;
self.output_position += Point::new(1, 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; self.column += 1;
if !self.inside_leading_tab { if !self.inside_leading_tab {
@ -651,11 +613,11 @@ mod tests {
let buffer_snapshot = buffer.read(cx).snapshot(cx); let buffer_snapshot = buffer.read(cx).snapshot(cx);
let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone()); let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
let (_, fold_snapshot) = FoldMap::new(inlay_snapshot); 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!(tab_snapshot.expand_tabs("\t".chars(), 0), 0);
assert_eq!(char_snapshot.expand_tabs("\t".chars(), 1), 4); assert_eq!(tab_snapshot.expand_tabs("\t".chars(), 1), 4);
assert_eq!(char_snapshot.expand_tabs("\ta".chars(), 2), 5); assert_eq!(tab_snapshot.expand_tabs("\ta".chars(), 2), 5);
} }
#[gpui::test] #[gpui::test]
@ -668,16 +630,16 @@ mod tests {
let buffer_snapshot = buffer.read(cx).snapshot(cx); let buffer_snapshot = buffer.read(cx).snapshot(cx);
let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone()); let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
let (_, fold_snapshot) = FoldMap::new(inlay_snapshot); 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; tab_snapshot.max_expansion_column = max_expansion_column;
assert_eq!(char_snapshot.text(), output); assert_eq!(tab_snapshot.text(), output);
for (ix, c) in input.char_indices() { for (ix, c) in input.char_indices() {
assert_eq!( assert_eq!(
char_snapshot tab_snapshot
.chunks( .chunks(
CharPoint::new(0, ix as u32)..char_snapshot.max_point(), TabPoint::new(0, ix as u32)..tab_snapshot.max_point(),
false, false,
Highlights::default(), Highlights::default(),
) )
@ -691,13 +653,13 @@ mod tests {
let input_point = Point::new(0, ix as u32); let input_point = Point::new(0, ix as u32);
let output_point = Point::new(0, output.find(c).unwrap() as u32); let output_point = Point::new(0, output.find(c).unwrap() as u32);
assert_eq!( assert_eq!(
char_snapshot.to_char_point(FoldPoint(input_point)), tab_snapshot.to_tab_point(FoldPoint(input_point)),
CharPoint(output_point), TabPoint(output_point),
"to_char_point({input_point:?})" "to_tab_point({input_point:?})"
); );
assert_eq!( assert_eq!(
char_snapshot tab_snapshot
.to_fold_point(CharPoint(output_point), Bias::Left) .to_fold_point(TabPoint(output_point), Bias::Left)
.0, .0,
FoldPoint(input_point), FoldPoint(input_point),
"to_fold_point({output_point:?})" "to_fold_point({output_point:?})"
@ -715,10 +677,10 @@ mod tests {
let buffer_snapshot = buffer.read(cx).snapshot(cx); let buffer_snapshot = buffer.read(cx).snapshot(cx);
let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone()); let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
let (_, fold_snapshot) = FoldMap::new(inlay_snapshot); 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; tab_snapshot.max_expansion_column = max_expansion_column;
assert_eq!(char_snapshot.text(), input); assert_eq!(tab_snapshot.text(), input);
} }
#[gpui::test] #[gpui::test]
@ -729,10 +691,10 @@ mod tests {
let buffer_snapshot = buffer.read(cx).snapshot(cx); let buffer_snapshot = buffer.read(cx).snapshot(cx);
let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone()); let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
let (_, fold_snapshot) = FoldMap::new(inlay_snapshot); 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!( assert_eq!(
chunks(&char_snapshot, CharPoint::zero()), chunks(&tab_snapshot, TabPoint::zero()),
vec![ vec![
(" ".to_string(), true), (" ".to_string(), true),
(" ".to_string(), false), (" ".to_string(), false),
@ -741,7 +703,7 @@ mod tests {
] ]
); );
assert_eq!( assert_eq!(
chunks(&char_snapshot, CharPoint::new(0, 2)), chunks(&tab_snapshot, TabPoint::new(0, 2)),
vec![ vec![
(" ".to_string(), true), (" ".to_string(), true),
(" ".to_string(), false), (" ".to_string(), false),
@ -750,7 +712,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 chunks = Vec::new();
let mut was_tab = false; let mut was_tab = false;
let mut text = String::new(); let mut text = String::new();
@ -796,12 +758,12 @@ mod tests {
let (inlay_snapshot, _) = inlay_map.randomly_mutate(&mut 0, &mut rng); let (inlay_snapshot, _) = inlay_map.randomly_mutate(&mut 0, &mut rng);
log::info!("InlayMap text: {:?}", inlay_snapshot.text()); log::info!("InlayMap text: {:?}", inlay_snapshot.text());
let (mut char_map, _) = CharMap::new(fold_snapshot.clone(), tab_size); let (mut tab_map, _) = TabMap::new(fold_snapshot.clone(), tab_size);
let tabs_snapshot = char_map.set_max_expansion_column(32); let tabs_snapshot = tab_map.set_max_expansion_column(32);
let text = text::Rope::from(tabs_snapshot.text().as_str()); let text = text::Rope::from(tabs_snapshot.text().as_str());
log::info!( log::info!(
"CharMap text (tab size: {}): {:?}", "TabMap text (tab size: {}): {:?}",
tab_size, tab_size,
tabs_snapshot.text(), tabs_snapshot.text(),
); );
@ -809,11 +771,11 @@ mod tests {
for _ in 0..5 { for _ in 0..5 {
let end_row = rng.gen_range(0..=text.max_point().row); let end_row = rng.gen_range(0..=text.max_point().row);
let end_column = rng.gen_range(0..=text.line_len(end_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_row = rng.gen_range(0..=text.max_point().row);
let start_column = rng.gen_range(0..=text.line_len(start_row)); let start_column = rng.gen_range(0..=text.line_len(start_row));
let mut start = 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 { if start > end {
mem::swap(&mut start, &mut end); mem::swap(&mut start, &mut end);
} }

View file

@ -1,6 +1,6 @@
use super::{ use super::{
char_map::{self, CharPoint, CharSnapshot, TabEdit},
fold_map::FoldBufferRows, fold_map::FoldBufferRows,
tab_map::{self, TabEdit, TabPoint, TabSnapshot},
Highlights, Highlights,
}; };
use gpui::{AppContext, Context, Font, LineWrapper, Model, ModelContext, Pixels, Task}; 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 sum_tree::{Bias, Cursor, SumTree};
use text::Patch; use text::Patch;
pub use super::char_map::TextSummary; pub use super::tab_map::TextSummary;
pub type WrapEdit = text::Edit<u32>; pub type WrapEdit = text::Edit<u32>;
/// Handles soft wrapping of text. /// 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. /// See the [`display_map` module documentation](crate::display_map) for more information.
pub struct WrapMap { pub struct WrapMap {
snapshot: WrapSnapshot, snapshot: WrapSnapshot,
pending_edits: VecDeque<(CharSnapshot, Vec<TabEdit>)>, pending_edits: VecDeque<(TabSnapshot, Vec<TabEdit>)>,
interpolated_edits: Patch<u32>, interpolated_edits: Patch<u32>,
edits_since_sync: Patch<u32>, edits_since_sync: Patch<u32>,
wrap_width: Option<Pixels>, wrap_width: Option<Pixels>,
@ -30,7 +30,7 @@ pub struct WrapMap {
#[derive(Clone)] #[derive(Clone)]
pub struct WrapSnapshot { pub struct WrapSnapshot {
char_snapshot: CharSnapshot, tab_snapshot: TabSnapshot,
transforms: SumTree<Transform>, transforms: SumTree<Transform>,
interpolated: bool, interpolated: bool,
} }
@ -51,11 +51,11 @@ struct TransformSummary {
pub struct WrapPoint(pub Point); pub struct WrapPoint(pub Point);
pub struct WrapChunks<'a> { pub struct WrapChunks<'a> {
input_chunks: char_map::TabChunks<'a>, input_chunks: tab_map::TabChunks<'a>,
input_chunk: Chunk<'a>, input_chunk: Chunk<'a>,
output_position: WrapPoint, output_position: WrapPoint,
max_output_row: u32, max_output_row: u32,
transforms: Cursor<'a, Transform, (WrapPoint, CharPoint)>, transforms: Cursor<'a, Transform, (WrapPoint, TabPoint)>,
snapshot: &'a WrapSnapshot, snapshot: &'a WrapSnapshot,
} }
@ -66,7 +66,7 @@ pub struct WrapBufferRows<'a> {
output_row: u32, output_row: u32,
soft_wrapped: bool, soft_wrapped: bool,
max_output_row: u32, max_output_row: u32,
transforms: Cursor<'a, Transform, (WrapPoint, CharPoint)>, transforms: Cursor<'a, Transform, (WrapPoint, TabPoint)>,
} }
impl<'a> WrapBufferRows<'a> { impl<'a> WrapBufferRows<'a> {
@ -86,7 +86,7 @@ impl<'a> WrapBufferRows<'a> {
impl WrapMap { impl WrapMap {
pub fn new( pub fn new(
char_snapshot: CharSnapshot, tab_snapshot: TabSnapshot,
font: Font, font: Font,
font_size: Pixels, font_size: Pixels,
wrap_width: Option<Pixels>, wrap_width: Option<Pixels>,
@ -99,7 +99,7 @@ impl WrapMap {
pending_edits: Default::default(), pending_edits: Default::default(),
interpolated_edits: Default::default(), interpolated_edits: Default::default(),
edits_since_sync: Default::default(), edits_since_sync: Default::default(),
snapshot: WrapSnapshot::new(char_snapshot), snapshot: WrapSnapshot::new(tab_snapshot),
background_task: None, background_task: None,
}; };
this.set_wrap_width(wrap_width, cx); this.set_wrap_width(wrap_width, cx);
@ -117,17 +117,17 @@ impl WrapMap {
pub fn sync( pub fn sync(
&mut self, &mut self,
char_snapshot: CharSnapshot, tab_snapshot: TabSnapshot,
edits: Vec<TabEdit>, edits: Vec<TabEdit>,
cx: &mut ModelContext<Self>, cx: &mut ModelContext<Self>,
) -> (WrapSnapshot, Patch<u32>) { ) -> (WrapSnapshot, Patch<u32>) {
if self.wrap_width.is_some() { 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); self.flush_edits(cx);
} else { } else {
self.edits_since_sync = self self.edits_since_sync = self
.edits_since_sync .edits_since_sync
.compose(self.snapshot.interpolate(char_snapshot, &edits)); .compose(self.snapshot.interpolate(tab_snapshot, &edits));
self.snapshot.interpolated = false; self.snapshot.interpolated = false;
} }
@ -177,11 +177,11 @@ impl WrapMap {
let (font, font_size) = self.font_with_size.clone(); let (font, font_size) = self.font_with_size.clone();
let task = cx.background_executor().spawn(async move { let task = cx.background_executor().spawn(async move {
let mut line_wrapper = text_system.line_wrapper(font, font_size); let mut line_wrapper = text_system.line_wrapper(font, font_size);
let char_snapshot = new_snapshot.char_snapshot.clone(); let tab_snapshot = new_snapshot.tab_snapshot.clone();
let range = CharPoint::zero()..char_snapshot.max_point(); let range = TabPoint::zero()..tab_snapshot.max_point();
let edits = new_snapshot let edits = new_snapshot
.update( .update(
char_snapshot, tab_snapshot,
&[TabEdit { &[TabEdit {
old: range.clone(), old: range.clone(),
new: range.clone(), new: range.clone(),
@ -221,7 +221,7 @@ impl WrapMap {
} else { } else {
let old_rows = self.snapshot.transforms.summary().output.lines.row + 1; let old_rows = self.snapshot.transforms.summary().output.lines.row + 1;
self.snapshot.transforms = SumTree::default(); 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() { if !summary.lines.is_zero() {
self.snapshot self.snapshot
.transforms .transforms
@ -239,8 +239,8 @@ impl WrapMap {
fn flush_edits(&mut self, cx: &mut ModelContext<Self>) { fn flush_edits(&mut self, cx: &mut ModelContext<Self>) {
if !self.snapshot.interpolated { if !self.snapshot.interpolated {
let mut to_remove_len = 0; let mut to_remove_len = 0;
for (char_snapshot, _) in &self.pending_edits { for (tab_snapshot, _) in &self.pending_edits {
if char_snapshot.version <= self.snapshot.char_snapshot.version { if tab_snapshot.version <= self.snapshot.tab_snapshot.version {
to_remove_len += 1; to_remove_len += 1;
} else { } else {
break; break;
@ -262,9 +262,9 @@ impl WrapMap {
let update_task = cx.background_executor().spawn(async move { let update_task = cx.background_executor().spawn(async move {
let mut edits = Patch::default(); let mut edits = Patch::default();
let mut line_wrapper = text_system.line_wrapper(font, font_size); 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 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; .await;
edits = edits.compose(&wrap_edits); edits = edits.compose(&wrap_edits);
} }
@ -301,11 +301,11 @@ impl WrapMap {
let was_interpolated = self.snapshot.interpolated; let was_interpolated = self.snapshot.interpolated;
let mut to_remove_len = 0; let mut to_remove_len = 0;
for (char_snapshot, edits) in &self.pending_edits { for (tab_snapshot, edits) in &self.pending_edits {
if char_snapshot.version <= self.snapshot.char_snapshot.version { if tab_snapshot.version <= self.snapshot.tab_snapshot.version {
to_remove_len += 1; to_remove_len += 1;
} else { } 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.edits_since_sync = self.edits_since_sync.compose(&interpolated_edits);
self.interpolated_edits = self.interpolated_edits.compose(&interpolated_edits); self.interpolated_edits = self.interpolated_edits.compose(&interpolated_edits);
} }
@ -318,49 +318,45 @@ impl WrapMap {
} }
impl WrapSnapshot { impl WrapSnapshot {
fn new(char_snapshot: CharSnapshot) -> Self { fn new(tab_snapshot: TabSnapshot) -> Self {
let mut transforms = SumTree::default(); let mut transforms = SumTree::default();
let extent = char_snapshot.text_summary(); let extent = tab_snapshot.text_summary();
if !extent.lines.is_zero() { if !extent.lines.is_zero() {
transforms.push(Transform::isomorphic(extent), &()); transforms.push(Transform::isomorphic(extent), &());
} }
Self { Self {
transforms, transforms,
char_snapshot, tab_snapshot,
interpolated: true, interpolated: true,
} }
} }
pub fn buffer_snapshot(&self) -> &MultiBufferSnapshot { pub fn buffer_snapshot(&self) -> &MultiBufferSnapshot {
self.char_snapshot.buffer_snapshot() self.tab_snapshot.buffer_snapshot()
} }
fn interpolate( fn interpolate(&mut self, new_tab_snapshot: TabSnapshot, tab_edits: &[TabEdit]) -> Patch<u32> {
&mut self,
new_char_snapshot: CharSnapshot,
tab_edits: &[TabEdit],
) -> Patch<u32> {
let mut new_transforms; let mut new_transforms;
if tab_edits.is_empty() { if tab_edits.is_empty() {
new_transforms = self.transforms.clone(); new_transforms = self.transforms.clone();
} else { } 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(); let mut tab_edits_iter = tab_edits.iter().peekable();
new_transforms = new_transforms =
old_cursor.slice(&tab_edits_iter.peek().unwrap().old.start, Bias::Right, &()); old_cursor.slice(&tab_edits_iter.peek().unwrap().old.start, Bias::Right, &());
while let Some(edit) = tab_edits_iter.next() { while let Some(edit) = tab_edits_iter.next() {
if edit.new.start > CharPoint::from(new_transforms.summary().input.lines) { if edit.new.start > TabPoint::from(new_transforms.summary().input.lines) {
let summary = new_char_snapshot.text_summary_for_range( let summary = new_tab_snapshot.text_summary_for_range(
CharPoint::from(new_transforms.summary().input.lines)..edit.new.start, TabPoint::from(new_transforms.summary().input.lines)..edit.new.start,
); );
new_transforms.push_or_extend(Transform::isomorphic(summary)); new_transforms.push_or_extend(Transform::isomorphic(summary));
} }
if !edit.new.is_empty() { if !edit.new.is_empty() {
new_transforms.push_or_extend(Transform::isomorphic( 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()),
)); ));
} }
@ -369,7 +365,7 @@ impl WrapSnapshot {
if next_edit.old.start > old_cursor.end(&()) { if next_edit.old.start > old_cursor.end(&()) {
if old_cursor.end(&()) > edit.old.end { if old_cursor.end(&()) > edit.old.end {
let summary = self let summary = self
.char_snapshot .tab_snapshot
.text_summary_for_range(edit.old.end..old_cursor.end(&())); .text_summary_for_range(edit.old.end..old_cursor.end(&()));
new_transforms.push_or_extend(Transform::isomorphic(summary)); new_transforms.push_or_extend(Transform::isomorphic(summary));
} }
@ -383,7 +379,7 @@ impl WrapSnapshot {
} else { } else {
if old_cursor.end(&()) > edit.old.end { if old_cursor.end(&()) > edit.old.end {
let summary = self let summary = self
.char_snapshot .tab_snapshot
.text_summary_for_range(edit.old.end..old_cursor.end(&())); .text_summary_for_range(edit.old.end..old_cursor.end(&()));
new_transforms.push_or_extend(Transform::isomorphic(summary)); new_transforms.push_or_extend(Transform::isomorphic(summary));
} }
@ -396,7 +392,7 @@ impl WrapSnapshot {
let old_snapshot = mem::replace( let old_snapshot = mem::replace(
self, self,
WrapSnapshot { WrapSnapshot {
char_snapshot: new_char_snapshot, tab_snapshot: new_tab_snapshot,
transforms: new_transforms, transforms: new_transforms,
interpolated: true, interpolated: true,
}, },
@ -407,7 +403,7 @@ impl WrapSnapshot {
async fn update( async fn update(
&mut self, &mut self,
new_char_snapshot: CharSnapshot, new_tab_snapshot: TabSnapshot,
tab_edits: &[TabEdit], tab_edits: &[TabEdit],
wrap_width: Pixels, wrap_width: Pixels,
line_wrapper: &mut LineWrapper, line_wrapper: &mut LineWrapper,
@ -444,27 +440,27 @@ impl WrapSnapshot {
new_transforms = self.transforms.clone(); new_transforms = self.transforms.clone();
} else { } else {
let mut row_edits = row_edits.into_iter().peekable(); 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( 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, Bias::Right,
&(), &(),
); );
while let Some(edit) = row_edits.next() { while let Some(edit) = row_edits.next() {
if edit.new_rows.start > new_transforms.summary().input.lines.row { if edit.new_rows.start > new_transforms.summary().input.lines.row {
let summary = new_char_snapshot.text_summary_for_range( let summary = new_tab_snapshot.text_summary_for_range(
CharPoint(new_transforms.summary().input.lines) TabPoint(new_transforms.summary().input.lines)
..CharPoint::new(edit.new_rows.start, 0), ..TabPoint::new(edit.new_rows.start, 0),
); );
new_transforms.push_or_extend(Transform::isomorphic(summary)); new_transforms.push_or_extend(Transform::isomorphic(summary));
} }
let mut line = String::new(); let mut line = String::new();
let mut remaining = None; let mut remaining = None;
let mut chunks = new_char_snapshot.chunks( let mut chunks = new_tab_snapshot.chunks(
CharPoint::new(edit.new_rows.start, 0)..new_char_snapshot.max_point(), TabPoint::new(edit.new_rows.start, 0)..new_tab_snapshot.max_point(),
false, false,
Highlights::default(), Highlights::default(),
); );
@ -511,19 +507,19 @@ impl WrapSnapshot {
} }
new_transforms.extend(edit_transforms, &()); 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 let Some(next_edit) = row_edits.peek() {
if next_edit.old_rows.start > old_cursor.end(&()).row() { if next_edit.old_rows.start > old_cursor.end(&()).row() {
if old_cursor.end(&()) > CharPoint::new(edit.old_rows.end, 0) { if old_cursor.end(&()) > TabPoint::new(edit.old_rows.end, 0) {
let summary = self.char_snapshot.text_summary_for_range( let summary = self.tab_snapshot.text_summary_for_range(
CharPoint::new(edit.old_rows.end, 0)..old_cursor.end(&()), TabPoint::new(edit.old_rows.end, 0)..old_cursor.end(&()),
); );
new_transforms.push_or_extend(Transform::isomorphic(summary)); new_transforms.push_or_extend(Transform::isomorphic(summary));
} }
old_cursor.next(&()); old_cursor.next(&());
new_transforms.append( new_transforms.append(
old_cursor.slice( old_cursor.slice(
&CharPoint::new(next_edit.old_rows.start, 0), &TabPoint::new(next_edit.old_rows.start, 0),
Bias::Right, Bias::Right,
&(), &(),
), ),
@ -531,9 +527,9 @@ impl WrapSnapshot {
); );
} }
} else { } else {
if old_cursor.end(&()) > CharPoint::new(edit.old_rows.end, 0) { if old_cursor.end(&()) > TabPoint::new(edit.old_rows.end, 0) {
let summary = self.char_snapshot.text_summary_for_range( let summary = self.tab_snapshot.text_summary_for_range(
CharPoint::new(edit.old_rows.end, 0)..old_cursor.end(&()), TabPoint::new(edit.old_rows.end, 0)..old_cursor.end(&()),
); );
new_transforms.push_or_extend(Transform::isomorphic(summary)); new_transforms.push_or_extend(Transform::isomorphic(summary));
} }
@ -546,7 +542,7 @@ impl WrapSnapshot {
let old_snapshot = mem::replace( let old_snapshot = mem::replace(
self, self,
WrapSnapshot { WrapSnapshot {
char_snapshot: new_char_snapshot, tab_snapshot: new_tab_snapshot,
transforms: new_transforms, transforms: new_transforms,
interpolated: false, interpolated: false,
}, },
@ -599,17 +595,17 @@ impl WrapSnapshot {
) -> WrapChunks<'a> { ) -> WrapChunks<'a> {
let output_start = WrapPoint::new(rows.start, 0); let output_start = WrapPoint::new(rows.start, 0);
let output_end = WrapPoint::new(rows.end, 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, &()); 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()) { if transforms.item().map_or(false, |t| t.is_isomorphic()) {
input_start.0 += output_start.0 - transforms.start().0 .0; input_start.0 += output_start.0 - transforms.start().0 .0;
} }
let input_end = self let input_end = self
.to_char_point(output_end) .to_tab_point(output_end)
.min(self.char_snapshot.max_point()); .min(self.tab_snapshot.max_point());
WrapChunks { WrapChunks {
input_chunks: self.char_snapshot.chunks( input_chunks: self.tab_snapshot.chunks(
input_start..input_end, input_start..input_end,
language_aware, language_aware,
highlights, highlights,
@ -627,7 +623,7 @@ impl WrapSnapshot {
} }
pub fn line_len(&self, row: u32) -> u32 { 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, &()); cursor.seek(&WrapPoint::new(row + 1, 0), Bias::Left, &());
if cursor if cursor
.item() .item()
@ -635,7 +631,7 @@ impl WrapSnapshot {
{ {
let overshoot = row - cursor.start().0.row(); let overshoot = row - cursor.start().0.row();
let tab_row = cursor.start().1.row() + overshoot; 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 { if overshoot == 0 {
cursor.start().0.column() + (tab_line_len - cursor.start().1.column()) cursor.start().0.column() + (tab_line_len - cursor.start().1.column())
} else { } else {
@ -652,17 +648,15 @@ impl WrapSnapshot {
let start = WrapPoint::new(rows.start, 0); let start = WrapPoint::new(rows.start, 0);
let end = WrapPoint::new(rows.end, 0); let end = WrapPoint::new(rows.end, 0);
let mut cursor = self.transforms.cursor::<(WrapPoint, CharPoint)>(&()); let mut cursor = self.transforms.cursor::<(WrapPoint, TabPoint)>(&());
cursor.seek(&start, Bias::Right, &()); cursor.seek(&start, Bias::Right, &());
if let Some(transform) = cursor.item() { if let Some(transform) = cursor.item() {
let start_in_transform = start.0 - cursor.start().0 .0; let start_in_transform = start.0 - cursor.start().0 .0;
let end_in_transform = cmp::min(end, cursor.end(&()).0).0 - cursor.start().0 .0; let end_in_transform = cmp::min(end, cursor.end(&()).0).0 - cursor.start().0 .0;
if transform.is_isomorphic() { if transform.is_isomorphic() {
let char_start = CharPoint(cursor.start().1 .0 + start_in_transform); let tab_start = TabPoint(cursor.start().1 .0 + start_in_transform);
let char_end = CharPoint(cursor.start().1 .0 + end_in_transform); let tab_end = TabPoint(cursor.start().1 .0 + end_in_transform);
summary += &self summary += &self.tab_snapshot.text_summary_for_range(tab_start..tab_end);
.char_snapshot
.text_summary_for_range(char_start..char_end);
} else { } else {
debug_assert_eq!(start_in_transform.row, end_in_transform.row); debug_assert_eq!(start_in_transform.row, end_in_transform.row);
let indent_len = end_in_transform.column - start_in_transform.column; let indent_len = end_in_transform.column - start_in_transform.column;
@ -687,9 +681,9 @@ impl WrapSnapshot {
let end_in_transform = end.0 - cursor.start().0 .0; let end_in_transform = end.0 - cursor.start().0 .0;
if transform.is_isomorphic() { if transform.is_isomorphic() {
let char_start = cursor.start().1; let char_start = cursor.start().1;
let char_end = CharPoint(char_start.0 + end_in_transform); let char_end = TabPoint(char_start.0 + end_in_transform);
summary += &self summary += &self
.char_snapshot .tab_snapshot
.text_summary_for_range(char_start..char_end); .text_summary_for_range(char_start..char_end);
} else { } else {
debug_assert_eq!(end_in_transform, Point::new(1, 0)); debug_assert_eq!(end_in_transform, Point::new(1, 0));
@ -724,14 +718,14 @@ impl WrapSnapshot {
} }
pub fn buffer_rows(&self, start_row: u32) -> WrapBufferRows { 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, &()); transforms.seek(&WrapPoint::new(start_row, 0), Bias::Left, &());
let mut input_row = transforms.start().1.row(); let mut input_row = transforms.start().1.row();
if transforms.item().map_or(false, |t| t.is_isomorphic()) { if transforms.item().map_or(false, |t| t.is_isomorphic()) {
input_row += start_row - transforms.start().0.row(); input_row += start_row - transforms.start().0.row();
} }
let soft_wrapped = transforms.item().map_or(false, |t| !t.is_isomorphic()); 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(); let input_buffer_row = input_buffer_rows.next().unwrap();
WrapBufferRows { WrapBufferRows {
transforms, transforms,
@ -743,26 +737,26 @@ impl WrapSnapshot {
} }
} }
pub fn to_char_point(&self, point: WrapPoint) -> CharPoint { pub fn to_tab_point(&self, point: WrapPoint) -> TabPoint {
let mut cursor = self.transforms.cursor::<(WrapPoint, CharPoint)>(&()); let mut cursor = self.transforms.cursor::<(WrapPoint, TabPoint)>(&());
cursor.seek(&point, Bias::Right, &()); 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()) { 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 { 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 { 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 { pub fn tab_point_to_wrap_point(&self, point: TabPoint) -> WrapPoint {
let mut cursor = self.transforms.cursor::<(CharPoint, WrapPoint)>(&()); let mut cursor = self.transforms.cursor::<(TabPoint, WrapPoint)>(&());
cursor.seek(&point, Bias::Right, &()); cursor.seek(&point, Bias::Right, &());
WrapPoint(cursor.start().1 .0 + (point.0 - cursor.start().0 .0)) WrapPoint(cursor.start().1 .0 + (point.0 - cursor.start().0 .0))
} }
@ -777,10 +771,7 @@ impl WrapSnapshot {
} }
} }
self.char_point_to_wrap_point( self.tab_point_to_wrap_point(self.tab_snapshot.clip_point(self.to_tab_point(point), bias))
self.char_snapshot
.clip_point(self.to_char_point(point), bias),
)
} }
pub fn prev_row_boundary(&self, mut point: WrapPoint) -> u32 { pub fn prev_row_boundary(&self, mut point: WrapPoint) -> u32 {
@ -790,7 +781,7 @@ impl WrapSnapshot {
*point.column_mut() = 0; *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, &()); cursor.seek(&point, Bias::Right, &());
if cursor.item().is_none() { if cursor.item().is_none() {
cursor.prev(&()); cursor.prev(&());
@ -810,7 +801,7 @@ impl WrapSnapshot {
pub fn next_row_boundary(&self, mut point: WrapPoint) -> Option<u32> { pub fn next_row_boundary(&self, mut point: WrapPoint) -> Option<u32> {
point.0 += Point::new(1, 0); 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, &()); cursor.seek(&point, Bias::Right, &());
while let Some(transform) = cursor.item() { while let Some(transform) = cursor.item() {
if transform.is_isomorphic() && cursor.start().1.column() == 0 { if transform.is_isomorphic() && cursor.start().1.column() == 0 {
@ -842,8 +833,8 @@ impl WrapSnapshot {
#[cfg(test)] #[cfg(test)]
{ {
assert_eq!( assert_eq!(
CharPoint::from(self.transforms.summary().input.lines), TabPoint::from(self.transforms.summary().input.lines),
self.char_snapshot.max_point() self.tab_snapshot.max_point()
); );
{ {
@ -856,18 +847,18 @@ impl WrapSnapshot {
} }
let text = language::Rope::from(self.text().as_str()); 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 expected_buffer_rows = Vec::new();
let mut prev_tab_row = 0; let mut prev_tab_row = 0;
for display_row in 0..=self.max_point().row() { for display_row in 0..=self.max_point().row() {
let char_point = self.to_char_point(WrapPoint::new(display_row, 0)); let tab_point = self.to_tab_point(WrapPoint::new(display_row, 0));
if char_point.row() == prev_tab_row && display_row != 0 { if tab_point.row() == prev_tab_row && display_row != 0 {
expected_buffer_rows.push(None); expected_buffer_rows.push(None);
} else { } else {
expected_buffer_rows.push(input_buffer_rows.next().unwrap()); 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)); assert_eq!(self.line_len(display_row), text.line_len(display_row));
} }
@ -889,14 +880,14 @@ impl<'a> WrapChunks<'a> {
let output_start = WrapPoint::new(rows.start, 0); let output_start = WrapPoint::new(rows.start, 0);
let output_end = WrapPoint::new(rows.end, 0); let output_end = WrapPoint::new(rows.end, 0);
self.transforms.seek(&output_start, Bias::Right, &()); self.transforms.seek(&output_start, Bias::Right, &());
let mut input_start = CharPoint(self.transforms.start().1 .0); let mut input_start = TabPoint(self.transforms.start().1 .0);
if self.transforms.item().map_or(false, |t| t.is_isomorphic()) { if self.transforms.item().map_or(false, |t| t.is_isomorphic()) {
input_start.0 += output_start.0 - self.transforms.start().0 .0; input_start.0 += output_start.0 - self.transforms.start().0 .0;
} }
let input_end = self let input_end = self
.snapshot .snapshot
.to_char_point(output_end) .to_tab_point(output_end)
.min(self.snapshot.char_snapshot.max_point()); .min(self.snapshot.tab_snapshot.max_point());
self.input_chunks.seek(input_start..input_end); self.input_chunks.seek(input_start..input_end);
self.input_chunk = Chunk::default(); self.input_chunk = Chunk::default();
self.output_position = output_start; self.output_position = output_start;
@ -951,11 +942,13 @@ impl<'a> Iterator for WrapChunks<'a> {
} else { } else {
*self.output_position.column_mut() += char_len as u32; *self.output_position.column_mut() += char_len as u32;
} }
if self.output_position >= transform_end { if self.output_position >= transform_end {
self.transforms.next(&()); self.transforms.next(&());
break; break;
} }
} }
let (prefix, suffix) = self.input_chunk.text.split_at(input_len); let (prefix, suffix) = self.input_chunk.text.split_at(input_len);
self.input_chunk.text = suffix; self.input_chunk.text = suffix;
Some(Chunk { Some(Chunk {
@ -1110,7 +1103,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 { fn zero(_cx: &()) -> Self {
Default::default() Default::default()
} }
@ -1120,7 +1113,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 { fn cmp(&self, cursor_location: &TransformSummary, _: &()) -> std::cmp::Ordering {
Ord::cmp(&self.0, &cursor_location.input.lines) Ord::cmp(&self.0, &cursor_location.input.lines)
} }
@ -1168,7 +1161,7 @@ fn consolidate_wrap_edits(edits: Vec<WrapEdit>) -> Vec<WrapEdit> {
mod tests { mod tests {
use super::*; use super::*;
use crate::{ 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, MultiBuffer,
}; };
use gpui::{font, px, test::observe}; use gpui::{font, px, test::observe};
@ -1220,9 +1213,9 @@ mod tests {
log::info!("InlayMap text: {:?}", inlay_snapshot.text()); log::info!("InlayMap text: {:?}", inlay_snapshot.text());
let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot.clone()); let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot.clone());
log::info!("FoldMap text: {:?}", fold_snapshot.text()); log::info!("FoldMap text: {:?}", fold_snapshot.text());
let (mut char_map, _) = CharMap::new(fold_snapshot.clone(), tab_size); let (mut tab_map, _) = TabMap::new(fold_snapshot.clone(), tab_size);
let tabs_snapshot = char_map.set_max_expansion_column(32); let tabs_snapshot = tab_map.set_max_expansion_column(32);
log::info!("CharMap text: {:?}", tabs_snapshot.text()); log::info!("TabMap text: {:?}", tabs_snapshot.text());
let mut line_wrapper = text_system.line_wrapper(font.clone(), font_size); let mut line_wrapper = text_system.line_wrapper(font.clone(), font_size);
let unwrapped_text = tabs_snapshot.text(); let unwrapped_text = tabs_snapshot.text();
@ -1268,7 +1261,7 @@ mod tests {
20..=39 => { 20..=39 => {
for (fold_snapshot, fold_edits) in fold_map.randomly_mutate(&mut rng) { for (fold_snapshot, fold_edits) in fold_map.randomly_mutate(&mut rng) {
let (tabs_snapshot, tab_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) = let (mut snapshot, wrap_edits) =
wrap_map.update(cx, |map, cx| map.sync(tabs_snapshot, tab_edits, cx)); wrap_map.update(cx, |map, cx| map.sync(tabs_snapshot, tab_edits, cx));
snapshot.check_invariants(); snapshot.check_invariants();
@ -1281,7 +1274,7 @@ mod tests {
inlay_map.randomly_mutate(&mut next_inlay_id, &mut rng); inlay_map.randomly_mutate(&mut next_inlay_id, &mut rng);
let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits); let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
let (tabs_snapshot, tab_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) = let (mut snapshot, wrap_edits) =
wrap_map.update(cx, |map, cx| map.sync(tabs_snapshot, tab_edits, cx)); wrap_map.update(cx, |map, cx| map.sync(tabs_snapshot, tab_edits, cx));
snapshot.check_invariants(); snapshot.check_invariants();
@ -1305,8 +1298,8 @@ mod tests {
log::info!("InlayMap text: {:?}", inlay_snapshot.text()); log::info!("InlayMap text: {:?}", inlay_snapshot.text());
let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits); let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
log::info!("FoldMap text: {:?}", fold_snapshot.text()); log::info!("FoldMap text: {:?}", fold_snapshot.text());
let (tabs_snapshot, tab_edits) = char_map.sync(fold_snapshot, fold_edits, tab_size); let (tabs_snapshot, tab_edits) = tab_map.sync(fold_snapshot, fold_edits, tab_size);
log::info!("CharMap text: {:?}", tabs_snapshot.text()); log::info!("TabMap text: {:?}", tabs_snapshot.text());
let unwrapped_text = tabs_snapshot.text(); let unwrapped_text = tabs_snapshot.text();
let expected_text = wrap_text(&unwrapped_text, wrap_width, &mut line_wrapper); let expected_text = wrap_text(&unwrapped_text, wrap_width, &mut line_wrapper);
@ -1352,7 +1345,7 @@ mod tests {
if tab_size.get() == 1 if tab_size.get() == 1
|| !wrapped_snapshot || !wrapped_snapshot
.char_snapshot .tab_snapshot
.fold_snapshot .fold_snapshot
.text() .text()
.contains('\t') .contains('\t')

View file

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

View file

@ -1,7 +1,6 @@
use crate::{ use crate::{
display_map::{InlayOffset, ToDisplayPoint}, display_map::{InlayOffset, ToDisplayPoint},
hover_links::{InlayHighlight, RangeInEditor}, hover_links::{InlayHighlight, RangeInEditor},
is_invisible,
scroll::ScrollAmount, scroll::ScrollAmount,
Anchor, AnchorRangeExt, DisplayPoint, DisplayRow, Editor, EditorSettings, EditorSnapshot, Anchor, AnchorRangeExt, DisplayPoint, DisplayRow, Editor, EditorSettings, EditorSnapshot,
Hover, RangeToAnchorExt, Hover, RangeToAnchorExt,
@ -12,7 +11,7 @@ use gpui::{
StyleRefinement, Styled, Task, TextStyleRefinement, View, ViewContext, StyleRefinement, Styled, Task, TextStyleRefinement, View, ViewContext,
}; };
use itertools::Itertools; use itertools::Itertools;
use language::{Diagnostic, DiagnosticEntry, Language, LanguageRegistry}; use language::{DiagnosticEntry, Language, LanguageRegistry};
use lsp::DiagnosticSeverity; use lsp::DiagnosticSeverity;
use markdown::{Markdown, MarkdownStyle}; use markdown::{Markdown, MarkdownStyle};
use multi_buffer::ToOffset; use multi_buffer::ToOffset;
@ -200,6 +199,7 @@ fn show_hover(
if editor.pending_rename.is_some() { if editor.pending_rename.is_some() {
return None; return None;
} }
let snapshot = editor.snapshot(cx); let snapshot = editor.snapshot(cx);
let (buffer, buffer_position) = editor 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 // If there's a diagnostic, assign it on the hover state and notify
let mut local_diagnostic = snapshot let local_diagnostic = snapshot
.buffer_snapshot .buffer_snapshot
.diagnostics_in_range::<_, usize>(anchor..anchor, false) .diagnostics_in_range::<_, usize>(anchor..anchor, false)
// Find the entry with the most specific range // 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 diagnostic_popover = if let Some(local_diagnostic) = local_diagnostic {
let text = match local_diagnostic.diagnostic.source { let text = match local_diagnostic.diagnostic.source {
Some(ref source) => { Some(ref source) => {
@ -324,6 +288,7 @@ fn show_hover(
} }
None => local_diagnostic.diagnostic.message.clone(), None => local_diagnostic.diagnostic.message.clone(),
}; };
let mut border_color: Option<Hsla> = None; let mut border_color: Option<Hsla> = None;
let mut background_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) Markdown::new_text(text, markdown_style.clone(), None, cx, None)
}) })
.ok(); .ok();
Some(DiagnosticPopover { Some(DiagnosticPopover {
local_diagnostic, local_diagnostic,
primary_diagnostic, primary_diagnostic,
@ -466,6 +432,7 @@ fn show_hover(
cx.notify(); cx.notify();
cx.refresh(); cx.refresh();
})?; })?;
anyhow::Ok(()) anyhow::Ok(())
} }
.log_err() .log_err()

View file

@ -1,7 +1,6 @@
use crate::{ use crate::{
black, fill, point, px, size, Bounds, Half, Hsla, LineLayout, Pixels, Point, Result, black, fill, point, px, size, Bounds, Hsla, LineLayout, Pixels, Point, Result, SharedString,
SharedString, StrikethroughStyle, UnderlineStyle, WindowContext, WrapBoundary, StrikethroughStyle, UnderlineStyle, WindowContext, WrapBoundary, WrappedLineLayout,
WrappedLineLayout,
}; };
use derive_more::{Deref, DerefMut}; use derive_more::{Deref, DerefMut};
use smallvec::SmallVec; use smallvec::SmallVec;
@ -130,9 +129,8 @@ fn paint_line(
let text_system = cx.text_system().clone(); let text_system = cx.text_system().clone();
let mut glyph_origin = origin; let mut glyph_origin = origin;
let mut prev_glyph_position = Point::default(); 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() { 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() { for (glyph_ix, glyph) in run.glyphs.iter().enumerate() {
glyph_origin.x += glyph.position.x - prev_glyph_position.x; glyph_origin.x += glyph.position.x - prev_glyph_position.x;
@ -141,9 +139,6 @@ fn paint_line(
wraps.next(); wraps.next();
if let Some((background_origin, background_color)) = current_background.as_mut() 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( cx.paint_quad(fill(
Bounds { Bounds {
origin: *background_origin, origin: *background_origin,
@ -155,9 +150,6 @@ fn paint_line(
background_origin.y += line_height; background_origin.y += line_height;
} }
if let Some((underline_origin, underline_style)) = current_underline.as_mut() { 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( cx.paint_underline(
*underline_origin, *underline_origin,
glyph_origin.x - underline_origin.x, glyph_origin.x - underline_origin.x,
@ -169,9 +161,6 @@ fn paint_line(
if let Some((strikethrough_origin, strikethrough_style)) = if let Some((strikethrough_origin, strikethrough_style)) =
current_strikethrough.as_mut() current_strikethrough.as_mut()
{ {
if glyph_origin.x == strikethrough_origin.x {
strikethrough_origin.x -= max_glyph_size.width.half();
};
cx.paint_strikethrough( cx.paint_strikethrough(
*strikethrough_origin, *strikethrough_origin,
glyph_origin.x - strikethrough_origin.x, 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_underline: Option<(Point<Pixels>, UnderlineStyle)> = None;
let mut finished_strikethrough: Option<(Point<Pixels>, StrikethroughStyle)> = None; let mut finished_strikethrough: Option<(Point<Pixels>, StrikethroughStyle)> = None;
if glyph.index >= run_end { if glyph.index >= run_end {
let mut style_run = decoration_runs.next(); if let Some(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((_, background_color)) = &mut current_background { if let Some((_, background_color)) = &mut current_background {
if style_run.background_color.as_ref() != Some(background_color) { if style_run.background_color.as_ref() != Some(background_color) {
finished_background = current_background.take(); finished_background = current_background.take();
@ -262,14 +240,10 @@ fn paint_line(
} }
if let Some((background_origin, background_color)) = finished_background { 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( cx.paint_quad(fill(
Bounds { Bounds {
origin: background_origin, origin: background_origin,
size: size(width, line_height), size: size(glyph_origin.x - background_origin.x, line_height),
}, },
background_color, background_color,
)); ));
@ -325,10 +299,7 @@ fn paint_line(
last_line_end_x -= glyph.position.x; last_line_end_x -= glyph.position.x;
} }
if let Some((mut background_origin, background_color)) = current_background.take() { if let Some((background_origin, background_color)) = current_background.take() {
if last_line_end_x == background_origin.x {
background_origin.x -= max_glyph_size.width.half()
};
cx.paint_quad(fill( cx.paint_quad(fill(
Bounds { Bounds {
origin: background_origin, origin: background_origin,
@ -338,10 +309,7 @@ fn paint_line(
)); ));
} }
if let Some((mut underline_start, underline_style)) = current_underline.take() { if let Some((underline_start, underline_style)) = current_underline.take() {
if last_line_end_x == underline_start.x {
underline_start.x -= max_glyph_size.width.half()
};
cx.paint_underline( cx.paint_underline(
underline_start, underline_start,
last_line_end_x - underline_start.x, 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 let Some((strikethrough_start, strikethrough_style)) = current_strikethrough.take() {
if last_line_end_x == strikethrough_start.x {
strikethrough_start.x -= max_glyph_size.width.half()
};
cx.paint_strikethrough( cx.paint_strikethrough(
strikethrough_start, strikethrough_start,
last_line_end_x - strikethrough_start.x, last_line_end_x - strikethrough_start.x,

View file

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