@@ -410,7 +410,7 @@ pub struct Editor {
add_selections_state: Option<AddSelectionsState>,
select_next_state: Option<SelectNextState>,
selection_history: SelectionHistory,
- autoclose_stack: InvalidationStack<BracketPairState>,
+ autoclose_regions: Vec<AutocloseRegion>,
snippet_stack: InvalidationStack<SnippetState>,
select_larger_syntax_node_stack: Vec<Box<[Selection<usize>]>>,
ime_transaction: Option<TransactionId>,
@@ -569,8 +569,9 @@ struct SelectNextState {
done: bool,
}
-struct BracketPairState {
- ranges: Vec<Range<Anchor>>,
+struct AutocloseRegion {
+ selection_id: usize,
+ range: Range<Anchor>,
pair: BracketPair,
}
@@ -1010,7 +1011,7 @@ impl Editor {
add_selections_state: None,
select_next_state: None,
selection_history: Default::default(),
- autoclose_stack: Default::default(),
+ autoclose_regions: Default::default(),
snippet_stack: Default::default(),
select_larger_syntax_node_stack: Vec::new(),
ime_transaction: Default::default(),
@@ -1401,8 +1402,7 @@ impl Editor {
self.add_selections_state = None;
self.select_next_state = None;
self.select_larger_syntax_node_stack.clear();
- self.autoclose_stack
- .invalidate(&self.selections.disjoint_anchors(), buffer);
+ self.invalidate_autoclose_regions(&self.selections.disjoint_anchors(), buffer);
self.snippet_stack
.invalidate(&self.selections.disjoint_anchors(), buffer);
self.take_rename(false, cx);
@@ -1849,15 +1849,158 @@ impl Editor {
return;
}
- if !self.skip_autoclose_end(text, cx) {
- self.transact(cx, |this, cx| {
- if !this.surround_with_bracket_pair(text, cx) {
- this.insert(text, cx);
- this.autoclose_bracket_pairs(cx);
+ let text: Arc<str> = text.into();
+ let selections = self.selections.all_adjusted(cx);
+ let mut edits = Vec::new();
+ let mut new_selections = Vec::with_capacity(selections.len());
+ let mut new_autoclose_regions = Vec::new();
+ let snapshot = self.buffer.read(cx).read(cx);
+
+ for (selection, autoclose_region) in
+ self.selections_with_autoclose_regions(selections, &snapshot)
+ {
+ if let Some(language) = snapshot.language_at(selection.head()) {
+ // Determine if the inserted text matches the opening or closing
+ // bracket of any of this language's bracket pairs.
+ let mut bracket_pair = None;
+ let mut is_bracket_pair_start = false;
+ for pair in language.brackets() {
+ if pair.start.ends_with(text.as_ref()) {
+ bracket_pair = Some(pair.clone());
+ is_bracket_pair_start = true;
+ break;
+ } else if pair.end.as_str() == text.as_ref() {
+ bracket_pair = Some(pair.clone());
+ break;
+ }
}
- });
- self.trigger_completion_on_input(text, cx);
+
+ if let Some(bracket_pair) = bracket_pair {
+ if selection.is_empty() {
+ if is_bracket_pair_start {
+ let prefix_len = bracket_pair.start.len() - text.len();
+
+ // If the inserted text is a suffix of an opening bracket and the
+ // selection is preceded by the rest of the opening bracket, then
+ // insert the closing bracket.
+ let should_autoclose = selection.start.column > (prefix_len as u32)
+ && snapshot.contains_str_at(
+ Point::new(
+ selection.start.row,
+ selection.start.column - (prefix_len as u32),
+ ),
+ &bracket_pair.start[..prefix_len],
+ )
+ && snapshot
+ .chars_at(selection.start)
+ .next()
+ .map_or(true, |c| language.should_autoclose_before(c));
+ if should_autoclose {
+ let anchor = snapshot.anchor_before(selection.end);
+ new_selections
+ .push((selection.map(|_| anchor.clone()), text.len()));
+ new_autoclose_regions.push((
+ anchor.clone(),
+ text.len(),
+ selection.id,
+ bracket_pair.clone(),
+ ));
+ edits.push((
+ selection.range(),
+ format!("{}{}", text, bracket_pair.end).into(),
+ ));
+ continue;
+ }
+ } else if let Some(region) = autoclose_region {
+ // If the selection is followed by an auto-inserted closing bracket,
+ // then don't insert anything else; just move the selection past the
+ // closing bracket.
+ let should_skip = selection.end == region.range.end.to_point(&snapshot);
+ if should_skip {
+ let anchor = snapshot.anchor_after(selection.end);
+ new_selections.push((
+ selection.map(|_| anchor.clone()),
+ region.pair.end.len(),
+ ));
+ continue;
+ }
+ }
+ }
+ // If an opening bracket is typed while text is selected, then
+ // surround that text with the bracket pair.
+ else if is_bracket_pair_start {
+ edits.push((selection.start..selection.start, text.clone()));
+ edits.push((
+ selection.end..selection.end,
+ bracket_pair.end.as_str().into(),
+ ));
+ new_selections.push((
+ Selection {
+ id: selection.id,
+ start: snapshot.anchor_after(selection.start),
+ end: snapshot.anchor_before(selection.end),
+ reversed: selection.reversed,
+ goal: selection.goal,
+ },
+ 0,
+ ));
+ continue;
+ }
+ }
+ }
+
+ // If not handling any auto-close operation, then just replace the selected
+ // text with the given input and move the selection to the end of the
+ // newly inserted text.
+ let anchor = snapshot.anchor_after(selection.end);
+ new_selections.push((selection.map(|_| anchor.clone()), 0));
+ edits.push((selection.start..selection.end, text.clone()));
}
+
+ drop(snapshot);
+ self.transact(cx, |this, cx| {
+ this.buffer.update(cx, |buffer, cx| {
+ buffer.edit(edits, Some(AutoindentMode::EachLine), cx);
+ });
+
+ let new_anchor_selections = new_selections.iter().map(|e| &e.0);
+ let new_selection_deltas = new_selections.iter().map(|e| e.1);
+ let snapshot = this.buffer.read(cx).read(cx);
+ let new_selections = resolve_multiple::<usize, _>(new_anchor_selections, &snapshot)
+ .zip(new_selection_deltas)
+ .map(|(selection, delta)| selection.map(|e| e + delta))
+ .collect::<Vec<_>>();
+
+ let mut i = 0;
+ for (position, delta, selection_id, pair) in new_autoclose_regions {
+ let position = position.to_offset(&snapshot) + delta;
+ let start = snapshot.anchor_before(position);
+ let end = snapshot.anchor_after(position);
+ while let Some(existing_state) = this.autoclose_regions.get(i) {
+ match existing_state.range.start.cmp(&start, &snapshot) {
+ Ordering::Less => i += 1,
+ Ordering::Greater => break,
+ Ordering::Equal => match end.cmp(&existing_state.range.end, &snapshot) {
+ Ordering::Less => i += 1,
+ Ordering::Equal => break,
+ Ordering::Greater => break,
+ },
+ }
+ }
+ this.autoclose_regions.insert(
+ i,
+ AutocloseRegion {
+ selection_id,
+ range: start..end,
+ pair,
+ },
+ );
+ }
+
+ drop(snapshot);
+ this.change_selections(None, cx, |s| s.select(new_selections));
+ this.trigger_completion_on_input(&text, cx);
+ });
}
pub fn newline(&mut self, _: &Newline, cx: &mut ViewContext<Self>) {
@@ -2029,232 +2172,89 @@ impl Editor {
}
}
- fn surround_with_bracket_pair(&mut self, text: &str, cx: &mut ViewContext<Self>) -> bool {
- let snapshot = self.buffer.read(cx).snapshot(cx);
- if let Some(pair) = snapshot
- .language()
- .and_then(|language| language.brackets().iter().find(|b| b.start == text))
- .cloned()
- {
- if self
- .selections
- .all::<usize>(cx)
- .iter()
- .any(|selection| selection.is_empty())
- {
- return false;
- }
-
- let mut selections = self.selections.disjoint_anchors().to_vec();
- for selection in &mut selections {
- selection.end = selection.end.bias_left(&snapshot);
- }
- drop(snapshot);
-
- self.buffer.update(cx, |buffer, cx| {
- let pair_start: Arc<str> = pair.start.clone().into();
- let pair_end: Arc<str> = pair.end.clone().into();
- buffer.edit(
- selections.iter().flat_map(|s| {
- [
- (s.start.clone()..s.start.clone(), pair_start.clone()),
- (s.end.clone()..s.end.clone(), pair_end.clone()),
- ]
- }),
- None,
- cx,
- );
- });
-
- let snapshot = self.buffer.read(cx).read(cx);
- for selection in &mut selections {
- selection.end = selection.end.bias_right(&snapshot);
- }
- drop(snapshot);
-
- self.change_selections(None, cx, |s| s.select_anchors(selections));
- true
- } else {
- false
- }
- }
-
- fn autoclose_bracket_pairs(&mut self, cx: &mut ViewContext<Self>) {
+ /// If any empty selections is touching the start of its innermost containing autoclose
+ /// region, expand it to select the brackets.
+ fn select_autoclose_pair(&mut self, cx: &mut ViewContext<Self>) {
let selections = self.selections.all::<usize>(cx);
- let mut bracket_pair_state = None;
- let mut new_selections = None;
- self.buffer.update(cx, |buffer, cx| {
- let mut snapshot = buffer.snapshot(cx);
- let left_biased_selections = selections
- .iter()
- .map(|selection| selection.map(|p| snapshot.anchor_before(p)))
- .collect::<Vec<_>>();
-
- let autoclose_pair = snapshot.language().and_then(|language| {
- let first_selection_start = selections.first().unwrap().start;
- let pair = language.brackets().iter().find(|pair| {
- pair.close
- && snapshot.contains_str_at(
- first_selection_start.saturating_sub(pair.start.len()),
- &pair.start,
- )
- });
- pair.and_then(|pair| {
- let should_autoclose = selections.iter().all(|selection| {
- // Ensure all selections are parked at the end of a pair start.
- if snapshot.contains_str_at(
- selection.start.saturating_sub(pair.start.len()),
- &pair.start,
- ) {
- snapshot
- .chars_at(selection.start)
- .next()
- .map_or(true, |c| language.should_autoclose_before(c))
- } else {
- false
+ let buffer = self.buffer.read(cx).read(cx);
+ let mut new_selections = Vec::new();
+ for (mut selection, region) in self.selections_with_autoclose_regions(selections, &buffer) {
+ if let (Some(region), true) = (region, selection.is_empty()) {
+ let mut range = region.range.to_offset(&buffer);
+ if selection.start == range.start {
+ if range.start >= region.pair.start.len() {
+ range.start -= region.pair.start.len();
+ if buffer.contains_str_at(range.start, ®ion.pair.start) {
+ if buffer.contains_str_at(range.end, ®ion.pair.end) {
+ range.end += region.pair.end.len();
+ selection.start = range.start;
+ selection.end = range.end;
+ }
}
- });
-
- if should_autoclose {
- Some(pair.clone())
- } else {
- None
}
- })
- });
-
- if let Some(pair) = autoclose_pair {
- let selection_ranges = selections
- .iter()
- .map(|selection| {
- let start = selection.start.to_offset(&snapshot);
- start..start
- })
- .collect::<SmallVec<[_; 32]>>();
-
- let pair_end: Arc<str> = pair.end.clone().into();
- buffer.edit(
- selection_ranges
- .iter()
- .map(|range| (range.clone(), pair_end.clone())),
- None,
- cx,
- );
- snapshot = buffer.snapshot(cx);
-
- new_selections = Some(
- resolve_multiple::<usize, _>(left_biased_selections.iter(), &snapshot)
- .collect::<Vec<_>>(),
- );
-
- if pair.end.len() == 1 {
- let mut delta = 0;
- bracket_pair_state = Some(BracketPairState {
- ranges: selections
- .iter()
- .map(move |selection| {
- let offset = selection.start + delta;
- delta += 1;
- snapshot.anchor_before(offset)..snapshot.anchor_after(offset)
- })
- .collect(),
- pair,
- });
}
}
- });
-
- if let Some(new_selections) = new_selections {
- self.change_selections(None, cx, |s| {
- s.select(new_selections);
- });
- }
- if let Some(bracket_pair_state) = bracket_pair_state {
- self.autoclose_stack.push(bracket_pair_state);
- }
- }
-
- fn skip_autoclose_end(&mut self, text: &str, cx: &mut ViewContext<Self>) -> bool {
- let buffer = self.buffer.read(cx).snapshot(cx);
- let old_selections = self.selections.all::<usize>(cx);
- let autoclose_pair = if let Some(autoclose_pair) = self.autoclose_stack.last() {
- autoclose_pair
- } else {
- return false;
- };
- if text != autoclose_pair.pair.end {
- return false;
+ new_selections.push(selection);
}
- debug_assert_eq!(old_selections.len(), autoclose_pair.ranges.len());
-
- if old_selections
- .iter()
- .zip(autoclose_pair.ranges.iter().map(|r| r.to_offset(&buffer)))
- .all(|(selection, autoclose_range)| {
- let autoclose_range_end = autoclose_range.end.to_offset(&buffer);
- selection.is_empty() && selection.start == autoclose_range_end
- })
- {
- let new_selections = old_selections
- .into_iter()
- .map(|selection| {
- let cursor = selection.start + 1;
- Selection {
- id: selection.id,
- start: cursor,
- end: cursor,
- reversed: false,
- goal: SelectionGoal::None,
- }
- })
- .collect();
- self.autoclose_stack.pop();
- self.change_selections(Some(Autoscroll::Fit), cx, |s| {
- s.select(new_selections);
- });
- true
- } else {
- false
- }
+ drop(buffer);
+ self.change_selections(None, cx, |selections| selections.select(new_selections));
}
- fn select_autoclose_pair(&mut self, cx: &mut ViewContext<Self>) -> bool {
- let buffer = self.buffer.read(cx).snapshot(cx);
- let old_selections = self.selections.all::<usize>(cx);
- let autoclose_pair = if let Some(autoclose_pair) = self.autoclose_stack.last() {
- autoclose_pair
- } else {
- return false;
- };
+ /// Iterate the given selections, and for each one, find the smallest surrounding
+ /// autoclose region. This uses the ordering of the selections and the autoclose
+ /// regions to avoid repeated comparisons.
+ fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
+ &'a self,
+ selections: impl IntoIterator<Item = Selection<D>>,
+ buffer: &'a MultiBufferSnapshot,
+ ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
+ let mut i = 0;
+ let mut pair_states = self.autoclose_regions.as_slice();
+ selections.into_iter().map(move |selection| {
+ let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
+
+ let mut enclosing = None;
+ while let Some(pair_state) = pair_states.get(i) {
+ if pair_state.range.end.to_offset(buffer) < range.start {
+ pair_states = &pair_states[i + 1..];
+ i = 0;
+ } else if pair_state.range.start.to_offset(buffer) > range.end {
+ break;
+ } else if pair_state.selection_id == selection.id {
+ enclosing = Some(pair_state);
+ i += 1;
+ }
+ }
- debug_assert_eq!(old_selections.len(), autoclose_pair.ranges.len());
+ (selection.clone(), enclosing)
+ })
+ }
- let mut new_selections = Vec::new();
- for (selection, autoclose_range) in old_selections
- .iter()
- .zip(autoclose_pair.ranges.iter().map(|r| r.to_offset(&buffer)))
- {
- if selection.is_empty()
- && autoclose_range.is_empty()
- && selection.start == autoclose_range.start
- {
- new_selections.push(Selection {
- id: selection.id,
- start: selection.start - autoclose_pair.pair.start.len(),
- end: selection.end + autoclose_pair.pair.end.len(),
- reversed: true,
- goal: selection.goal,
- });
- } else {
- return false;
+ /// Remove any autoclose regions that no longer contain their selection.
+ fn invalidate_autoclose_regions(
+ &mut self,
+ mut selections: &[Selection<Anchor>],
+ buffer: &MultiBufferSnapshot,
+ ) {
+ self.autoclose_regions.retain(|state| {
+ let mut i = 0;
+ while let Some(selection) = selections.get(i) {
+ if selection.end.cmp(&state.range.start, buffer).is_lt() {
+ selections = &selections[1..];
+ continue;
+ }
+ if selection.start.cmp(&state.range.end, buffer).is_gt() {
+ break;
+ }
+ if selection.id == state.selection_id {
+ return true;
+ } else {
+ i += 1;
+ }
}
- }
-
- self.change_selections(Some(Autoscroll::Fit), cx, |selections| {
- selections.select(new_selections)
+ false
});
- true
}
fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
@@ -2909,51 +2909,47 @@ impl Editor {
pub fn backspace(&mut self, _: &Backspace, cx: &mut ViewContext<Self>) {
self.transact(cx, |this, cx| {
- if !this.select_autoclose_pair(cx) {
- let mut selections = this.selections.all::<Point>(cx);
- if !this.selections.line_mode {
- let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
- for selection in &mut selections {
- if selection.is_empty() {
- let old_head = selection.head();
- let mut new_head = movement::left(
- &display_map,
- old_head.to_display_point(&display_map),
- )
- .to_point(&display_map);
- if let Some((buffer, line_buffer_range)) = display_map
- .buffer_snapshot
- .buffer_line_for_row(old_head.row)
- {
- let indent_size =
- buffer.indent_size_for_line(line_buffer_range.start.row);
- let language_name =
- buffer.language().map(|language| language.name());
- let indent_len = match indent_size.kind {
- IndentKind::Space => {
- cx.global::<Settings>().tab_size(language_name.as_deref())
- }
- IndentKind::Tab => NonZeroU32::new(1).unwrap(),
- };
- if old_head.column <= indent_size.len && old_head.column > 0 {
- let indent_len = indent_len.get();
- new_head = cmp::min(
- new_head,
- Point::new(
- old_head.row,
- ((old_head.column - 1) / indent_len) * indent_len,
- ),
- );
+ this.select_autoclose_pair(cx);
+ let mut selections = this.selections.all::<Point>(cx);
+ if !this.selections.line_mode {
+ let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
+ for selection in &mut selections {
+ if selection.is_empty() {
+ let old_head = selection.head();
+ let mut new_head =
+ movement::left(&display_map, old_head.to_display_point(&display_map))
+ .to_point(&display_map);
+ if let Some((buffer, line_buffer_range)) = display_map
+ .buffer_snapshot
+ .buffer_line_for_row(old_head.row)
+ {
+ let indent_size =
+ buffer.indent_size_for_line(line_buffer_range.start.row);
+ let language_name = buffer.language().map(|language| language.name());
+ let indent_len = match indent_size.kind {
+ IndentKind::Space => {
+ cx.global::<Settings>().tab_size(language_name.as_deref())
}
+ IndentKind::Tab => NonZeroU32::new(1).unwrap(),
+ };
+ if old_head.column <= indent_size.len && old_head.column > 0 {
+ let indent_len = indent_len.get();
+ new_head = cmp::min(
+ new_head,
+ Point::new(
+ old_head.row,
+ ((old_head.column - 1) / indent_len) * indent_len,
+ ),
+ );
}
-
- selection.set_head(new_head, SelectionGoal::None);
}
+
+ selection.set_head(new_head, SelectionGoal::None);
}
}
-
- this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(selections));
}
+
+ this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(selections));
this.insert("", cx);
});
}
@@ -3957,17 +3953,16 @@ impl Editor {
cx: &mut ViewContext<Self>,
) {
self.transact(cx, |this, cx| {
- if !this.select_autoclose_pair(cx) {
- this.change_selections(Some(Autoscroll::Fit), cx, |s| {
- let line_mode = s.line_mode;
- s.move_with(|map, selection| {
- if selection.is_empty() && !line_mode {
- let cursor = movement::previous_word_start(map, selection.head());
- selection.set_head(cursor, SelectionGoal::None);
- }
- });
+ this.select_autoclose_pair(cx);
+ this.change_selections(Some(Autoscroll::Fit), cx, |s| {
+ let line_mode = s.line_mode;
+ s.move_with(|map, selection| {
+ if selection.is_empty() && !line_mode {
+ let cursor = movement::previous_word_start(map, selection.head());
+ selection.set_head(cursor, SelectionGoal::None);
+ }
});
- }
+ });
this.insert("", cx);
});
}
@@ -3978,17 +3973,16 @@ impl Editor {
cx: &mut ViewContext<Self>,
) {
self.transact(cx, |this, cx| {
- if !this.select_autoclose_pair(cx) {
- this.change_selections(Some(Autoscroll::Fit), cx, |s| {
- let line_mode = s.line_mode;
- s.move_with(|map, selection| {
- if selection.is_empty() && !line_mode {
- let cursor = movement::previous_subword_start(map, selection.head());
- selection.set_head(cursor, SelectionGoal::None);
- }
- });
+ this.select_autoclose_pair(cx);
+ this.change_selections(Some(Autoscroll::Fit), cx, |s| {
+ let line_mode = s.line_mode;
+ s.move_with(|map, selection| {
+ if selection.is_empty() && !line_mode {
+ let cursor = movement::previous_subword_start(map, selection.head());
+ selection.set_head(cursor, SelectionGoal::None);
+ }
});
- }
+ });
this.insert("", cx);
});
}
@@ -6495,12 +6489,6 @@ impl<T> DerefMut for InvalidationStack<T> {
}
}
-impl InvalidationRegion for BracketPairState {
- fn ranges(&self) -> &[Range<Anchor>] {
- &self.ranges
- }
-}
-
impl InvalidationRegion for SnippetState {
fn ranges(&self) -> &[Range<Anchor>] {
&self.ranges[self.active_index]