Move highlighted_text_in_range from Buffer to buffer::Snapshot
This commit is contained in:
+109
-90
@@ -62,7 +62,11 @@ type HashMap<K, V> = std::collections::HashMap<K, V>;
|
||||
type HashSet<T> = std::collections::HashSet<T>;
|
||||
|
||||
thread_local! {
|
||||
pub static PARSER: RefCell<Parser> = RefCell::new(Parser::new());
|
||||
static PARSER: RefCell<Parser> = RefCell::new(Parser::new());
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref QUERY_CURSORS: Mutex<Vec<QueryCursor>> = Default::default();
|
||||
}
|
||||
|
||||
pub struct Buffer {
|
||||
@@ -78,8 +82,7 @@ pub struct Buffer {
|
||||
history: History,
|
||||
file: Option<FileHandle>,
|
||||
language: Option<Arc<Language>>,
|
||||
tree: Option<(Tree, time::Global)>,
|
||||
query_cursor: Mutex<Option<tree_sitter::QueryCursor>>,
|
||||
tree: Mutex<Option<(Tree, time::Global)>>,
|
||||
is_parsing: bool,
|
||||
selections: HashMap<SelectionSetId, Arc<[Selection]>>,
|
||||
pub selections_last_update: SelectionsVersion,
|
||||
@@ -489,10 +492,9 @@ impl Buffer {
|
||||
undo_map: Default::default(),
|
||||
history,
|
||||
file,
|
||||
tree: None,
|
||||
tree: Mutex::new(None),
|
||||
is_parsing: false,
|
||||
language,
|
||||
query_cursor: Mutex::new(Some(QueryCursor::new())),
|
||||
saved_mtime,
|
||||
selections: HashMap::default(),
|
||||
selections_last_update: 0,
|
||||
@@ -506,8 +508,14 @@ impl Buffer {
|
||||
result
|
||||
}
|
||||
|
||||
pub fn snapshot(&self) -> Rope {
|
||||
self.visible_text.clone()
|
||||
pub fn snapshot(&self) -> Snapshot {
|
||||
let mut cursors = QUERY_CURSORS.lock();
|
||||
Snapshot {
|
||||
text: self.visible_text.clone(),
|
||||
tree: self.syntax_tree(),
|
||||
language: self.language.clone(),
|
||||
query_cursor: Some(cursors.pop().unwrap_or_else(|| QueryCursor::new())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn file(&self) -> Option<&FileHandle> {
|
||||
@@ -519,13 +527,13 @@ impl Buffer {
|
||||
new_file: Option<FileHandle>,
|
||||
ctx: &mut ModelContext<Self>,
|
||||
) -> Task<Result<()>> {
|
||||
let snapshot = self.snapshot();
|
||||
let text = self.visible_text.clone();
|
||||
let version = self.version.clone();
|
||||
let file = self.file.clone();
|
||||
|
||||
ctx.spawn(|handle, mut ctx| async move {
|
||||
if let Some(file) = new_file.as_ref().or(file.as_ref()) {
|
||||
let result = ctx.read(|ctx| file.save(snapshot, ctx.as_ref())).await;
|
||||
let result = ctx.read(|ctx| file.save(text, ctx.as_ref())).await;
|
||||
if result.is_ok() {
|
||||
handle.update(&mut ctx, |me, ctx| me.did_save(version, new_file, ctx));
|
||||
}
|
||||
@@ -552,8 +560,8 @@ impl Buffer {
|
||||
ctx.emit(Event::Saved);
|
||||
}
|
||||
|
||||
pub fn syntax_tree(&mut self) -> Option<Tree> {
|
||||
if let Some((mut tree, tree_version)) = self.tree.take() {
|
||||
pub fn syntax_tree(&self) -> Option<Tree> {
|
||||
if let Some((tree, tree_version)) = self.tree.lock().as_mut() {
|
||||
let mut delta = 0_isize;
|
||||
for Edit {
|
||||
old_range,
|
||||
@@ -575,9 +583,8 @@ impl Buffer {
|
||||
});
|
||||
delta += new_bytes as isize - old_bytes as isize;
|
||||
}
|
||||
let result = tree.clone();
|
||||
self.tree = Some((tree, self.version()));
|
||||
Some(result)
|
||||
*tree_version = self.version();
|
||||
Some(tree.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@@ -585,6 +592,7 @@ impl Buffer {
|
||||
|
||||
fn should_reparse(&self) -> bool {
|
||||
self.tree
|
||||
.lock()
|
||||
.as_ref()
|
||||
.map_or(true, |(_, tree_version)| *tree_version != self.version)
|
||||
}
|
||||
@@ -616,7 +624,7 @@ impl Buffer {
|
||||
.await;
|
||||
|
||||
handle.update(&mut ctx, |this, ctx| {
|
||||
this.tree = Some((new_tree, new_version));
|
||||
*this.tree.lock() = Some((new_tree, new_version));
|
||||
ctx.emit(Event::Reparsed);
|
||||
ctx.notify();
|
||||
});
|
||||
@@ -754,47 +762,6 @@ impl Buffer {
|
||||
self.visible_text.chunks_in_range(start..end)
|
||||
}
|
||||
|
||||
pub fn highlighted_text_for_range<T: ToOffset>(&self, range: Range<T>) -> HighlightedChunks {
|
||||
let start = range.start.to_offset(self);
|
||||
let end = range.end.to_offset(self);
|
||||
let chunks = self.visible_text.chunks_in_range(start..end);
|
||||
|
||||
if let (Some(language), Some((tree, _))) = (&self.language, self.tree.as_ref()) {
|
||||
let mut cursor = self
|
||||
.query_cursor
|
||||
.lock()
|
||||
.take()
|
||||
.unwrap_or_else(|| QueryCursor::new());
|
||||
|
||||
cursor.set_byte_range(start, end);
|
||||
let cursor_ref = unsafe { mem::transmute::<_, &'static mut QueryCursor>(&mut cursor) };
|
||||
let captures = cursor_ref.captures(
|
||||
&language.highlight_query,
|
||||
tree.root_node(),
|
||||
TextProvider(&self.visible_text),
|
||||
);
|
||||
|
||||
HighlightedChunks {
|
||||
range: start..end,
|
||||
chunks,
|
||||
highlights: Some(Highlights {
|
||||
captures: captures.peekable(),
|
||||
stack: Default::default(),
|
||||
theme_mapping: language.theme_mapping(),
|
||||
cursor,
|
||||
}),
|
||||
buffer: self,
|
||||
}
|
||||
} else {
|
||||
HighlightedChunks {
|
||||
range: start..end,
|
||||
chunks,
|
||||
highlights: None,
|
||||
buffer: self,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn chars(&self) -> impl Iterator<Item = char> + '_ {
|
||||
self.chars_at(0)
|
||||
}
|
||||
@@ -2049,9 +2016,8 @@ impl Clone for Buffer {
|
||||
deferred_ops: self.deferred_ops.clone(),
|
||||
file: self.file.clone(),
|
||||
language: self.language.clone(),
|
||||
tree: self.tree.clone(),
|
||||
tree: Mutex::new(self.tree.lock().clone()),
|
||||
is_parsing: false,
|
||||
query_cursor: Mutex::new(Some(QueryCursor::new())),
|
||||
deferred_replicas: self.deferred_replicas.clone(),
|
||||
replica_id: self.replica_id,
|
||||
local_clock: self.local_clock.clone(),
|
||||
@@ -2060,6 +2026,79 @@ impl Clone for Buffer {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Snapshot {
|
||||
text: Rope,
|
||||
tree: Option<Tree>,
|
||||
language: Option<Arc<Language>>,
|
||||
query_cursor: Option<QueryCursor>,
|
||||
}
|
||||
|
||||
impl Snapshot {
|
||||
pub fn len(&self) -> usize {
|
||||
self.text.len()
|
||||
}
|
||||
|
||||
pub fn text(&self) -> Rope {
|
||||
self.text.clone()
|
||||
}
|
||||
|
||||
pub fn text_for_range(&self, range: Range<usize>) -> Chunks {
|
||||
self.text.chunks_in_range(range)
|
||||
}
|
||||
|
||||
pub fn highlighted_text_for_range(&mut self, range: Range<usize>) -> HighlightedChunks {
|
||||
let chunks = self.text.chunks_in_range(range.clone());
|
||||
if let Some((language, tree)) = self.language.as_ref().zip(self.tree.as_ref()) {
|
||||
let query_cursor = self.query_cursor.as_mut().unwrap();
|
||||
query_cursor.set_byte_range(range.start, range.end);
|
||||
let captures = query_cursor.captures(
|
||||
&language.highlight_query,
|
||||
tree.root_node(),
|
||||
TextProvider(&self.text),
|
||||
);
|
||||
|
||||
HighlightedChunks {
|
||||
range,
|
||||
chunks,
|
||||
highlights: Some(Highlights {
|
||||
captures,
|
||||
next_capture: None,
|
||||
stack: Default::default(),
|
||||
theme_mapping: language.theme_mapping(),
|
||||
}),
|
||||
}
|
||||
} else {
|
||||
HighlightedChunks {
|
||||
range,
|
||||
chunks,
|
||||
highlights: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clip_offset(&self, offset: usize, bias: Bias) -> usize {
|
||||
self.text.clip_offset(offset, bias)
|
||||
}
|
||||
|
||||
pub fn clip_point(&self, point: Point, bias: Bias) -> Point {
|
||||
self.text.clip_point(point, bias)
|
||||
}
|
||||
|
||||
pub fn to_offset(&self, point: Point) -> usize {
|
||||
self.text.to_offset(point)
|
||||
}
|
||||
|
||||
pub fn to_point(&self, offset: usize) -> Point {
|
||||
self.text.to_point(offset)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Snapshot {
|
||||
fn drop(&mut self) {
|
||||
QUERY_CURSORS.lock().push(self.query_cursor.take().unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
struct RopeBuilder<'a> {
|
||||
old_visible_cursor: rope::Cursor<'a>,
|
||||
old_deleted_cursor: rope::Cursor<'a>,
|
||||
@@ -2202,9 +2241,9 @@ impl<'a> tree_sitter::TextProvider<'a> for TextProvider<'a> {
|
||||
}
|
||||
|
||||
struct Highlights<'a> {
|
||||
captures: iter::Peekable<tree_sitter::QueryCaptures<'a, 'a, TextProvider<'a>>>,
|
||||
captures: tree_sitter::QueryCaptures<'a, 'a, TextProvider<'a>>,
|
||||
next_capture: Option<(tree_sitter::QueryMatch<'a, 'a>, usize)>,
|
||||
stack: Vec<(usize, StyleId)>,
|
||||
cursor: QueryCursor,
|
||||
theme_mapping: ThemeMap,
|
||||
}
|
||||
|
||||
@@ -2212,7 +2251,6 @@ pub struct HighlightedChunks<'a> {
|
||||
range: Range<usize>,
|
||||
chunks: Chunks<'a>,
|
||||
highlights: Option<Highlights<'a>>,
|
||||
buffer: &'a Buffer,
|
||||
}
|
||||
|
||||
impl<'a> HighlightedChunks<'a> {
|
||||
@@ -2220,21 +2258,9 @@ impl<'a> HighlightedChunks<'a> {
|
||||
self.range.start = offset;
|
||||
self.chunks.seek(self.range.start);
|
||||
if let Some(highlights) = self.highlights.as_mut() {
|
||||
let language = self.buffer.language.as_ref().unwrap();
|
||||
let tree = &self.buffer.tree.as_ref().unwrap().0;
|
||||
let cursor_ref =
|
||||
unsafe { mem::transmute::<_, &'static mut QueryCursor>(&mut highlights.cursor) };
|
||||
highlights
|
||||
.cursor
|
||||
.set_byte_range(self.range.start, self.range.end);
|
||||
highlights.stack.clear();
|
||||
highlights.captures = cursor_ref
|
||||
.captures(
|
||||
&language.highlight_query,
|
||||
tree.root_node(),
|
||||
TextProvider(&self.buffer.visible_text),
|
||||
)
|
||||
.peekable();
|
||||
highlights.next_capture.take();
|
||||
highlights.captures.advance_to_byte(self.range.start);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2258,7 +2284,11 @@ impl<'a> Iterator for HighlightedChunks<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
while let Some((mat, capture_ix)) = highlights.captures.peek() {
|
||||
if highlights.next_capture.is_none() {
|
||||
highlights.next_capture = highlights.captures.next();
|
||||
}
|
||||
|
||||
while let Some((mat, capture_ix)) = highlights.next_capture.as_ref() {
|
||||
let capture = mat.captures[*capture_ix as usize];
|
||||
if self.range.start < capture.node.start_byte() {
|
||||
next_capture_start = capture.node.start_byte();
|
||||
@@ -2266,7 +2296,7 @@ impl<'a> Iterator for HighlightedChunks<'a> {
|
||||
} else {
|
||||
let style_id = highlights.theme_mapping.get(capture.index);
|
||||
highlights.stack.push((capture.node.end_byte(), style_id));
|
||||
highlights.captures.next().unwrap();
|
||||
highlights.next_capture = highlights.captures.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2296,17 +2326,6 @@ impl<'a> Iterator for HighlightedChunks<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Drop for HighlightedChunks<'a> {
|
||||
fn drop(&mut self) {
|
||||
if let Some(highlights) = self.highlights.take() {
|
||||
let mut buffer_cursor = self.buffer.query_cursor.lock();
|
||||
if buffer_cursor.is_none() {
|
||||
*buffer_cursor = Some(highlights.cursor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Ord, PartialOrd, Eq, PartialEq, Clone, Debug)]
|
||||
struct FragmentId(Arc<[u16]>);
|
||||
|
||||
@@ -3448,7 +3467,7 @@ mod tests {
|
||||
|
||||
let buffer = Buffer::from_history(0, History::new(text.into()), None, rust_lang, ctx);
|
||||
assert!(buffer.is_parsing);
|
||||
assert!(buffer.tree.is_none());
|
||||
assert!(buffer.syntax_tree().is_none());
|
||||
buffer
|
||||
});
|
||||
|
||||
@@ -3562,7 +3581,7 @@ mod tests {
|
||||
|
||||
fn get_tree_sexp(buffer: &ModelHandle<Buffer>, ctx: &gpui::TestAppContext) -> String {
|
||||
buffer.read_with(ctx, |buffer, _| {
|
||||
buffer.tree.as_ref().unwrap().0.root_node().to_sexp()
|
||||
buffer.syntax_tree().unwrap().root_node().to_sexp()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2148,8 +2148,8 @@ impl BufferView {
|
||||
let mut line = String::new();
|
||||
let mut styles = Vec::new();
|
||||
let mut row = rows.start;
|
||||
let snapshot = self.display_map.snapshot(ctx);
|
||||
let chunks = snapshot.highlighted_chunks_at(rows.start, ctx);
|
||||
let mut snapshot = self.display_map.snapshot(ctx);
|
||||
let chunks = snapshot.highlighted_chunks_at(rows.start);
|
||||
let theme = settings.theme.clone();
|
||||
|
||||
'outer: for (chunk, style_ix) in chunks.chain(Some(("\n", StyleId::default()))) {
|
||||
|
||||
@@ -46,7 +46,7 @@ impl FoldMap {
|
||||
pub fn snapshot(&self, ctx: &AppContext) -> FoldMapSnapshot {
|
||||
FoldMapSnapshot {
|
||||
transforms: self.sync(ctx).clone(),
|
||||
buffer: self.buffer.clone(),
|
||||
buffer: self.buffer.read(ctx).snapshot(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,11 +210,11 @@ impl FoldMap {
|
||||
}
|
||||
|
||||
pub fn to_buffer_offset(&self, point: DisplayPoint, ctx: &AppContext) -> usize {
|
||||
self.snapshot(ctx).to_buffer_offset(point, ctx)
|
||||
self.snapshot(ctx).to_buffer_offset(point)
|
||||
}
|
||||
|
||||
pub fn to_display_offset(&self, point: DisplayPoint, ctx: &AppContext) -> DisplayOffset {
|
||||
self.snapshot(ctx).to_display_offset(point, ctx)
|
||||
self.snapshot(ctx).to_display_offset(point)
|
||||
}
|
||||
|
||||
pub fn to_buffer_point(&self, display_point: DisplayPoint, ctx: &AppContext) -> Point {
|
||||
@@ -394,7 +394,7 @@ impl FoldMap {
|
||||
|
||||
pub struct FoldMapSnapshot {
|
||||
transforms: SumTree<Transform>,
|
||||
buffer: ModelHandle<Buffer>,
|
||||
buffer: buffer::Snapshot,
|
||||
}
|
||||
|
||||
impl FoldMapSnapshot {
|
||||
@@ -413,13 +413,12 @@ impl FoldMapSnapshot {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn chunks_at<'a>(&'a self, offset: DisplayOffset, ctx: &'a AppContext) -> Chunks<'a> {
|
||||
pub fn chunks_at(&self, offset: DisplayOffset) -> Chunks {
|
||||
let mut transform_cursor = self.transforms.cursor::<DisplayOffset, TransformSummary>();
|
||||
transform_cursor.seek(&offset, SeekBias::Right, &());
|
||||
let overshoot = offset.0 - transform_cursor.start().display.bytes;
|
||||
let buffer_offset = transform_cursor.start().buffer.bytes + overshoot;
|
||||
let buffer = self.buffer.read(ctx);
|
||||
let rope_cursor = buffer.text_for_range(buffer_offset..buffer.len());
|
||||
let rope_cursor = self.buffer.text_for_range(buffer_offset..self.buffer.len());
|
||||
Chunks {
|
||||
transform_cursor,
|
||||
buffer_offset,
|
||||
@@ -427,34 +426,27 @@ impl FoldMapSnapshot {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn highlighted_chunks_at<'a>(
|
||||
&'a self,
|
||||
offset: DisplayOffset,
|
||||
ctx: &'a AppContext,
|
||||
) -> HighlightedChunks<'a> {
|
||||
pub fn highlighted_chunks_at(&mut self, offset: DisplayOffset) -> HighlightedChunks {
|
||||
let mut transform_cursor = self.transforms.cursor::<DisplayOffset, TransformSummary>();
|
||||
transform_cursor.seek(&offset, SeekBias::Right, &());
|
||||
let overshoot = offset.0 - transform_cursor.start().display.bytes;
|
||||
let buffer_offset = transform_cursor.start().buffer.bytes + overshoot;
|
||||
let buffer = self.buffer.read(ctx);
|
||||
HighlightedChunks {
|
||||
transform_cursor,
|
||||
buffer_offset,
|
||||
buffer_chunks: buffer.highlighted_text_for_range(buffer_offset..buffer.len()),
|
||||
buffer_chunks: self
|
||||
.buffer
|
||||
.highlighted_text_for_range(buffer_offset..self.buffer.len()),
|
||||
buffer_chunk: None,
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
self.chunks_at(offset, ctx).flat_map(str::chars)
|
||||
pub fn chars_at<'a>(&'a self, point: DisplayPoint) -> impl Iterator<Item = char> + 'a {
|
||||
let offset = self.to_display_offset(point);
|
||||
self.chunks_at(offset).flat_map(str::chars)
|
||||
}
|
||||
|
||||
pub fn to_display_offset(&self, point: DisplayPoint, ctx: &AppContext) -> DisplayOffset {
|
||||
pub fn to_display_offset(&self, point: DisplayPoint) -> DisplayOffset {
|
||||
let mut cursor = self.transforms.cursor::<DisplayPoint, TransformSummary>();
|
||||
cursor.seek(&point, SeekBias::Right, &());
|
||||
let overshoot = point.0 - cursor.start().display.lines;
|
||||
@@ -462,27 +454,24 @@ impl FoldMapSnapshot {
|
||||
if !overshoot.is_zero() {
|
||||
let transform = cursor.item().expect("display point out of range");
|
||||
assert!(transform.display_text.is_none());
|
||||
let end_buffer_offset =
|
||||
(cursor.start().buffer.lines + overshoot).to_offset(self.buffer.read(ctx));
|
||||
let end_buffer_offset = self
|
||||
.buffer
|
||||
.to_offset(cursor.start().buffer.lines + overshoot);
|
||||
offset += end_buffer_offset - cursor.start().buffer.bytes;
|
||||
}
|
||||
DisplayOffset(offset)
|
||||
}
|
||||
|
||||
pub fn to_buffer_offset(&self, point: DisplayPoint, ctx: &AppContext) -> usize {
|
||||
pub fn to_buffer_offset(&self, point: DisplayPoint) -> usize {
|
||||
let mut cursor = self.transforms.cursor::<DisplayPoint, TransformSummary>();
|
||||
cursor.seek(&point, SeekBias::Right, &());
|
||||
let overshoot = point.0 - cursor.start().display.lines;
|
||||
(cursor.start().buffer.lines + overshoot).to_offset(self.buffer.read(ctx))
|
||||
self.buffer
|
||||
.to_offset(cursor.start().buffer.lines + overshoot)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn clip_offset(
|
||||
&self,
|
||||
offset: DisplayOffset,
|
||||
bias: Bias,
|
||||
ctx: &AppContext,
|
||||
) -> DisplayOffset {
|
||||
pub fn clip_offset(&self, offset: DisplayOffset, bias: Bias) -> DisplayOffset {
|
||||
let mut cursor = self.transforms.cursor::<DisplayOffset, TransformSummary>();
|
||||
cursor.seek(&offset, SeekBias::Right, &());
|
||||
if let Some(transform) = cursor.item() {
|
||||
@@ -496,7 +485,7 @@ impl FoldMapSnapshot {
|
||||
} else {
|
||||
let overshoot = offset.0 - transform_start;
|
||||
let buffer_offset = cursor.start().buffer.bytes + overshoot;
|
||||
let clipped_buffer_offset = self.buffer.read(ctx).clip_offset(buffer_offset, bias);
|
||||
let clipped_buffer_offset = self.buffer.clip_offset(buffer_offset, bias);
|
||||
DisplayOffset(
|
||||
(offset.0 as isize + (clipped_buffer_offset as isize - buffer_offset as isize))
|
||||
as usize,
|
||||
@@ -507,7 +496,7 @@ impl FoldMapSnapshot {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clip_point(&self, point: DisplayPoint, bias: Bias, ctx: &AppContext) -> DisplayPoint {
|
||||
pub fn clip_point(&self, point: DisplayPoint, bias: Bias) -> DisplayPoint {
|
||||
let mut cursor = self.transforms.cursor::<DisplayPoint, TransformSummary>();
|
||||
cursor.seek(&point, SeekBias::Right, &());
|
||||
if let Some(transform) = cursor.item() {
|
||||
@@ -521,8 +510,7 @@ impl FoldMapSnapshot {
|
||||
} else {
|
||||
let overshoot = point.0 - transform_start;
|
||||
let buffer_position = cursor.start().buffer.lines + overshoot;
|
||||
let clipped_buffer_position =
|
||||
self.buffer.read(ctx).clip_point(buffer_position, bias);
|
||||
let clipped_buffer_position = self.buffer.clip_point(buffer_position, bias);
|
||||
DisplayPoint::new(
|
||||
point.row(),
|
||||
((point.column() as i32) + clipped_buffer_position.column as i32
|
||||
@@ -1131,11 +1119,10 @@ mod tests {
|
||||
let offset = map.snapshot(app.as_ref()).clip_offset(
|
||||
DisplayOffset(rng.gen_range(0..=map.len(app.as_ref()))),
|
||||
Bias::Right,
|
||||
app.as_ref(),
|
||||
);
|
||||
assert_eq!(
|
||||
map.snapshot(app.as_ref())
|
||||
.chunks_at(offset, app.as_ref())
|
||||
.chunks_at(offset)
|
||||
.collect::<String>(),
|
||||
&expected_text[offset.0..],
|
||||
);
|
||||
@@ -1218,9 +1205,7 @@ mod tests {
|
||||
|
||||
impl FoldMap {
|
||||
fn text(&self, app: &AppContext) -> String {
|
||||
self.snapshot(app)
|
||||
.chunks_at(DisplayOffset(0), app)
|
||||
.collect()
|
||||
self.snapshot(app).chunks_at(DisplayOffset(0)).collect()
|
||||
}
|
||||
|
||||
fn merged_fold_ranges(&self, app: &AppContext) -> Vec<Range<usize>> {
|
||||
|
||||
@@ -62,16 +62,14 @@ impl DisplayMap {
|
||||
}
|
||||
|
||||
pub fn text(&self, ctx: &AppContext) -> String {
|
||||
self.snapshot(ctx)
|
||||
.chunks_at(DisplayPoint::zero(), ctx)
|
||||
.collect()
|
||||
self.snapshot(ctx).chunks_at(DisplayPoint::zero()).collect()
|
||||
}
|
||||
|
||||
pub fn line(&self, display_row: u32, ctx: &AppContext) -> String {
|
||||
let mut result = String::new();
|
||||
for chunk in self
|
||||
.snapshot(ctx)
|
||||
.chunks_at(DisplayPoint::new(display_row, 0), ctx)
|
||||
.chunks_at(DisplayPoint::new(display_row, 0))
|
||||
{
|
||||
if let Some(ix) = chunk.find('\n') {
|
||||
result.push_str(&chunk[0..ix]);
|
||||
@@ -88,7 +86,7 @@ impl DisplayMap {
|
||||
let mut is_blank = true;
|
||||
for c in self
|
||||
.snapshot(ctx)
|
||||
.chars_at(DisplayPoint::new(display_row, 0), ctx)
|
||||
.chars_at(DisplayPoint::new(display_row, 0))
|
||||
{
|
||||
if c == ' ' {
|
||||
indent += 1;
|
||||
@@ -138,12 +136,11 @@ impl DisplayMapSnapshot {
|
||||
self.folds_snapshot.buffer_rows(start_row)
|
||||
}
|
||||
|
||||
pub fn chunks_at<'a>(&'a self, point: DisplayPoint, app: &'a AppContext) -> Chunks<'a> {
|
||||
let (point, expanded_char_column, to_next_stop) =
|
||||
self.collapse_tabs(point, Bias::Left, app);
|
||||
pub fn chunks_at(&self, point: DisplayPoint) -> Chunks {
|
||||
let (point, expanded_char_column, to_next_stop) = self.collapse_tabs(point, Bias::Left);
|
||||
let fold_chunks = self
|
||||
.folds_snapshot
|
||||
.chunks_at(self.folds_snapshot.to_display_offset(point, app), app);
|
||||
.chunks_at(self.folds_snapshot.to_display_offset(point));
|
||||
Chunks {
|
||||
fold_chunks,
|
||||
column: expanded_char_column,
|
||||
@@ -153,15 +150,11 @@ impl DisplayMapSnapshot {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn highlighted_chunks_at<'a>(
|
||||
&'a self,
|
||||
row: u32,
|
||||
app: &'a AppContext,
|
||||
) -> HighlightedChunks<'a> {
|
||||
pub fn highlighted_chunks_at(&mut self, row: u32) -> HighlightedChunks {
|
||||
let point = DisplayPoint::new(row, 0);
|
||||
let offset = self.folds_snapshot.to_display_offset(point, app);
|
||||
let offset = self.folds_snapshot.to_display_offset(point);
|
||||
HighlightedChunks {
|
||||
fold_chunks: self.folds_snapshot.highlighted_chunks_at(offset, app),
|
||||
fold_chunks: self.folds_snapshot.highlighted_chunks_at(offset),
|
||||
column: 0,
|
||||
tab_size: self.tab_size,
|
||||
chunk: "",
|
||||
@@ -169,18 +162,14 @@ impl DisplayMapSnapshot {
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
pub fn chars_at<'a>(&'a self, point: DisplayPoint) -> impl Iterator<Item = char> + 'a {
|
||||
self.chunks_at(point).flat_map(str::chars)
|
||||
}
|
||||
|
||||
pub fn column_to_chars(&self, display_row: u32, target: u32, ctx: &AppContext) -> u32 {
|
||||
pub fn column_to_chars(&self, display_row: u32, target: u32) -> u32 {
|
||||
let mut count = 0;
|
||||
let mut column = 0;
|
||||
for c in self.chars_at(DisplayPoint::new(display_row, 0), ctx) {
|
||||
for c in self.chars_at(DisplayPoint::new(display_row, 0)) {
|
||||
if column >= target {
|
||||
break;
|
||||
}
|
||||
@@ -190,10 +179,10 @@ impl DisplayMapSnapshot {
|
||||
count
|
||||
}
|
||||
|
||||
pub fn column_from_chars(&self, display_row: u32, char_count: u32, ctx: &AppContext) -> u32 {
|
||||
pub fn column_from_chars(&self, display_row: u32, char_count: u32) -> u32 {
|
||||
let mut count = 0;
|
||||
let mut column = 0;
|
||||
for c in self.chars_at(DisplayPoint::new(display_row, 0), ctx) {
|
||||
for c in self.chars_at(DisplayPoint::new(display_row, 0)) {
|
||||
if c == '\n' || count >= char_count {
|
||||
break;
|
||||
}
|
||||
@@ -203,32 +192,26 @@ impl DisplayMapSnapshot {
|
||||
column
|
||||
}
|
||||
|
||||
pub fn clip_point(&self, point: DisplayPoint, bias: Bias, ctx: &AppContext) -> DisplayPoint {
|
||||
pub fn clip_point(&self, point: DisplayPoint, bias: Bias) -> DisplayPoint {
|
||||
self.expand_tabs(
|
||||
self.folds_snapshot
|
||||
.clip_point(self.collapse_tabs(point, bias, ctx).0, bias, ctx),
|
||||
ctx,
|
||||
.clip_point(self.collapse_tabs(point, bias).0, bias),
|
||||
)
|
||||
}
|
||||
|
||||
fn expand_tabs(&self, mut point: DisplayPoint, ctx: &AppContext) -> DisplayPoint {
|
||||
fn expand_tabs(&self, mut point: DisplayPoint) -> DisplayPoint {
|
||||
let chars = self
|
||||
.folds_snapshot
|
||||
.chars_at(DisplayPoint(Point::new(point.row(), 0)), ctx);
|
||||
.chars_at(DisplayPoint(Point::new(point.row(), 0)));
|
||||
let expanded = expand_tabs(chars, point.column() as usize, self.tab_size);
|
||||
*point.column_mut() = expanded as u32;
|
||||
point
|
||||
}
|
||||
|
||||
fn collapse_tabs(
|
||||
&self,
|
||||
mut point: DisplayPoint,
|
||||
bias: Bias,
|
||||
ctx: &AppContext,
|
||||
) -> (DisplayPoint, usize, usize) {
|
||||
fn collapse_tabs(&self, mut point: DisplayPoint, bias: Bias) -> (DisplayPoint, usize, usize) {
|
||||
let chars = self
|
||||
.folds_snapshot
|
||||
.chars_at(DisplayPoint(Point::new(point.row(), 0)), ctx);
|
||||
.chars_at(DisplayPoint(Point::new(point.row(), 0)));
|
||||
let expanded = point.column() as usize;
|
||||
let (collapsed, expanded_char_column, to_next_stop) =
|
||||
collapse_tabs(chars, expanded, bias, self.tab_size);
|
||||
@@ -276,11 +259,11 @@ impl DisplayPoint {
|
||||
}
|
||||
|
||||
fn expand_tabs(self, map: &DisplayMap, ctx: &AppContext) -> Self {
|
||||
map.snapshot(ctx).expand_tabs(self, ctx)
|
||||
map.snapshot(ctx).expand_tabs(self)
|
||||
}
|
||||
|
||||
fn collapse_tabs(self, map: &DisplayMap, bias: Bias, ctx: &AppContext) -> Self {
|
||||
map.snapshot(ctx).collapse_tabs(self, bias, ctx).0
|
||||
map.snapshot(ctx).collapse_tabs(self, bias).0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -288,7 +271,7 @@ impl Point {
|
||||
pub fn to_display_point(self, map: &DisplayMap, ctx: &AppContext) -> DisplayPoint {
|
||||
let mut display_point = map.fold_map.to_display_point(self, ctx);
|
||||
let snapshot = map.fold_map.snapshot(ctx);
|
||||
let chars = snapshot.chars_at(DisplayPoint::new(display_point.row(), 0), ctx);
|
||||
let chars = snapshot.chars_at(DisplayPoint::new(display_point.row(), 0));
|
||||
*display_point.column_mut() =
|
||||
expand_tabs(chars, display_point.column() as usize, map.tab_size) as u32;
|
||||
display_point
|
||||
@@ -487,19 +470,19 @@ mod tests {
|
||||
|
||||
assert_eq!(
|
||||
&map.snapshot(app.as_ref())
|
||||
.chunks_at(DisplayPoint::new(1, 0), app.as_ref())
|
||||
.chunks_at(DisplayPoint::new(1, 0))
|
||||
.collect::<String>()[0..10],
|
||||
" b bb"
|
||||
);
|
||||
assert_eq!(
|
||||
&map.snapshot(app.as_ref())
|
||||
.chunks_at(DisplayPoint::new(1, 2), app.as_ref())
|
||||
.chunks_at(DisplayPoint::new(1, 2))
|
||||
.collect::<String>()[0..10],
|
||||
" b bbbb"
|
||||
);
|
||||
assert_eq!(
|
||||
&map.snapshot(app.as_ref())
|
||||
.chunks_at(DisplayPoint::new(1, 6), app.as_ref())
|
||||
.chunks_at(DisplayPoint::new(1, 6))
|
||||
.collect::<String>()[0..13],
|
||||
" bbbbb\nc c"
|
||||
);
|
||||
@@ -534,7 +517,7 @@ mod tests {
|
||||
),
|
||||
] {
|
||||
assert_eq!(
|
||||
map.clip_point(DisplayPoint::new(1, input_column as u32), bias, ctx),
|
||||
map.clip_point(DisplayPoint::new(1, input_column as u32), bias),
|
||||
DisplayPoint::new(1, output_column as u32),
|
||||
"clip_point(({}, {}))",
|
||||
1,
|
||||
@@ -584,7 +567,7 @@ mod tests {
|
||||
);
|
||||
assert_eq!(
|
||||
map.snapshot(ctx)
|
||||
.chunks_at(DisplayPoint::new(0, "✅ ".len() as u32), ctx)
|
||||
.chunks_at(DisplayPoint::new(0, "✅ ".len() as u32))
|
||||
.collect::<String>(),
|
||||
" α\nβ \n🏀β γ"
|
||||
);
|
||||
@@ -598,26 +581,20 @@ mod tests {
|
||||
);
|
||||
assert_eq!(
|
||||
map.snapshot(ctx)
|
||||
.chunks_at(DisplayPoint::new(0, "✅ ".len() as u32), ctx)
|
||||
.chunks_at(DisplayPoint::new(0, "✅ ".len() as u32))
|
||||
.collect::<String>(),
|
||||
" α\nβ \n🏀β γ"
|
||||
);
|
||||
|
||||
// Clipping display points inside of multi-byte characters
|
||||
assert_eq!(
|
||||
map.snapshot(ctx).clip_point(
|
||||
DisplayPoint::new(0, "✅".len() as u32 - 1),
|
||||
Bias::Left,
|
||||
ctx
|
||||
),
|
||||
map.snapshot(ctx)
|
||||
.clip_point(DisplayPoint::new(0, "✅".len() as u32 - 1), Bias::Left),
|
||||
DisplayPoint::new(0, 0)
|
||||
);
|
||||
assert_eq!(
|
||||
map.snapshot(ctx).clip_point(
|
||||
DisplayPoint::new(0, "✅".len() as u32 - 1),
|
||||
Bias::Right,
|
||||
ctx
|
||||
),
|
||||
map.snapshot(ctx)
|
||||
.clip_point(DisplayPoint::new(0, "✅".len() as u32 - 1), Bias::Right),
|
||||
DisplayPoint::new(0, "✅".len() as u32)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ pub fn left(map: &DisplayMap, mut point: DisplayPoint, app: &AppContext) -> Resu
|
||||
*point.row_mut() -= 1;
|
||||
*point.column_mut() = map.line_len(point.row(), app);
|
||||
}
|
||||
Ok(map.snapshot(app).clip_point(point, Bias::Left, app))
|
||||
Ok(map.snapshot(app).clip_point(point, Bias::Left))
|
||||
}
|
||||
|
||||
pub fn right(map: &DisplayMap, mut point: DisplayPoint, app: &AppContext) -> Result<DisplayPoint> {
|
||||
@@ -20,7 +20,7 @@ pub fn right(map: &DisplayMap, mut point: DisplayPoint, app: &AppContext) -> Res
|
||||
*point.row_mut() += 1;
|
||||
*point.column_mut() = 0;
|
||||
}
|
||||
Ok(map.snapshot(app).clip_point(point, Bias::Right, app))
|
||||
Ok(map.snapshot(app).clip_point(point, Bias::Right))
|
||||
}
|
||||
|
||||
pub fn up(
|
||||
@@ -33,12 +33,12 @@ pub fn up(
|
||||
let goal_column = if let SelectionGoal::Column(column) = goal {
|
||||
column
|
||||
} else {
|
||||
map.column_to_chars(point.row(), point.column(), app)
|
||||
map.column_to_chars(point.row(), point.column())
|
||||
};
|
||||
|
||||
if point.row() > 0 {
|
||||
*point.row_mut() -= 1;
|
||||
*point.column_mut() = map.column_from_chars(point.row(), goal_column, app);
|
||||
*point.column_mut() = map.column_from_chars(point.row(), goal_column);
|
||||
} else {
|
||||
point = DisplayPoint::new(0, 0);
|
||||
}
|
||||
@@ -57,12 +57,12 @@ pub fn down(
|
||||
let goal_column = if let SelectionGoal::Column(column) = goal {
|
||||
column
|
||||
} else {
|
||||
map.column_to_chars(point.row(), point.column(), app)
|
||||
map.column_to_chars(point.row(), point.column())
|
||||
};
|
||||
|
||||
if point.row() < max_point.row() {
|
||||
*point.row_mut() += 1;
|
||||
*point.column_mut() = map.column_from_chars(point.row(), goal_column, app);
|
||||
*point.column_mut() = map.column_from_chars(point.row(), goal_column);
|
||||
} else {
|
||||
point = max_point;
|
||||
}
|
||||
@@ -107,7 +107,7 @@ pub fn prev_word_boundary(
|
||||
let mut boundary = DisplayPoint::new(point.row(), 0);
|
||||
let mut column = 0;
|
||||
let mut prev_c = None;
|
||||
for c in map.snapshot(app).chars_at(boundary, app) {
|
||||
for c in map.snapshot(app).chars_at(boundary) {
|
||||
if column >= point.column() {
|
||||
break;
|
||||
}
|
||||
@@ -129,7 +129,7 @@ pub fn next_word_boundary(
|
||||
app: &AppContext,
|
||||
) -> Result<DisplayPoint> {
|
||||
let mut prev_c = None;
|
||||
for c in map.snapshot(app).chars_at(point, app) {
|
||||
for c in map.snapshot(app).chars_at(point) {
|
||||
if prev_c.is_some() && (c == '\n' || char_kind(prev_c.unwrap()) != char_kind(c)) {
|
||||
break;
|
||||
}
|
||||
|
||||
+3
-2
@@ -1483,7 +1483,8 @@ mod tests {
|
||||
let path = tree.update(&mut app, |tree, ctx| {
|
||||
let path = tree.files(0).next().unwrap().path().clone();
|
||||
assert_eq!(path.file_name().unwrap(), "file1");
|
||||
smol::block_on(tree.save(&path, buffer.read(ctx).snapshot(), ctx.as_ref())).unwrap();
|
||||
smol::block_on(tree.save(&path, buffer.read(ctx).snapshot().text(), ctx.as_ref()))
|
||||
.unwrap();
|
||||
path
|
||||
});
|
||||
|
||||
@@ -1512,7 +1513,7 @@ mod tests {
|
||||
let file = app.update(|ctx| tree.file("", ctx)).await;
|
||||
app.update(|ctx| {
|
||||
assert_eq!(file.path().file_name(), None);
|
||||
smol::block_on(file.save(buffer.read(ctx).snapshot(), ctx.as_ref())).unwrap();
|
||||
smol::block_on(file.save(buffer.read(ctx).snapshot().text(), ctx.as_ref())).unwrap();
|
||||
});
|
||||
|
||||
let history = app.read(|ctx| file.load_history(ctx)).await.unwrap();
|
||||
|
||||
Reference in New Issue
Block a user