@@ -19,7 +19,7 @@ use crate::{
indent::IndentDirection,
motion::{self, Motion, first_non_whitespace, next_line_end, right},
object::Object,
- state::{Mark, Mode, Operator},
+ state::{Mark, Mode, ObjectScope, Operator},
surrounds::SurroundsType,
};
use collections::BTreeSet;
@@ -454,64 +454,82 @@ impl Vim {
) {
let mut waiting_operator: Option<Operator> = None;
match self.maybe_pop_operator() {
- Some(Operator::Object { around, whitespace }) => match self.maybe_pop_operator() {
- Some(Operator::Change) => self.change_object(object, around, times, window, cx),
- Some(Operator::Delete) => {
- self.delete_object(object, around, whitespace, times, window, cx)
- }
- Some(Operator::Yank) => self.yank_object(object, around, times, window, cx),
- Some(Operator::Indent) => {
- self.indent_object(object, around, IndentDirection::In, times, window, cx)
- }
- Some(Operator::Outdent) => {
- self.indent_object(object, around, IndentDirection::Out, times, window, cx)
- }
- Some(Operator::AutoIndent) => {
- self.indent_object(object, around, IndentDirection::Auto, times, window, cx)
- }
- Some(Operator::ShellCommand) => {
- self.shell_command_object(object, around, window, cx);
- }
- Some(Operator::Rewrap) => self.rewrap_object(object, around, times, window, cx),
- Some(Operator::Lowercase) => {
- self.convert_object(object, around, ConvertTarget::LowerCase, times, window, cx)
- }
- Some(Operator::Uppercase) => {
- self.convert_object(object, around, ConvertTarget::UpperCase, times, window, cx)
- }
- Some(Operator::OppositeCase) => self.convert_object(
- object,
- around,
- ConvertTarget::OppositeCase,
- times,
- window,
- cx,
- ),
- Some(Operator::Rot13) => {
- self.convert_object(object, around, ConvertTarget::Rot13, times, window, cx)
- }
- Some(Operator::Rot47) => {
- self.convert_object(object, around, ConvertTarget::Rot47, times, window, cx)
- }
- Some(Operator::AddSurrounds { target: None }) => {
- waiting_operator = Some(Operator::AddSurrounds {
- target: Some(SurroundsType::Object(object, around)),
- });
- }
- Some(Operator::ToggleComments) => {
- self.toggle_comments_object(object, around, times, window, cx)
- }
- Some(Operator::ReplaceWithRegister) => {
- self.replace_with_register_object(object, around, window, cx)
- }
- Some(Operator::Exchange) => self.exchange_object(object, around, window, cx),
- Some(Operator::HelixMatch) => {
- self.select_current_object(object, around, window, cx)
- }
- _ => {
- // Can't do anything for namespace operators. Ignoring
+ Some(Operator::Object { scope }) => {
+ let (around, whitespace) = match scope {
+ ObjectScope::Inside => (false, false),
+ ObjectScope::Around => (true, true),
+ ObjectScope::AroundTrimmed => (true, false),
+ };
+
+ match self.maybe_pop_operator() {
+ Some(Operator::Change) => self.change_object(object, around, times, window, cx),
+ Some(Operator::Delete) => {
+ self.delete_object(object, around, whitespace, times, window, cx)
+ }
+ Some(Operator::Yank) => self.yank_object(object, around, times, window, cx),
+ Some(Operator::Indent) => {
+ self.indent_object(object, around, IndentDirection::In, times, window, cx)
+ }
+ Some(Operator::Outdent) => {
+ self.indent_object(object, around, IndentDirection::Out, times, window, cx)
+ }
+ Some(Operator::AutoIndent) => {
+ self.indent_object(object, around, IndentDirection::Auto, times, window, cx)
+ }
+ Some(Operator::ShellCommand) => {
+ self.shell_command_object(object, around, window, cx);
+ }
+ Some(Operator::Rewrap) => self.rewrap_object(object, around, times, window, cx),
+ Some(Operator::Lowercase) => self.convert_object(
+ object,
+ around,
+ ConvertTarget::LowerCase,
+ times,
+ window,
+ cx,
+ ),
+ Some(Operator::Uppercase) => self.convert_object(
+ object,
+ around,
+ ConvertTarget::UpperCase,
+ times,
+ window,
+ cx,
+ ),
+ Some(Operator::OppositeCase) => self.convert_object(
+ object,
+ around,
+ ConvertTarget::OppositeCase,
+ times,
+ window,
+ cx,
+ ),
+ Some(Operator::Rot13) => {
+ self.convert_object(object, around, ConvertTarget::Rot13, times, window, cx)
+ }
+ Some(Operator::Rot47) => {
+ self.convert_object(object, around, ConvertTarget::Rot47, times, window, cx)
+ }
+ Some(Operator::AddSurrounds { target: None }) => {
+ waiting_operator = Some(Operator::AddSurrounds {
+ target: Some(SurroundsType::Object(object, around)),
+ });
+ }
+ Some(Operator::ToggleComments) => {
+ self.toggle_comments_object(object, around, times, window, cx)
+ }
+ Some(Operator::ReplaceWithRegister) => {
+ self.replace_with_register_object(object, around, window, cx)
+ }
+ Some(Operator::Exchange) => self.exchange_object(object, around, window, cx),
+ Some(Operator::HelixMatch) => {
+ self.select_current_object(object, around, window, cx)
+ }
+ _ => {
+ // Can't do anything for namespace operators. Ignoring
+ }
}
- },
+ }
Some(Operator::HelixNext { around }) => {
self.select_next_object(object, around, window, cx);
}
@@ -87,8 +87,7 @@ pub enum Operator {
Yank,
Replace,
Object {
- around: bool,
- whitespace: bool,
+ scope: ObjectScope,
},
FindForward {
before: bool,
@@ -150,6 +149,28 @@ pub enum Operator {
},
}
+/// Controls how the object interacts with its delimiters and the surrounding
+/// whitespace.
+#[derive(Clone, Debug, PartialEq)]
+pub(crate) enum ObjectScope {
+ /// Inside the delimiters, excluding whitespace.
+ ///
+ /// Used by the `i` operator (e.g., `diw` for "delete inner word").
+ /// Selects only the content between delimiters without including
+ /// the delimiters themselves or surrounding whitespace.
+ Inside,
+ /// Around the delimiters, including surrounding whitespace.
+ ///
+ /// Used by the `a` operator (e.g., `daw` for "delete a word").
+ /// Selects the content, the delimiters, and any surrounding whitespace.
+ Around,
+ /// Around the delimiters, excluding surrounding whitespace.
+ ///
+ /// Similar to `Around`, but does not include whitespace adjacent to
+ /// the delimiters.
+ AroundTrimmed,
+}
+
#[derive(Default, Clone, Debug)]
pub enum RecordedSelection {
#[default]
@@ -997,8 +1018,12 @@ pub struct SearchState {
impl Operator {
pub fn id(&self) -> &'static str {
match self {
- Operator::Object { around: false, .. } => "i",
- Operator::Object { around: true, .. } => "a",
+ Operator::Object {
+ scope: ObjectScope::Inside,
+ } => "i",
+ Operator::Object {
+ scope: ObjectScope::Around | ObjectScope::AroundTrimmed,
+ } => "a",
Operator::Change => "c",
Operator::Delete => "d",
Operator::Yank => "y",
@@ -42,7 +42,7 @@ use serde::Deserialize;
pub use settings::{
ModeContent, Settings, SettingsStore, UseSystemClipboard, update_settings_file,
};
-use state::{Mode, Operator, RecordedSelection, SearchState, VimGlobals};
+use state::{Mode, ObjectScope, Operator, RecordedSelection, SearchState, VimGlobals};
use std::{mem, ops::Range, sync::Arc};
use surrounds::SurroundsType;
use theme::ThemeSettings;
@@ -662,14 +662,13 @@ impl Vim {
Vim::globals(cx).forced_motion = true;
});
Vim::action(editor, cx, |vim, action: &PushObject, window, cx| {
- vim.push_operator(
- Operator::Object {
- around: action.around,
- whitespace: action.whitespace,
- },
- window,
- cx,
- )
+ let scope = match (action.around, action.whitespace) {
+ (false, _) => ObjectScope::Inside,
+ (true, true) => ObjectScope::Around,
+ (true, false) => ObjectScope::AroundTrimmed,
+ };
+
+ vim.push_operator(Operator::Object { scope }, window, cx)
});
Vim::action(editor, cx, |vim, action: &PushFindForward, window, cx| {