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 " Zero-match the next subword baundary for repeatable cin- and cin_ commands. function! subwords#visual_beginning(prefer_camel, around) return subwords#visual(a:prefer_camel, a:around) . "oi v" 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("") 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 "\(next-subword)" else return "\(prev-subword)" endif endfunction