From 631a6a2f4520f342d5f809ed30603ce03bc3fe76 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 19 Jul 2021 18:40:57 +0200 Subject: [PATCH] Re-enable `wrap_map` module and fix compile errors Co-Authored-By: Nathan Sobo --- zed/src/editor/display_map.rs | 2 +- zed/src/editor/display_map/fold_map.rs | 12 +- zed/src/editor/display_map/tab_map.rs | 115 ++++++++-- zed/src/editor/display_map/wrap_map.rs | 284 ++++++++++++++----------- 4 files changed, 256 insertions(+), 157 deletions(-) diff --git a/zed/src/editor/display_map.rs b/zed/src/editor/display_map.rs index 44f8af34034ebeca37bca4d4e133ceb7fe8cbd4b..516dd15fa6b1c02a6dfbda2487c14447fbc2f5e2 100644 --- a/zed/src/editor/display_map.rs +++ b/zed/src/editor/display_map.rs @@ -1,6 +1,6 @@ mod fold_map; mod tab_map; -// mod wrap_map; +mod wrap_map; use super::{buffer, Anchor, Bias, Buffer, Point, ToOffset, ToPoint}; use fold_map::FoldMap; diff --git a/zed/src/editor/display_map/fold_map.rs b/zed/src/editor/display_map/fold_map.rs index f3a6d9aa5cbb2a299932abe8518f88a03c545e41..c294ee5bc37a5b45a7437476db70be269ac5c2d0 100644 --- a/zed/src/editor/display_map/fold_map.rs +++ b/zed/src/editor/display_map/fold_map.rs @@ -483,14 +483,14 @@ impl Snapshot { summary } - pub fn len(&self) -> usize { - self.transforms.summary().output.bytes + pub fn len(&self) -> OutputOffset { + OutputOffset(self.transforms.summary().output.bytes) } pub fn line_len(&self, row: u32) -> u32 { let line_start = self.to_output_offset(OutputPoint::new(row, 0)).0; let line_end = if row >= self.max_point().row() { - self.len() + self.len().0 } else { self.to_output_offset(OutputPoint::new(row + 1, 0)).0 - 1 }; @@ -1442,8 +1442,10 @@ mod tests { } for _ in 0..5 { - let offset = snapshot - .clip_offset(OutputOffset(rng.gen_range(0..=snapshot.len())), Bias::Right); + let offset = snapshot.clip_offset( + OutputOffset(rng.gen_range(0..=snapshot.len().0)), + Bias::Right, + ); assert_eq!( snapshot.chunks_at(offset).collect::(), &expected_text[offset.0..], diff --git a/zed/src/editor/display_map/tab_map.rs b/zed/src/editor/display_map/tab_map.rs index 2628af0a32d29829582e88325080ef115c6c3979..06962e5273cc342cc7c06f56bf3071642c730e9e 100644 --- a/zed/src/editor/display_map/tab_map.rs +++ b/zed/src/editor/display_map/tab_map.rs @@ -47,6 +47,35 @@ pub struct Snapshot { } impl Snapshot { + pub fn text_summary(&self) -> TextSummary { + // TODO: expand tabs on first and last line, ignoring the longest row. + let summary = self.input.text_summary(); + TextSummary { + lines: summary.lines, + first_line_chars: summary.first_line_chars, + last_line_chars: summary.last_line_chars, + longest_row: summary.longest_row, + longest_row_chars: summary.longest_row_chars, + } + } + + pub fn text_summary_for_rows(&self, rows: Range) -> TextSummary { + // TODO: expand tabs on first and last line, ignoring the longest row. + let range = InputPoint::new(rows.start, 0)..InputPoint::new(rows.end, 0); + let summary = self.input.text_summary_for_range(range); + TextSummary { + lines: summary.lines, + first_line_chars: summary.first_line_chars, + last_line_chars: summary.last_line_chars, + longest_row: summary.longest_row, + longest_row_chars: summary.longest_row_chars, + } + } + + pub fn version(&self) -> usize { + self.input.version + } + pub fn chunks_at(&self, point: OutputPoint) -> Chunks { let (point, expanded_char_column, to_next_stop) = self.to_input_point(point, Bias::Left); let fold_chunks = self.input.chunks_at(self.input.to_output_offset(point)); @@ -73,6 +102,15 @@ impl Snapshot { } } + #[cfg(test)] + pub fn text(&self) -> String { + self.chunks_at(Default::default()).collect() + } + + pub fn len(&self) -> OutputOffset { + self.to_output_offset(self.input.len()) + } + pub fn line_len(&self, row: u32) -> u32 { self.to_output_point(InputPoint::new(row, self.input.line_len(row))) .column() @@ -184,27 +222,7 @@ impl Snapshot { } #[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub struct OutputOffset(usize); - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct Edit { - pub old_bytes: Range, - pub new_bytes: Range, -} - -impl Edit { - pub fn delta(&self) -> isize { - self.inserted_bytes() as isize - self.deleted_bytes() as isize - } - - pub fn deleted_bytes(&self) -> usize { - self.old_bytes.end.0 - self.old_bytes.start.0 - } - - pub fn inserted_bytes(&self) -> usize { - self.new_bytes.end.0 - self.new_bytes.start.0 - } -} +pub struct OutputOffset(pub usize); #[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)] pub struct OutputPoint(super::Point); @@ -235,6 +253,61 @@ impl OutputPoint { } } +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Edit { + pub old_bytes: Range, + pub new_bytes: Range, +} + +impl Edit { + pub fn delta(&self) -> isize { + self.inserted_bytes() as isize - self.deleted_bytes() as isize + } + + pub fn deleted_bytes(&self) -> usize { + self.old_bytes.end.0 - self.old_bytes.start.0 + } + + pub fn inserted_bytes(&self) -> usize { + self.new_bytes.end.0 - self.new_bytes.start.0 + } +} + +#[derive(Clone, Debug, Default, Eq, PartialEq)] +pub struct TextSummary { + pub lines: super::Point, + pub first_line_chars: u32, + pub last_line_chars: u32, + pub longest_row: u32, + pub longest_row_chars: u32, +} + +impl<'a> std::ops::AddAssign<&'a Self> for TextSummary { + fn add_assign(&mut self, other: &'a Self) { + let joined_chars = self.last_line_chars + other.first_line_chars; + if joined_chars > self.longest_row_chars { + self.longest_row = self.lines.row; + self.longest_row_chars = joined_chars; + } + if other.longest_row_chars > self.longest_row_chars { + self.longest_row = self.lines.row + other.longest_row; + self.longest_row_chars = other.longest_row_chars; + } + + if self.lines.row == 0 { + self.first_line_chars += other.first_line_chars; + } + + if other.lines.row == 0 { + self.last_line_chars += other.first_line_chars; + } else { + self.last_line_chars = other.last_line_chars; + } + + self.lines += &other.lines; + } +} + // Handles a tab width <= 16 const SPACES: &'static str = " "; diff --git a/zed/src/editor/display_map/wrap_map.rs b/zed/src/editor/display_map/wrap_map.rs index 5b97e162315d7e1c7ae777479e0d908fa7e52c76..f49e51ae3c3434bedc637d6d743812b87786e5cd 100644 --- a/zed/src/editor/display_map/wrap_map.rs +++ b/zed/src/editor/display_map/wrap_map.rs @@ -1,24 +1,22 @@ -use std::sync::Arc; - +use super::tab_map::{ + Edit as InputEdit, OutputOffset as InputOffset, Snapshot as InputSnapshot, TextSummary, +}; use crate::{ - editor::{ - display_map::fold_map::{self, DisplayOffset, FoldedPoint}, - Point, TextSummary, - }, + editor::Point, sum_tree::{self, SumTree}, - util::Bias, }; use gpui::{font_cache::FamilyId, AppContext, FontCache, FontSystem, Task}; use parking_lot::Mutex; use postage::{prelude::Sink, watch}; use smol::channel; +use std::sync::Arc; #[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)] -pub struct WrappedPoint(Point); +pub struct OutputPoint(super::Point); -impl WrappedPoint { +impl OutputPoint { pub fn new(row: u32, column: u32) -> Self { - Self(Point::new(row, column)) + Self(super::Point::new(row, column)) } pub fn zero() -> Self { @@ -42,13 +40,32 @@ impl WrappedPoint { } } -#[derive(Clone, Default)] +#[derive(Clone)] pub struct Snapshot { transforms: SumTree, - folds_snapshot: fold_map::Snapshot, + input: InputSnapshot, version: usize, } +impl Snapshot { + fn new(input: InputSnapshot) -> Self { + Self { + transforms: SumTree::from_item( + Transform { + summary: TransformSummary { + input: input.text_summary(), + output: input.text_summary(), + }, + display_text: None, + }, + &(), + ), + version: input.version(), + input, + } + } +} + struct State { snapshot: Snapshot, interpolated_version: usize, @@ -63,38 +80,26 @@ pub struct Config { pub struct WrapMap { state: Mutex, - edits_tx: channel::Sender<(fold_map::Snapshot, Vec)>, + edits_tx: channel::Sender<(InputSnapshot, Vec)>, background_snapshots: watch::Receiver, _background_task: Task<()>, } impl WrapMap { - pub fn new(folds_snapshot: fold_map::Snapshot, config: Config, cx: &AppContext) -> Self { + pub fn new(input: InputSnapshot, config: Config, cx: &AppContext) -> Self { let font_cache = cx.font_cache().clone(); let font_system = cx.platform().fonts(); - let snapshot = Snapshot { - transforms: SumTree::from_item( - Transform { - summary: TransformSummary { - folded: folds_snapshot.text_summary(), - wrapped: folds_snapshot.text_summary(), - }, - display_text: None, - }, - &(), - ), - folds_snapshot, - version: folds_snapshot.version, - }; + let snapshot = Snapshot::new(input.clone()); let (background_snapshots_tx, background_snapshots_rx) = watch::channel_with(snapshot.clone()); let (edits_tx, edits_rx) = channel::unbounded(); - let background_task = cx.background().spawn(async move { - let mut wrapper = BackgroundWrapper::new(config, font_cache, font_system); - wrapper - .run(folds_snapshot, edits_rx, background_snapshots_tx) - .await; - }); + let background_task = { + let snapshot = snapshot.clone(); + cx.background().spawn(async move { + let mut wrapper = BackgroundWrapper::new(snapshot, config, font_cache, font_system); + wrapper.run(input, edits_rx, background_snapshots_tx).await; + }) + }; Self { state: Mutex::new(State { @@ -107,9 +112,9 @@ impl WrapMap { } } - pub fn sync(&self, folds_snapshot: fold_map::Snapshot, edits: Vec) -> Snapshot { + pub fn sync(&self, input: InputSnapshot, edits: Vec) -> Snapshot { // TODO: interpolate - self.edits_tx.try_send((folds_snapshot, edits)).unwrap(); + self.edits_tx.try_send((input, edits)).unwrap(); self.state.lock().snapshot.clone() } } @@ -122,24 +127,29 @@ struct BackgroundWrapper { } impl BackgroundWrapper { - fn new(config: Config, font_cache: Arc, font_system: Arc) -> Self { + fn new( + snapshot: Snapshot, + config: Config, + font_cache: Arc, + font_system: Arc, + ) -> Self { Self { config, font_cache, font_system, - snapshot: Snapshot::default(), + snapshot, } } async fn run( &mut self, - snapshot: fold_map::Snapshot, - edits_rx: channel::Receiver<(fold_map::Snapshot, Vec)>, + snapshot: InputSnapshot, + edits_rx: channel::Receiver<(InputSnapshot, Vec)>, mut snapshots_tx: watch::Sender, ) { - let edit = fold_map::Edit { - old_bytes: DisplayOffset(0)..DisplayOffset(0), - new_bytes: DisplayOffset(0)..DisplayOffset(snapshot.len()), + let edit = InputEdit { + old_bytes: InputOffset(0)..InputOffset(0), + new_bytes: InputOffset(0)..snapshot.len(), }; self.sync(snapshot, vec![edit]); if snapshots_tx.send(self.snapshot.clone()).await.is_err() { @@ -154,7 +164,7 @@ impl BackgroundWrapper { } } - fn sync(&mut self, snapshot: fold_map::Snapshot, edits: Vec) { + fn sync(&mut self, snapshot: InputSnapshot, edits: Vec) { let font_id = self .font_cache .select_font(self.config.font_family, &Default::default()); @@ -163,76 +173,76 @@ impl BackgroundWrapper { let mut new_transforms = SumTree::new(); { - let mut old_cursor = self.snapshot.transforms.cursor::(); - let mut position = DisplayPoint::zero(); - for edit in edits { - let old_start = DisplayPoint::new( - edit.old_bytes.start.to_display_point(&self.snapshot).row(), - 0, - ); - let old_end = DisplayPoint::new( - edit.old_bytes.end.to_display_point(&self.snapshot).row() + 1, - 0, - ); - let new_start = - DisplayPoint::new(edit.new_bytes.start.to_display_point(&snapshot).row(), 0); - let new_end = - DisplayPoint::new(edit.new_bytes.end.to_display_point(&snapshot).row() + 1, 0); - - if position > old_cursor.seek_start() && old_start >= old_cursor.seek_end(&()) { - old_cursor.next(&()); - } - - let prefix = old_cursor.slice(&old_start, Bias::Right, &()); - new_transforms.push_tree(prefix, &()); - new_transforms.push( - Transform::isomorphic( - self.snapshot - .folds_snapshot - .text_summary_for_range(position..old_start), - ), - &(), - ); - - let mut row = new_start.row(); - let mut line = String::new(); - 'outer: for chunk in snapshot.chunks_at(snapshot.to_display_offset(new_start)) { - for (ix, line_chunk) in chunk.split('\n').enumerate() { - if ix > 0 { - let mut prev_boundary_ix = 0; - for boundary_ix in self - .font_system - .wrap_line(&line, font_id, font_size, wrap_width) - { - let wrapped = &line[prev_boundary_ix..boundary_ix]; - new_transforms - .push(Transform::isomorphic(TextSummary::from(wrapped))); - new_transforms.push(Transform::newline()); - prev_boundary_ix = boundary_ix; - } - - line.clear(); - row += 1; - if row == new_end.row() { - break 'outer; - } - } - - line.push_str(line_chunk); - } - } - - old_cursor.seek_forward(&old_end, Bias::Right, &()); - position = old_end; - } - - if position > old_cursor.seek_start() && old_start >= old_cursor.seek_end(&()) { - old_cursor.next(&()); - } + // let mut old_cursor = self.snapshot.transforms.cursor::(); + // let mut position = DisplayPoint::zero(); + // for edit in edits { + // let old_start = DisplayPoint::new( + // edit.old_bytes.start.to_display_point(&self.snapshot).row(), + // 0, + // ); + // let old_end = DisplayPoint::new( + // edit.old_bytes.end.to_display_point(&self.snapshot).row() + 1, + // 0, + // ); + // let new_start = + // DisplayPoint::new(edit.new_bytes.start.to_display_point(&snapshot).row(), 0); + // let new_end = + // DisplayPoint::new(edit.new_bytes.end.to_display_point(&snapshot).row() + 1, 0); + + // if position > old_cursor.seek_start() && old_start >= old_cursor.seek_end(&()) { + // old_cursor.next(&()); + // } + + // let prefix = old_cursor.slice(&old_start, Bias::Right, &()); + // new_transforms.push_tree(prefix, &()); + // new_transforms.push( + // Transform::isomorphic( + // self.snapshot + // .folds_snapshot + // .text_summary_for_range(position..old_start), + // ), + // &(), + // ); + + // let mut row = new_start.row(); + // let mut line = String::new(); + // 'outer: for chunk in snapshot.chunks_at(snapshot.to_display_offset(new_start)) { + // for (ix, line_chunk) in chunk.split('\n').enumerate() { + // if ix > 0 { + // let mut prev_boundary_ix = 0; + // for boundary_ix in self + // .font_system + // .wrap_line(&line, font_id, font_size, wrap_width) + // { + // let wrapped = &line[prev_boundary_ix..boundary_ix]; + // new_transforms + // .push(Transform::isomorphic(TextSummary::from(wrapped))); + // new_transforms.push(Transform::newline()); + // prev_boundary_ix = boundary_ix; + // } + + // line.clear(); + // row += 1; + // if row == new_end.row() { + // break 'outer; + // } + // } + + // line.push_str(line_chunk); + // } + // } + + // old_cursor.seek_forward(&old_end, Bias::Right, &()); + // position = old_end; + // } + + // if position > old_cursor.seek_start() && old_start >= old_cursor.seek_end(&()) { + // old_cursor.next(&()); + // } } self.snapshot.transforms = new_transforms; - self.snapshot.version = snapshot.version; + self.snapshot.version = snapshot.version(); } } @@ -246,8 +256,8 @@ impl Transform { fn isomorphic(summary: TextSummary) -> Self { Self { summary: TransformSummary { - folded: summary.clone(), - wrapped: summary, + input: summary.clone(), + output: summary, }, display_text: None, } @@ -256,8 +266,14 @@ impl Transform { fn newline() -> Self { Self { summary: TransformSummary { - folded: TextSummary::default(), - wrapped: TextSummary::from("\n"), + input: TextSummary::default(), + output: TextSummary { + lines: Point::new(1, 0), + first_line_chars: 0, + last_line_chars: 0, + longest_row: 0, + longest_row_chars: 0, + }, }, display_text: Some("\n"), } @@ -274,22 +290,22 @@ impl sum_tree::Item for Transform { #[derive(Clone, Debug, Default, Eq, PartialEq)] struct TransformSummary { - folded: TextSummary, - wrapped: TextSummary, + input: TextSummary, + output: TextSummary, } impl sum_tree::Summary for TransformSummary { type Context = (); fn add_summary(&mut self, other: &Self, _: &()) { - self.folded += &other.folded; - self.wrapped += &other.wrapped; + self.input += &other.input; + self.output += &other.output; } } impl<'a> sum_tree::Dimension<'a, TransformSummary> for Point { fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) { - *self += &summary.folded.lines; + *self += &summary.input.lines; } } @@ -297,12 +313,14 @@ impl<'a> sum_tree::Dimension<'a, TransformSummary> for Point { mod tests { use super::*; use crate::{ - editor::{display_map::fold_map::FoldMap, Buffer}, + editor::{ + display_map::{fold_map::FoldMap, tab_map::TabMap}, + Buffer, + }, util::RandomCharIter, }; use rand::prelude::*; use std::env; - use Bias::{Left, Right}; #[gpui::test] fn test_random_wraps(cx: &mut gpui::MutableAppContext) { @@ -329,7 +347,9 @@ mod tests { Buffer::new(0, text, cx) }); let fold_map = FoldMap::new(buffer.clone(), cx.as_ref()); - let (snapshot, _) = fold_map.read(cx.as_ref()); + let (folds_snapshot, edits) = fold_map.read(cx.as_ref()); + let tab_map = TabMap::new(folds_snapshot.clone(), rng.gen_range(1..=4)); + let (tabs_snapshot, _) = tab_map.sync(folds_snapshot, edits); let font_cache = cx.font_cache().clone(); let font_system = cx.platform().fonts(); let config = Config { @@ -340,16 +360,20 @@ mod tests { let font_id = font_cache .select_font(config.font_family, &Default::default()) .unwrap(); - let mut wrapper = - BackgroundWrapper::new(config.clone(), font_cache.clone(), font_system.clone()); - let edit = fold_map::Edit { - old_bytes: DisplayOffset(0)..DisplayOffset(0), - new_bytes: DisplayOffset(0)..DisplayOffset(snapshot.len()), + let mut wrapper = BackgroundWrapper::new( + Snapshot::new(tabs_snapshot.clone()), + config.clone(), + font_cache.clone(), + font_system.clone(), + ); + let edit = InputEdit { + old_bytes: InputOffset(0)..InputOffset(0), + new_bytes: InputOffset(0)..tabs_snapshot.len(), }; - wrapper.sync(snapshot.clone(), vec![edit]); + wrapper.sync(tabs_snapshot.clone(), vec![edit]); let mut expected_text = String::new(); - for line in snapshot.text().lines() { + for line in tabs_snapshot.text().lines() { let mut prev_ix = 0; for ix in font_system.wrap_line(line, font_id, 14.0, config.wrap_width) { expected_text.push_str(&line[prev_ix..ix]);