From e23965e7c95fcb3a312ed0881523bb958c0fb425 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 15 Dec 2021 10:00:50 +0100 Subject: [PATCH] Implement `MultiBuffer::reversed_chars_at` --- crates/editor/src/multi_buffer.rs | 55 ++++++++++++++++++++++++++++--- crates/text/src/text.rs | 5 +++ 2 files changed, 56 insertions(+), 4 deletions(-) diff --git a/crates/editor/src/multi_buffer.rs b/crates/editor/src/multi_buffer.rs index 96002cb6df22c5c530c6b6a111a537c84d3d6360..d28beda8d7de25e9f5e8f278803d3e5ed03f2f58 100644 --- a/crates/editor/src/multi_buffer.rs +++ b/crates/editor/src/multi_buffer.rs @@ -14,6 +14,7 @@ use std::{ cmp, io, iter::{self, FromIterator, Peekable}, ops::{Range, Sub}, + str, sync::Arc, time::{Duration, Instant, SystemTime}, }; @@ -754,9 +755,47 @@ impl MultiBufferSnapshot { &'a self, position: T, ) -> impl Iterator + 'a { - // TODO - let offset = position.to_offset(self); - self.as_singleton().unwrap().reversed_chars_at(offset) + let mut offset = position.to_offset(self); + let mut cursor = self.excerpts.cursor::(); + cursor.seek(&offset, Bias::Left, &()); + let mut excerpt_chunks = cursor.item().map(|excerpt| { + let start_after_header = cursor.start() + excerpt.header_height as usize; + let mut end_before_footer = cursor.start() + excerpt.text_summary.bytes; + if excerpt.has_trailing_newline { + end_before_footer -= 1; + } + + let start = excerpt.range.start.to_offset(&excerpt.buffer); + let end = + start + (cmp::min(offset, end_before_footer).saturating_sub(start_after_header)); + excerpt.buffer.reversed_chunks_in_range(start..end) + }); + iter::from_fn(move || { + if offset == *cursor.start() { + cursor.prev(&()); + let excerpt = cursor.item()?; + excerpt_chunks = Some( + excerpt + .buffer + .reversed_chunks_in_range(excerpt.range.clone()), + ); + } + + let excerpt = cursor.item().unwrap(); + if offset <= cursor.start() + excerpt.header_height as usize { + let header_height = offset - cursor.start(); + offset -= header_height; + Some(unsafe { str::from_utf8_unchecked(&NEWLINES[..header_height]) }) + } else if offset == cursor.end(&()) && excerpt.has_trailing_newline { + offset -= 1; + Some("\n") + } else { + let chunk = excerpt_chunks.as_mut().unwrap().next().unwrap(); + offset -= chunk.len(); + Some(chunk) + } + }) + .flat_map(|c| c.chars().rev()) } pub fn chars_at<'a, T: ToOffset>(&'a self, position: T) -> impl Iterator + 'a { @@ -1593,7 +1632,7 @@ impl<'a> Iterator for MultiBufferChunks<'a> { if self.header_height > 0 { let chunk = Chunk { text: unsafe { - std::str::from_utf8_unchecked(&NEWLINES[..self.header_height as usize]) + str::from_utf8_unchecked(&NEWLINES[..self.header_height as usize]) }, ..Default::default() }; @@ -2152,6 +2191,14 @@ mod tests { start_ix..end_ix ); } + + for _ in 0..10 { + let end_ix = snapshot.clip_offset(rng.gen_range(0..=snapshot.len()), Bias::Right); + assert_eq!( + expected_text[..end_ix].chars().rev().collect::(), + snapshot.reversed_chars_at(end_ix).collect::() + ); + } } let snapshot = list.read(cx).snapshot(cx); diff --git a/crates/text/src/text.rs b/crates/text/src/text.rs index 8114835b8219d83397d846802258f6302a73b5ec..055e80b29a78fafa4eb67c726dc985d1cb97bcc2 100644 --- a/crates/text/src/text.rs +++ b/crates/text/src/text.rs @@ -1332,6 +1332,11 @@ impl BufferSnapshot { self.visible_text.reversed_chars_at(offset) } + pub fn reversed_chunks_in_range(&self, range: Range) -> rope::Chunks { + let range = range.start.to_offset(self)..range.end.to_offset(self); + self.visible_text.reversed_chunks_in_range(range) + } + pub fn bytes_in_range<'a, T: ToOffset>(&'a self, range: Range) -> rope::Bytes<'a> { let start = range.start.to_offset(self); let end = range.end.to_offset(self);