@@ -37,6 +37,7 @@ pub enum Object {
SquareBrackets,
CurlyBrackets,
AngleBrackets,
+ SyntaxNode,
Argument,
IndentObj { include_below: bool },
Tag,
@@ -276,7 +277,10 @@ actions!(
Method,
Class,
Comment,
- EntireFile
+ /// Selects the entire file.
+ EntireFile,
+ /// Selects a syntax node.
+ SyntaxNode,
]
);
@@ -368,6 +372,9 @@ pub fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
vim.object(Object::IndentObj { include_below }, window, cx)
},
);
+ Vim::action(editor, cx, |vim, _: &SyntaxNode, window, cx| {
+ vim.object(Object::SyntaxNode, window, cx);
+ });
}
impl Vim {
@@ -409,7 +416,8 @@ impl Object {
| Object::Class
| Object::EntireFile
| Object::Comment
- | Object::IndentObj { .. } => true,
+ | Object::IndentObj { .. }
+ | Object::SyntaxNode => true,
}
}
@@ -437,7 +445,8 @@ impl Object {
| Object::Comment
| Object::EntireFile
| Object::CurlyBrackets
- | Object::AngleBrackets => true,
+ | Object::AngleBrackets
+ | Object::SyntaxNode => true,
}
}
@@ -467,7 +476,8 @@ impl Object {
| Object::Tag
| Object::Comment
| Object::Argument
- | Object::IndentObj { .. } => Mode::Visual,
+ | Object::IndentObj { .. }
+ | Object::SyntaxNode => Mode::Visual,
Object::Method | Object::Class => {
if around {
Mode::VisualLine
@@ -683,6 +693,13 @@ impl Object {
Object::Argument => argument(map, relative_to, around),
Object::IndentObj { include_below } => indent(map, relative_to, around, include_below),
Object::EntireFile => entire_file(map),
+ Object::SyntaxNode => {
+ if around {
+ larger_syntax_node(map, selection, times.unwrap_or(1))
+ } else {
+ smaller_syntax_node(map, selection, times.unwrap_or(1))
+ }
+ }
}
}
@@ -1654,6 +1671,61 @@ fn surrounding_markers(
)
}
+fn smaller_syntax_node(
+ map: &DisplaySnapshot,
+ selection: Selection<DisplayPoint>,
+ unwrap_or: usize,
+) -> Option<Range<DisplayPoint>> {
+ todo!("take in editor and use ")
+}
+
+fn larger_syntax_node(
+ map: &DisplaySnapshot,
+ selection: Selection<DisplayPoint>,
+ count: usize,
+) -> Option<Range<DisplayPoint>> {
+ let selection = selection.map(|p| {
+ map.display_point_to_anchor(p, Bias::Left)
+ .to_offset(&map.buffer_snapshot)
+ });
+ let old_range = selection.start..selection.end;
+ let buffer = &map.buffer_snapshot;
+
+ if let Some((node, _)) = buffer.syntax_ancestor(old_range.clone()) {
+ // manually select word at selection
+ if ["string_content", "inline"].contains(&node.kind()) {
+ let (word_range, _) = buffer.surrounding_word(old_range.start, None);
+ // ignore if word is already selected
+ if !word_range.is_empty() && old_range != word_range {
+ let (last_word_range, _) = buffer.surrounding_word(old_range.end, None);
+ // only select word if start and end point belongs to same word
+ if word_range == last_word_range {
+ return Some(
+ word_range.start.to_display_point(map)
+ ..word_range.end.to_display_point(map),
+ );
+ }
+ }
+ }
+ }
+
+ let mut new_range = old_range.clone();
+ while let Some((node, containing_range)) = buffer.syntax_ancestor(new_range.clone()) {
+ new_range = match containing_range {
+ multi_buffer::MultiOrSingleBufferOffsetRange::Single(_) => break,
+ multi_buffer::MultiOrSingleBufferOffsetRange::Multi(range) => range,
+ };
+ if !node.is_named() {
+ continue;
+ }
+ if !map.intersects_fold(new_range.start) && !map.intersects_fold(new_range.end) {
+ break;
+ }
+ }
+
+ return Some(new_range.start.to_display_point(map)..new_range.end.to_display_point(map));
+}
+
#[cfg(test)]
mod test {
use gpui::KeyBinding;