diff --git a/crates/buffer/src/anchor.rs b/crates/buffer/src/anchor.rs index da47d8924b..bb0e7b386a 100644 --- a/crates/buffer/src/anchor.rs +++ b/crates/buffer/src/anchor.rs @@ -113,6 +113,14 @@ impl Anchor { buffer.anchor_after(self) } } + + pub fn summary<'a, D, C>(&self, content: C) -> D + where + D: TextDimension<'a>, + C: Into>, + { + content.into().summary_for_anchor(self) + } } impl AnchorMap { @@ -124,24 +132,15 @@ impl AnchorMap { self.entries.len() } - pub fn offsets<'a>( - &'a self, - content: impl Into> + 'a, - ) -> impl Iterator + 'a { + pub fn iter<'a, D, C>(&'a self, content: C) -> impl Iterator + 'a + where + D: 'a + TextDimension<'a>, + C: 'a + Into>, + { let content = content.into(); content .summaries_for_anchors(self) - .map(move |(sum, value)| (sum.bytes, value)) - } - - pub fn points<'a>( - &'a self, - content: impl Into> + 'a, - ) -> impl Iterator + 'a { - let content = content.into(); - content - .summaries_for_anchors(self) - .map(move |(sum, value)| (sum.lines, value)) + .map(move |(sum, value)| (sum, value)) } } @@ -154,18 +153,12 @@ impl AnchorSet { self.0.len() } - pub fn offsets<'a>( - &'a self, - content: impl Into> + 'a, - ) -> impl Iterator + 'a { - self.0.offsets(content).map(|(offset, _)| offset) - } - - pub fn points<'a>( - &'a self, - content: impl Into> + 'a, - ) -> impl Iterator + 'a { - self.0.points(content).map(|(point, _)| point) + pub fn iter<'a, D, C>(&'a self, content: C) -> impl Iterator + 'a + where + D: 'a + TextDimension<'a>, + C: 'a + Into>, + { + self.0.iter(content).map(|(position, _)| position) } } diff --git a/crates/buffer/src/lib.rs b/crates/buffer/src/lib.rs index 85847468c8..a01430d309 100644 --- a/crates/buffer/src/lib.rs +++ b/crates/buffer/src/lib.rs @@ -1719,7 +1719,10 @@ impl<'a> Content<'a> { result } - fn summary_for_anchor(&self, anchor: &Anchor) -> TextSummary { + fn summary_for_anchor(&self, anchor: &Anchor) -> D + where + D: TextDimension<'a>, + { let cx = Some(anchor.version.clone()); let mut cursor = self.fragments.cursor::<(VersionedFullOffset, usize)>(); cursor.seek( @@ -1735,16 +1738,19 @@ impl<'a> Content<'a> { self.text_summary_for_range(0..cursor.start().1 + overshoot) } - fn text_summary_for_range(&self, range: Range) -> TextSummary { + fn text_summary_for_range(&self, range: Range) -> D + where + D: TextDimension<'a>, + { self.visible_text.cursor(range.start).summary(range.end) } - fn summaries_for_anchors( - &self, - map: &'a AnchorMap, - ) -> impl Iterator { + fn summaries_for_anchors(&self, map: &'a AnchorMap) -> impl Iterator + where + D: TextDimension<'a>, + { let cx = Some(map.version.clone()); - let mut summary = TextSummary::default(); + let mut summary = D::default(); let mut rope_cursor = self.visible_text.cursor(0); let mut cursor = self.fragments.cursor::<(VersionedFullOffset, usize)>(); map.entries.iter().map(move |((offset, bias), value)| { @@ -1754,7 +1760,7 @@ impl<'a> Content<'a> { } else { 0 }; - summary += rope_cursor.summary::(cursor.start().1 + overshoot); + summary.add_assign(&rope_cursor.summary(cursor.start().1 + overshoot)); (summary.clone(), value) }) } @@ -1928,7 +1934,7 @@ impl<'a> Content<'a> { fn point_for_offset(&self, offset: usize) -> Result { if offset <= self.len() { - Ok(self.text_summary_for_range(0..offset).lines) + Ok(self.text_summary_for_range(0..offset)) } else { Err(anyhow!("offset out of bounds")) } @@ -2316,13 +2322,13 @@ impl ToOffset for usize { impl ToOffset for Anchor { fn to_offset<'a>(&self, content: impl Into>) -> usize { - content.into().summary_for_anchor(self).bytes + content.into().summary_for_anchor(self) } } impl<'a> ToOffset for &'a Anchor { fn to_offset<'b>(&self, content: impl Into>) -> usize { - content.into().summary_for_anchor(self).bytes + content.into().summary_for_anchor(self) } } @@ -2332,7 +2338,7 @@ pub trait ToPoint { impl ToPoint for Anchor { fn to_point<'a>(&self, content: impl Into>) -> Point { - content.into().summary_for_anchor(self).lines + content.into().summary_for_anchor(self) } } diff --git a/crates/buffer/src/selection.rs b/crates/buffer/src/selection.rs index 8d804c522d..dfd4522368 100644 --- a/crates/buffer/src/selection.rs +++ b/crates/buffer/src/selection.rs @@ -36,19 +36,29 @@ pub struct SelectionState { pub goal: SelectionGoal, } +impl Selection { + pub fn head(&self) -> T { + if self.reversed { + self.start.clone() + } else { + self.end.clone() + } + } + + pub fn tail(&self) -> T { + if self.reversed { + self.end.clone() + } else { + self.start.clone() + } + } +} + impl Selection { pub fn is_empty(&self) -> bool { self.start == self.end } - pub fn head(&self) -> T { - if self.reversed { - self.start - } else { - self.end - } - } - pub fn set_head(&mut self, head: T) { if head.cmp(&self.tail()) < Ordering::Equal { if !self.reversed { @@ -65,14 +75,6 @@ impl Selection { } } - pub fn tail(&self) -> T { - if self.reversed { - self.end - } else { - self.start - } - } - pub fn point_range(&self, buffer: &Buffer) -> Range { let start = self.start.to_point(buffer); let end = self.end.to_point(buffer); diff --git a/crates/editor/src/lib.rs b/crates/editor/src/lib.rs index 275fa804b1..7cccd0438a 100644 --- a/crates/editor/src/lib.rs +++ b/crates/editor/src/lib.rs @@ -21,7 +21,8 @@ use smallvec::SmallVec; use smol::Timer; use std::{ cell::RefCell, - cmp, iter, mem, + cmp::{self, Ordering}, + iter, mem, ops::{Range, RangeInclusive}, rc::Rc, sync::Arc, @@ -293,7 +294,7 @@ pub struct Editor { buffer: ModelHandle, display_map: ModelHandle, selection_set_id: SelectionSetId, - pending_selection: Option>, + pending_selection: Option>, next_selection_id: usize, add_selections_state: Option, autoclose_stack: Vec, @@ -618,10 +619,11 @@ impl Editor { } let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); - let cursor = position.to_buffer_point(&display_map, Bias::Left); + let buffer = self.buffer.read(cx); + let cursor = buffer.anchor_before(position.to_buffer_point(&display_map, Bias::Left)); let selection = Selection { id: post_inc(&mut self.next_selection_id), - start: cursor, + start: cursor.clone(), end: cursor, reversed: false, goal: SelectionGoal::None, @@ -642,9 +644,22 @@ impl Editor { cx: &mut ViewContext, ) { let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); - let cursor = position.to_buffer_point(&display_map, Bias::Left); - if let Some(selection) = self.pending_selection.as_mut() { - selection.set_head(cursor); + if let Some(pending_selection) = self.pending_selection.as_mut() { + let buffer = self.buffer.read(cx); + let cursor = buffer.anchor_before(position.to_buffer_point(&display_map, Bias::Left)); + if cursor.cmp(&pending_selection.tail(), buffer).unwrap() < Ordering::Equal { + if !pending_selection.reversed { + pending_selection.end = pending_selection.start.clone(); + pending_selection.reversed = true; + } + pending_selection.start = cursor; + } else { + if pending_selection.reversed { + pending_selection.start = pending_selection.end.clone(); + pending_selection.reversed = false; + } + pending_selection.end = cursor; + } } else { log::error!("update_selection dispatched with no pending selection"); return; @@ -655,12 +670,8 @@ impl Editor { } fn end_selection(&mut self, cx: &mut ViewContext) { - if let Some(selection) = self.pending_selection.take() { - let mut selections = self.selections::(cx).collect::>(); - let ix = self.selection_insertion_index(&selections, selection.start); - selections.insert(ix, selection); - self.update_selections(selections, false, cx); - } + let selections = self.selections::(cx).collect::>(); + self.update_selections(selections, false, cx); } pub fn is_selecting(&self) -> bool { @@ -669,13 +680,27 @@ impl Editor { pub fn cancel(&mut self, _: &Cancel, cx: &mut ViewContext) { if let Some(pending_selection) = self.pending_selection.take() { + let buffer = self.buffer.read(cx); + let pending_selection = Selection { + id: pending_selection.id, + start: pending_selection.start.to_point(buffer), + end: pending_selection.end.to_point(buffer), + reversed: pending_selection.reversed, + goal: pending_selection.goal, + }; if self.selections::(cx).next().is_none() { self.update_selections(vec![pending_selection], true, cx); } } else { - let selection_count = self.selection_count(cx); let selections = self.selections::(cx); - let mut oldest_selection = selections.min_by_key(|s| s.id).unwrap().clone(); + let mut selection_count = 0; + let mut oldest_selection = selections + .min_by_key(|s| { + selection_count += 1; + s.id + }) + .unwrap() + .clone(); if selection_count == 1 { oldest_selection.start = oldest_selection.head().clone(); oldest_selection.end = oldest_selection.head().clone(); @@ -970,7 +995,6 @@ impl Editor { } fn skip_autoclose_end(&mut self, text: &str, cx: &mut ViewContext) -> bool { - let old_selection_count = self.selection_count(cx); let old_selections = self.selections::(cx).collect::>(); let autoclose_pair_state = if let Some(autoclose_pair_state) = self.autoclose_stack.last() { autoclose_pair_state @@ -981,7 +1005,7 @@ impl Editor { return false; } - debug_assert_eq!(old_selection_count, autoclose_pair_state.ranges.len()); + debug_assert_eq!(old_selections.len(), autoclose_pair_state.ranges.len()); let buffer = self.buffer.read(cx); if old_selections @@ -2220,21 +2244,6 @@ impl Editor { .map(|(set_id, _)| *set_id) } - pub fn last_selection(&self, cx: &AppContext) -> Selection { - if let Some(pending_selection) = self.pending_selection.as_ref() { - pending_selection.clone() - } else { - let buffer = self.buffer.read(cx); - let last_selection = buffer - .selection_set(self.selection_set_id) - .unwrap() - .selections::(buffer) - .max_by_key(|s| s.id) - .unwrap(); - last_selection - } - } - pub fn selections_in_range<'a>( &'a self, set_id: SelectionSetId, @@ -2251,10 +2260,14 @@ impl Editor { let start = range.start.to_buffer_point(&display_map, Bias::Left); let start_index = self.selection_insertion_index(&selections, start); let pending_selection = if set_id.replica_id == self.buffer.read(cx).replica_id() { - self.pending_selection.as_ref().and_then(|s| { - let selection_range = s.display_range(&display_map); - if selection_range.start <= range.end || selection_range.end <= range.end { - Some(selection_range) + self.pending_selection.as_ref().and_then(|pending| { + let mut selection_start = pending.start.to_display_point(&display_map, Bias::Left); + let mut selection_end = pending.end.to_display_point(&display_map, Bias::Left); + if pending.reversed { + mem::swap(&mut selection_start, &mut selection_end); + } + if selection_start <= range.end || selection_end <= range.end { + Some(selection_start..selection_end) } else { None } @@ -2283,25 +2296,46 @@ impl Editor { } } - fn selections<'a, D>( - &mut self, - cx: &'a mut ViewContext, - ) -> impl 'a + Iterator> + pub fn selections<'a, D>(&self, cx: &'a AppContext) -> impl 'a + Iterator> where - D: 'a + TextDimension<'a>, + D: 'a + TextDimension<'a> + Ord, { - self.end_selection(cx); let buffer = self.buffer.read(cx); - buffer + let mut selections = buffer .selection_set(self.selection_set_id) .unwrap() .selections::(buffer) - } + .peekable(); + let mut pending_selection = self.pending_selection.clone().map(|selection| Selection { + id: selection.id, + start: selection.start.summary::(buffer), + end: selection.end.summary::(buffer), + reversed: selection.reversed, + goal: selection.goal, + }); + iter::from_fn(move || { + if let Some(pending) = pending_selection.as_mut() { + while let Some(next_selection) = selections.peek() { + if pending.start <= next_selection.end && pending.end >= next_selection.start { + let next_selection = selections.next().unwrap(); + if next_selection.start < pending.start { + pending.start = next_selection.start; + } + if next_selection.end > pending.end { + pending.end = next_selection.end; + } + } else if next_selection.end < pending.start { + return selections.next(); + } else { + break; + } + } - fn selection_count(&mut self, cx: &mut ViewContext) -> usize { - self.end_selection(cx); - let buffer = self.buffer.read(cx); - buffer.selection_set(self.selection_set_id).unwrap().len() + pending_selection.take() + } else { + selections.next() + } + }) } fn update_selections( @@ -2329,6 +2363,7 @@ impl Editor { } } + self.pending_selection = None; self.add_selections_state = None; self.select_larger_syntax_node_stack.clear(); while let Some(autoclose_pair_state) = self.autoclose_stack.last() { diff --git a/crates/language/src/lib.rs b/crates/language/src/lib.rs index fb259a704d..7e38cc0c16 100644 --- a/crates/language/src/lib.rs +++ b/crates/language/src/lib.rs @@ -830,9 +830,14 @@ impl Buffer { for request in autoindent_requests { let old_to_new_rows = request .edited - .points(&request.before_edit) + .iter::(&request.before_edit) .map(|point| point.row) - .zip(request.edited.points(&snapshot).map(|point| point.row)) + .zip( + request + .edited + .iter::(&snapshot) + .map(|point| point.row), + ) .collect::>(); let mut old_suggestions = HashMap::::default(); diff --git a/crates/workspace/src/items.rs b/crates/workspace/src/items.rs index 8692b072d4..d6c20c8fc0 100644 --- a/crates/workspace/src/items.rs +++ b/crates/workspace/src/items.rs @@ -1,7 +1,7 @@ use super::{Item, ItemView}; use crate::{status_bar::StatusItemView, Settings}; use anyhow::Result; -use buffer::{Point, ToOffset}; +use buffer::{Point, Selection, ToPoint}; use editor::{Editor, EditorSettings, Event}; use gpui::{ elements::*, fonts::TextStyle, AppContext, Entity, ModelHandle, RenderContext, Subscription, @@ -181,17 +181,21 @@ impl CursorPosition { fn update_position(&mut self, editor: ViewHandle, cx: &mut ViewContext) { let editor = editor.read(cx); + let buffer = editor.buffer().read(cx); - let last_selection = editor.last_selection(cx); - self.position = Some(last_selection.head()); - if last_selection.is_empty() { - self.selected_count = 0; - } else { - let buffer = editor.buffer().read(cx); - let start = last_selection.start.to_offset(buffer); - let end = last_selection.end.to_offset(buffer); - self.selected_count = end - start; + self.selected_count = 0; + let mut last_selection: Option> = None; + for selection in editor.selections::(cx) { + self.selected_count += selection.end - selection.start; + if last_selection + .as_ref() + .map_or(true, |last_selection| selection.id > last_selection.id) + { + last_selection = Some(selection); + } } + self.position = last_selection.map(|s| s.head().to_point(buffer)); + cx.notify(); } }