Skip to content

Commit

Permalink
Restore Vim's special case behavior for cw. Fixes #9, #27, #36, #41.
Browse files Browse the repository at this point in the history
  • Loading branch information
chaoren committed Dec 20, 2020
1 parent 257a59c commit f2be984
Show file tree
Hide file tree
Showing 8 changed files with 204 additions and 86 deletions.
39 changes: 0 additions & 39 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,45 +138,6 @@ let g:wordmotion_mappings['w'] = '<M-w>'
call wordmotion#reload()
```

## Caveats

There are some special cases with how Vim's word motions work.\
E.g.,
```
Vim:
^foo [b]ar$ -> dw -> ^foo[ ]$
^ baz$ ^ baz$
^[f]oo bar$ -> cw -> ^[ ]bar$
This plugin:
^foo [b]ar$ -> dw -> ^foo [b]az$
^ baz$
^[f]oo bar$ -> cw -> ^[b]ar$
```
This plugin faithfully follows the motion of `w` when executing `dw` and `cw`,
while Vim replaces these two special cases with the behavior of `de` and `ce`,
respectively.

If you want to restore Vim's special case behavior with `dw` and `cw`, you can
do this:
```
nmap dw de
nmap cw ce
```

If you want the same behavior for uppercase motions (i.e., `dW` and `cW`), you
can set `g:wordmotion_uppercase_spaces`.\
E.g.,
```
let g:wordmotion_uppercase_spaces = [' ']
```
will enable this behavior without adding extra space characters to uppercase
motions.

## Related
[camelcasemotion][3]

Expand Down
17 changes: 12 additions & 5 deletions autoload/wordmotion.vim
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,22 @@ endfunction
call wordmotion#init()

function wordmotion#motion(count, mode, flags, uppercase, extra)
let l:flags = a:flags

if a:mode == 'o' && v:operator == 'c' && l:flags == ''
" special case (see :help cw)
let l:flags = 'ce'
endif

if a:mode == 'x'
normal! gv
elseif a:mode == 'o' && a:flags =~# 'e'
elseif a:mode == 'o' && l:flags =~# 'e'
" need to make this inclusive for operator pending mode
normal! v
endif

let l:words = a:extra + [a:uppercase ? s:uS . '\+' : s:word]
if a:flags != 'e' " e does not stop in an empty line
if l:flags != 'e' " e does not stop in an empty line
call add(l:words, '^$')
endif

Expand All @@ -64,17 +71,17 @@ function wordmotion#motion(count, mode, flags, uppercase, extra)
let l:pos = getpos('.')

for @_ in range(a:count)
call search(l:pattern, a:flags . 'W')
call search(l:pattern, l:flags . 'W')
endfor

" ugly hack for 'w' going forwards at end of file
" and 'ge' going backwards at beginning of file
if a:count && l:pos == getpos('.')
" cursor didn't move
if a:flags == 'be' && line('.') == 1
if l:flags == 'be' && line('.') == 1
" at first line and going backwards, let's go to the front
normal! 0
elseif a:flags == '' && line('.') == line('$')
elseif l:flags == '' && line('.') == line('$')
" at last line and going forwards, let's go to the back
if a:mode == 'o'
" need to include last character if in operator pending mode
Expand Down
40 changes: 1 addition & 39 deletions doc/wordmotion.txt
Original file line number Diff line number Diff line change
Expand Up @@ -178,42 +178,4 @@ then later re-enable it
let g:wordmotion_mappings['w'] = '<M-w>'
call wordmotion#reload()
<
CAVEATS *wordmotion-caveats*

There are some special cases with how Vim's word motions work.
E.g.,

Vim:
>
^foo [b]ar$ -> dw -> ^foo[ ]$
^ baz$ ^ baz$
^[f]oo bar$ -> cw -> ^[ ]bar$
This plugin:
>
^foo [b]ar$ -> dw -> ^foo [b]az$
^ baz$
^[f]oo bar$ -> cw -> ^[b]ar$
<
This plugin faithfully follows the motion of `w` when executing `dw` and `cw`,
while Vim replaces these two special cases with the behavior of `de` and `ce`,
respectively.

If you want to restore Vim's special case behavior with `dw` and `cw`, you can
do this:
>
nmap dw de
nmap cw ce
<
If you want the same behavior for uppercase motions (i.e., `dW` and `cW`), you
can set `g:wordmotion_uppercase_spaces`.
E.g.,
>
let g:wordmotion_uppercase_spaces = [' ']
<
will enable this behavior without adding extra space characters to uppercase
motions.

vim:tw=78:ts=8:sw=8:noet:ft=help:norl:
vim:tw=78:ts=8:noet:ft=help:norl:
2 changes: 1 addition & 1 deletion tests/custom_mappings.vader
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
After:
After (Restore default options):
call wordmotion#_default()

Execute (Check mappings with prefix):
Expand Down
184 changes: 184 additions & 0 deletions tests/cwdw.vader
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
# regular words

Given (This is some text):
This is some text

# first word of line, start of word

Do (dw at [T]his):
dw

Then (Assert that cursor is at [i]s):
Assert 1, col('.')

Expect (This deleted):
is some text

Do (cw at [T]his):
cwThat\<Esc>

Then (Assert that cursor is at Tha[t]):
Assert 4, col('.')

Expect (This changed to That):
That is some text

# first word of line, middle of word

Do (dw at T[h]is):
l
dw

Then (Assert that cursor is at [i]s):
Assert 2, col('.')

Expect (his deleted):
Tis some text

Do (cw at T[h]is):
l
cwThat\<Esc>

Then (Assert that cursor is at TTha[t]):
Assert 5, col('.')

Expect (This changed to TThat):
TThat is some text

# first word of line, end of word

Do (dw at Thi[s]):
e
dw

Then (Assert that cursor is at [i]s):
Assert 5, col('.')

Expect (his deleted):
Thiis some text

Do (cw at Thi[s]):
e
cwThat\<Esc>

Then (Assert that cursor is at ThiTha[t]):
Assert 7, col('.')

Expect (This changed to ThiThat):
ThiThat is some text

# middle of line, start of word

Do (dw at [s]ome):
2w
dw

Then (Assert that cursor is at [t]ext):
Assert 9, col('.')

Expect (some deleted):
This is text

Do (cw at [s]ome):
2w
cwother\<Esc>

Then (Assert that cursor is at othe[r]):
Assert 13, col('.')

Expect (some changed to other):
This is other text

# middle of line, middle of word

Do (dw at s[o]me):
2wl
dw

Then (Assert that cursor is at [t]ext):
Assert 10, col('.')

Expect (some deleted):
This is stext

Do (cw at s[o]me):
2wl
cwother\<Esc>

Then (Assert that cursor is at othe[r]):
Assert 13, col('.')

Expect (some changed to sother):
This is sother text

# middle of line, end of word

Do (dw at som[e]):
3e
dw

Then (Assert that cursor is at [t]ext):
Assert 12, col('.')

Expect (some deleted):
This is somtext

Do (cw at som[e]):
3e
cwother\<Esc>

Then (Assert that cursor is at somothe[r]):
Assert 15, col('.')

Expect (some changed to somother):
This is somother text

# end of line, start of word

Do (dw at [t]ext):
3w
dw

Then (Assert that cursor is at some[_]):
Assert 13, col('.')

Expect (text deleted):
This is some

Do (cw at [t]ext):
3w
cwchange\<Esc>

Then (Assert that cursor is at chang[e]):
Assert 19, col('.')

Expect (text changed to change):
This is some change

# end of line, middle of word

Do (dw at t[e]xt):
3wl
dw

Then (Assert that cursor is at _[t]):
Assert 14, col('.')

Expect (text deleted):
This is some t

Do (cw at t[e]xt):
3wl
cwchange\<Esc>

Then (Assert that cursor is at tchang[e]):
Assert 20, col('.')

Expect (text changed to tchange):
This is some tchange

# single character words

Given (a b c\nd e f):
a b c
d e f
2 changes: 1 addition & 1 deletion tests/default_mappings.vader
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
After:
After (Restore default options):
call wordmotion#_default()

Execute (Check default mappings):
Expand Down
4 changes: 4 additions & 0 deletions tests/issues.vader
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,17 @@ Given (Weird characters: https://github.com/chaoren/vim-wordmotion/issues/1):

Execute (Move 2 words):
normal 2w

Then (Assert that cursor is at [g]hi):
AssertEqual 'g', getline('.')[col('.') - 1]

Given (Underscores: https://github.com/chaoren/vim-wordmotion/issues/5):
abc_def_ghi

Execute (Move 2 words):
normal 2w

Then (Assert that cursor is at _[d]ef_):
AssertEqual 'g', getline('.')[col('.') - 1]

Given (CamelCase):
Expand Down
2 changes: 1 addition & 1 deletion tests/spaces.vader
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
After:
After (Restore default options):
call wordmotion#_default()

Given (Lorem ipsum deliminated by '_'):
Expand Down

0 comments on commit f2be984

Please sign in to comment.