mirror of
https://github.com/zed-industries/zed.git
synced 2025-02-03 00:39:51 +00:00
Use chunk-wise DisplayMap iteration when laying out lines
This commit is contained in:
parent
a9583d0074
commit
c62a679e12
4 changed files with 101 additions and 111 deletions
|
@ -2140,23 +2140,26 @@ impl BufferView {
|
|||
|
||||
let mut layouts = Vec::with_capacity(rows.len());
|
||||
let mut line = String::new();
|
||||
let mut line_len = 0;
|
||||
let mut row = rows.start;
|
||||
let snapshot = self.display_map.snapshot(ctx);
|
||||
let chars = snapshot.chars_at(DisplayPoint::new(rows.start, 0), ctx);
|
||||
for char in chars.chain(Some('\n')) {
|
||||
if char == '\n' {
|
||||
layouts.push(layout_cache.layout_str(&line, font_size, &[(0..line_len, font_id)]));
|
||||
let chunks = snapshot.chunks_at(DisplayPoint::new(rows.start, 0), ctx);
|
||||
for (chunk_row, chunk_line) in chunks
|
||||
.chain(Some("\n"))
|
||||
.flat_map(|chunk| chunk.split("\n").enumerate())
|
||||
{
|
||||
if chunk_row > 0 {
|
||||
layouts.push(layout_cache.layout_str(
|
||||
&line,
|
||||
font_size,
|
||||
&[(0..line.chars().count(), font_id)],
|
||||
));
|
||||
line.clear();
|
||||
line_len = 0;
|
||||
row += 1;
|
||||
if row == rows.end {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
line_len += 1;
|
||||
line.push(char);
|
||||
}
|
||||
line.push_str(chunk_line);
|
||||
}
|
||||
|
||||
Ok(layouts)
|
||||
|
|
|
@ -3,7 +3,7 @@ use super::{
|
|||
Anchor, Buffer, DisplayPoint, Edit, Point, ToOffset,
|
||||
};
|
||||
use crate::{
|
||||
editor::{buffer, rope},
|
||||
editor::buffer,
|
||||
sum_tree::{self, Cursor, FilterCursor, SeekBias, SumTree},
|
||||
time,
|
||||
};
|
||||
|
@ -11,7 +11,6 @@ use gpui::{AppContext, ModelHandle};
|
|||
use parking_lot::{Mutex, MutexGuard};
|
||||
use std::{
|
||||
cmp::{self, Ordering},
|
||||
iter::Take,
|
||||
ops::Range,
|
||||
};
|
||||
|
||||
|
@ -442,19 +441,16 @@ impl FoldMapSnapshot {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn chars_at<'a>(&'a self, point: DisplayPoint, ctx: &'a AppContext) -> Chars<'a> {
|
||||
pub fn chars_at<'a>(
|
||||
&'a self,
|
||||
point: DisplayPoint,
|
||||
ctx: &'a AppContext,
|
||||
) -> impl Iterator<Item = char> + 'a {
|
||||
let offset = self.to_display_offset(point, ctx);
|
||||
let mut cursor = self.transforms.cursor();
|
||||
cursor.seek(&offset, SeekBias::Right, &());
|
||||
Chars {
|
||||
cursor,
|
||||
offset: offset.0,
|
||||
buffer: self.buffer.read(ctx),
|
||||
buffer_chars: None,
|
||||
}
|
||||
self.chunks_at(offset, ctx).flat_map(str::chars)
|
||||
}
|
||||
|
||||
fn to_display_offset(&self, point: DisplayPoint, ctx: &AppContext) -> DisplayOffset {
|
||||
pub fn to_display_offset(&self, point: DisplayPoint, ctx: &AppContext) -> DisplayOffset {
|
||||
let mut cursor = self.transforms.cursor::<DisplayPoint, TransformSummary>();
|
||||
cursor.seek(&point, SeekBias::Right, &());
|
||||
let overshoot = point.0 - cursor.start().display.lines;
|
||||
|
@ -628,41 +624,6 @@ impl<'a> Iterator for BufferRows<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct Chars<'a> {
|
||||
cursor: Cursor<'a, Transform, DisplayOffset, TransformSummary>,
|
||||
offset: usize,
|
||||
buffer: &'a Buffer,
|
||||
buffer_chars: Option<Take<rope::Chars<'a>>>,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for Chars<'a> {
|
||||
type Item = char;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if let Some(c) = self.buffer_chars.as_mut().and_then(|chars| chars.next()) {
|
||||
self.offset += c.len_utf8();
|
||||
return Some(c);
|
||||
}
|
||||
|
||||
while self.offset == self.cursor.end().display.bytes && self.cursor.item().is_some() {
|
||||
self.cursor.next();
|
||||
}
|
||||
|
||||
self.cursor.item().and_then(|transform| {
|
||||
if let Some(c) = transform.display_text {
|
||||
self.offset += c.len();
|
||||
Some(c.chars().next().unwrap())
|
||||
} else {
|
||||
let overshoot = self.offset - self.cursor.start().display.bytes;
|
||||
let buffer_start = self.cursor.start().buffer.bytes + overshoot;
|
||||
let char_count = self.cursor.end().buffer.bytes - buffer_start;
|
||||
self.buffer_chars = Some(self.buffer.chars_at(buffer_start).take(char_count));
|
||||
self.next()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Chunks<'a> {
|
||||
transform_cursor: Cursor<'a, Transform, DisplayOffset, TransformSummary>,
|
||||
buffer_chunks: buffer::ChunksIter<'a>,
|
||||
|
|
|
@ -67,15 +67,24 @@ impl DisplayMap {
|
|||
|
||||
pub fn text(&self, ctx: &AppContext) -> String {
|
||||
self.snapshot(ctx)
|
||||
.chars_at(DisplayPoint::zero(), ctx)
|
||||
.chunks_at(DisplayPoint::zero(), ctx)
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn line(&self, display_row: u32, ctx: &AppContext) -> String {
|
||||
self.snapshot(ctx)
|
||||
.chars_at(DisplayPoint::new(display_row, 0), ctx)
|
||||
.take_while(|c| *c != '\n')
|
||||
.collect()
|
||||
let mut result = String::new();
|
||||
for chunk in self
|
||||
.snapshot(ctx)
|
||||
.chunks_at(DisplayPoint::new(display_row, 0), ctx)
|
||||
{
|
||||
if let Some(ix) = chunk.find('\n') {
|
||||
result.push_str(&chunk[0..ix]);
|
||||
break;
|
||||
} else {
|
||||
result.push_str(chunk);
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
pub fn line_indent(&self, display_row: u32, ctx: &AppContext) -> (u32, bool) {
|
||||
|
@ -83,7 +92,8 @@ impl DisplayMap {
|
|||
let mut is_blank = true;
|
||||
for c in self
|
||||
.snapshot(ctx)
|
||||
.chars_at(DisplayPoint::new(display_row, 0), ctx)
|
||||
.chunks_at(DisplayPoint::new(display_row, 0), ctx)
|
||||
.flat_map(str::chars)
|
||||
{
|
||||
if c == ' ' {
|
||||
indent += 1;
|
||||
|
@ -132,21 +142,28 @@ impl DisplayMapSnapshot {
|
|||
self.folds_snapshot.buffer_rows(start_row)
|
||||
}
|
||||
|
||||
pub fn chars_at<'a>(&'a self, point: DisplayPoint, app: &'a AppContext) -> Chars<'a> {
|
||||
pub fn chunks_at<'a>(&'a self, point: DisplayPoint, app: &'a AppContext) -> Chunks<'a> {
|
||||
let column = point.column() as usize;
|
||||
let (point, to_next_stop) = self.collapse_tabs(point, Bias::Left, app);
|
||||
let mut fold_chars = self.folds_snapshot.chars_at(point, app);
|
||||
if to_next_stop > 0 {
|
||||
fold_chars.next();
|
||||
}
|
||||
Chars {
|
||||
fold_chars,
|
||||
let (point, _) = self.collapse_tabs(point, Bias::Left, app);
|
||||
let fold_chunks = self
|
||||
.folds_snapshot
|
||||
.chunks_at(self.folds_snapshot.to_display_offset(point, app), app);
|
||||
Chunks {
|
||||
fold_chunks,
|
||||
column,
|
||||
to_next_stop,
|
||||
tab_size: self.tab_size,
|
||||
chunk: "",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn chars_at<'a>(
|
||||
&'a self,
|
||||
point: DisplayPoint,
|
||||
app: &'a AppContext,
|
||||
) -> impl Iterator<Item = char> + 'a {
|
||||
self.chunks_at(point, app).flat_map(str::chars)
|
||||
}
|
||||
|
||||
fn expand_tabs(&self, mut point: DisplayPoint, ctx: &AppContext) -> DisplayPoint {
|
||||
let chars = self
|
||||
.folds_snapshot
|
||||
|
@ -238,38 +255,50 @@ impl Anchor {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct Chars<'a> {
|
||||
fold_chars: fold_map::Chars<'a>,
|
||||
pub struct Chunks<'a> {
|
||||
fold_chunks: fold_map::Chunks<'a>,
|
||||
chunk: &'a str,
|
||||
column: usize,
|
||||
to_next_stop: usize,
|
||||
tab_size: usize,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for Chars<'a> {
|
||||
type Item = char;
|
||||
impl<'a> Iterator for Chunks<'a> {
|
||||
type Item = &'a str;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.to_next_stop > 0 {
|
||||
self.to_next_stop -= 1;
|
||||
self.column += 1;
|
||||
Some(' ')
|
||||
// Handles a tab width <= 16
|
||||
const SPACES: &'static str = " ";
|
||||
|
||||
if self.chunk.is_empty() {
|
||||
if let Some(chunk) = self.fold_chunks.next() {
|
||||
self.chunk = chunk;
|
||||
} else {
|
||||
self.fold_chars.next().map(|c| match c {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
for (ix, c) in self.chunk.char_indices() {
|
||||
match c {
|
||||
'\t' => {
|
||||
self.to_next_stop = self.tab_size - self.column % self.tab_size - 1;
|
||||
self.column += 1;
|
||||
' '
|
||||
if ix > 0 {
|
||||
let (prefix, suffix) = self.chunk.split_at(ix);
|
||||
self.chunk = suffix;
|
||||
return Some(prefix);
|
||||
} else {
|
||||
self.chunk = &self.chunk[1..];
|
||||
let len = self.tab_size - self.column % self.tab_size;
|
||||
self.column += len;
|
||||
return Some(&SPACES[0..len]);
|
||||
}
|
||||
'\n' => {
|
||||
self.column = 0;
|
||||
c
|
||||
}
|
||||
_ => {
|
||||
self.column += 1;
|
||||
c
|
||||
'\n' => self.column = 0,
|
||||
_ => self.column += 1,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
let result = Some(self.chunk);
|
||||
self.chunk = "";
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -321,7 +350,7 @@ mod tests {
|
|||
use crate::test::*;
|
||||
|
||||
#[gpui::test]
|
||||
fn test_chars_at(app: &mut gpui::MutableAppContext) {
|
||||
fn test_chunks_at(app: &mut gpui::MutableAppContext) {
|
||||
let text = sample_text(6, 6);
|
||||
let buffer = app.add_model(|ctx| Buffer::new(0, text, ctx));
|
||||
let map = DisplayMap::new(buffer.clone(), 4, app.as_ref());
|
||||
|
@ -340,24 +369,21 @@ mod tests {
|
|||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
map.snapshot(app.as_ref())
|
||||
.chars_at(DisplayPoint::new(1, 0), app.as_ref())
|
||||
.take(10)
|
||||
.collect::<String>(),
|
||||
&map.snapshot(app.as_ref())
|
||||
.chunks_at(DisplayPoint::new(1, 0), app.as_ref())
|
||||
.collect::<String>()[0..10],
|
||||
" b bb"
|
||||
);
|
||||
assert_eq!(
|
||||
map.snapshot(app.as_ref())
|
||||
.chars_at(DisplayPoint::new(1, 2), app.as_ref())
|
||||
.take(10)
|
||||
.collect::<String>(),
|
||||
&map.snapshot(app.as_ref())
|
||||
.chunks_at(DisplayPoint::new(1, 2), app.as_ref())
|
||||
.collect::<String>()[0..10],
|
||||
" b bbbb"
|
||||
);
|
||||
assert_eq!(
|
||||
map.snapshot(app.as_ref())
|
||||
.chars_at(DisplayPoint::new(1, 6), app.as_ref())
|
||||
.take(13)
|
||||
.collect::<String>(),
|
||||
&map.snapshot(app.as_ref())
|
||||
.chunks_at(DisplayPoint::new(1, 6), app.as_ref())
|
||||
.collect::<String>()[0..13],
|
||||
" bbbbb\nc c"
|
||||
);
|
||||
}
|
||||
|
|
|
@ -114,7 +114,7 @@ pub fn prev_word_boundary(
|
|||
}
|
||||
|
||||
prev_c = Some(c);
|
||||
column += 1;
|
||||
column += c.len_utf8() as u32;
|
||||
}
|
||||
Ok(boundary)
|
||||
}
|
||||
|
@ -135,7 +135,7 @@ pub fn next_word_boundary(
|
|||
*point.row_mut() += 1;
|
||||
*point.column_mut() = 0;
|
||||
} else {
|
||||
*point.column_mut() += 1;
|
||||
*point.column_mut() += c.len_utf8() as u32;
|
||||
}
|
||||
prev_c = Some(c);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue