diff options
Diffstat (limited to 'autoload')
-rw-r--r-- | autoload/subwords.vim | 185 |
1 files changed, 185 insertions, 0 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 |