diff options
author | Josh Rahm <rahm@google.com> | 2022-08-30 12:12:16 -0600 |
---|---|---|
committer | Josh Rahm <rahm@google.com> | 2022-08-30 12:12:16 -0600 |
commit | 80c891e10223e560c641589fe81be6fa07f0cf1f (patch) | |
tree | 5aeb7a72bbce32f757f63ec292ee9bf8a7a5b827 | |
parent | 52f035e111cfe766b030d603238b0552453d036c (diff) | |
download | fieldmarshal.vim-80c891e10223e560c641589fe81be6fa07f0cf1f.tar.gz fieldmarshal.vim-80c891e10223e560c641589fe81be6fa07f0cf1f.tar.bz2 fieldmarshal.vim-80c891e10223e560c641589fe81be6fa07f0cf1f.zip |
subwords.vim: split functions into the autoload directory.
-rw-r--r-- | autoload/subwords.vim | 185 | ||||
-rw-r--r-- | plugin/subwords.vim | 222 |
2 files changed, 207 insertions, 200 deletions
diff --git a/autoload/subwords.vim b/autoload/subwords.vim new file mode 100644 index 0000000..eef8f8b --- /dev/null +++ b/autoload/subwords.vim @@ -0,0 +1,185 @@ +let s:subword_motion = "" +let s:subword_nosave = 0 + +" Return the type of meta-word (i.e. camelCase, snake_case). If +" a:prefer_camel is set, then a word like ThisIs_A_MixOfCamel_And_Snake will +" some_snake_cae SomeCamelCase SOME_CONSTANT_CASE +" return 'camel', otherwise it'll return 'snake'. +function! s:detect_word_type(prefer_camel, word) abort + let is_camel = 0 + if a:word =~ '[a-z][A-Z]' + let is_camel = 1 + + if a:prefer_camel + " The word contains a camelCase boundary. + return 'camel' + endif + endif + + if a:word =~ '_\|\W' + " The word contains a sake_case boundary. + return 'snake' + endif + + if is_camel + return 'camel' + endif + + " There is not discernible type, it's probably just a single word. + return 'word' +endfunction + +" Move the cursor to the next subowrd. +function! subwords#next(vis, forward) + if ! s:subword_nosave + if a:forward + let s:subword_motion = 'next' + else + let s:subword_motion = 'prev' + endif + endif + + let i = 0 + while i < v:count1 + call search( + \ '\([a-z]\zs[A-Z]\ze\)\|\(\W\|_\)\zs\w\ze', (a:forward ? '' : 'b')) + let i += 1 + endwhile + + let s:subword_nosave = 0 +endfunction + +" Visually highlights a subword. +" +" If a:prefer_camel is set, then WordsLike_This will be interpreted with +" camelCase. +" +" if a:around is set, the highlight around the subword (otherwise highlight the +" inner subword). +function! subwords#visual(prefer_camel, around) + + " Detect the type of the word. + let word = expand("<cword>") + let t = s:detect_word_type(a:prefer_camel, word) + + let expr = '' + + if t == 'camel' + let expr = 'v' + + let line = getline('.') + let c = col('.') - 1 + let i = 0 + + while line[c] =~ '[a-z]' && c >= 0 + let c -= 1 + let i += 1 + endwhile + + " camelCase + + if c >= 0 && ! (line[c] =~ '[A-Z_]') + " If we are at the beginning of the meta word, the don't go back too far. + let i -= 1 + endif + + if i > 0 + let expr .= printf("%dh", i) + endif + + let expr .= 'o' + + let c = col('.') - 1 + let i = 0 + + if line[c] =~ '[A-Z_]' + " Actually on the starting capital letter, include it, but start counting + " from the next character. + let i += 1 + let c += 1 + endif + + while c < len(line) && line[c] =~ '[a-z]' + let c += 1 + let i += 1 + endwhile + + if i > 1 + let expr .= printf("%dl", i - 1) + endif + + elseif t == "snake" + let expr = 'v' + + let line = getline('.') + let c = col('.') - 1 + let i = 0 + + while c >= 0 && !( line[c] =~ '\W' ) && line[c] != '_' + let c -= 1 + let i += 1 + endwhile + + let lhs_under = c >= 0 && line[c] == '_' + + let i -= 1 + let c += 1 + + if i > 0 + let expr .= printf('%dho', i) + endif + + let c = col('.') - 1 + let i = 0 + + while c < len(line) && !(line[c] =~ '\W') && line[c] != '_' + let c += 1 + let i += 1 + endwhile + + let rhs_under = c < len(line) && line[c] == '_' + + let i -= 1 + let c -= 1 + + if i > 0 + let expr .= printf('%dl', i) + endif + + if a:around + if rhs_under + let expr .= 'l' + elseif lhs_under + let expr .= 'oho' + endif + endif + + + elseif t == "word" + " Just a word? Easy peasy. + let expr = 'viw' + endif + + return expr +endfunction! + +" Clear the subword motion. (used for replacement TtfF) +function! subwords#clear_mark() + let s:subword_motion = "" + return '' +endfunction + +function! subwords#repeat(char) + if s:subword_motion == '' + return a:char + endif + + let mot = (s:subword_motion == 'next') != (a:char == ',') + + let s:subword_nosave = 1 + if mot + return "\<Plug>(next-subword)" + else + return "\<Plug>(prev-subword)" + endif +endfunction diff --git a/plugin/subwords.vim b/plugin/subwords.vim index a8a38ee..067eb2c 100644 --- a/plugin/subwords.vim +++ b/plugin/subwords.vim @@ -8,10 +8,10 @@ " of snake_case identifier will include the underscores (_). The 'i_' references " the ci_ -onoremap <silent> <Plug>(inner-sub-word) :<c-u>exec "norm " . <sid>v_subword(v:true, v:false)<cr> -vnoremap <silent> <Plug>(inner-sub-word) :<c-u>exec "norm " . <sid>v_subword(v:true, v:false)<cr> -onoremap <silent> <Plug>(around-sub-word) :<c-u>exec "norm " . <sid>v_subword(v:true, v:true)<cr> -vnoremap <silent> <Plug>(around-sub-word) :<c-u>exec "norm " . <sid>v_subword(v:true, v:true)<cr> +onoremap <silent> <Plug>(inner-sub-word) :<c-u>exec "norm " . subwords#visual(v:true, v:false)<cr> +vnoremap <silent> <Plug>(inner-sub-word) :<c-u>exec "norm " . subwords#visual(v:true, v:false)<cr> +onoremap <silent> <Plug>(around-sub-word) :<c-u>exec "norm " . subwords#visual(v:true, v:true)<cr> +vnoremap <silent> <Plug>(around-sub-word) :<c-u>exec "norm " . subwords#visual(v:true, v:true)<cr> if ! exists('g:subwords_include_bindings') let g:subwords_include_bindings = 1 @@ -19,206 +19,28 @@ endif " These mappings are the same as above, except prefer_camel is turned off, so " snake case is used in the case of a conflict. -onoremap <silent> <Plug>(inner-sub-word-prefer-snake) :<c-u>exec "norm " . <sid>v_subword(v:false, v:false)<cr> -vnoremap <silent> <Plug>(inner-sub-word-prefer-snake) :<c-u>exec "norm " . <sid>v_subword(v:false, v:false)<cr> -onoremap <silent> <Plug>(around-sub-word-prefer-snake) :<c-u>exec "norm " . <sid>v_subword(v:false, v:true)<cr> -vnoremap <silent> <Plug>(around-sub-word-prefer-snake) :<c-u>exec "norm " . <sid>v_subword(v:false, v:true)<cr> +onoremap <silent> <Plug>(inner-sub-word-prefer-snake) :<c-u>exec "norm " . subwords#visual(v:false, v:false)<cr> +vnoremap <silent> <Plug>(inner-sub-word-prefer-snake) :<c-u>exec "norm " . subwords#visual(v:false, v:false)<cr> +onoremap <silent> <Plug>(around-sub-word-prefer-snake) :<c-u>exec "norm " . subwords#visual(v:false, v:true)<cr> +vnoremap <silent> <Plug>(around-sub-word-prefer-snake) :<c-u>exec "norm " . subwords#visual(v:false, v:true)<cr> " Movement keys for subwords. These all have prefer_camel set to true, the idea " being it's pretty easy to navigate underscores with f_ and t_, but more " difficult to navigate upper case letters. -noremap <silent> <Plug>(next-subword) :<c-u>silent! call <SID>next_subword(v:false, v:true)<cr> -noremap <silent> <Plug>(prev-subword) :<c-u>silent! call <SID>next_subword(v:false, v:false)<cr> -vnoremap <expr> <silent> <Plug>(next-subword) visualmode() . ":\<c-u>silent! call \<SID>next_subword(visualmode(), v:true)\<cr>m'gv``" -vnoremap <expr> <silent> <Plug>(prev-subword) visualmode() . ":\<c-u>silent! call \<SID>next_subword(visualmode(), v:false)<cr>m'gv``" - -function! s:clear_subword_mark() - let s:subword_motion = "" - return '' -endfunction - -function! s:subword_repeat(char) - if s:subword_motion == '' - return a:char - endif - - let mot = (s:subword_motion == 'next') != (a:char == ',') - - let s:subword_nosave = 1 - if mot - return "\<Plug>(next-subword)" - else - return "\<Plug>(prev-subword)" - endif -endfunction - -noremap <expr> <silent> <Plug>(subwords-replace-;) <SID>subword_repeat(';') -noremap <expr> <silent> <Plug>(subwords-replace-,) <SID>subword_repeat(',') -vnoremap <expr> <silent> <Plug>(subwords-replace-;) <SID>subword_repeat(';') -vnoremap <expr> <silent> <Plug>(subwords-replace-,) <SID>subword_repeat(',') - -noremap <expr> <silent> <Plug>(subwords-replace-t) <SID>clear_subword_mark() . "t" -noremap <expr> <silent> <Plug>(subwords-replace-f) <SID>clear_subword_mark() . "f" -noremap <expr> <silent> <Plug>(subwords-replace-T) <SID>clear_subword_mark() . "T" -noremap <expr> <silent> <Plug>(subwords-replace-F) <SID>clear_subword_mark() . "F" - -let s:subword_motion = "" -let s:subword_nosave = 0 - -" Return the type of meta-word (i.e. camelCase, snake_case). If -" a:prefer_camel is set, then a word like ThisIs_A_MixOfCamel_And_Snake will -" some_snake_cae SomeCamelCase SOME_CONSTANT_CASE -" return 'camel', otherwise it'll return 'snake'. -function! s:detect_word_type(prefer_camel, word) abort - let is_camel = 0 - if a:word =~ '[a-z][A-Z]' - let is_camel = 1 - - if a:prefer_camel - " The word contains a camelCase boundary. - return 'camel' - endif - endif - - if a:word =~ '_' - " The word contains a sake_case boundary. - return 'snake' - endif - - if is_camel - return 'camel' - endif - - " There is not discernible type, it's probably just a single word. - return 'word' -endfunction - -function! s:next_subword(vis, forward) - if ! s:subword_nosave - if a:forward - let s:subword_motion = 'next' - else - let s:subword_motion = 'prev' - endif - endif - - let i = 0 - while i < v:count1 - call search( - \ '\([a-z]\zs[A-Z]\ze\)\|\(\W\|_\)\zs\w\ze', (a:forward ? '' : 'b')) - let i += 1 - endwhile - - let s:subword_nosave = 0 -endfunction - -" Highlight an inner subword. -function! s:v_subword(prefer_camel, around) - - " Detect the type of the word. - let word = expand("<cword>") - let t = s:detect_word_type(a:prefer_camel, word) - - let expr = '' - - if t == 'camel' - let expr = 'v' - - let line = getline('.') - let c = col('.') - 1 - let i = 0 - - while line[c] =~ '[a-z]' && c >= 0 - let c -= 1 - let i += 1 - endwhile - - " camelCase - - if c >= 0 && ! (line[c] =~ '[A-Z_]') - " If we are at the beginning of the meta word, the don't go back too far. - let i -= 1 - endif - - if i > 0 - let expr .= printf("%dh", i) - endif - - let expr .= 'o' - - let c = col('.') - 1 - let i = 0 - - if line[c] =~ '[A-Z_]' - " Actually on the starting capital letter, include it, but start counting - " from the next character. - let i += 1 - let c += 1 - endif - - while c < len(line) && line[c] =~ '[a-z]' - let c += 1 - let i += 1 - endwhile - - if i > 1 - let expr .= printf("%dl", i - 1) - endif - - elseif t == "snake" - let expr = 'v' - - let line = getline('.') - let c = col('.') - 1 - let i = 0 - - while c >= 0 && !( line[c] =~ '\W' ) && line[c] != '_' - let c -= 1 - let i += 1 - endwhile - - let lhs_under = c >= 0 && line[c] == '_' - - let i -= 1 - let c += 1 - - if i > 0 - let expr .= printf('%dho', i) - endif - - let c = col('.') - 1 - let i = 0 - - while c < len(line) && !(line[c] =~ '\W') && line[c] != '_' - let c += 1 - let i += 1 - endwhile - - let rhs_under = c < len(line) && line[c] == '_' - - let i -= 1 - let c -= 1 - - if i > 0 - let expr .= printf('%dl', i) - endif - - if a:around - if rhs_under - let expr .= 'l' - elseif lhs_under - let expr .= 'oho' - endif - endif - - - elseif t == "word" - " Just a word? Easy peasy. - let expr = 'viw' - endif - - return expr -endfunction! +noremap <silent> <Plug>(next-subword) :<c-u>silent! call subwords#next(v:false, v:true)<cr> +noremap <silent> <Plug>(prev-subword) :<c-u>silent! call subwords#next(v:false, v:false)<cr> +vnoremap <expr> <silent> <Plug>(next-subword) visualmode() . ":\<c-u>silent! call subwords#next(visualmode(), v:true)\<cr>m'gv``" +vnoremap <expr> <silent> <Plug>(prev-subword) visualmode() . ":\<c-u>silent! call subwords#next(visualmode(), v:false)<cr>m'gv``" + +noremap <expr> <silent> <Plug>(subwords-replace-;) subwords#repeat(';') +noremap <expr> <silent> <Plug>(subwords-replace-,) subwords#repeat(',') +vnoremap <expr> <silent> <Plug>(subwords-replace-;) subwords#repeat(';') +vnoremap <expr> <silent> <Plug>(subwords-replace-,) subwords#repeat(',') + +noremap <expr> <silent> <Plug>(subwords-replace-t) subwords#clear_mark() . "t" +noremap <expr> <silent> <Plug>(subwords-replace-f) subwords#clear_mark() . "f" +noremap <expr> <silent> <Plug>(subwords-replace-T) subwords#clear_mark() . "T" +noremap <expr> <silent> <Plug>(subwords-replace-F) subwords#clear_mark() . "F" if g:subwords_include_bindings onoremap <silent> i_ <Plug>(inner-sub-word-prefer-snake) |