diff --git a/gpui/src/executor.rs b/gpui/src/executor.rs index f9cb8edc44e21bf92e9084b82279f0b0bd26ebd5..0fed99faae9d63faa466e1e4d274b56281ae2ccb 100644 --- a/gpui/src/executor.rs +++ b/gpui/src/executor.rs @@ -385,14 +385,14 @@ impl Background { } } - pub fn block_on(&self, timeout: Duration, future: F) -> Option + pub fn block_with_timeout(&self, timeout: Duration, mut future: F) -> Result where T: 'static, - F: Future, + F: 'static + Unpin + Future, { - match self { + let output = match self { Self::Production { .. } => { - smol::block_on(async move { util::timeout(timeout, future).await.ok() }) + smol::block_on(util::timeout(timeout, Pin::new(&mut future))).ok() } Self::Deterministic(executor) => { let max_ticks = { @@ -400,8 +400,14 @@ impl Background { let range = state.block_on_ticks.clone(); state.rng.gen_range(range) }; - executor.block_on(max_ticks, future) + executor.block_on(max_ticks, Pin::new(&mut future)) } + }; + + if let Some(output) = output { + Ok(output) + } else { + Err(future) } } diff --git a/zed/src/editor.rs b/zed/src/editor.rs index 0ee4b398d5f73e251f9e4f59a45d1e823aa94285..cccad713ef69049d13bf3432b1c8464c473ea17e 100644 --- a/zed/src/editor.rs +++ b/zed/src/editor.rs @@ -2198,8 +2198,8 @@ impl Editor { font_cache.em_width(font_id, settings.buffer_font_size) } - pub fn set_wrap_width(&self, width: f32) { - self.display_map.set_wrap_width(Some(width)); + pub fn set_wrap_width(&self, width: f32, cx: &AppContext) { + self.display_map.set_wrap_width(Some(width), cx); } // TODO: Can we make this not return a result? diff --git a/zed/src/editor/display_map.rs b/zed/src/editor/display_map.rs index 80de76849dc2439b3de09de1b68baa975dd765ea..53d4bb82299805eacb55ab07241ceaca51abc2f6 100644 --- a/zed/src/editor/display_map.rs +++ b/zed/src/editor/display_map.rs @@ -1,4 +1,5 @@ mod fold_map; +mod line_wrapper; mod tab_map; mod wrap_map; @@ -74,8 +75,8 @@ impl DisplayMap { self.wrap_map.sync(snapshot, edits, cx); } - pub fn set_wrap_width(&self, width: Option) { - self.wrap_map.set_wrap_width(width); + pub fn set_wrap_width(&self, width: Option, cx: &AppContext) { + self.wrap_map.set_wrap_width(width, cx); } pub fn notifications(&self) -> impl Stream { @@ -212,11 +213,11 @@ impl DisplayMapSnapshot { } #[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)] -pub struct DisplayPoint(wrap_map::OutputPoint); +pub struct DisplayPoint(wrap_map::WrapPoint); impl DisplayPoint { pub fn new(row: u32, column: u32) -> Self { - Self(wrap_map::OutputPoint::new(row, column)) + Self(wrap_map::WrapPoint::new(row, column)) } pub fn zero() -> Self { diff --git a/zed/src/editor/display_map/line_wrapper.rs b/zed/src/editor/display_map/line_wrapper.rs new file mode 100644 index 0000000000000000000000000000000000000000..0e3a9f7b6b2187979233a97f401b815a9905ad94 --- /dev/null +++ b/zed/src/editor/display_map/line_wrapper.rs @@ -0,0 +1,108 @@ +use crate::Settings; +use gpui::{fonts::FontId, FontCache, FontSystem}; +use parking_lot::Mutex; +use std::{collections::HashMap, sync::Arc}; + +pub struct LineWrapper { + font_system: Arc, + font_cache: Arc, + font_id: FontId, + font_size: f32, + cached_ascii_char_widths: Mutex<[f32; 128]>, + cached_other_char_widths: Mutex>, +} + +impl LineWrapper { + pub fn new( + font_system: Arc, + font_cache: Arc, + settings: Settings, + ) -> Self { + let font_id = font_cache + .select_font(settings.buffer_font_family, &Default::default()) + .unwrap(); + let font_size = settings.buffer_font_size; + Self { + font_cache, + font_system, + font_id, + font_size, + cached_ascii_char_widths: Mutex::new([f32::NAN; 128]), + cached_other_char_widths: Mutex::new(HashMap::new()), + } + } + + pub fn wrap_line_with_shaping(&self, line: &str, wrap_width: f32) -> Vec { + self.font_system + .wrap_line(line, self.font_id, self.font_size, wrap_width) + } + + pub fn wrap_line_without_shaping(&self, line: &str, wrap_width: f32) -> Vec { + let mut width = 0.0; + let mut result = Vec::new(); + let mut last_boundary_ix = 0; + let mut last_boundary_width = 0.0; + let mut prev_c = '\0'; + for (ix, c) in line.char_indices() { + if self.is_boundary(prev_c, c) { + last_boundary_ix = ix; + last_boundary_width = width; + } + + let char_width = self.width_for_char(c); + width += char_width; + if width > wrap_width { + if last_boundary_ix > 0 { + result.push(last_boundary_ix); + width -= last_boundary_width; + last_boundary_ix = 0; + } else { + result.push(ix); + width = char_width; + } + } + prev_c = c; + } + result + } + + fn is_boundary(&self, prev: char, next: char) -> bool { + if prev == ' ' || next == ' ' { + return true; + } + false + } + + fn width_for_char(&self, c: char) -> f32 { + if (c as u32) < 128 { + let mut cached_ascii_char_widths = self.cached_ascii_char_widths.lock(); + let mut width = cached_ascii_char_widths[c as usize]; + if width.is_nan() { + width = self.compute_width_for_char(c); + cached_ascii_char_widths[c as usize] = width; + } + width + } else { + let mut cached_other_char_widths = self.cached_other_char_widths.lock(); + let mut width = cached_other_char_widths + .get(&c) + .copied() + .unwrap_or(f32::NAN); + if width.is_nan() { + width = self.compute_width_for_char(c); + cached_other_char_widths.insert(c, width); + } + width + } + } + + fn compute_width_for_char(&self, c: char) -> f32 { + self.font_system + .layout_line( + &c.to_string(), + self.font_size, + &[(1, self.font_id, Default::default())], + ) + .width + } +} diff --git a/zed/src/editor/display_map/wrap_map.rs b/zed/src/editor/display_map/wrap_map.rs index 5664b0fa60f5208c098b41a6f33f874dfb82c959..ee906c07a04a4e4d4cd9645a2973c5d5e19d83ef 100644 --- a/zed/src/editor/display_map/wrap_map.rs +++ b/zed/src/editor/display_map/wrap_map.rs @@ -1,7 +1,8 @@ use super::{ fold_map, + line_wrapper::LineWrapper, tab_map::{ - self, Edit as InputEdit, OutputPoint as InputPoint, Snapshot as InputSnapshot, TextSummary, + self, Edit as TabEdit, OutputPoint as TabPoint, Snapshot as TabSnapshot, TextSummary, }, }; use crate::{ @@ -11,100 +12,257 @@ use crate::{ util::Bias, Settings, }; -use gpui::{fonts::FontId, AppContext, FontCache, FontSystem, Task}; +use gpui::{executor::Background, AppContext, Task}; use parking_lot::Mutex; -use postage::{ - prelude::{Sink, Stream}, - watch, -}; -use smol::channel; -use std::{ - collections::{HashMap, VecDeque}, - ops::Range, - sync::Arc, - time::Duration, -}; +use postage::{prelude::Stream, sink::Sink, watch}; +use smol::future::yield_now; +use std::{collections::VecDeque, ops::Range, sync::Arc, time::Duration}; + +#[derive(Clone)] +pub struct WrapMap(Arc>); + +struct WrapMapState { + snapshot: Snapshot, + pending_edits: VecDeque<(TabSnapshot, Vec)>, + wrap_width: Option, + background_task: Option>, + updates: (watch::Sender<()>, watch::Receiver<()>), + line_wrapper: Arc, +} + +#[derive(Clone)] +pub struct Snapshot { + tab_snapshot: TabSnapshot, + transforms: SumTree, +} + +#[derive(Clone, Debug, Default, Eq, PartialEq)] +struct Transform { + summary: TransformSummary, + display_text: Option<&'static str>, +} + +#[derive(Clone, Debug, Default, Eq, PartialEq)] +struct TransformSummary { + input: TextSummary, + output: TextSummary, +} #[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)] -pub struct OutputPoint(super::Point); +pub struct WrapPoint(super::Point); -impl OutputPoint { - pub fn new(row: u32, column: u32) -> Self { - Self(super::Point::new(row, column)) - } +pub struct Chunks<'a> { + input_chunks: tab_map::Chunks<'a>, + input_chunk: &'a str, + input_position: TabPoint, + transforms: Cursor<'a, Transform, WrapPoint, TabPoint>, +} - pub fn zero() -> Self { - Self::new(0, 0) - } +pub struct HighlightedChunks<'a> { + input_chunks: tab_map::HighlightedChunks<'a>, + input_chunk: &'a str, + style_id: StyleId, + output_position: WrapPoint, + max_output_row: u32, + transforms: Cursor<'a, Transform, WrapPoint, TabPoint>, +} - pub fn row(self) -> u32 { - self.0.row - } +pub struct BufferRows<'a> { + input_buffer_rows: fold_map::BufferRows<'a>, + input_buffer_row: u32, + output_row: u32, + max_output_row: u32, + transforms: Cursor<'a, Transform, WrapPoint, TabPoint>, +} - pub fn column(self) -> u32 { - self.0.column +impl WrapMap { + pub fn new( + tab_snapshot: TabSnapshot, + settings: Settings, + wrap_width: Option, + cx: &AppContext, + ) -> Self { + let this = Self(Arc::new(Mutex::new(WrapMapState { + background_task: None, + wrap_width: None, + updates: watch::channel(), + pending_edits: Default::default(), + snapshot: Snapshot::new(tab_snapshot), + line_wrapper: Arc::new(LineWrapper::new( + cx.platform().fonts(), + cx.font_cache().clone(), + settings, + )), + }))); + this.set_wrap_width(wrap_width, cx); + this } - pub fn row_mut(&mut self) -> &mut u32 { - &mut self.0.row + pub fn notifications(&self) -> impl Stream { + self.0.lock().updates.1.clone() } - pub fn column_mut(&mut self) -> &mut u32 { - &mut self.0.column + pub fn sync( + &self, + tab_snapshot: TabSnapshot, + edits: Vec, + cx: &AppContext, + ) -> Snapshot { + self.0.lock().pending_edits.push_back((tab_snapshot, edits)); + self.flush_edits(cx.background()); + self.0.lock().snapshot.clone() + } + + pub fn set_wrap_width(&self, wrap_width: Option, cx: &AppContext) { + let mut state = self.0.lock(); + if wrap_width == state.wrap_width { + return; + } + + state.background_task.take(); + + if let Some(wrap_width) = wrap_width { + let mut new_snapshot = state.snapshot.clone(); + let line_wrapper = state.line_wrapper.clone(); + let task = cx.background().spawn(async move { + let tab_snapshot = new_snapshot.tab_snapshot.clone(); + let range = TabPoint::zero()..tab_snapshot.max_point(); + new_snapshot + .update( + tab_snapshot, + &[TabEdit { + old_lines: range.clone(), + new_lines: range.clone(), + }], + wrap_width, + line_wrapper.as_ref(), + ) + .await; + new_snapshot + }); + + let executor = cx.background(); + match executor.block_with_timeout(Duration::from_millis(5), task) { + Ok(snapshot) => state.snapshot = snapshot, + Err(wrap_task) => { + let this = self.clone(); + let exec = executor.clone(); + state.background_task = Some(executor.spawn(async move { + this.0.lock().snapshot = wrap_task.await; + this.flush_edits(&exec); + this.0.lock().updates.0.blocking_send(()).ok(); + })); + } + } + } } -} -#[derive(Clone)] -pub struct Snapshot { - transforms: SumTree, - input: InputSnapshot, + fn flush_edits(&self, executor: &Arc) { + let mut state = self.0.lock(); + + while let Some((tab_snapshot, _)) = state.pending_edits.front() { + if tab_snapshot.version() <= state.snapshot.tab_snapshot.version() { + state.pending_edits.pop_front(); + } else { + break; + } + } + + if state.pending_edits.is_empty() { + return; + } + + if let Some(wrap_width) = state.wrap_width { + if state.background_task.is_none() { + let pending_edits = state.pending_edits.clone(); + let mut snapshot = state.snapshot.clone(); + let line_wrapper = state.line_wrapper.clone(); + + let update_task = executor.spawn(async move { + for (tab_snapshot, edits) in pending_edits { + snapshot + .update(tab_snapshot, &edits, wrap_width, &line_wrapper) + .await; + } + snapshot + }); + + match executor.block_with_timeout(Duration::from_micros(500), update_task) { + Ok(snapshot) => { + state.snapshot = snapshot; + } + Err(update_task) => { + let this = self.clone(); + let exec = executor.clone(); + state.background_task = Some(executor.spawn(async move { + this.0.lock().snapshot = update_task.await; + this.flush_edits(&exec); + this.0.lock().updates.0.blocking_send(()).ok(); + })); + } + } + } + } + + while let Some((tab_snapshot, _)) = state.pending_edits.front() { + if tab_snapshot.version() <= state.snapshot.tab_snapshot.version() { + state.pending_edits.pop_front(); + } else { + break; + } + } + + for (tab_snapshot, edits) in state.pending_edits.clone() { + state.snapshot.interpolate(tab_snapshot, &edits); + } + } } impl Snapshot { - fn new(input: InputSnapshot) -> Self { + fn new(tab_snapshot: TabSnapshot) -> Self { + let extent = tab_snapshot.text_summary(); Self { transforms: SumTree::from_item( Transform { summary: TransformSummary { - input: input.text_summary(), - output: input.text_summary(), + input: extent.clone(), + output: extent.clone(), }, display_text: None, }, &(), ), - input, + tab_snapshot, } } - fn interpolate(&mut self, new_snapshot: InputSnapshot, edits: &[InputEdit]) { + fn interpolate(&mut self, new_tab_snapshot: TabSnapshot, edits: &[TabEdit]) { let mut new_transforms; if edits.is_empty() { new_transforms = self.transforms.clone(); } else { - let mut old_cursor = self.transforms.cursor::(); + let mut old_cursor = self.transforms.cursor::(); let mut edits = edits.into_iter().peekable(); new_transforms = old_cursor.slice(&edits.peek().unwrap().old_lines.start, Bias::Right, &()); while let Some(edit) = edits.next() { - if edit.new_lines.start > InputPoint::from(new_transforms.summary().input.lines) { - let summary = new_snapshot.text_summary_for_range( - InputPoint::from(new_transforms.summary().input.lines) - ..edit.new_lines.start, + if edit.new_lines.start > TabPoint::from(new_transforms.summary().input.lines) { + let summary = new_tab_snapshot.text_summary_for_range( + TabPoint::from(new_transforms.summary().input.lines)..edit.new_lines.start, ); new_transforms.push_or_extend(Transform::isomorphic(summary)); } new_transforms.push_or_extend(Transform::isomorphic( - new_snapshot.text_summary_for_range(edit.new_lines.clone()), + new_tab_snapshot.text_summary_for_range(edit.new_lines.clone()), )); old_cursor.seek_forward(&edit.old_lines.end, Bias::Right, &()); if let Some(next_edit) = edits.peek() { if next_edit.old_lines.start > old_cursor.seek_end(&()) { if old_cursor.seek_end(&()) > edit.old_lines.end { - let summary = self.input.text_summary_for_range( + let summary = self.tab_snapshot.text_summary_for_range( edit.old_lines.end..old_cursor.seek_end(&()), ); new_transforms.push_or_extend(Transform::isomorphic(summary)); @@ -118,7 +276,7 @@ impl Snapshot { } else { if old_cursor.seek_end(&()) > edit.old_lines.end { let summary = self - .input + .tab_snapshot .text_summary_for_range(edit.old_lines.end..old_cursor.seek_end(&())); new_transforms.push_or_extend(Transform::isomorphic(summary)); } @@ -129,15 +287,144 @@ impl Snapshot { } self.transforms = new_transforms; - self.input = new_snapshot; + self.tab_snapshot = new_tab_snapshot; } - pub fn chunks_at(&self, point: OutputPoint) -> Chunks { - let mut transforms = self.transforms.cursor::(); + async fn update( + &mut self, + new_tab_snapshot: TabSnapshot, + edits: &[TabEdit], + wrap_width: f32, + line_wrapper: &LineWrapper, + ) { + #[derive(Debug)] + struct RowEdit { + old_rows: Range, + new_rows: Range, + } + + let mut edits = edits.into_iter().peekable(); + let mut row_edits = Vec::new(); + while let Some(edit) = edits.next() { + let mut row_edit = RowEdit { + old_rows: edit.old_lines.start.row()..edit.old_lines.end.row() + 1, + new_rows: edit.new_lines.start.row()..edit.new_lines.end.row() + 1, + }; + + while let Some(next_edit) = edits.peek() { + if next_edit.old_lines.start.row() <= row_edit.old_rows.end { + row_edit.old_rows.end = next_edit.old_lines.end.row() + 1; + row_edit.new_rows.end = next_edit.new_lines.end.row() + 1; + edits.next(); + } else { + break; + } + } + + row_edits.push(row_edit); + } + + let mut new_transforms; + if row_edits.is_empty() { + new_transforms = self.transforms.clone(); + } else { + let mut row_edits = row_edits.into_iter().peekable(); + let mut old_cursor = self.transforms.cursor::(); + + new_transforms = old_cursor.slice( + &TabPoint::new(row_edits.peek().unwrap().old_rows.start, 0), + Bias::Right, + &(), + ); + + while let Some(edit) = row_edits.next() { + if edit.new_rows.start > new_transforms.summary().input.lines.row { + let summary = new_tab_snapshot.text_summary_for_range( + TabPoint::new(new_transforms.summary().input.lines.row, 0) + ..TabPoint::new(edit.new_rows.start, 0), + ); + new_transforms.push_or_extend(Transform::isomorphic(summary)); + } + + let mut line = String::new(); + let mut remaining = None; + let mut chunks = new_tab_snapshot.chunks_at(TabPoint::new(edit.new_rows.start, 0)); + for _ in edit.new_rows.start..edit.new_rows.end { + while let Some(chunk) = remaining.take().or_else(|| chunks.next()) { + if let Some(ix) = chunk.find('\n') { + line.push_str(&chunk[..ix + 1]); + remaining = Some(&chunk[ix + 1..]); + break; + } else { + line.push_str(chunk) + } + } + + if line.is_empty() { + break; + } + + let mut prev_boundary_ix = 0; + for boundary_ix in line_wrapper.wrap_line_without_shaping(&line, wrap_width) { + let wrapped = &line[prev_boundary_ix..boundary_ix]; + new_transforms + .push_or_extend(Transform::isomorphic(TextSummary::from(wrapped))); + new_transforms.push_or_extend(Transform::newline()); + prev_boundary_ix = boundary_ix; + } + + if prev_boundary_ix < line.len() { + new_transforms.push_or_extend(Transform::isomorphic(TextSummary::from( + &line[prev_boundary_ix..], + ))); + } + + line.clear(); + yield_now().await; + } + + old_cursor.seek_forward(&TabPoint::new(edit.old_rows.end, 0), Bias::Right, &()); + if let Some(next_edit) = row_edits.peek() { + if next_edit.old_rows.start > old_cursor.seek_end(&()).row() { + if old_cursor.seek_end(&()) > TabPoint::new(edit.old_rows.end, 0) { + let summary = self.tab_snapshot.text_summary_for_range( + TabPoint::new(edit.old_rows.end, 0)..old_cursor.seek_end(&()), + ); + new_transforms.push_or_extend(Transform::isomorphic(summary)); + } + old_cursor.next(&()); + new_transforms.push_tree( + old_cursor.slice( + &TabPoint::new(next_edit.old_rows.start, 0), + Bias::Right, + &(), + ), + &(), + ); + } + } else { + if old_cursor.seek_end(&()) > TabPoint::new(edit.old_rows.end, 0) { + let summary = self.tab_snapshot.text_summary_for_range( + TabPoint::new(edit.old_rows.end, 0)..old_cursor.seek_end(&()), + ); + new_transforms.push_or_extend(Transform::isomorphic(summary)); + } + old_cursor.next(&()); + new_transforms.push_tree(old_cursor.suffix(&()), &()); + } + } + } + + self.transforms = new_transforms; + self.tab_snapshot = new_tab_snapshot; + } + + pub fn chunks_at(&self, point: WrapPoint) -> Chunks { + let mut transforms = self.transforms.cursor::(); transforms.seek(&point, Bias::Right, &()); let input_position = - InputPoint(transforms.sum_start().0 + (point.0 - transforms.seek_start().0)); - let input_chunks = self.input.chunks_at(input_position); + TabPoint(transforms.sum_start().0 + (point.0 - transforms.seek_start().0)); + let input_chunks = self.tab_snapshot.chunks_at(input_position); Chunks { input_chunks, transforms, @@ -147,15 +434,17 @@ 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::(); + let output_start = WrapPoint::new(rows.start, 0); + let output_end = WrapPoint::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()); + TabPoint(transforms.sum_start().0 + (output_start.0 - transforms.seek_start().0)); + let input_end = self + .to_input_point(output_end) + .min(self.tab_snapshot.max_point()); HighlightedChunks { - input_chunks: self.input.highlighted_chunks(input_start..input_end), + input_chunks: self.tab_snapshot.highlighted_chunks(input_start..input_end), input_chunk: "", style_id: StyleId::default(), output_position: output_start, @@ -164,13 +453,13 @@ impl Snapshot { } } - pub fn max_point(&self) -> OutputPoint { - self.to_output_point(self.input.max_point()) + pub fn max_point(&self) -> WrapPoint { + self.to_output_point(self.tab_snapshot.max_point()) } pub fn line_len(&self, row: u32) -> u32 { let mut len = 0; - for chunk in self.chunks_at(OutputPoint::new(row, 0)) { + for chunk in self.chunks_at(WrapPoint::new(row, 0)) { if let Some(newline_ix) = chunk.find('\n') { len += newline_ix; break; @@ -186,10 +475,10 @@ impl Snapshot { } 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 mut transforms = self.transforms.cursor::(); + transforms.seek(&WrapPoint::new(start_row, 0), Bias::Right, &()); let input_row = transforms.sum_start().row() + (start_row - transforms.seek_start().row()); - let mut input_buffer_rows = self.input.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(); BufferRows { transforms, @@ -200,21 +489,21 @@ impl Snapshot { } } - pub fn to_input_point(&self, point: OutputPoint) -> InputPoint { - let mut cursor = self.transforms.cursor::(); + pub fn to_input_point(&self, point: WrapPoint) -> TabPoint { + let mut cursor = self.transforms.cursor::(); cursor.seek(&point, Bias::Right, &()); - InputPoint(cursor.sum_start().0 + (point.0 - cursor.seek_start().0)) + TabPoint(cursor.sum_start().0 + (point.0 - cursor.seek_start().0)) } - pub fn to_output_point(&self, point: InputPoint) -> OutputPoint { - let mut cursor = self.transforms.cursor::(); + pub fn to_output_point(&self, point: TabPoint) -> WrapPoint { + let mut cursor = self.transforms.cursor::(); cursor.seek(&point, Bias::Right, &()); - OutputPoint(cursor.sum_start().0 + (point.0 - cursor.seek_start().0)) + WrapPoint(cursor.sum_start().0 + (point.0 - cursor.seek_start().0)) } - pub fn clip_point(&self, mut point: OutputPoint, bias: Bias) -> OutputPoint { + pub fn clip_point(&self, mut point: WrapPoint, bias: Bias) -> WrapPoint { if bias == Bias::Left { - let mut cursor = self.transforms.cursor::(); + let mut cursor = self.transforms.cursor::(); cursor.seek(&point, Bias::Right, &()); let transform = cursor.item().expect("invalid point"); if !transform.is_isomorphic() { @@ -222,34 +511,13 @@ impl Snapshot { } } - self.to_output_point(self.input.clip_point(self.to_input_point(point), bias)) + self.to_output_point( + self.tab_snapshot + .clip_point(self.to_input_point(point), bias), + ) } } -pub struct Chunks<'a> { - input_chunks: tab_map::Chunks<'a>, - input_chunk: &'a str, - input_position: InputPoint, - transforms: Cursor<'a, Transform, OutputPoint, InputPoint>, -} - -pub struct HighlightedChunks<'a> { - input_chunks: tab_map::HighlightedChunks<'a>, - input_chunk: &'a str, - style_id: StyleId, - output_position: OutputPoint, - max_output_row: u32, - transforms: Cursor<'a, Transform, OutputPoint, InputPoint>, -} - -pub struct BufferRows<'a> { - input_buffer_rows: fold_map::BufferRows<'a>, - input_buffer_row: u32, - output_row: u32, - max_output_row: u32, - transforms: Cursor<'a, Transform, OutputPoint, InputPoint>, -} - impl<'a> Iterator for Chunks<'a> { type Item = &'a str; @@ -344,7 +612,7 @@ impl<'a> Iterator for BufferRows<'a> { let buffer_row = self.input_buffer_row; self.output_row += 1; self.transforms - .seek_forward(&OutputPoint::new(self.output_row, 0), Bias::Left, &()); + .seek_forward(&WrapPoint::new(self.output_row, 0), Bias::Left, &()); if self.transforms.item().map_or(false, |t| t.is_isomorphic()) { self.input_buffer_row = self.input_buffer_rows.next().unwrap(); } @@ -353,460 +621,6 @@ impl<'a> Iterator for BufferRows<'a> { } } -struct State { - snapshot: Snapshot, - pending_edits: VecDeque<(InputSnapshot, Vec)>, - last_background_output_version: usize, -} - -pub struct WrapMap { - state: Mutex, - notifications_rx: watch::Receiver<()>, - background_changes_tx: channel::Sender, - background_state: Arc>, - _background_task: Task<()>, -} - -impl WrapMap { - pub fn new( - input: InputSnapshot, - settings: Settings, - wrap_width: Option, - cx: &AppContext, - ) -> Self { - let snapshot = Snapshot::new(input.clone()); - let (mut wrapper, background_state, notifications_rx) = BackgroundWrapper::new( - snapshot.clone(), - settings, - wrap_width, - cx.font_cache().clone(), - cx.platform().fonts(), - ); - let (background_changes_tx, background_changes_rx) = channel::unbounded(); - let background_task = cx.background().spawn(async move { - wrapper.run(input, background_changes_rx).await; - }); - - Self { - state: Mutex::new(State { - snapshot, - pending_edits: VecDeque::new(), - last_background_output_version: 0, - }), - background_changes_tx, - background_state, - _background_task: background_task, - notifications_rx, - } - } - - pub fn sync(&self, input: InputSnapshot, edits: Vec, cx: &AppContext) -> Snapshot { - let mut state = &mut *self.state.lock(); - let mut background_state = self.background_state.lock(); - - let block = if input.version() > state.snapshot.input.version() { - self.background_changes_tx - .try_send(Change::Input { - snapshot: input.clone(), - edits: edits.clone(), - }) - .unwrap(); - state.pending_edits.push_back((input.clone(), edits)); - true - } else { - background_state.is_wrapping - }; - - println!("will block? {}", block); - let mut updated_from_background = false; - if block { - let (sync_tx, sync_rx) = channel::unbounded(); - background_state.sync_tx = Some(sync_tx); - drop(background_state); - cx.background().block_on(Duration::from_micros(500), async { - loop { - sync_rx.recv().await.unwrap(); - let background_state = self.background_state.lock(); - state.snapshot = background_state.snapshot.clone(); - state.last_background_output_version = background_state.output_version; - updated_from_background = true; - if state.snapshot.input.version() == input.version() { - break; - } - } - }); - } else { - drop(background_state); - } - - { - let mut background_state = self.background_state.lock(); - background_state.sync_tx = None; - if background_state.output_version > state.last_background_output_version { - println!("last chance to update"); - state.snapshot = background_state.snapshot.clone(); - state.last_background_output_version = background_state.output_version; - updated_from_background = true; - } - } - - println!("updated from background? {}", updated_from_background); - if updated_from_background { - while let Some((pending_input, _)) = state.pending_edits.front() { - if pending_input.version() <= state.snapshot.input.version() { - state.pending_edits.pop_front(); - } else { - break; - } - } - - for (input, edits) in &state.pending_edits { - state.snapshot.interpolate(input.clone(), &edits); - } - } - - state.snapshot.clone() - } - - pub fn set_wrap_width(&self, width: Option) { - self.background_changes_tx - .try_send(Change::Width(width)) - .unwrap(); - } - - pub fn notifications(&self) -> impl Stream { - self.notifications_rx.clone() - } -} - -struct BackgroundWrapper { - wrap_width: Option, - state: Arc>, - snapshot: Snapshot, - notifications_tx: watch::Sender<()>, - line_wrapper: LineWrapper, -} - -struct BackgroundState { - is_wrapping: bool, - sync_tx: Option>, - snapshot: Snapshot, - output_version: usize, -} - -struct LineWrapper { - font_system: Arc, - font_cache: Arc, - font_id: FontId, - font_size: f32, - cached_ascii_char_widths: [f32; 128], - cached_other_char_widths: HashMap, -} - -enum Change { - Input { - snapshot: InputSnapshot, - edits: Vec, - }, - Width(Option), -} - -impl BackgroundWrapper { - fn new( - snapshot: Snapshot, - settings: Settings, - wrap_width: Option, - font_cache: Arc, - font_system: Arc, - ) -> (Self, Arc>, watch::Receiver<()>) { - let (notifications_tx, notifications_rx) = watch::channel(); - let state = Arc::new(Mutex::new(BackgroundState { - is_wrapping: false, - sync_tx: None, - snapshot: snapshot.clone(), - output_version: 0, - })); - let wrapper = Self { - wrap_width, - state: state.clone(), - snapshot, - line_wrapper: LineWrapper::new(font_system, font_cache, settings), - notifications_tx, - }; - (wrapper, state, notifications_rx) - } - - async fn run(&mut self, input: InputSnapshot, changes_rx: channel::Receiver) { - let edit = InputEdit { - old_lines: Default::default()..input.max_point(), - new_lines: Default::default()..input.max_point(), - }; - self.sync(input, vec![edit]); - - while let Ok(change) = changes_rx.recv().await { - match change { - Change::Input { snapshot, edits } => { - self.sync(snapshot, edits); - } - Change::Width(wrap_width) => { - if self.wrap_width != wrap_width { - self.wrap_width = wrap_width; - let edit = InputEdit { - old_lines: Default::default()..self.snapshot.input.max_point(), - new_lines: Default::default()..self.snapshot.input.max_point(), - }; - self.sync(self.snapshot.input.clone(), vec![edit]); - } - } - }; - } - } - - fn sync(&mut self, new_snapshot: InputSnapshot, edits: Vec) { - #[derive(Debug)] - struct RowEdit { - old_rows: Range, - new_rows: Range, - } - - self.state.lock().is_wrapping = true; - - let mut edits = edits.into_iter().peekable(); - let mut row_edits = Vec::new(); - while let Some(edit) = edits.next() { - let mut row_edit = RowEdit { - old_rows: edit.old_lines.start.row()..edit.old_lines.end.row() + 1, - new_rows: edit.new_lines.start.row()..edit.new_lines.end.row() + 1, - }; - - while let Some(next_edit) = edits.peek() { - if next_edit.old_lines.start.row() <= row_edit.old_rows.end { - row_edit.old_rows.end = next_edit.old_lines.end.row() + 1; - row_edit.new_rows.end = next_edit.new_lines.end.row() + 1; - edits.next(); - } else { - break; - } - } - - row_edits.push(row_edit); - } - - let mut new_transforms; - if row_edits.is_empty() { - new_transforms = self.snapshot.transforms.clone(); - } else { - let mut row_edits = row_edits.into_iter().peekable(); - let mut old_cursor = self.snapshot.transforms.cursor::(); - - new_transforms = old_cursor.slice( - &InputPoint::new(row_edits.peek().unwrap().old_rows.start, 0), - Bias::Right, - &(), - ); - - while let Some(edit) = row_edits.next() { - if edit.new_rows.start > new_transforms.summary().input.lines.row { - let summary = new_snapshot.text_summary_for_range( - InputPoint::new(new_transforms.summary().input.lines.row, 0) - ..InputPoint::new(edit.new_rows.start, 0), - ); - new_transforms.push_or_extend(Transform::isomorphic(summary)); - } - - let mut input_row = edit.new_rows.start; - let mut line = String::new(); - let mut remaining = None; - let mut chunks = new_snapshot.chunks_at(InputPoint::new(input_row, 0)); - loop { - while let Some(chunk) = remaining.take().or_else(|| chunks.next()) { - if let Some(ix) = chunk.find('\n') { - line.push_str(&chunk[..ix + 1]); - remaining = Some(&chunk[ix + 1..]); - break; - } else { - line.push_str(chunk) - } - } - - if line.is_empty() { - break; - } - - let mut prev_boundary_ix = 0; - if let Some(wrap_width) = self.wrap_width { - for boundary_ix in self - .line_wrapper - .wrap_line_without_shaping(&line, wrap_width) - { - let wrapped = &line[prev_boundary_ix..boundary_ix]; - new_transforms - .push_or_extend(Transform::isomorphic(TextSummary::from(wrapped))); - new_transforms.push_or_extend(Transform::newline()); - prev_boundary_ix = boundary_ix; - } - } - - if prev_boundary_ix < line.len() { - new_transforms.push_or_extend(Transform::isomorphic(TextSummary::from( - &line[prev_boundary_ix..], - ))); - } - - line.clear(); - input_row += 1; - if input_row == edit.new_rows.end { - break; - } - } - - old_cursor.seek_forward(&InputPoint::new(edit.old_rows.end, 0), Bias::Right, &()); - if let Some(next_edit) = row_edits.peek() { - if next_edit.old_rows.start > old_cursor.seek_end(&()).row() { - if old_cursor.seek_end(&()) > InputPoint::new(edit.old_rows.end, 0) { - let summary = self.snapshot.input.text_summary_for_range( - InputPoint::new(edit.old_rows.end, 0)..old_cursor.seek_end(&()), - ); - new_transforms.push_or_extend(Transform::isomorphic(summary)); - } - old_cursor.next(&()); - new_transforms.push_tree( - old_cursor.slice( - &InputPoint::new(next_edit.old_rows.start, 0), - Bias::Right, - &(), - ), - &(), - ); - } - } else { - if old_cursor.seek_end(&()) > InputPoint::new(edit.old_rows.end, 0) { - let summary = self.snapshot.input.text_summary_for_range( - InputPoint::new(edit.old_rows.end, 0)..old_cursor.seek_end(&()), - ); - new_transforms.push_or_extend(Transform::isomorphic(summary)); - } - old_cursor.next(&()); - new_transforms.push_tree(old_cursor.suffix(&()), &()); - } - } - } - - self.snapshot.transforms = new_transforms; - self.snapshot.input = new_snapshot; - - let mut state = self.state.lock(); - state.output_version += 1; - state.is_wrapping = false; - state.snapshot = self.snapshot.clone(); - if let Some(sync_tx) = state.sync_tx.as_ref() { - let _ = sync_tx.try_send(()); - } else { - let _ = self.notifications_tx.try_send(()); - } - } -} - -impl LineWrapper { - fn new( - font_system: Arc, - font_cache: Arc, - settings: Settings, - ) -> Self { - let font_id = font_cache - .select_font(settings.buffer_font_family, &Default::default()) - .unwrap(); - let font_size = settings.buffer_font_size; - Self { - font_cache, - font_system, - font_id, - font_size, - cached_ascii_char_widths: [f32::NAN; 128], - cached_other_char_widths: HashMap::new(), - } - } - - fn wrap_line_with_shaping(&mut self, line: &str, wrap_width: f32) -> Vec { - self.font_system - .wrap_line(line, self.font_id, self.font_size, wrap_width) - } - - fn wrap_line_without_shaping(&mut self, line: &str, wrap_width: f32) -> Vec { - let mut width = 0.0; - let mut result = Vec::new(); - let mut last_boundary_ix = 0; - let mut last_boundary_width = 0.0; - let mut prev_c = '\0'; - for (ix, c) in line.char_indices() { - if self.is_boundary(prev_c, c) { - last_boundary_ix = ix; - last_boundary_width = width; - } - - let char_width = self.width_for_char(c); - width += char_width; - if width > wrap_width { - if last_boundary_ix > 0 { - result.push(last_boundary_ix); - width -= last_boundary_width; - last_boundary_ix = 0; - } else { - result.push(ix); - width = char_width; - } - } - prev_c = c; - } - result - } - - fn is_boundary(&self, prev: char, next: char) -> bool { - if prev == ' ' || next == ' ' { - return true; - } - false - } - - fn width_for_char(&mut self, c: char) -> f32 { - if (c as u32) < 128 { - let mut width = self.cached_ascii_char_widths[c as usize]; - if width.is_nan() { - width = self.compute_width_for_char(c); - self.cached_ascii_char_widths[c as usize] = width; - } - width - } else { - let mut width = self - .cached_other_char_widths - .get(&c) - .copied() - .unwrap_or(f32::NAN); - if width.is_nan() { - width = self.compute_width_for_char(c); - self.cached_other_char_widths.insert(c, width); - } - width - } - } - - fn compute_width_for_char(&self, c: char) -> f32 { - self.font_system - .layout_line( - &c.to_string(), - self.font_size, - &[(1, self.font_id, Default::default())], - ) - .width - } -} - -#[derive(Clone, Debug, Default, Eq, PartialEq)] -struct Transform { - summary: TransformSummary, - display_text: Option<&'static str>, -} - impl Transform { fn isomorphic(summary: TextSummary) -> Self { Self { @@ -867,10 +681,30 @@ impl SumTree { } } -#[derive(Clone, Debug, Default, Eq, PartialEq)] -struct TransformSummary { - input: TextSummary, - output: TextSummary, +impl WrapPoint { + pub fn new(row: u32, column: u32) -> Self { + Self(super::Point::new(row, column)) + } + + pub fn zero() -> Self { + Self::new(0, 0) + } + + pub fn row(self) -> u32 { + self.0.row + } + + pub fn column(self) -> u32 { + self.0.column + } + + pub fn row_mut(&mut self) -> &mut u32 { + &mut self.0.row + } + + pub fn column_mut(&mut self) -> &mut u32 { + &mut self.0.column + } } impl sum_tree::Summary for TransformSummary { @@ -882,13 +716,13 @@ impl sum_tree::Summary for TransformSummary { } } -impl<'a> sum_tree::Dimension<'a, TransformSummary> for InputPoint { +impl<'a> sum_tree::Dimension<'a, TransformSummary> for TabPoint { fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) { self.0 += summary.input.lines; } } -impl<'a> sum_tree::Dimension<'a, TransformSummary> for OutputPoint { +impl<'a> sum_tree::Dimension<'a, TransformSummary> for WrapPoint { fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) { self.0 += summary.output.lines; } @@ -984,7 +818,7 @@ mod tests { font_cache.clone(), font_system.clone(), ); - let edit = InputEdit { + let edit = TabEdit { old_lines: Default::default()..tabs_snapshot.max_point(), new_lines: Default::default()..tabs_snapshot.max_point(), }; @@ -995,7 +829,7 @@ mod tests { let expected_text = wrap_text(&unwrapped_text, wrap_width, &mut line_wrapper); let actual_text = wrapper .snapshot - .chunks_at(OutputPoint::zero()) + .chunks_at(WrapPoint::zero()) .collect::(); assert_eq!( actual_text, expected_text, @@ -1048,13 +882,13 @@ mod tests { impl Snapshot { fn text(&self) -> String { - self.chunks_at(OutputPoint::zero()).collect() + self.chunks_at(WrapPoint::zero()).collect() } fn check_invariants(&self) { assert_eq!( - InputPoint::from(self.transforms.summary().input.lines), - self.input.max_point() + TabPoint::from(self.transforms.summary().input.lines), + self.tab_snapshot.max_point() ); let mut transforms = self.transforms.cursor::<(), ()>().peekable(); diff --git a/zed/src/editor/element.rs b/zed/src/editor/element.rs index b1b2a81adc964099394bf54e976dee433d4c152b..86e425df411e5b028e2d64bd3b4f9c6f75c40f7b 100644 --- a/zed/src/editor/element.rs +++ b/zed/src/editor/element.rs @@ -366,7 +366,7 @@ impl Element for EditorElement { let wrap_width = text_size.x() - text_offset.x() - overscroll.x(); // TODO: Core text doesn't seem to be keeping our lines below the specified wrap width. Find out why. let wrap_width = wrap_width - em_width; - view.set_wrap_width(wrap_width); + view.set_wrap_width(wrap_width, app); let autoscroll_horizontally = view.autoscroll_vertically(size.y(), line_height, app);