Start rendering highlighted text and line numbers via the wrap map

Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
Max Brunsfeld 2021-07-20 18:03:02 -07:00
parent b513df3844
commit 72fdd3fb9a
6 changed files with 161 additions and 26 deletions

View file

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

View file

@ -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<f32>) {
self.wrap_map.set_wrap_width(width);
}
pub fn notifications(&self) -> impl Stream<Item = ()> {
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<u32>) -> tab_map::HighlightedChunks {
self.tabs_snapshot.highlighted_chunks_for_rows(rows)
pub fn highlighted_chunks_for_rows(&mut self, rows: Range<u32>) -> wrap_map::HighlightedChunks {
self.wraps_snapshot.highlighted_chunks_for_rows(rows)
}
pub fn chars_at<'a>(&'a self, point: DisplayPoint) -> impl Iterator<Item = char> + 'a {

View file

@ -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<Self::Item> {
@ -1450,7 +1450,7 @@ mod tests {
.to_output_point(InputPoint::new(*input_row, 0))
.row();
assert_eq!(
snapshot.input_rows(output_row).collect::<Vec<_>>(),
snapshot.buffer_rows(output_row).collect::<Vec<_>>(),
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::<Vec<_>>(), [0, 3, 5, 6]);
assert_eq!(snapshot.input_rows(3).collect::<Vec<_>>(), [6]);
assert_eq!(snapshot.buffer_rows(0).collect::<Vec<_>>(), [0, 3, 5, 6]);
assert_eq!(snapshot.buffer_rows(3).collect::<Vec<_>>(), [6]);
}
impl FoldMap {

View file

@ -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<u32>) -> HighlightedChunks {
let start = self.input.to_output_offset(InputPoint::new(rows.start, 0));
let end = self
pub fn highlighted_chunks(&mut self, range: Range<OutputPoint>) -> 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()

View file

@ -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<u32>) -> HighlightedChunks {
let output_start = OutputPoint::new(rows.start, 0);
let output_end = OutputPoint::new(rows.end, 0);
let mut transforms = self.transforms.cursor::<OutputPoint, InputPoint>();
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::<OutputPoint, InputPoint>();
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::<OutputPoint, InputPoint>();
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<Self::Item> {
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<Self::Item> {
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<InputEdit>)>,
@ -222,7 +331,7 @@ impl WrapMap {
input: InputSnapshot,
settings: Settings,
wrap_width: Option<f32>,
cx: &mut ViewContext<Editor>,
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<InputEdit>, 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<Item = ()> {
self.background_snapshot.clone().map(|_| ())
}
}
struct BackgroundWrapper {
@ -362,6 +477,7 @@ impl BackgroundWrapper {
}
}
};
if snapshot_tx.send(self.snapshot.clone()).await.is_err() {
break;
}

View file

@ -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);