use crate::{ Vim, motion::{self, Motion}, object::{Object, surrounding_markers}, state::Mode,};use editor::{Bias, MultiBufferOffset, movement};use gpui::{Context, Window};use language::BracketPair; use std::sync::Arc; /// A char-based surround pair definition./// Single source of truth for all supported surround pairs.#[derive(Clone, Copy, Debug, PartialEq)]pub struct SurroundPair { pub open: char, pub close: char,} impl SurroundPair { pub const fn new(open: char, close: char) -> Self { Self { open, close } } pub fn to_bracket_pair(self) -> BracketPair { BracketPair { start: self.open.to_string(), end: self.close.to_string(), close: true, surround: true, newline: false, } } pub fn to_object(self) -> Option<Object> { match self.open { '\'' => Some(Object::Quotes), '`' => Some(Object::BackQuotes), '"' => Some(Object::DoubleQuotes), '|' => Some(Object::VerticalBars), '(' => Some(Object::Parentheses), '[' => Some(Object::SquareBrackets), '{' => Some(Object::CurlyBrackets), '<' => Some(Object::AngleBrackets), _ => None, } }} /// All supported surround pairs - single source of truth.pub const SURROUND_PAIRS: &[SurroundPair] = &[ SurroundPair::new('(', ')'), SurroundPair::new('[', ']'), SurroundPair::new('{', '}'), SurroundPair::new('<', '>'), SurroundPair::new('"', '"'), SurroundPair::new('\'', '\''), SurroundPair::new('`', '`'), SurroundPair::new('|', '|'),]; /// Bracket-only pairs for AnyBrackets matching.const BRACKET_PAIRS: &[SurroundPair] = &[ SurroundPair::new('(', ')'), SurroundPair::new('[', ']'), SurroundPair::new('{', '}'), SurroundPair::new('<', '>'),]; #[derive(Clone, Debug, PartialEq, Eq)]pub enum SurroundsType { Motion(Motion), Object(Object, bool), Selection,} impl Vim { pub fn add_surrounds( &mut self, text: Arc<str>, target: SurroundsType, window: &mut Window, cx: &mut Context<Self>, ) { self.stop_recording(cx); let count = Vim::take_count(cx); let forced_motion = Vim::take_forced_motion(cx); let mode = self.mode; self.update_editor(cx, |_, editor, cx| { let text_layout_details = editor.text_layout_details(window); editor.transact(window, cx, |editor, window, cx| { editor.set_clip_at_line_ends(false, cx); let pair = bracket_pair_for_str_vim(&text); let surround = pair.end != surround_alias((*text).as_ref()); let display_map = editor.display_snapshot(cx); let display_selections = editor.selections.all_adjusted_display(&display_map); let mut edits = Vec::new(); let mut anchors = Vec::new(); for selection in &display_selections { let range = match &target { SurroundsType::Object(object, around) => { object.range(&display_map, selection.clone(), *around, None) } SurroundsType::Motion(motion) => { motion .range( &display_map, selection.clone(), count, &text_layout_details, forced_motion, ) .map(|(mut range, _)| { // The Motion::CurrentLine operation will contain the newline of the current line and leading/trailing whitespace if let Motion::CurrentLine = motion { range.start = motion::first_non_whitespace( &display_map, false, range.start, ); range.end = movement::saturating_right( &display_map, motion::last_non_whitespace(&display_map, range.end, 1), ); } range }) } SurroundsType::Selection => Some(selection.range()), }; if let Some(range) = range { let start = range.start.to_offset(&display_map, Bias::Right); let end = range.end.to_offset(&display_map, Bias::Left); let (start_cursor_str, end_cursor_str) = if mode == Mode::VisualLine { (format!("{}\n", pair.start), format!("\n{}", pair.end)) } else { let maybe_space = if surround { " " } else { "" }; ( format!("{}{}", pair.start, maybe_space), format!("{}{}", maybe_space, pair.end), ) }; let start_anchor = display_map.buffer_snapshot().anchor_before(start); edits.push((start..start, start_cursor_str)); edits.push((end..end, end_cursor_str)); anchors.push(start_anchor..start_anchor); } else { let start_anchor = display_map .buffer_snapshot() .anchor_before(selection.head().to_offset(&display_map, Bias::Left)); anchors.push(start_anchor..start_anchor); } } editor.edit(edits, cx); editor.set_clip_at_line_ends(true, cx); editor.change_selections(Default::default(), window, cx, |s| { if mode == Mode::VisualBlock { s.select_anchor_ranges(anchors.into_iter().take(1)) } else { s.select_anchor_ranges(anchors) } }); }); }); self.switch_mode(Mode::Normal, false, window, cx); } pub fn delete_surrounds( &mut self, text: Arc<str>, window: &mut Window, cx: &mut Context<Self>, ) { self.stop_recording(cx); // only legitimate surrounds can be removed let Some(first_char) = text.chars().next() else { return; }; let Some(surround_pair) = surround_pair_for_char_vim(first_char) else { return; }; let Some(pair_object) = surround_pair.to_object() else { return; }; let pair = surround_pair.to_bracket_pair(); let surround = pair.end != *text; self.update_editor(cx, |_, editor, cx| { editor.transact(window, cx, |editor, window, cx| { editor.set_clip_at_line_ends(false, cx); let display_map = editor.display_snapshot(cx); let display_selections = editor.selections.all_display(&display_map); let mut edits = Vec::new(); let mut anchors = Vec::new(); for selection in &display_selections { let start = selection.start.to_offset(&display_map, Bias::Left); if let Some(range) = pair_object.range(&display_map, selection.clone(), true, None) { // If the current parenthesis object is single-line, // then we need to filter whether it is the current line or not if !pair_object.is_multiline() { let is_same_row = selection.start.row() == range.start.row() && selection.end.row() == range.end.row(); if !is_same_row { anchors.push(start..start); continue; } } // This is a bit cumbersome, and it is written to deal with some special cases, as shown below // hello«ˇ "hello in a word" »again. // Sometimes the expand_selection will not be matched at both ends, and there will be extra spaces // In order to be able to accurately match and replace in this case, some cumbersome methods are used let mut chars_and_offset = display_map .buffer_chars_at(range.start.to_offset(&display_map, Bias::Left)) .peekable(); while let Some((ch, offset)) = chars_and_offset.next() { if ch.to_string() == pair.start { let start = offset; let mut end = start + 1usize; if surround && let Some((next_ch, _)) = chars_and_offset.peek() && next_ch.eq(&' ') { end += 1; } edits.push((start..end, "")); anchors.push(start..start); break; } } let mut reverse_chars_and_offsets = display_map .reverse_buffer_chars_at(range.end.to_offset(&display_map, Bias::Left)) .peekable(); while let Some((ch, offset)) = reverse_chars_and_offsets.next() { if ch.to_string() == pair.end { let mut start = offset; let end = start + 1usize; if surround && let Some((next_ch, _)) = reverse_chars_and_offsets.peek() && next_ch.eq(&' ') { start -= 1; } edits.push((start..end, "")); break; } } } else { anchors.push(start..start); } } editor.change_selections(Default::default(), window, cx, |s| { s.select_ranges(anchors); }); edits.sort_by_key(|(range, _)| range.start); editor.edit(edits, cx); editor.set_clip_at_line_ends(true, cx); }); }); } pub fn change_surrounds( &mut self, text: Arc<str>, target: Object, opening: bool, window: &mut Window, cx: &mut Context<Self>, ) { if let Some(will_replace_pair) = self.object_to_bracket_pair(target, cx) { self.stop_recording(cx); self.update_editor(cx, |_, editor, cx| { editor.transact(window, cx, |editor, window, cx| { editor.set_clip_at_line_ends(false, cx); let pair = bracket_pair_for_str_vim(&text); // A single space should be added if the new surround is a // bracket and not a quote (pair.start != pair.end) and if // the bracket used is the opening bracket. let add_space = !(pair.start == pair.end) && (pair.end != surround_alias((*text).as_ref())); // Space should be preserved if either the surrounding // characters being updated are quotes // (will_replace_pair.start == will_replace_pair.end) or if // the bracket used in the command is not an opening // bracket. let preserve_space = will_replace_pair.start == will_replace_pair.end || !opening; let display_map = editor.display_snapshot(cx); let selections = editor.selections.all_adjusted_display(&display_map); let mut edits = Vec::new(); let mut anchors = Vec::new(); for selection in &selections { let start = selection.start.to_offset(&display_map, Bias::Left); if let Some(range) = target.range(&display_map, selection.clone(), true, None) { if !target.is_multiline() { let is_same_row = selection.start.row() == range.start.row() && selection.end.row() == range.end.row(); if !is_same_row { anchors.push(start..start); continue; } } // Keeps track of the length of the string that is // going to be edited on the start so we can ensure // that the end replacement string does not exceed // this value. Helpful when dealing with newlines. let mut edit_len = 0; let mut open_range_end = MultiBufferOffset(0); let mut chars_and_offset = display_map .buffer_chars_at(range.start.to_offset(&display_map, Bias::Left)) .peekable(); while let Some((ch, offset)) = chars_and_offset.next() { if ch.to_string() == will_replace_pair.start { let mut open_str = pair.start.clone(); let start = offset; open_range_end = start + 1usize; while let Some((next_ch, _)) = chars_and_offset.next() && next_ch == ' ' { open_range_end += 1; if preserve_space { open_str.push(next_ch); } } if add_space { open_str.push(' '); }; edit_len = open_range_end - start; edits.push((start..open_range_end, open_str)); anchors.push(start..start); break; } } let mut reverse_chars_and_offsets = display_map .reverse_buffer_chars_at( range.end.to_offset(&display_map, Bias::Left), ) .peekable(); while let Some((ch, offset)) = reverse_chars_and_offsets.next() { if ch.to_string() == will_replace_pair.end { let mut close_str = String::new(); let mut start = offset; let end = start + 1usize; while let Some((next_ch, _)) = reverse_chars_and_offsets.next() && next_ch == ' ' && close_str.len() < edit_len - 1 && start > open_range_end { start -= 1; if preserve_space { close_str.push(next_ch); } } if add_space { close_str.push(' '); }; close_str.push_str(&pair.end); edits.push((start..end, close_str)); break; } } } else { anchors.push(start..start); } } let stable_anchors = editor .selections .disjoint_anchors_arc() .iter() .map(|selection| { let start = selection.start.bias_left(&display_map.buffer_snapshot()); start..start }) .collect::<Vec<_>>(); edits.sort_by_key(|(range, _)| range.start); editor.edit(edits, cx); editor.set_clip_at_line_ends(true, cx); editor.change_selections(Default::default(), window, cx, |s| { s.select_anchor_ranges(stable_anchors); }); }); }); } } /// Checks if any of the current cursors are surrounded by a valid pair of brackets. /// /// This method supports multiple cursors and checks each cursor for a valid pair of brackets. /// A pair of brackets is considered valid if it is well-formed and properly closed. /// /// If a valid pair of brackets is found, the method returns `true` and the cursor is automatically moved to the start of the bracket pair. /// If no valid pair of brackets is found for any cursor, the method returns `false`. pub fn check_and_move_to_valid_bracket_pair( &mut self, object: Object, window: &mut Window, cx: &mut Context<Self>, ) -> bool { let mut valid = false; if let Some(pair) = self.object_to_bracket_pair(object, cx) { self.update_editor(cx, |_, editor, cx| { editor.transact(window, cx, |editor, window, cx| { editor.set_clip_at_line_ends(false, cx); let display_map = editor.display_snapshot(cx); let selections = editor.selections.all_adjusted_display(&display_map); let mut anchors = Vec::new(); for selection in &selections { let start = selection.start.to_offset(&display_map, Bias::Left); if let Some(range) = object.range(&display_map, selection.clone(), true, None) { // If the current parenthesis object is single-line, // then we need to filter whether it is the current line or not if object.is_multiline() || (!object.is_multiline() && selection.start.row() == range.start.row() && selection.end.row() == range.end.row()) { valid = true; let chars_and_offset = display_map .buffer_chars_at( range.start.to_offset(&display_map, Bias::Left), ) .peekable(); for (ch, offset) in chars_and_offset { if ch.to_string() == pair.start { anchors.push(offset..offset); break; } } } else { anchors.push(start..start) } } else { anchors.push(start..start) } } editor.change_selections(Default::default(), window, cx, |s| { s.select_ranges(anchors); }); editor.set_clip_at_line_ends(true, cx); }); }); } valid } fn object_to_bracket_pair( &self, object: Object, cx: &mut Context<Self>, ) -> Option<BracketPair> { if let Some(pair) = object_to_surround_pair(object) { return Some(pair.to_bracket_pair()); } if object != Object::AnyBrackets { return None; } // If we're dealing with `AnyBrackets`, which can map to multiple bracket // pairs, we'll need to first determine which `BracketPair` to target. // As such, we keep track of the smallest range size, so that in cases // like `({ name: "John" })` if the cursor is inside the curly brackets, // we target the curly brackets instead of the parentheses. let mut best_pair = None; let mut min_range_size = usize::MAX; let _ = self.editor.update(cx, |editor, cx| { let display_map = editor.display_snapshot(cx); let selections = editor.selections.all_adjusted_display(&display_map);Lines 1–500 of 1717. Load more
use crate::{ Vim, motion::{self, Motion}, object::{Object, surrounding_markers}, state::Mode, }; use editor::{Bias, MultiBufferOffset, movement}; use gpui::{Context, Window}; use language::BracketPair; use std::sync::Arc; /// A char-based surround pair definition. /// Single source of truth for all supported surround pairs. #[derive(Clone, Copy, Debug, PartialEq)] pub struct SurroundPair { pub open: char, pub close: char, } impl SurroundPair { pub const fn new(open: char, close: char) -> Self { Self { open, close } } pub fn to_bracket_pair(self) -> BracketPair { BracketPair { start: self.open.to_string(), end: self.close.to_string(), close: true, surround: true, newline: false, } } pub fn to_object(self) -> Option<Object> { match self.open { '\'' => Some(Object::Quotes), '`' => Some(Object::BackQuotes), '"' => Some(Object::DoubleQuotes), '|' => Some(Object::VerticalBars), '(' => Some(Object::Parentheses), '[' => Some(Object::SquareBrackets), '{' => Some(Object::CurlyBrackets), '<' => Some(Object::AngleBrackets), _ => None, } } } /// All supported surround pairs - single source of truth. pub const SURROUND_PAIRS: &[SurroundPair] = &[ SurroundPair::new('(', ')'), SurroundPair::new('[', ']'), SurroundPair::new('{', '}'), SurroundPair::new('<', '>'), SurroundPair::new('"', '"'), SurroundPair::new('\'', '\''), SurroundPair::new('`', '`'), SurroundPair::new('|', '|'), ]; /// Bracket-only pairs for AnyBrackets matching. const BRACKET_PAIRS: &[SurroundPair] = &[ SurroundPair::new('(', ')'), SurroundPair::new('[', ']'), SurroundPair::new('{', '}'), SurroundPair::new('<', '>'), ]; #[derive(Clone, Debug, PartialEq, Eq)] pub enum SurroundsType { Motion(Motion), Object(Object, bool), Selection, } impl Vim { pub fn add_surrounds( &mut self, text: Arc<str>, target: SurroundsType, window: &mut Window, cx: &mut Context<Self>, ) { self.stop_recording(cx); let count = Vim::take_count(cx); let forced_motion = Vim::take_forced_motion(cx); let mode = self.mode; self.update_editor(cx, |_, editor, cx| { let text_layout_details = editor.text_layout_details(window); editor.transact(window, cx, |editor, window, cx| { editor.set_clip_at_line_ends(false, cx); let pair = bracket_pair_for_str_vim(&text); let surround = pair.end != surround_alias((*text).as_ref()); let display_map = editor.display_snapshot(cx); let display_selections = editor.selections.all_adjusted_display(&display_map); let mut edits = Vec::new(); let mut anchors = Vec::new(); for selection in &display_selections { let range = match &target { SurroundsType::Object(object, around) => { object.range(&display_map, selection.clone(), *around, None) } SurroundsType::Motion(motion) => { motion .range( &display_map, selection.clone(), count, &text_layout_details, forced_motion, ) .map(|(mut range, _)| { // The Motion::CurrentLine operation will contain the newline of the current line and leading/trailing whitespace if let Motion::CurrentLine = motion { range.start = motion::first_non_whitespace( &display_map, false, range.start, ); range.end = movement::saturating_right( &display_map, motion::last_non_whitespace(&display_map, range.end, 1), ); } range }) } SurroundsType::Selection => Some(selection.range()), }; if let Some(range) = range { let start = range.start.to_offset(&display_map, Bias::Right); let end = range.end.to_offset(&display_map, Bias::Left); let (start_cursor_str, end_cursor_str) = if mode == Mode::VisualLine { (format!("{}\n", pair.start), format!("\n{}", pair.end)) } else { let maybe_space = if surround { " " } else { "" }; ( format!("{}{}", pair.start, maybe_space), format!("{}{}", maybe_space, pair.end), ) }; let start_anchor = display_map.buffer_snapshot().anchor_before(start); edits.push((start..start, start_cursor_str)); edits.push((end..end, end_cursor_str)); anchors.push(start_anchor..start_anchor); } else { let start_anchor = display_map .buffer_snapshot() .anchor_before(selection.head().to_offset(&display_map, Bias::Left)); anchors.push(start_anchor..start_anchor); } } editor.edit(edits, cx); editor.set_clip_at_line_ends(true, cx); editor.change_selections(Default::default(), window, cx, |s| { if mode == Mode::VisualBlock { s.select_anchor_ranges(anchors.into_iter().take(1)) } else { s.select_anchor_ranges(anchors) } }); }); }); self.switch_mode(Mode::Normal, false, window, cx); } pub fn delete_surrounds( &mut self, text: Arc<str>, window: &mut Window, cx: &mut Context<Self>, ) { self.stop_recording(cx); // only legitimate surrounds can be removed let Some(first_char) = text.chars().next() else { return; }; let Some(surround_pair) = surround_pair_for_char_vim(first_char) else { return; }; let Some(pair_object) = surround_pair.to_object() else { return; }; let pair = surround_pair.to_bracket_pair(); let surround = pair.end != *text; self.update_editor(cx, |_, editor, cx| { editor.transact(window, cx, |editor, window, cx| { editor.set_clip_at_line_ends(false, cx); let display_map = editor.display_snapshot(cx); let display_selections = editor.selections.all_display(&display_map); let mut edits = Vec::new(); let mut anchors = Vec::new(); for selection in &display_selections { let start = selection.start.to_offset(&display_map, Bias::Left); if let Some(range) = pair_object.range(&display_map, selection.clone(), true, None) { // If the current parenthesis object is single-line, // then we need to filter whether it is the current line or not if !pair_object.is_multiline() { let is_same_row = selection.start.row() == range.start.row() && selection.end.row() == range.end.row(); if !is_same_row { anchors.push(start..start); continue; } } // This is a bit cumbersome, and it is written to deal with some special cases, as shown below // hello«ˇ "hello in a word" »again. // Sometimes the expand_selection will not be matched at both ends, and there will be extra spaces // In order to be able to accurately match and replace in this case, some cumbersome methods are used let mut chars_and_offset = display_map .buffer_chars_at(range.start.to_offset(&display_map, Bias::Left)) .peekable(); while let Some((ch, offset)) = chars_and_offset.next() { if ch.to_string() == pair.start { let start = offset; let mut end = start + 1usize; if surround && let Some((next_ch, _)) = chars_and_offset.peek() && next_ch.eq(&' ') { end += 1; } edits.push((start..end, "")); anchors.push(start..start); break; } } let mut reverse_chars_and_offsets = display_map .reverse_buffer_chars_at(range.end.to_offset(&display_map, Bias::Left)) .peekable(); while let Some((ch, offset)) = reverse_chars_and_offsets.next() { if ch.to_string() == pair.end { let mut start = offset; let end = start + 1usize; if surround && let Some((next_ch, _)) = reverse_chars_and_offsets.peek() && next_ch.eq(&' ') { start -= 1; } edits.push((start..end, "")); break; } } } else { anchors.push(start..start); } } editor.change_selections(Default::default(), window, cx, |s| { s.select_ranges(anchors); }); edits.sort_by_key(|(range, _)| range.start); editor.edit(edits, cx); editor.set_clip_at_line_ends(true, cx); }); }); } pub fn change_surrounds( &mut self, text: Arc<str>, target: Object, opening: bool, window: &mut Window, cx: &mut Context<Self>, ) { if let Some(will_replace_pair) = self.object_to_bracket_pair(target, cx) { self.stop_recording(cx); self.update_editor(cx, |_, editor, cx| { editor.transact(window, cx, |editor, window, cx| { editor.set_clip_at_line_ends(false, cx); let pair = bracket_pair_for_str_vim(&text); // A single space should be added if the new surround is a // bracket and not a quote (pair.start != pair.end) and if // the bracket used is the opening bracket. let add_space = !(pair.start == pair.end) && (pair.end != surround_alias((*text).as_ref())); // Space should be preserved if either the surrounding // characters being updated are quotes // (will_replace_pair.start == will_replace_pair.end) or if // the bracket used in the command is not an opening // bracket. let preserve_space = will_replace_pair.start == will_replace_pair.end || !opening; let display_map = editor.display_snapshot(cx); let selections = editor.selections.all_adjusted_display(&display_map); let mut edits = Vec::new(); let mut anchors = Vec::new(); for selection in &selections { let start = selection.start.to_offset(&display_map, Bias::Left); if let Some(range) = target.range(&display_map, selection.clone(), true, None) { if !target.is_multiline() { let is_same_row = selection.start.row() == range.start.row() && selection.end.row() == range.end.row(); if !is_same_row { anchors.push(start..start); continue; } } // Keeps track of the length of the string that is // going to be edited on the start so we can ensure // that the end replacement string does not exceed // this value. Helpful when dealing with newlines. let mut edit_len = 0; let mut open_range_end = MultiBufferOffset(0); let mut chars_and_offset = display_map .buffer_chars_at(range.start.to_offset(&display_map, Bias::Left)) .peekable(); while let Some((ch, offset)) = chars_and_offset.next() { if ch.to_string() == will_replace_pair.start { let mut open_str = pair.start.clone(); let start = offset; open_range_end = start + 1usize; while let Some((next_ch, _)) = chars_and_offset.next() && next_ch == ' ' { open_range_end += 1; if preserve_space { open_str.push(next_ch); } } if add_space { open_str.push(' '); }; edit_len = open_range_end - start; edits.push((start..open_range_end, open_str)); anchors.push(start..start); break; } } let mut reverse_chars_and_offsets = display_map .reverse_buffer_chars_at( range.end.to_offset(&display_map, Bias::Left), ) .peekable(); while let Some((ch, offset)) = reverse_chars_and_offsets.next() { if ch.to_string() == will_replace_pair.end { let mut close_str = String::new(); let mut start = offset; let end = start + 1usize; while let Some((next_ch, _)) = reverse_chars_and_offsets.next() && next_ch == ' ' && close_str.len() < edit_len - 1 && start > open_range_end { start -= 1; if preserve_space { close_str.push(next_ch); } } if add_space { close_str.push(' '); }; close_str.push_str(&pair.end); edits.push((start..end, close_str)); break; } } } else { anchors.push(start..start); } } let stable_anchors = editor .selections .disjoint_anchors_arc() .iter() .map(|selection| { let start = selection.start.bias_left(&display_map.buffer_snapshot()); start..start }) .collect::<Vec<_>>(); edits.sort_by_key(|(range, _)| range.start); editor.edit(edits, cx); editor.set_clip_at_line_ends(true, cx); editor.change_selections(Default::default(), window, cx, |s| { s.select_anchor_ranges(stable_anchors); }); }); }); } } /// Checks if any of the current cursors are surrounded by a valid pair of brackets. /// /// This method supports multiple cursors and checks each cursor for a valid pair of brackets. /// A pair of brackets is considered valid if it is well-formed and properly closed. /// /// If a valid pair of brackets is found, the method returns `true` and the cursor is automatically moved to the start of the bracket pair. /// If no valid pair of brackets is found for any cursor, the method returns `false`. pub fn check_and_move_to_valid_bracket_pair( &mut self, object: Object, window: &mut Window, cx: &mut Context<Self>, ) -> bool { let mut valid = false; if let Some(pair) = self.object_to_bracket_pair(object, cx) { self.update_editor(cx, |_, editor, cx| { editor.transact(window, cx, |editor, window, cx| { editor.set_clip_at_line_ends(false, cx); let display_map = editor.display_snapshot(cx); let selections = editor.selections.all_adjusted_display(&display_map); let mut anchors = Vec::new(); for selection in &selections { let start = selection.start.to_offset(&display_map, Bias::Left); if let Some(range) = object.range(&display_map, selection.clone(), true, None) { // If the current parenthesis object is single-line, // then we need to filter whether it is the current line or not if object.is_multiline() || (!object.is_multiline() && selection.start.row() == range.start.row() && selection.end.row() == range.end.row()) { valid = true; let chars_and_offset = display_map .buffer_chars_at( range.start.to_offset(&display_map, Bias::Left), ) .peekable(); for (ch, offset) in chars_and_offset { if ch.to_string() == pair.start { anchors.push(offset..offset); break; } } } else { anchors.push(start..start) } } else { anchors.push(start..start) } } editor.change_selections(Default::default(), window, cx, |s| { s.select_ranges(anchors); }); editor.set_clip_at_line_ends(true, cx); }); }); } valid } fn object_to_bracket_pair( &self, object: Object, cx: &mut Context<Self>, ) -> Option<BracketPair> { if let Some(pair) = object_to_surround_pair(object) { return Some(pair.to_bracket_pair()); } if object != Object::AnyBrackets { return None; } // If we're dealing with `AnyBrackets`, which can map to multiple bracket // pairs, we'll need to first determine which `BracketPair` to target. // As such, we keep track of the smallest range size, so that in cases // like `({ name: "John" })` if the cursor is inside the curly brackets, // we target the curly brackets instead of the parentheses. let mut best_pair = None; let mut min_range_size = usize::MAX; let _ = self.editor.update(cx, |editor, cx| { let display_map = editor.display_snapshot(cx); let selections = editor.selections.all_adjusted_display(&display_map);