on master help sign in
Add WORD textobject (#991)
a252ecdOmnikar
book/src/usage.md+1
@@ -62,6 +62,7 @@ Currently supported: `word`, `surround`, `function`, `class`, `parameter`.
62| Key after `mi` or `ma` | Textobject selected |
62| Key after `mi` or `ma` | Textobject selected |
63| --- | --- |
63| --- | --- |
64| `w` | Word |
64| `w` | Word |
65+| `W` | WORD |
65| `(`, `[`, `'`, etc | Specified surround pairs |
66| `(`, `[`, `'`, etc | Specified surround pairs |
66| `f` | Function |
67| `f` | Function |
67| `c` | Class |
68| `c` | Class |
helix-core/src/textobject.rs+6−5
@@ -10,7 +10,7 @@ use crate::surround;
10use crate::syntax::LanguageConfiguration;
10use crate::syntax::LanguageConfiguration;
11use crate::Range;
11use crate::Range;
12
12
13−fn find_word_boundary(slice: RopeSlice, mut pos: usize, direction: Direction) -> usize {
13+fn find_word_boundary(slice: RopeSlice, mut pos: usize, direction: Direction, long: bool) -> usize {
14 use CharCategory::{Eol, Whitespace};
14 use CharCategory::{Eol, Whitespace};
15
15
16 let iter = match direction {
16 let iter = match direction {
@@ -33,7 +33,7 @@ fn find_word_boundary(slice: RopeSlice, mut pos: usize, direction: Direction) ->
33 match categorize_char(ch) {
33 match categorize_char(ch) {
34 Eol | Whitespace => return pos,
34 Eol | Whitespace => return pos,
35 category => {
35 category => {
36− if category != prev_category && pos != 0 && pos != slice.len_chars() {
36+ if !long && category != prev_category && pos != 0 && pos != slice.len_chars() {
37 return pos;
37 return pos;
38 } else {
38 } else {
39 match direction {
39 match direction {
@@ -70,13 +70,14 @@ pub fn textobject_word(
70 range: Range,
70 range: Range,
71 textobject: TextObject,
71 textobject: TextObject,
72 _count: usize,
72 _count: usize,
73+ long: bool,
73) -> Range {
74) -> Range {
74 let pos = range.cursor(slice);
75 let pos = range.cursor(slice);
75
76
76− let word_start = find_word_boundary(slice, pos, Direction::Backward);
77+ let word_start = find_word_boundary(slice, pos, Direction::Backward, long);
77 let word_end = match slice.get_char(pos).map(categorize_char) {
78 let word_end = match slice.get_char(pos).map(categorize_char) {
78 None | Some(CharCategory::Whitespace | CharCategory::Eol) => pos,
79 None | Some(CharCategory::Whitespace | CharCategory::Eol) => pos,
79− _ => find_word_boundary(slice, pos + 1, Direction::Forward),
80+ _ => find_word_boundary(slice, pos + 1, Direction::Forward, long),
80 };
81 };
81
82
82 // Special case.
83 // Special case.
@@ -268,7 +269,7 @@ mod test {
268 let slice = doc.slice(..);
269 let slice = doc.slice(..);
269 for &case in scenario {
270 for &case in scenario {
270 let (pos, objtype, expected_range) = case;
271 let (pos, objtype, expected_range) = case;
271− let result = textobject_word(slice, Range::point(pos), objtype, 1);
272+ let result = textobject_word(slice, Range::point(pos), objtype, 1, false);
272 assert_eq!(
273 assert_eq!(
273 result,
274 result,
274 expected_range.into(),
275 expected_range.into(),
helix-term/src/commands.rs+2−1
@@ -4672,7 +4672,8 @@ fn select_textobject(cx: &mut Context, objtype: textobject::TextObject) {
4672
4672
4673 let selection = doc.selection(view.id).clone().transform(|range| {
4673 let selection = doc.selection(view.id).clone().transform(|range| {
4674 match ch {
4674 match ch {
4675− 'w' => textobject::textobject_word(text, range, objtype, count),
4675+ 'w' => textobject::textobject_word(text, range, objtype, count, false),
4676+ 'W' => textobject::textobject_word(text, range, objtype, count, true),
4676 'c' => textobject_treesitter("class", range),
4677 'c' => textobject_treesitter("class", range),
4677 'f' => textobject_treesitter("function", range),
4678 'f' => textobject_treesitter("function", range),
4678 'p' => textobject_treesitter("parameter", range),
4679 'p' => textobject_treesitter("parameter", range),
helix-term/src/ui/prompt.rs+1
@@ -491,6 +491,7 @@ impl Component for Prompt {
491 doc.selection(view.id).primary(),
491 doc.selection(view.id).primary(),
492 textobject::TextObject::Inside,
492 textobject::TextObject::Inside,
493 1,
493 1,
494+ false,
494 );
495 );
495 let line = text.slice(range.from()..range.to()).to_string();
496 let line = text.slice(range.from()..range.to()).to_string();
496 if !line.is_empty() {
497 if !line.is_empty() {