diff --git a/zed/src/editor/buffer/anchor.rs b/zed/src/editor/buffer/anchor.rs index 60106daba5af22db74067985cff1da335238f31d..3b2687f96daf63e6149d3400f4034fe8b8e94359 100644 --- a/zed/src/editor/buffer/anchor.rs +++ b/zed/src/editor/buffer/anchor.rs @@ -70,24 +70,24 @@ impl Anchor { }) } - pub fn bias_left(&self, buffer: &Buffer) -> Result { + pub fn bias_left(&self, buffer: &Buffer) -> Anchor { match self { Anchor::Start | Anchor::Middle { bias: AnchorBias::Left, .. - } => Ok(self.clone()), + } => self.clone(), _ => buffer.anchor_before(self), } } - pub fn bias_right(&self, buffer: &Buffer) -> Result { + pub fn bias_right(&self, buffer: &Buffer) -> Anchor { match self { Anchor::End | Anchor::Middle { bias: AnchorBias::Right, .. - } => Ok(self.clone()), + } => self.clone(), _ => buffer.anchor_after(self), } } diff --git a/zed/src/editor/buffer/mod.rs b/zed/src/editor/buffer/mod.rs index 8a57201ccff3d5c549f5e9caafd60606806614be..4113359f9de82314bb055ce4be4a7ed1fa08e191 100644 --- a/zed/src/editor/buffer/mod.rs +++ b/zed/src/editor/buffer/mod.rs @@ -14,13 +14,11 @@ use crate::{ operation_queue::{self, OperationQueue}, sum_tree::{self, FilterCursor, SeekBias, SumTree}, time::{self, ReplicaId}, - util::RandomCharIter, worktree::FileHandle, }; use anyhow::{anyhow, Result}; use gpui::{AppContext, Entity, ModelContext, Task}; use lazy_static::lazy_static; -use rand::prelude::*; use std::{ cmp, hash::BuildHasher, @@ -607,15 +605,14 @@ impl Buffer { self.fragments.extent::() } - pub fn line_len(&self, row: u32) -> Result { - let row_start_offset = Point::new(row, 0).to_offset(self)?; + pub fn line_len(&self, row: u32) -> u32 { + let row_start_offset = Point::new(row, 0).to_offset(self); let row_end_offset = if row >= self.max_point().row { self.len() } else { - Point::new(row + 1, 0).to_offset(self)? - 1 + Point::new(row + 1, 0).to_offset(self) - 1 }; - - Ok((row_end_offset - row_start_offset) as u32) + (row_end_offset - row_start_offset) as u32 } pub fn rightmost_point(&self) -> Point { @@ -630,33 +627,32 @@ impl Buffer { self.visible_text.max_point() } - pub fn line(&self, row: u32) -> Result { - Ok(self - .chars_at(Point::new(row, 0))? + pub fn line(&self, row: u32) -> String { + self.chars_at(Point::new(row, 0)) .take_while(|c| *c != '\n') - .collect()) + .collect() } pub fn text(&self) -> String { - self.chars().collect() + self.text_for_range(0..self.len()).collect() } pub fn text_for_range<'a, T: ToOffset>( &'a self, range: Range, - ) -> Result> { - let start = range.start.to_offset(self)?; - let end = range.end.to_offset(self)?; - Ok(self.chars_at(start)?.take(end - start)) + ) -> impl 'a + Iterator { + let start = range.start.to_offset(self); + let end = range.end.to_offset(self); + self.visible_text.chunks_in_range(start..end) } pub fn chars(&self) -> rope::Chars { - self.chars_at(0).unwrap() + self.chars_at(0) } - pub fn chars_at(&self, position: T) -> Result { - let offset = position.to_offset(self)?; - Ok(self.visible_text.chars_at(offset)) + pub fn chars_at(&self, position: T) -> rope::Chars { + let offset = position.to_offset(self); + self.visible_text.chars_at(offset) } pub fn selections_changed_since(&self, since: SelectionsVersion) -> bool { @@ -763,8 +759,8 @@ impl Buffer { let old_ranges = old_ranges .into_iter() - .map(|range| Ok(range.start.to_offset(self)?..range.end.to_offset(self)?)) - .collect::>>>()?; + .map(|range| range.start.to_offset(self)..range.end.to_offset(self)) + .collect::>>(); let has_new_text = new_text.is_some(); let ops = self.splice_fragments( @@ -802,50 +798,6 @@ impl Buffer { } } - pub fn simulate_typing(&mut self, rng: &mut T) { - let end = rng.gen_range(0..self.len() + 1); - let start = rng.gen_range(0..end + 1); - let mut range = start..end; - - let new_text_len = rng.gen_range(0..100); - let new_text: String = RandomCharIter::new(&mut *rng).take(new_text_len).collect(); - - for char in new_text.chars() { - self.edit(Some(range.clone()), char.to_string().as_str(), None) - .unwrap(); - range = range.end + 1..range.end + 1; - } - } - - pub fn randomly_edit( - &mut self, - rng: &mut T, - old_range_count: usize, - ctx: Option<&mut ModelContext>, - ) -> (Vec>, String, Vec) - where - T: Rng, - { - let mut old_ranges: Vec> = Vec::new(); - for _ in 0..old_range_count { - let last_end = old_ranges.last().map_or(0, |last_range| last_range.end + 1); - if last_end > self.len() { - break; - } - let end = rng.gen_range(last_end..self.len() + 1); - let start = rng.gen_range(last_end..end + 1); - old_ranges.push(start..end); - } - let new_text_len = rng.gen_range(0..10); - let new_text: String = RandomCharIter::new(&mut *rng).take(new_text_len).collect(); - - let operations = self - .edit(old_ranges.iter().cloned(), new_text.as_str(), ctx) - .unwrap(); - - (old_ranges, new_text, operations) - } - pub fn add_selection_set( &mut self, selections: impl Into>, @@ -1777,8 +1729,7 @@ impl Buffer { .unwrap_or(&FragmentId::max_value()), ); - // TODO: extent could be expressed in bytes, which would save a linear scan. - let range_in_insertion = 0..text.chars().count(); + let range_in_insertion = 0..text.len(); let mut split_tree = SumTree::new(); split_tree.push( InsertionSplit { @@ -1801,33 +1752,31 @@ impl Buffer { ) } - pub fn anchor_before(&self, position: T) -> Result { + pub fn anchor_before(&self, position: T) -> Anchor { self.anchor_at(position, AnchorBias::Left) } - pub fn anchor_after(&self, position: T) -> Result { + pub fn anchor_after(&self, position: T) -> Anchor { self.anchor_at(position, AnchorBias::Right) } - pub fn anchor_at(&self, position: T, bias: AnchorBias) -> Result { - let offset = position.to_offset(self)?; + pub fn anchor_at(&self, position: T, bias: AnchorBias) -> Anchor { + let offset = position.to_offset(self); let max_offset = self.len(); - if offset > max_offset { - return Err(anyhow!("offset is out of range")); - } + assert!(offset <= max_offset, "offset is out of range"); let seek_bias; match bias { AnchorBias::Left => { if offset == 0 { - return Ok(Anchor::Start); + return Anchor::Start; } else { seek_bias = SeekBias::Left; } } AnchorBias::Right => { if offset == max_offset { - return Ok(Anchor::End); + return Anchor::End; } else { seek_bias = SeekBias::Right; } @@ -1844,7 +1793,7 @@ impl Buffer { offset: offset_in_insertion, bias, }; - Ok(anchor) + anchor } fn fragment_id_for_anchor(&self, anchor: &Anchor) -> Result<&FragmentId> { @@ -1876,10 +1825,10 @@ impl Buffer { } } - fn summary_for_anchor(&self, anchor: &Anchor) -> Result { + fn summary_for_anchor(&self, anchor: &Anchor) -> TextSummary { match anchor { - Anchor::Start => Ok(TextSummary::default()), - Anchor::End => Ok(self.text_summary()), + Anchor::Start => TextSummary::default(), + Anchor::End => self.text_summary(), Anchor::Middle { insertion_id, offset, @@ -1893,24 +1842,20 @@ impl Buffer { let splits = self .insertion_splits .get(&insertion_id) - .ok_or_else(|| anyhow!("split does not exist for insertion id"))?; + .expect("split does not exist for insertion id"); let mut splits_cursor = splits.cursor::(); splits_cursor.seek(offset, seek_bias, &()); - let split = splits_cursor - .item() - .ok_or_else(|| anyhow!("split offset is out of range"))?; + let split = splits_cursor.item().expect("split offset is out of range"); let mut fragments_cursor = self.fragments.cursor::(); fragments_cursor.seek(&FragmentIdRef::new(&split.fragment_id), SeekBias::Left, &()); - let fragment = fragments_cursor - .item() - .ok_or_else(|| anyhow!("fragment id does not exist"))?; + let fragment = fragments_cursor.item().expect("fragment id does not exist"); let mut ix = *fragments_cursor.start(); if fragment.visible { ix += offset - fragment.range_in_insertion.start; } - Ok(self.text_summary_for_range(0..ix)) + self.text_summary_for_range(0..ix) } } } @@ -1922,6 +1867,14 @@ impl Buffer { Err(anyhow!("offset out of bounds")) } } + + pub fn next_char_boundary(&self, offset: usize) -> usize { + self.visible_text.next_char_boundary(offset) + } + + pub fn prev_char_boundary(&self, offset: usize) -> usize { + self.visible_text.prev_char_boundary(offset) + } } impl Clone for Buffer { @@ -2352,45 +2305,45 @@ impl operation_queue::Operation for Operation { } pub trait ToOffset { - fn to_offset(&self, buffer: &Buffer) -> Result; + fn to_offset(&self, buffer: &Buffer) -> usize; } impl ToOffset for Point { - fn to_offset(&self, buffer: &Buffer) -> Result { + fn to_offset(&self, buffer: &Buffer) -> usize { buffer.visible_text.to_offset(*self) } } impl ToOffset for usize { - fn to_offset(&self, _: &Buffer) -> Result { - Ok(*self) + fn to_offset(&self, _: &Buffer) -> usize { + *self } } impl ToOffset for Anchor { - fn to_offset(&self, buffer: &Buffer) -> Result { - Ok(buffer.summary_for_anchor(self)?.chars) + fn to_offset(&self, buffer: &Buffer) -> usize { + buffer.summary_for_anchor(self).bytes } } impl<'a> ToOffset for &'a Anchor { - fn to_offset(&self, buffer: &Buffer) -> Result { - Ok(buffer.summary_for_anchor(self)?.chars) + fn to_offset(&self, buffer: &Buffer) -> usize { + buffer.summary_for_anchor(self).bytes } } pub trait ToPoint { - fn to_point(&self, buffer: &Buffer) -> Result; + fn to_point(&self, buffer: &Buffer) -> Point; } impl ToPoint for Anchor { - fn to_point(&self, buffer: &Buffer) -> Result { - Ok(buffer.summary_for_anchor(self)?.lines) + fn to_point(&self, buffer: &Buffer) -> Point { + buffer.summary_for_anchor(self).lines } } impl ToPoint for usize { - fn to_point(&self, buffer: &Buffer) -> Result { + fn to_point(&self, buffer: &Buffer) -> Point { buffer.visible_text.to_point(*self) } } @@ -2400,13 +2353,15 @@ mod tests { use super::*; use crate::{ test::temp_tree, + util::RandomCharIter, worktree::{Worktree, WorktreeHandle}, }; - use cmp::Ordering; use gpui::App; + use rand::prelude::*; use serde_json::json; use std::{ cell::RefCell, + cmp::Ordering, collections::BTreeMap, fs, rc::Rc, @@ -2506,12 +2461,7 @@ mod tests { for _i in 0..10 { let (old_ranges, new_text, _) = buffer.randomly_mutate(rng, None); for old_range in old_ranges.iter().rev() { - reference_string = reference_string - .chars() - .take(old_range.start) - .chain(new_text.chars()) - .chain(reference_string.chars().skip(old_range.end)) - .collect(); + reference_string.replace_range(old_range.clone(), &new_text); } assert_eq!(buffer.text(), reference_string); @@ -2525,7 +2475,7 @@ mod tests { for (len, rows) in &line_lengths { for row in rows { - assert_eq!(buffer.line_len(*row).unwrap(), *len); + assert_eq!(buffer.line_len(*row), *len); } } @@ -2537,22 +2487,14 @@ mod tests { } for _ in 0..5 { - let end = rng.gen_range(0..buffer.len() + 1); - let start = rng.gen_range(0..end + 1); - - let line_lengths = line_lengths_in_range(&buffer, start..end); + let range = buffer.random_byte_range(0, rng); + let line_lengths = line_lengths_in_range(&buffer, range.clone()); let (longest_column, longest_rows) = line_lengths.iter().next_back().unwrap(); - let range_sum = buffer.text_summary_for_range(start..end); + let range_sum = buffer.text_summary_for_range(range.clone()); assert_eq!(range_sum.rightmost_point.column, *longest_column); assert!(longest_rows.contains(&range_sum.rightmost_point.row)); - let range_text = buffer - .text() - .chars() - .skip(start) - .take(end - start) - .collect::(); - assert_eq!(range_sum.chars, range_text.chars().count()); + let range_text = &buffer.text()[range]; assert_eq!(range_sum.bytes, range_text.len()); } @@ -2571,7 +2513,7 @@ mod tests { let old_len = old_range.end - old_range.start; let new_len = new_range.end - new_range.start; let old_start = (old_range.start as isize + delta) as usize; - let new_text: String = buffer.text_for_range(new_range).unwrap().collect(); + let new_text: String = buffer.text_for_range(new_range).collect(); old_buffer .edit(Some(old_start..old_start + old_len), new_text, None) .unwrap(); @@ -2595,13 +2537,12 @@ mod tests { buffer.edit(vec![18..18], "\npqrs\n", None).unwrap(); buffer.edit(vec![18..21], "\nPQ", None).unwrap(); - assert_eq!(buffer.line_len(0).unwrap(), 4); - assert_eq!(buffer.line_len(1).unwrap(), 3); - assert_eq!(buffer.line_len(2).unwrap(), 5); - assert_eq!(buffer.line_len(3).unwrap(), 3); - assert_eq!(buffer.line_len(4).unwrap(), 4); - assert_eq!(buffer.line_len(5).unwrap(), 0); - assert!(buffer.line_len(6).is_err()); + assert_eq!(buffer.line_len(0), 4); + assert_eq!(buffer.line_len(1), 3); + assert_eq!(buffer.line_len(2), 5); + assert_eq!(buffer.line_len(3), 3); + assert_eq!(buffer.line_len(4), 4); + assert_eq!(buffer.line_len(5), 0); buffer }); } @@ -2632,7 +2573,6 @@ mod tests { assert_eq!( buffer.text_summary_for_range(1..3), TextSummary { - chars: 2, bytes: 2, lines: Point::new(1, 0), first_line_len: 1, @@ -2642,7 +2582,6 @@ mod tests { assert_eq!( buffer.text_summary_for_range(1..12), TextSummary { - chars: 11, bytes: 11, lines: Point::new(3, 0), first_line_len: 1, @@ -2652,7 +2591,6 @@ mod tests { assert_eq!( buffer.text_summary_for_range(0..20), TextSummary { - chars: 20, bytes: 20, lines: Point::new(4, 1), first_line_len: 2, @@ -2662,7 +2600,6 @@ mod tests { assert_eq!( buffer.text_summary_for_range(0..22), TextSummary { - chars: 22, bytes: 22, lines: Point::new(4, 3), first_line_len: 2, @@ -2672,7 +2609,6 @@ mod tests { assert_eq!( buffer.text_summary_for_range(7..22), TextSummary { - chars: 15, bytes: 15, lines: Point::new(2, 3), first_line_len: 4, @@ -2692,19 +2628,19 @@ mod tests { buffer.edit(vec![18..18], "\npqrs", None).unwrap(); buffer.edit(vec![18..21], "\nPQ", None).unwrap(); - let chars = buffer.chars_at(Point::new(0, 0)).unwrap(); + let chars = buffer.chars_at(Point::new(0, 0)); assert_eq!(chars.collect::(), "abcd\nefgh\nijkl\nmno\nPQrs"); - let chars = buffer.chars_at(Point::new(1, 0)).unwrap(); + let chars = buffer.chars_at(Point::new(1, 0)); assert_eq!(chars.collect::(), "efgh\nijkl\nmno\nPQrs"); - let chars = buffer.chars_at(Point::new(2, 0)).unwrap(); + let chars = buffer.chars_at(Point::new(2, 0)); assert_eq!(chars.collect::(), "ijkl\nmno\nPQrs"); - let chars = buffer.chars_at(Point::new(3, 0)).unwrap(); + let chars = buffer.chars_at(Point::new(3, 0)); assert_eq!(chars.collect::(), "mno\nPQrs"); - let chars = buffer.chars_at(Point::new(4, 0)).unwrap(); + let chars = buffer.chars_at(Point::new(4, 0)); assert_eq!(chars.collect::(), "PQrs"); // Regression test: @@ -2712,7 +2648,7 @@ mod tests { buffer.edit(vec![0..0], "[workspace]\nmembers = [\n \"xray_core\",\n \"xray_server\",\n \"xray_cli\",\n \"xray_wasm\",\n]\n", None).unwrap(); buffer.edit(vec![60..60], "\n", None).unwrap(); - let chars = buffer.chars_at(Point::new(6, 0)).unwrap(); + let chars = buffer.chars_at(Point::new(6, 0)); assert_eq!(chars.collect::(), " \"xray_wasm\",\n]\n"); buffer @@ -2744,103 +2680,79 @@ mod tests { ctx.add_model(|ctx| { let mut buffer = Buffer::new(0, "", ctx); buffer.edit(vec![0..0], "abc", None).unwrap(); - let left_anchor = buffer.anchor_before(2).unwrap(); - let right_anchor = buffer.anchor_after(2).unwrap(); + let left_anchor = buffer.anchor_before(2); + let right_anchor = buffer.anchor_after(2); buffer.edit(vec![1..1], "def\n", None).unwrap(); assert_eq!(buffer.text(), "adef\nbc"); - assert_eq!(left_anchor.to_offset(&buffer).unwrap(), 6); - assert_eq!(right_anchor.to_offset(&buffer).unwrap(), 6); - assert_eq!( - left_anchor.to_point(&buffer).unwrap(), - Point { row: 1, column: 1 } - ); - assert_eq!( - right_anchor.to_point(&buffer).unwrap(), - Point { row: 1, column: 1 } - ); + assert_eq!(left_anchor.to_offset(&buffer), 6); + assert_eq!(right_anchor.to_offset(&buffer), 6); + assert_eq!(left_anchor.to_point(&buffer), Point { row: 1, column: 1 }); + assert_eq!(right_anchor.to_point(&buffer), Point { row: 1, column: 1 }); buffer.edit(vec![2..3], "", None).unwrap(); assert_eq!(buffer.text(), "adf\nbc"); - assert_eq!(left_anchor.to_offset(&buffer).unwrap(), 5); - assert_eq!(right_anchor.to_offset(&buffer).unwrap(), 5); - assert_eq!( - left_anchor.to_point(&buffer).unwrap(), - Point { row: 1, column: 1 } - ); - assert_eq!( - right_anchor.to_point(&buffer).unwrap(), - Point { row: 1, column: 1 } - ); + assert_eq!(left_anchor.to_offset(&buffer), 5); + assert_eq!(right_anchor.to_offset(&buffer), 5); + assert_eq!(left_anchor.to_point(&buffer), Point { row: 1, column: 1 }); + assert_eq!(right_anchor.to_point(&buffer), Point { row: 1, column: 1 }); buffer.edit(vec![5..5], "ghi\n", None).unwrap(); assert_eq!(buffer.text(), "adf\nbghi\nc"); - assert_eq!(left_anchor.to_offset(&buffer).unwrap(), 5); - assert_eq!(right_anchor.to_offset(&buffer).unwrap(), 9); - assert_eq!( - left_anchor.to_point(&buffer).unwrap(), - Point { row: 1, column: 1 } - ); - assert_eq!( - right_anchor.to_point(&buffer).unwrap(), - Point { row: 2, column: 0 } - ); + assert_eq!(left_anchor.to_offset(&buffer), 5); + assert_eq!(right_anchor.to_offset(&buffer), 9); + assert_eq!(left_anchor.to_point(&buffer), Point { row: 1, column: 1 }); + assert_eq!(right_anchor.to_point(&buffer), Point { row: 2, column: 0 }); buffer.edit(vec![7..9], "", None).unwrap(); assert_eq!(buffer.text(), "adf\nbghc"); - assert_eq!(left_anchor.to_offset(&buffer).unwrap(), 5); - assert_eq!(right_anchor.to_offset(&buffer).unwrap(), 7); - assert_eq!( - left_anchor.to_point(&buffer).unwrap(), - Point { row: 1, column: 1 }, - ); - assert_eq!( - right_anchor.to_point(&buffer).unwrap(), - Point { row: 1, column: 3 } - ); + assert_eq!(left_anchor.to_offset(&buffer), 5); + assert_eq!(right_anchor.to_offset(&buffer), 7); + assert_eq!(left_anchor.to_point(&buffer), Point { row: 1, column: 1 },); + assert_eq!(right_anchor.to_point(&buffer), Point { row: 1, column: 3 }); // Ensure anchoring to a point is equivalent to anchoring to an offset. assert_eq!( - buffer.anchor_before(Point { row: 0, column: 0 }).unwrap(), - buffer.anchor_before(0).unwrap() + buffer.anchor_before(Point { row: 0, column: 0 }), + buffer.anchor_before(0) ); assert_eq!( - buffer.anchor_before(Point { row: 0, column: 1 }).unwrap(), - buffer.anchor_before(1).unwrap() + buffer.anchor_before(Point { row: 0, column: 1 }), + buffer.anchor_before(1) ); assert_eq!( - buffer.anchor_before(Point { row: 0, column: 2 }).unwrap(), - buffer.anchor_before(2).unwrap() + buffer.anchor_before(Point { row: 0, column: 2 }), + buffer.anchor_before(2) ); assert_eq!( - buffer.anchor_before(Point { row: 0, column: 3 }).unwrap(), - buffer.anchor_before(3).unwrap() + buffer.anchor_before(Point { row: 0, column: 3 }), + buffer.anchor_before(3) ); assert_eq!( - buffer.anchor_before(Point { row: 1, column: 0 }).unwrap(), - buffer.anchor_before(4).unwrap() + buffer.anchor_before(Point { row: 1, column: 0 }), + buffer.anchor_before(4) ); assert_eq!( - buffer.anchor_before(Point { row: 1, column: 1 }).unwrap(), - buffer.anchor_before(5).unwrap() + buffer.anchor_before(Point { row: 1, column: 1 }), + buffer.anchor_before(5) ); assert_eq!( - buffer.anchor_before(Point { row: 1, column: 2 }).unwrap(), - buffer.anchor_before(6).unwrap() + buffer.anchor_before(Point { row: 1, column: 2 }), + buffer.anchor_before(6) ); assert_eq!( - buffer.anchor_before(Point { row: 1, column: 3 }).unwrap(), - buffer.anchor_before(7).unwrap() + buffer.anchor_before(Point { row: 1, column: 3 }), + buffer.anchor_before(7) ); assert_eq!( - buffer.anchor_before(Point { row: 1, column: 4 }).unwrap(), - buffer.anchor_before(8).unwrap() + buffer.anchor_before(Point { row: 1, column: 4 }), + buffer.anchor_before(8) ); // Comparison between anchors. - let anchor_at_offset_0 = buffer.anchor_before(0).unwrap(); - let anchor_at_offset_1 = buffer.anchor_before(1).unwrap(); - let anchor_at_offset_2 = buffer.anchor_before(2).unwrap(); + let anchor_at_offset_0 = buffer.anchor_before(0); + let anchor_at_offset_1 = buffer.anchor_before(1); + let anchor_at_offset_2 = buffer.anchor_before(2); assert_eq!( anchor_at_offset_0 @@ -2906,24 +2818,24 @@ mod tests { fn test_anchors_at_start_and_end(ctx: &mut gpui::MutableAppContext) { ctx.add_model(|ctx| { let mut buffer = Buffer::new(0, "", ctx); - let before_start_anchor = buffer.anchor_before(0).unwrap(); - let after_end_anchor = buffer.anchor_after(0).unwrap(); + let before_start_anchor = buffer.anchor_before(0); + let after_end_anchor = buffer.anchor_after(0); buffer.edit(vec![0..0], "abc", None).unwrap(); assert_eq!(buffer.text(), "abc"); - assert_eq!(before_start_anchor.to_offset(&buffer).unwrap(), 0); - assert_eq!(after_end_anchor.to_offset(&buffer).unwrap(), 3); + assert_eq!(before_start_anchor.to_offset(&buffer), 0); + assert_eq!(after_end_anchor.to_offset(&buffer), 3); - let after_start_anchor = buffer.anchor_after(0).unwrap(); - let before_end_anchor = buffer.anchor_before(3).unwrap(); + let after_start_anchor = buffer.anchor_after(0); + let before_end_anchor = buffer.anchor_before(3); buffer.edit(vec![3..3], "def", None).unwrap(); buffer.edit(vec![0..0], "ghi", None).unwrap(); assert_eq!(buffer.text(), "ghiabcdef"); - assert_eq!(before_start_anchor.to_offset(&buffer).unwrap(), 0); - assert_eq!(after_start_anchor.to_offset(&buffer).unwrap(), 3); - assert_eq!(before_end_anchor.to_offset(&buffer).unwrap(), 6); - assert_eq!(after_end_anchor.to_offset(&buffer).unwrap(), 9); + assert_eq!(before_start_anchor.to_offset(&buffer), 0); + assert_eq!(after_start_anchor.to_offset(&buffer), 3); + assert_eq!(before_end_anchor.to_offset(&buffer), 6); + assert_eq!(after_end_anchor.to_offset(&buffer), 9); buffer }); } @@ -3062,9 +2974,7 @@ mod tests { buffer.add_selection_set( (0..3) .map(|row| { - let anchor = buffer - .anchor_at(Point::new(row, 0), AnchorBias::Right) - .unwrap(); + let anchor = buffer.anchor_at(Point::new(row, 0), AnchorBias::Right); Selection { id: row as usize, start: anchor.clone(), @@ -3104,7 +3014,7 @@ mod tests { .iter() .map(|selection| { assert_eq!(selection.start, selection.end); - selection.start.to_point(&buffer).unwrap() + selection.start.to_point(&buffer) }) .collect::>(); assert_eq!( @@ -3324,6 +3234,39 @@ mod tests { } impl Buffer { + fn random_byte_range(&mut self, start_offset: usize, rng: &mut impl Rng) -> Range { + let end = self.next_char_boundary(rng.gen_range(start_offset..=self.len())); + let start = self.prev_char_boundary(rng.gen_range(start_offset..=end)); + start..end + } + + pub fn randomly_edit( + &mut self, + rng: &mut T, + old_range_count: usize, + ctx: Option<&mut ModelContext>, + ) -> (Vec>, String, Vec) + where + T: Rng, + { + let mut old_ranges: Vec> = Vec::new(); + for _ in 0..old_range_count { + let last_end = old_ranges.last().map_or(0, |last_range| last_range.end + 1); + if last_end > self.len() { + break; + } + old_ranges.push(self.random_byte_range(last_end, rng)); + } + let new_text_len = rng.gen_range(0..10); + let new_text: String = RandomCharIter::new(&mut *rng).take(new_text_len).collect(); + + let operations = self + .edit(old_ranges.iter().cloned(), new_text.as_str(), ctx) + .unwrap(); + + (old_ranges, new_text, operations) + } + pub fn randomly_mutate( &mut self, rng: &mut T, @@ -3349,9 +3292,7 @@ mod tests { } else { let mut ranges = Vec::new(); for _ in 0..5 { - let start = rng.gen_range(0..self.len() + 1); - let end = rng.gen_range(0..self.len() + 1); - ranges.push(start..end); + ranges.push(self.random_byte_range(0, rng)); } let new_selections = self.selections_from_ranges(ranges).unwrap(); @@ -3391,16 +3332,16 @@ mod tests { if range.start > range.end { selections.push(Selection { id: NEXT_SELECTION_ID.fetch_add(1, atomic::Ordering::SeqCst), - start: self.anchor_before(range.end)?, - end: self.anchor_before(range.start)?, + start: self.anchor_before(range.end), + end: self.anchor_before(range.start), reversed: true, goal: SelectionGoal::None, }); } else { selections.push(Selection { id: NEXT_SELECTION_ID.fetch_add(1, atomic::Ordering::SeqCst), - start: self.anchor_after(range.start)?, - end: self.anchor_before(range.end)?, + start: self.anchor_after(range.start), + end: self.anchor_before(range.end), reversed: false, goal: SelectionGoal::None, }); @@ -3414,8 +3355,8 @@ mod tests { .selections(set_id)? .iter() .map(move |selection| { - let start = selection.start.to_offset(self).unwrap(); - let end = selection.end.to_offset(self).unwrap(); + let start = selection.start.to_offset(self); + let end = selection.end.to_offset(self); if selection.reversed { end..start } else { @@ -3452,17 +3393,9 @@ mod tests { fn line_lengths_in_range(buffer: &Buffer, range: Range) -> BTreeMap> { let mut lengths = BTreeMap::new(); - for (row, line) in buffer - .text() - .chars() - .skip(range.start) - .take(range.len()) - .collect::() - .lines() - .enumerate() - { + for (row, line) in buffer.text()[range.start..range.end].lines().enumerate() { lengths - .entry(line.chars().count() as u32) + .entry(line.len() as u32) .or_insert(HashSet::default()) .insert(row as u32); } diff --git a/zed/src/editor/buffer/rope.rs b/zed/src/editor/buffer/rope.rs index 0b7ff6411b5382f5d71e57e50fd3f2023d35c4c7..3128aedbd31a2b71d801646e89c5cabebe282d32 100644 --- a/zed/src/editor/buffer/rope.rs +++ b/zed/src/editor/buffer/rope.rs @@ -1,6 +1,5 @@ use super::Point; use crate::sum_tree::{self, SeekBias, SumTree}; -use anyhow::{anyhow, Result}; use arrayvec::ArrayString; use smallvec::SmallVec; use std::{cmp, iter::Skip, ops::Range, str}; @@ -148,31 +147,27 @@ impl Rope { let mut cursor = self.chunks.cursor::(); cursor.seek(&offset, SeekBias::Left, &()); if let Some(chunk) = cursor.item() { - let ix = offset - cursor.start(); + let mut ix = offset - cursor.start(); while !chunk.0.is_char_boundary(ix) { ix += 1; offset += 1; } - offset - } else { - offset } + offset } - pub fn prev_char_boundary(&self, offset: usize) -> usize { + pub fn prev_char_boundary(&self, mut offset: usize) -> usize { assert!(offset <= self.summary().bytes); let mut cursor = self.chunks.cursor::(); cursor.seek(&offset, SeekBias::Left, &()); if let Some(chunk) = cursor.item() { - let ix = offset - cursor.start(); + let mut ix = offset - cursor.start(); while !chunk.0.is_char_boundary(ix) { ix -= 1; offset -= 1; } - offset - } else { - offset } + offset } } @@ -281,16 +276,15 @@ impl<'a> Iterator for ChunksIter<'a> { type Item = &'a str; fn next(&mut self) -> Option { - if *self.chunks.start() >= self.range.end { - None - } else if let Some(chunk) = self.chunks.item() { - let start = self.range.start.saturating_sub(*self.chunks.start()); - let end = self.range.end - self.chunks.start(); - self.chunks.next(); - Some(&chunk.0[start..end]) - } else { - None + if let Some(chunk) = self.chunks.item() { + if self.range.end > *self.chunks.start() { + let start = self.range.start.saturating_sub(*self.chunks.start()); + let end = self.range.end - self.chunks.start(); + self.chunks.next(); + return Some(&chunk.0[start..chunk.0.len().min(end)]); + } } + None } } @@ -357,22 +351,19 @@ pub struct TextSummary { impl<'a> From<&'a str> for TextSummary { fn from(text: &'a str) -> Self { - let mut chars = 0; let mut lines = Point::new(0, 0); let mut first_line_len = 0; let mut rightmost_point = Point::new(0, 0); - for c in text.chars() { - if c == '\n' { + for (i, line) in text.split('\n').enumerate() { + if i > 0 { lines.row += 1; - lines.column = 0; - } else { - lines.column += c.len_utf8() as u32; - if lines.row == 0 { - first_line_len = lines.column; - } - if lines.column > rightmost_point.column { - rightmost_point = lines; - } + } + lines.column = line.len() as u32; + if i == 0 { + first_line_len = lines.column; + } + if lines.column > rightmost_point.column { + rightmost_point = lines; } } @@ -560,56 +551,38 @@ mod tests { new_actual.append(cursor.suffix()); actual = new_actual; - let mut new_expected = String::new(); - new_expected.extend(expected.chars().take(start_ix)); - new_expected.push_str(&new_text); - new_expected.extend(expected.chars().skip(end_ix)); - expected = new_expected; + expected.replace_range(start_ix..end_ix, &new_text); assert_eq!(actual.text(), expected); log::info!("text: {:?}", expected); for _ in 0..5 { - let end_ix = rng.gen_range(0..=expected.chars().count()); - let start_ix = rng.gen_range(0..=end_ix); + let end_ix = actual.next_char_boundary(rng.gen_range(0..=expected.len())); + let start_ix = actual.prev_char_boundary(rng.gen_range(0..=end_ix)); assert_eq!( actual.chunks_in_range(start_ix..end_ix).collect::(), - expected - .chars() - .skip(start_ix) - .take(end_ix - start_ix) - .collect::() + &expected[start_ix..end_ix] ); } let mut point = Point::new(0, 0); - let mut offset = 0; - for ch in expected.chars() { - assert_eq!(actual.to_point(offset), point); - assert_eq!(actual.to_offset(point), offset); + for (ix, ch) in expected.char_indices().chain(Some((expected.len(), '\0'))) { + assert_eq!(actual.to_point(ix), point, "to_point({})", ix); + assert_eq!(actual.to_offset(point), ix, "to_offset({:?})", point); if ch == '\n' { - assert!(actual - .to_offset(Point::new(point.row, point.column + 1)) - .is_err()); - point.row += 1; point.column = 0 } else { - point.column += 1; + point.column += ch.len_utf8() as u32; } - offset += 1; } - assert_eq!(actual.to_point(offset).unwrap(), point); - assert!(actual.to_point(offset + 1).is_err()); - assert_eq!(actual.to_offset(point).unwrap(), offset); - assert!(actual.to_offset(Point::new(point.row + 1, 0)).is_err()); for _ in 0..5 { - let end_ix = rng.gen_range(0..=expected.chars().count()); - let start_ix = rng.gen_range(0..=end_ix); + let end_ix = actual.next_char_boundary(rng.gen_range(0..=expected.len())); + let start_ix = actual.prev_char_boundary(rng.gen_range(0..=end_ix)); assert_eq!( actual.cursor(start_ix).summary(end_ix), - TextSummary::from(&expected[byte_range]) + TextSummary::from(&expected[start_ix..end_ix]) ); } } diff --git a/zed/src/editor/buffer/selection.rs b/zed/src/editor/buffer/selection.rs index bff91a83e922a712889a62dbf5a6c80184be821c..95ad181beac7bf3988f2c632822c8697f26ea4f7 100644 --- a/zed/src/editor/buffer/selection.rs +++ b/zed/src/editor/buffer/selection.rs @@ -62,8 +62,8 @@ impl Selection { } pub fn range(&self, buffer: &Buffer) -> Range { - let start = self.start.to_point(buffer).unwrap(); - let end = self.end.to_point(buffer).unwrap(); + let start = self.start.to_point(buffer); + let end = self.end.to_point(buffer); if self.reversed { end..start } else { diff --git a/zed/src/editor/buffer_view.rs b/zed/src/editor/buffer_view.rs index 3319e81f41fdfb4bd9145d8f17bec366d99e7353..ebf10a4f4a4036f5d8d104a9fc12466b8d0b88e8 100644 --- a/zed/src/editor/buffer_view.rs +++ b/zed/src/editor/buffer_view.rs @@ -337,8 +337,8 @@ impl BufferView { buffer.add_selection_set( vec![Selection { id: post_inc(&mut next_selection_id), - start: buffer.anchor_before(0).unwrap(), - end: buffer.anchor_before(0).unwrap(), + start: buffer.anchor_before(0), + end: buffer.anchor_before(0), reversed: false, goal: SelectionGoal::None, }], @@ -588,8 +588,8 @@ impl BufferView { let buffer = self.buffer.read(ctx); let mut selections = Vec::new(); for range in ranges { - let mut start = range.start.to_offset(buffer).unwrap(); - let mut end = range.end.to_offset(buffer).unwrap(); + let mut start = range.start.to_offset(buffer); + let mut end = range.end.to_offset(buffer); let reversed = if start > end { mem::swap(&mut start, &mut end); true @@ -598,8 +598,8 @@ impl BufferView { }; selections.push(Selection { id: post_inc(&mut self.next_selection_id), - start: buffer.anchor_before(start).unwrap(), - end: buffer.anchor_before(end).unwrap(), + start: buffer.anchor_before(start), + end: buffer.anchor_before(end), reversed, goal: SelectionGoal::None, }); @@ -644,8 +644,8 @@ impl BufferView { { let buffer = self.buffer.read(ctx); for selection in self.selections(ctx.as_ref()) { - let start = selection.start.to_offset(buffer).unwrap(); - let end = selection.end.to_offset(buffer).unwrap(); + let start = selection.start.to_offset(buffer); + let end = selection.end.to_offset(buffer); old_selections.push((selection.id, start..end)); } } @@ -664,9 +664,7 @@ impl BufferView { .map(|(id, range)| { let start = range.start as isize; let end = range.end as isize; - let anchor = buffer - .anchor_before((start + delta + char_count) as usize) - .unwrap(); + let anchor = buffer.anchor_before((start + delta + char_count) as usize); let deleted_count = end - start; delta += char_count - deleted_count; Selection { @@ -785,13 +783,13 @@ impl BufferView { } } - let mut edit_start = Point::new(rows.start, 0).to_offset(buffer).unwrap(); + let mut edit_start = Point::new(rows.start, 0).to_offset(buffer); let edit_end; let cursor_buffer_row; - if let Ok(end_offset) = Point::new(rows.end, 0).to_offset(buffer) { + if buffer.max_point().row >= rows.end { // If there's a line after the range, delete the \n from the end of the row range // and position the cursor on the next line. - edit_end = end_offset; + edit_end = Point::new(rows.end, 0).to_offset(buffer); cursor_buffer_row = rows.end; } else { // If there isn't a line after the range, delete the \n from the line before the @@ -822,7 +820,7 @@ impl BufferView { let new_selections = new_cursors .into_iter() .map(|(id, cursor)| { - let anchor = buffer.anchor_before(cursor).unwrap(); + let anchor = buffer.anchor_before(cursor); Selection { id, start: anchor.clone(), @@ -848,8 +846,8 @@ impl BufferView { // when the selections are at the beginning of a line. let buffer = self.buffer.read(ctx); for selection in &mut selections { - selection.start = selection.start.bias_right(buffer).unwrap(); - selection.end = selection.end.bias_right(buffer).unwrap(); + selection.start = selection.start.bias_right(buffer); + selection.end = selection.end.bias_right(buffer); } } self.update_selections(selections.clone(), false, ctx); @@ -876,11 +874,10 @@ impl BufferView { // Copy the text from the selected row region and splice it at the start of the region. let start = Point::new(rows.start, 0); - let end = Point::new(rows.end - 1, buffer.line_len(rows.end - 1).unwrap()); + let end = Point::new(rows.end - 1, buffer.line_len(rows.end - 1)); let text = buffer .text_for_range(start..end) - .unwrap() - .chain(Some('\n')) + .chain(Some("\n")) .collect::(); edits.push((start, text)); } @@ -894,8 +891,8 @@ impl BufferView { // Restore bias on selections. let buffer = self.buffer.read(ctx); for selection in &mut selections { - selection.start = selection.start.bias_left(buffer).unwrap(); - selection.end = selection.end.bias_left(buffer).unwrap(); + selection.start = selection.start.bias_left(buffer); + selection.end = selection.end.bias_left(buffer); } self.update_selections(selections, true, ctx); @@ -935,13 +932,9 @@ impl BufferView { // Cut the text from the selected rows and paste it at the start of the previous line. if display_rows.start != 0 { - let start = Point::new(buffer_rows.start, 0).to_offset(buffer).unwrap(); - let end = Point::new( - buffer_rows.end - 1, - buffer.line_len(buffer_rows.end - 1).unwrap(), - ) - .to_offset(buffer) - .unwrap(); + let start = Point::new(buffer_rows.start, 0).to_offset(buffer); + let end = Point::new(buffer_rows.end - 1, buffer.line_len(buffer_rows.end - 1)) + .to_offset(buffer); let prev_row_display_start = DisplayPoint::new(display_rows.start - 1, 0); let prev_row_start = prev_row_display_start @@ -949,7 +942,7 @@ impl BufferView { .unwrap(); let mut text = String::new(); - text.extend(buffer.text_for_range(start..end).unwrap()); + text.extend(buffer.text_for_range(start..end)); text.push('\n'); edits.push((prev_row_start..prev_row_start, text)); edits.push((start - 1..end, String::new())); @@ -969,8 +962,8 @@ impl BufferView { // Move folds up. old_folds.push(start..end); for fold in self.display_map.folds_in_range(start..end, app).unwrap() { - let mut start = fold.start.to_point(buffer).unwrap(); - let mut end = fold.end.to_point(buffer).unwrap(); + let mut start = fold.start.to_point(buffer); + let mut end = fold.end.to_point(buffer); start.row -= row_delta; end.row -= row_delta; new_folds.push(start..end); @@ -1025,13 +1018,9 @@ impl BufferView { // Cut the text from the selected rows and paste it at the end of the next line. if display_rows.end <= self.display_map.max_point(app).row() { - let start = Point::new(buffer_rows.start, 0).to_offset(buffer).unwrap(); - let end = Point::new( - buffer_rows.end - 1, - buffer.line_len(buffer_rows.end - 1).unwrap(), - ) - .to_offset(buffer) - .unwrap(); + let start = Point::new(buffer_rows.start, 0).to_offset(buffer); + let end = Point::new(buffer_rows.end - 1, buffer.line_len(buffer_rows.end - 1)) + .to_offset(buffer); let next_row_display_end = DisplayPoint::new( display_rows.end, @@ -1043,7 +1032,7 @@ impl BufferView { let mut text = String::new(); text.push('\n'); - text.extend(buffer.text_for_range(start..end).unwrap()); + text.extend(buffer.text_for_range(start..end)); edits.push((start..end + 1, String::new())); edits.push((next_row_end..next_row_end, text)); @@ -1063,8 +1052,8 @@ impl BufferView { // Move folds down. old_folds.push(start..end); for fold in self.display_map.folds_in_range(start..end, app).unwrap() { - let mut start = fold.start.to_point(buffer).unwrap(); - let mut end = fold.end.to_point(buffer).unwrap(); + let mut start = fold.start.to_point(buffer); + let mut end = fold.end.to_point(buffer); start.row += row_delta; end.row += row_delta; new_folds.push(start..end); @@ -1095,19 +1084,19 @@ impl BufferView { let buffer = self.buffer.read(ctx); let max_point = buffer.max_point(); for selection in &mut selections { - let mut start = selection.start.to_point(buffer).expect("invalid start"); - let mut end = selection.end.to_point(buffer).expect("invalid end"); + let mut start = selection.start.to_point(buffer); + let mut end = selection.end.to_point(buffer); let is_entire_line = start == end; if is_entire_line { start = Point::new(start.row, 0); end = cmp::min(max_point, Point::new(start.row + 1, 0)); - selection.start = buffer.anchor_before(start).unwrap(); - selection.end = buffer.anchor_before(end).unwrap(); + selection.start = buffer.anchor_before(start); + selection.end = buffer.anchor_before(end); } let mut len = 0; - for ch in buffer.text_for_range(start..end).unwrap() { - text.push(ch); - len += 1; + for chunk in buffer.text_for_range(start..end) { + text.push_str(chunk); + len += chunk.len(); } clipboard_selections.push(ClipboardSelection { len, @@ -1130,17 +1119,17 @@ impl BufferView { let selections = self.selections(ctx.as_ref()); let mut clipboard_selections = Vec::with_capacity(selections.len()); for selection in selections { - let mut start = selection.start.to_point(buffer).expect("invalid start"); - let mut end = selection.end.to_point(buffer).expect("invalid end"); + let mut start = selection.start.to_point(buffer); + let mut end = selection.end.to_point(buffer); let is_entire_line = start == end; if is_entire_line { start = Point::new(start.row, 0); end = cmp::min(max_point, Point::new(start.row + 1, 0)); } let mut len = 0; - for ch in buffer.text_for_range(start..end).unwrap() { - text.push(ch); - len += 1; + for chunk in buffer.text_for_range(start..end) { + text.push_str(chunk); + len += chunk.len(); } clipboard_selections.push(ClipboardSelection { len, @@ -1176,14 +1165,14 @@ impl BufferView { String::from_iter(clipboard_chars.by_ref().take(clipboard_selection.len)); self.buffer.update(ctx, |buffer, ctx| { - let selection_start = selection.start.to_point(buffer).unwrap(); - let selection_end = selection.end.to_point(buffer).unwrap(); + let selection_start = selection.start.to_point(buffer); + let selection_end = selection.end.to_point(buffer); // If the corresponding selection was empty when this slice of the // clipboard text was written, then the entire line containing the // selection was copied. If this selection is also currently empty, // then paste the line before the current line of the buffer. - let new_selection_start = selection.end.bias_right(buffer).unwrap(); + let new_selection_start = selection.end.bias_right(buffer); if selection_start == selection_end && clipboard_selection.is_entire_line { let line_start = Point::new(selection_start.row, 0); buffer @@ -1195,7 +1184,7 @@ impl BufferView { .unwrap(); }; - let new_selection_start = new_selection_start.bias_left(buffer).unwrap(); + let new_selection_start = new_selection_start.bias_left(buffer); new_selections.push(Selection { id: selection.id, start: new_selection_start.clone(), @@ -1678,7 +1667,7 @@ impl BufferView { pub fn move_to_beginning(&mut self, _: &(), ctx: &mut ViewContext) { let buffer = self.buffer.read(ctx); - let cursor = buffer.anchor_before(Point::new(0, 0)).unwrap(); + let cursor = buffer.anchor_before(Point::new(0, 0)); let selection = Selection { id: post_inc(&mut self.next_selection_id), start: cursor.clone(), @@ -1697,7 +1686,7 @@ impl BufferView { pub fn move_to_end(&mut self, _: &(), ctx: &mut ViewContext) { let buffer = self.buffer.read(ctx); - let cursor = buffer.anchor_before(buffer.max_point()).unwrap(); + let cursor = buffer.anchor_before(buffer.max_point()); let selection = Selection { id: post_inc(&mut self.next_selection_id), start: cursor.clone(), @@ -1732,10 +1721,8 @@ impl BufferView { let max_point = buffer.max_point(); for selection in &mut selections { let (rows, _) = selection.buffer_rows_for_display_rows(true, &self.display_map, app); - selection.start = buffer.anchor_before(Point::new(rows.start, 0)).unwrap(); - selection.end = buffer - .anchor_before(cmp::min(max_point, Point::new(rows.end, 0))) - .unwrap(); + selection.start = buffer.anchor_before(Point::new(rows.start, 0)); + selection.end = buffer.anchor_before(cmp::min(max_point, Point::new(rows.end, 0))); selection.reversed = false; } self.update_selections(selections, true, ctx); @@ -1761,9 +1748,7 @@ impl BufferView { }); } for row in range.start.row + 1..range.end.row { - let cursor = buffer - .anchor_before(Point::new(row, buffer.line_len(row).unwrap())) - .unwrap(); + let cursor = buffer.anchor_before(Point::new(row, buffer.line_len(row))); new_selections.push(Selection { id: post_inc(&mut self.next_selection_id), start: cursor.clone(), @@ -2085,7 +2070,7 @@ impl BufferView { .to_buffer_point(&self.display_map, Bias::Left, app) .unwrap(); start.column = 0; - end.column = buffer.line_len(end.row).unwrap(); + end.column = buffer.line_len(end.row); start..end }) .collect::>(); diff --git a/zed/src/editor/display_map/fold_map.rs b/zed/src/editor/display_map/fold_map.rs index 89eb7b062e1e2ef8ec06df13bf303d8ec7d9b116..417ad04f319517e9015a7cc601efcf059abf4888 100644 --- a/zed/src/editor/display_map/fold_map.rs +++ b/zed/src/editor/display_map/fold_map.rs @@ -52,7 +52,7 @@ impl FoldMap { } pub fn len(&self, ctx: &AppContext) -> usize { - self.sync(ctx).summary().display.chars + self.sync(ctx).summary().display.bytes } pub fn line_len(&self, row: u32, ctx: &AppContext) -> Result { @@ -98,10 +98,9 @@ impl FoldMap { let mut folds = Vec::new(); let buffer = self.buffer.read(ctx); for range in ranges.into_iter() { - let range = range.start.to_offset(buffer)?..range.end.to_offset(buffer)?; + let range = range.start.to_offset(buffer)..range.end.to_offset(buffer); if range.start != range.end { - let fold = - Fold(buffer.anchor_after(range.start)?..buffer.anchor_before(range.end)?); + let fold = Fold(buffer.anchor_after(range.start)..buffer.anchor_before(range.end)); folds.push(fold); edits.push(Edit { old_range: range.clone(), @@ -147,8 +146,7 @@ impl FoldMap { // Remove intersecting folds and add their ranges to edits that are passed to apply_edits. let mut folds_cursor = self.intersecting_folds(range, ctx)?; while let Some(fold) = folds_cursor.item() { - let offset_range = - fold.0.start.to_offset(buffer).unwrap()..fold.0.end.to_offset(buffer).unwrap(); + let offset_range = fold.0.start.to_offset(buffer)..fold.0.end.to_offset(buffer); edits.push(Edit { old_range: offset_range.clone(), new_range: offset_range, @@ -190,8 +188,8 @@ impl FoldMap { T: ToOffset, { let buffer = self.buffer.read(ctx); - let start = buffer.anchor_before(range.start.to_offset(buffer)?)?; - let end = buffer.anchor_after(range.end.to_offset(buffer)?)?; + let start = buffer.anchor_before(range.start.to_offset(buffer)); + let end = buffer.anchor_after(range.end.to_offset(buffer)); Ok(self.folds.filter::<_, usize>(move |summary| { start.cmp(&summary.max_end, buffer).unwrap() == Ordering::Less && end.cmp(&summary.min_start, buffer).unwrap() == Ordering::Greater @@ -215,7 +213,7 @@ impl FoldMap { false } - pub fn to_buffer_offset(&self, point: DisplayPoint, ctx: &AppContext) -> Result { + pub fn to_buffer_offset(&self, point: DisplayPoint, ctx: &AppContext) -> usize { let transforms = self.sync(ctx); let mut cursor = transforms.cursor::(); cursor.seek(&point, SeekBias::Right, &()); @@ -305,11 +303,11 @@ impl FoldMap { edit.new_range.end = ((edit.new_range.start + edit.old_extent()) as isize + delta) as usize; - let anchor = buffer.anchor_before(edit.new_range.start).unwrap(); + let anchor = buffer.anchor_before(edit.new_range.start); let mut folds_cursor = self.folds.cursor::<_, ()>(); folds_cursor.seek(&Fold(anchor..Anchor::End), SeekBias::Left, buffer); let mut folds = folds_cursor - .map(|f| f.0.start.to_offset(buffer).unwrap()..f.0.end.to_offset(buffer).unwrap()) + .map(|f| f.0.start.to_offset(buffer)..f.0.end.to_offset(buffer)) .peekable(); while folds @@ -319,7 +317,7 @@ impl FoldMap { let mut fold = folds.next().unwrap(); let sum = new_transforms.summary(); - assert!(fold.start >= sum.buffer.chars); + assert!(fold.start >= sum.buffer.bytes); while folds .peek() @@ -331,8 +329,8 @@ impl FoldMap { } } - if fold.start > sum.buffer.chars { - let text_summary = buffer.text_summary_for_range(sum.buffer.chars..fold.start); + if fold.start > sum.buffer.bytes { + let text_summary = buffer.text_summary_for_range(sum.buffer.bytes..fold.start); new_transforms.push( Transform { summary: TransformSummary { @@ -350,7 +348,6 @@ impl FoldMap { Transform { summary: TransformSummary { display: TextSummary { - chars: 1, bytes: '…'.len_utf8(), lines: Point::new(0, 1), first_line_len: 1, @@ -366,9 +363,9 @@ impl FoldMap { } let sum = new_transforms.summary(); - if sum.buffer.chars < edit.new_range.end { + if sum.buffer.bytes < edit.new_range.end { let text_summary = - buffer.text_summary_for_range(sum.buffer.chars..edit.new_range.end); + buffer.text_summary_for_range(sum.buffer.bytes..edit.new_range.end); new_transforms.push( Transform { summary: TransformSummary { @@ -439,15 +436,15 @@ impl FoldMapSnapshot { let mut cursor = self.transforms.cursor::(); cursor.seek(&point, SeekBias::Right, &()); let overshoot = point.0 - cursor.start().display.lines; - let mut offset = cursor.start().display.chars; + let mut offset = cursor.start().display.bytes; if !overshoot.is_zero() { let transform = cursor .item() .ok_or_else(|| anyhow!("display point {:?} is out of range", point))?; assert!(transform.display_text.is_none()); let end_buffer_offset = - (cursor.start().buffer.lines + overshoot).to_offset(self.buffer.read(ctx))?; - offset += end_buffer_offset - cursor.start().buffer.chars; + (cursor.start().buffer.lines + overshoot).to_offset(self.buffer.read(ctx)); + offset += end_buffer_offset - cursor.start().buffer.bytes; } Ok(DisplayOffset(offset)) } @@ -620,7 +617,7 @@ impl<'a> Iterator for Chars<'a> { return Some(c); } - while self.offset == self.cursor.end().display.chars && self.cursor.item().is_some() { + while self.offset == self.cursor.end().display.bytes && self.cursor.item().is_some() { self.cursor.next(); } @@ -629,11 +626,10 @@ impl<'a> Iterator for Chars<'a> { self.offset += 1; Some(c) } else { - let overshoot = self.offset - self.cursor.start().display.chars; - let buffer_start = self.cursor.start().buffer.chars + overshoot; - let char_count = self.cursor.end().buffer.chars - buffer_start; - self.buffer_chars = - Some(self.buffer.chars_at(buffer_start).unwrap().take(char_count)); + 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() } }) @@ -651,7 +647,7 @@ pub struct DisplayOffset(usize); impl<'a> sum_tree::Dimension<'a, TransformSummary> for DisplayOffset { fn add_summary(&mut self, summary: &'a TransformSummary) { - self.0 += &summary.display.chars; + self.0 += &summary.display.bytes; } } @@ -663,7 +659,7 @@ impl<'a> sum_tree::Dimension<'a, TransformSummary> for Point { impl<'a> sum_tree::Dimension<'a, TransformSummary> for usize { fn add_summary(&mut self, summary: &'a TransformSummary) { - *self += &summary.buffer.chars; + *self += &summary.buffer.bytes; } } @@ -816,7 +812,7 @@ mod tests { let fold_ranges = map .folds_in_range(Point::new(1, 0)..Point::new(1, 3), app.as_ref()) .unwrap() - .map(|fold| fold.start.to_point(buffer).unwrap()..fold.end.to_point(buffer).unwrap()) + .map(|fold| fold.start.to_point(buffer)..fold.end.to_point(buffer)) .collect::>(); assert_eq!( fold_ranges, @@ -830,7 +826,7 @@ mod tests { #[gpui::test] fn test_random_folds(app: &mut gpui::MutableAppContext) { use crate::editor::ToPoint; - use crate::util::{byte_range_for_char_range, RandomCharIter}; + use crate::util::RandomCharIter; use rand::prelude::*; use std::env; @@ -905,10 +901,7 @@ mod tests { expected_buffer_rows.extend((fold_end.row + 1..=next_row).rev()); next_row = fold_start.row; - expected_text.replace_range( - byte_range_for_char_range(&expected_text, fold_range.start..fold_range.end), - "…", - ); + expected_text.replace_range(fold_range.start..fold_range.end, "…"); } expected_buffer_rows.extend((0..=next_row).rev()); expected_buffer_rows.reverse(); @@ -925,13 +918,13 @@ mod tests { let mut display_offset = DisplayOffset(0); for c in expected_text.chars() { let buffer_point = map.to_buffer_point(display_point, app.as_ref()); - let buffer_offset = buffer_point.to_offset(buffer).unwrap(); + let buffer_offset = buffer_point.to_offset(buffer); assert_eq!( map.to_display_point(buffer_point, app.as_ref()), display_point ); assert_eq!( - map.to_buffer_offset(display_point, app.as_ref()).unwrap(), + map.to_buffer_offset(display_point, app.as_ref()), buffer_offset ); assert_eq!( @@ -988,8 +981,8 @@ mod tests { } for fold_range in map.merged_fold_ranges(app.as_ref()) { - let display_point = map - .to_display_point(fold_range.start.to_point(buffer).unwrap(), app.as_ref()); + let display_point = + map.to_display_point(fold_range.start.to_point(buffer), app.as_ref()); assert!(map.is_line_folded(display_point.row(), app.as_ref())); } @@ -1001,8 +994,8 @@ mod tests { .items() .into_iter() .filter(|fold| { - let start = buffer.anchor_before(start).unwrap(); - let end = buffer.anchor_after(end).unwrap(); + let start = buffer.anchor_before(start); + let end = buffer.anchor_after(end); start.cmp(&fold.0.end, buffer).unwrap() == Ordering::Less && end.cmp(&fold.0.start, buffer).unwrap() == Ordering::Greater }) @@ -1069,9 +1062,7 @@ mod tests { folds.sort_by(|a, b| a.0.cmp(&b.0, buffer).unwrap()); let mut fold_ranges = folds .iter() - .map(|fold| { - fold.0.start.to_offset(buffer).unwrap()..fold.0.end.to_offset(buffer).unwrap() - }) + .map(|fold| fold.0.start.to_offset(buffer)..fold.0.end.to_offset(buffer)) .peekable(); let mut merged_ranges = Vec::new(); @@ -1097,7 +1088,7 @@ mod tests { let transforms = self.sync(ctx); let buffer = self.buffer.read(ctx); assert_eq!( - transforms.summary().buffer.chars, + transforms.summary().buffer.bytes, buffer.len(), "transform tree does not match buffer's length" ); diff --git a/zed/src/editor/display_map/mod.rs b/zed/src/editor/display_map/mod.rs index 7ee540bd2e094522f9f2fd417ed7a2b68f5a9a4f..dbc0a3005c4c342cf7dd3ca7da6f55af1f02a5f0 100644 --- a/zed/src/editor/display_map/mod.rs +++ b/zed/src/editor/display_map/mod.rs @@ -118,9 +118,10 @@ impl DisplayMap { bias: Bias, app: &AppContext, ) -> Result { - self.buffer + Ok(self + .buffer .read(app) - .anchor_before(point.to_buffer_point(self, bias, app)?) + .anchor_before(point.to_buffer_point(self, bias, app)?)) } pub fn anchor_after( @@ -129,9 +130,10 @@ impl DisplayMap { bias: Bias, app: &AppContext, ) -> Result { - self.buffer + Ok(self + .buffer .read(app) - .anchor_after(point.to_buffer_point(self, bias, app)?) + .anchor_after(point.to_buffer_point(self, bias, app)?)) } } @@ -222,8 +224,9 @@ impl DisplayPoint { } pub fn to_buffer_offset(self, map: &DisplayMap, bias: Bias, ctx: &AppContext) -> Result { - map.fold_map - .to_buffer_offset(self.collapse_tabs(&map, bias, ctx)?.0, ctx) + Ok(map + .fold_map + .to_buffer_offset(self.collapse_tabs(&map, bias, ctx)?.0, ctx)) } fn expand_tabs(self, map: &DisplayMap, ctx: &AppContext) -> Result { @@ -253,7 +256,7 @@ impl Point { impl Anchor { pub fn to_display_point(&self, map: &DisplayMap, app: &AppContext) -> Result { - self.to_point(map.buffer.read(app))? + self.to_point(map.buffer.read(app)) .to_display_point(map, app) } } diff --git a/zed/src/util.rs b/zed/src/util.rs index 18227dc2d228bf968d233c996cf7a0381e3e5ffa..6015ce7e0a4acc4a9929cd62bbda8db2e833f649 100644 --- a/zed/src/util.rs +++ b/zed/src/util.rs @@ -1,5 +1,5 @@ use rand::prelude::*; -use std::{cmp::Ordering, ops::Range}; +use std::cmp::Ordering; pub fn post_inc(value: &mut usize) -> usize { let prev = *value; @@ -33,6 +33,7 @@ where pub struct RandomCharIter(T); impl RandomCharIter { + #[cfg(test)] pub fn new(rng: T) -> Self { Self(rng) }