diff --git a/zed/src/editor.rs b/zed/src/editor.rs index 9f9a0e955a..c04c769307 100644 --- a/zed/src/editor.rs +++ b/zed/src/editor.rs @@ -21,7 +21,7 @@ use gpui::{ WeakViewHandle, }; use parking_lot::Mutex; -use postage::watch; +use postage::{prelude::Stream, watch}; use serde::{Deserialize, Serialize}; use smallvec::SmallVec; use smol::Timer; @@ -413,6 +413,14 @@ impl Editor { let display_map = DisplayMap::new(buffer.clone(), settings.borrow().clone(), None, cx.as_ref()); + let mut notifications = display_map.notifications(); + cx.spawn(|this, mut cx| async move { + while notifications.recv().await.is_some() { + this.update(&mut cx, |_, cx| cx.notify()); + } + }) + .detach(); + let mut next_selection_id = 0; let selection_set_id = buffer.update(cx, |buffer, cx| { buffer.add_selection_set( diff --git a/zed/src/editor/display_map.rs b/zed/src/editor/display_map.rs index d19285be85..4437407d9f 100644 --- a/zed/src/editor/display_map.rs +++ b/zed/src/editor/display_map.rs @@ -4,10 +4,11 @@ mod wrap_map; use super::{buffer, Anchor, Bias, Buffer, Point, Settings, ToOffset, ToPoint}; use fold_map::FoldMap; -pub use fold_map::InputRows; use gpui::{AppContext, ModelHandle}; +use postage::prelude::Stream; use std::ops::Range; use tab_map::TabMap; +pub use wrap_map::BufferRows; use wrap_map::WrapMap; pub struct DisplayMap { @@ -76,6 +77,10 @@ impl DisplayMap { pub fn set_wrap_width(&self, width: Option) { self.wrap_map.set_wrap_width(width); } + + pub fn notifications(&self) -> impl Stream { + self.wrap_map.notifications() + } } pub struct DisplayMapSnapshot { @@ -86,8 +91,8 @@ pub struct DisplayMapSnapshot { } impl DisplayMapSnapshot { - pub fn buffer_rows(&self, start_row: u32) -> InputRows { - self.folds_snapshot.input_rows(start_row) + pub fn buffer_rows(&self, start_row: u32) -> BufferRows { + self.wraps_snapshot.buffer_rows(start_row) } pub fn max_point(&self) -> DisplayPoint { @@ -98,8 +103,8 @@ impl DisplayMapSnapshot { self.wraps_snapshot.chunks_at(point.0) } - pub fn highlighted_chunks_for_rows(&mut self, rows: Range) -> tab_map::HighlightedChunks { - self.tabs_snapshot.highlighted_chunks_for_rows(rows) + pub fn highlighted_chunks_for_rows(&mut self, rows: Range) -> wrap_map::HighlightedChunks { + self.wraps_snapshot.highlighted_chunks_for_rows(rows) } pub fn chars_at<'a>(&'a self, point: DisplayPoint) -> impl Iterator + 'a { diff --git a/zed/src/editor/display_map/fold_map.rs b/zed/src/editor/display_map/fold_map.rs index ff88cde06f..6053f217b3 100644 --- a/zed/src/editor/display_map/fold_map.rs +++ b/zed/src/editor/display_map/fold_map.rs @@ -491,7 +491,7 @@ impl Snapshot { (line_end - line_start) as u32 } - pub fn input_rows(&self, start_row: u32) -> InputRows { + pub fn buffer_rows(&self, start_row: u32) -> BufferRows { if start_row > self.transforms.summary().output.lines.row { panic!("invalid display row {}", start_row); } @@ -499,7 +499,7 @@ impl Snapshot { let output_point = OutputPoint::new(start_row, 0); let mut cursor = self.transforms.cursor(); cursor.seek(&output_point, Bias::Left, &()); - InputRows { + BufferRows { output_point, cursor, } @@ -880,12 +880,12 @@ impl<'a> sum_tree::Dimension<'a, FoldSummary> for usize { } } -pub struct InputRows<'a> { +pub struct BufferRows<'a> { cursor: Cursor<'a, Transform, OutputPoint, InputPoint>, output_point: OutputPoint, } -impl<'a> Iterator for InputRows<'a> { +impl<'a> Iterator for BufferRows<'a> { type Item = u32; fn next(&mut self) -> Option { @@ -1450,7 +1450,7 @@ mod tests { .to_output_point(InputPoint::new(*input_row, 0)) .row(); assert_eq!( - snapshot.input_rows(output_row).collect::>(), + snapshot.buffer_rows(output_row).collect::>(), expected_input_rows[idx..], ); } @@ -1544,8 +1544,8 @@ mod tests { let (snapshot, _) = map.read(cx.as_ref()); assert_eq!(snapshot.text(), "aa…cccc\nd…eeeee\nffffff\n"); - assert_eq!(snapshot.input_rows(0).collect::>(), [0, 3, 5, 6]); - assert_eq!(snapshot.input_rows(3).collect::>(), [6]); + assert_eq!(snapshot.buffer_rows(0).collect::>(), [0, 3, 5, 6]); + assert_eq!(snapshot.buffer_rows(3).collect::>(), [6]); } impl FoldMap { diff --git a/zed/src/editor/display_map/tab_map.rs b/zed/src/editor/display_map/tab_map.rs index 41228d82bd..0356312457 100644 --- a/zed/src/editor/display_map/tab_map.rs +++ b/zed/src/editor/display_map/tab_map.rs @@ -1,7 +1,7 @@ use parking_lot::Mutex; use super::fold_map::{ - Chunks as InputChunks, Edit as InputEdit, HighlightedChunks as InputHighlightedChunks, + self, Chunks as InputChunks, Edit as InputEdit, HighlightedChunks as InputHighlightedChunks, OutputOffset as InputOffset, OutputPoint as InputPoint, Snapshot as InputSnapshot, }; use crate::{editor::rope, settings::StyleId, util::Bias}; @@ -94,13 +94,15 @@ impl Snapshot { } } - pub fn highlighted_chunks_for_rows(&mut self, rows: Range) -> HighlightedChunks { - let start = self.input.to_output_offset(InputPoint::new(rows.start, 0)); - let end = self + pub fn highlighted_chunks(&mut self, range: Range) -> HighlightedChunks { + let input_start = self .input - .to_output_offset(InputPoint::new(rows.end, 0).min(self.input.max_point())); + .to_output_offset(self.to_input_point(range.start, Bias::Left).0); + let input_end = self + .input + .to_output_offset(self.to_input_point(range.end, Bias::Left).0); HighlightedChunks { - input_chunks: self.input.highlighted_chunks(start..end), + input_chunks: self.input.highlighted_chunks(input_start..input_end), column: 0, tab_size: self.tab_size, chunk: "", @@ -108,6 +110,10 @@ impl Snapshot { } } + pub fn buffer_rows(&self, row: u32) -> fold_map::BufferRows { + self.input.buffer_rows(row) + } + #[cfg(test)] pub fn text(&self) -> String { self.chunks_at(Default::default()).collect() diff --git a/zed/src/editor/display_map/wrap_map.rs b/zed/src/editor/display_map/wrap_map.rs index df4ef107b5..4385f7a815 100644 --- a/zed/src/editor/display_map/wrap_map.rs +++ b/zed/src/editor/display_map/wrap_map.rs @@ -1,13 +1,17 @@ -use super::tab_map::{ - self, Edit as InputEdit, OutputPoint as InputPoint, Snapshot as InputSnapshot, TextSummary, +use super::{ + fold_map, + tab_map::{ + self, Edit as InputEdit, OutputPoint as InputPoint, Snapshot as InputSnapshot, TextSummary, + }, }; use crate::{ - editor::{Editor, Point}, + editor::Point, + settings::StyleId, sum_tree::{self, Cursor, SumTree}, util::Bias, Settings, }; -use gpui::{AppContext, FontCache, FontSystem, Task, ViewContext}; +use gpui::{AppContext, FontCache, FontSystem, Task}; use parking_lot::Mutex; use postage::{ prelude::{Sink, Stream}, @@ -139,10 +143,41 @@ impl Snapshot { } } + pub fn highlighted_chunks_for_rows(&mut self, rows: Range) -> HighlightedChunks { + let output_start = OutputPoint::new(rows.start, 0); + let output_end = OutputPoint::new(rows.end, 0); + let mut transforms = self.transforms.cursor::(); + transforms.seek(&output_start, Bias::Right, &()); + let input_start = + InputPoint(transforms.sum_start().0 + (output_start.0 - transforms.seek_start().0)); + let input_end = self.to_input_point(output_end).min(self.input.max_point()); + HighlightedChunks { + input_chunks: self.input.highlighted_chunks(input_start..input_end), + input_position: input_start, + style_id: StyleId::default(), + input_chunk: "", + transforms, + } + } + pub fn max_point(&self) -> OutputPoint { self.to_output_point(self.input.max_point()) } + pub fn buffer_rows(&self, start_row: u32) -> BufferRows { + let mut transforms = self.transforms.cursor::(); + transforms.seek(&OutputPoint::new(start_row, 0), Bias::Right, &()); + let input_row = transforms.sum_start().row(); + let mut input_buffer_rows = self.input.buffer_rows(start_row); + let input_buffer_row = input_buffer_rows.next().unwrap(); + BufferRows { + transforms, + input_row, + input_buffer_row, + input_buffer_rows, + } + } + pub fn to_input_point(&self, point: OutputPoint) -> InputPoint { let mut cursor = self.transforms.cursor::(); cursor.seek(&point, Bias::Right, &()); @@ -167,6 +202,21 @@ pub struct Chunks<'a> { transforms: Cursor<'a, Transform, OutputPoint, InputPoint>, } +pub struct HighlightedChunks<'a> { + input_chunks: tab_map::HighlightedChunks<'a>, + input_chunk: &'a str, + style_id: StyleId, + input_position: InputPoint, + transforms: Cursor<'a, Transform, OutputPoint, InputPoint>, +} + +pub struct BufferRows<'a> { + input_buffer_rows: fold_map::BufferRows<'a>, + input_row: u32, + input_buffer_row: u32, + transforms: Cursor<'a, Transform, OutputPoint, InputPoint>, +} + impl<'a> Iterator for Chunks<'a> { type Item = &'a str; @@ -205,6 +255,65 @@ impl<'a> Iterator for Chunks<'a> { } } +impl<'a> Iterator for HighlightedChunks<'a> { + type Item = (&'a str, StyleId); + + fn next(&mut self) -> Option { + let transform = self.transforms.item()?; + if let Some(display_text) = transform.display_text { + self.transforms.next(&()); + return Some((display_text, self.style_id)); + } + + if self.input_chunk.is_empty() { + let (chunk, style_id) = self.input_chunks.next().unwrap(); + self.input_chunk = chunk; + self.style_id = style_id; + } + + let mut input_len = 0; + let transform_end = self.transforms.sum_end(&()); + for c in self.input_chunk.chars() { + let char_len = c.len_utf8(); + input_len += char_len; + if c == '\n' { + *self.input_position.row_mut() += 1; + *self.input_position.column_mut() = 0; + } else { + *self.input_position.column_mut() += char_len as u32; + } + + if self.input_position >= transform_end { + self.transforms.next(&()); + break; + } + } + + let (prefix, suffix) = self.input_chunk.split_at(input_len); + self.input_chunk = suffix; + Some((prefix, self.style_id)) + } +} + +impl<'a> Iterator for BufferRows<'a> { + type Item = u32; + + fn next(&mut self) -> Option { + let result = self.input_buffer_row; + if self.input_row + 1 < self.transforms.sum_end(&()).row() { + self.input_row += 1; + self.input_buffer_row = self.input_buffer_rows.next().unwrap(); + } else { + self.transforms.seek_forward( + &OutputPoint::new(self.transforms.seek_start().row() + 1, 0), + Bias::Right, + &(), + ); + } + Some(result) + } +} + struct State { snapshot: Snapshot, pending_edits: VecDeque<(InputSnapshot, Vec)>, @@ -222,7 +331,7 @@ impl WrapMap { input: InputSnapshot, settings: Settings, wrap_width: Option, - cx: &mut ViewContext, + cx: &AppContext, ) -> Self { let font_cache = cx.font_cache().clone(); let font_system = cx.platform().fonts(); @@ -252,7 +361,9 @@ impl WrapMap { pub fn sync(&self, input: InputSnapshot, edits: Vec, cx: &AppContext) -> Snapshot { let mut background_snapshot = self.background_snapshot.clone(); - let mut snapshot = self.background_snapshot.borrow().clone(); + let mut snapshot = background_snapshot.borrow().clone(); + + log::info!("sync version: {:?}", snapshot.input.version()); if !edits.is_empty() { self.background_changes_tx @@ -295,6 +406,10 @@ impl WrapMap { .try_send(Change::Width(width)) .unwrap(); } + + pub fn notifications(&self) -> impl Stream { + self.background_snapshot.clone().map(|_| ()) + } } struct BackgroundWrapper { @@ -362,6 +477,7 @@ impl BackgroundWrapper { } } }; + if snapshot_tx.send(self.snapshot.clone()).await.is_err() { break; } diff --git a/zed/src/editor/element.rs b/zed/src/editor/element.rs index 70a254f0d5..76b22e3a9f 100644 --- a/zed/src/editor/element.rs +++ b/zed/src/editor/element.rs @@ -338,7 +338,6 @@ impl Element for EditorElement { } let view = self.view(app); - view.set_width(size.x()); let font_cache = &cx.font_cache; let layout_cache = &cx.text_layout_cache; @@ -362,6 +361,7 @@ impl Element for EditorElement { let gutter_size = vec2f(gutter_width, size.y()); let text_size = size - vec2f(gutter_width, 0.0); + view.set_width(text_size.x()); let autoscroll_horizontally = view.autoscroll_vertically(size.y(), line_height, app);