From ff48eaa00a03c2bf5b1114d8fd52420278e1b848 Mon Sep 17 00:00:00 2001 From: Ghjuvan Lacambre Date: Fri, 6 Sep 2024 16:55:33 +0200 Subject: [PATCH] Fix "Single-character CamelCase segments seem not to work" (#11) The code isn't pretty but we wouldn't be using Vimscript if we had any beauty standards. --- autoload/textobj/variable_segment.vim | 81 ++++++++++++++++++++++++++- 1 file changed, 79 insertions(+), 2 deletions(-) diff --git a/autoload/textobj/variable_segment.vim b/autoload/textobj/variable_segment.vim index 3c2e40a..89bcab5 100644 --- a/autoload/textobj/variable_segment.vim +++ b/autoload/textobj/variable_segment.vim @@ -1,8 +1,85 @@ +" Given three positions, return the position closest to the first one. +function! s:closest_position(main, a, b) + let a_distance = mapnew(a:main, {k,v->abs(v-a:a[k])}) + let b_distance = mapnew(a:main, {k,v->abs(v-a:b[k])}) + if a_distance[1] < b_distance[1] + return a:a + endif + if a_distance[1] > b_distance[1] + return a:b + endif + if a_distance[2] < b_distance[2] + return a:a + endif + if a_distance[2] > b_distance[2] + return a:b + endif + " Distances are the same, return either one + return a:a +endfunction! + function! s:select(object_type, right_boundary) let left_boundaries = ['_\+\k', '\<', '\l\u', '\u\u\ze\l', '\a\d', '\d\a'] - call search(join(left_boundaries, '\|'), 'bce') - let start_position = getpos('.') + " Gather all possible matches + let cursor_position = getpos('.') + let all_positions = [] + for boundary in left_boundaries + " search() moves the cursor, so we need to reset the cursor's position + " before each search + call setpos('.', cursor_position) + if search(boundary, 'bce') > 0 + call add(all_positions, getpos('.')) + endif + endfor + + " Try to find a good match on the same line and on the left of the cursor + let start_position = v:null + let potential_matches = filter(copy(all_positions), + \ {v -> v[1] == cursor_position[1] && v[2] <= cursor_position[2]}) + if len(potential_matches) > 0 + let start_position = reduce(potential_matches, + \ {a, b -> s:closest_position(cursor_position, a, b)}) + endif + + if type(start_position) == type(v:null) + " No match found yet, try on the same line but on the right of the + " cursor + let potential_matches = filter(copy(all_positions), + \ {v -> v[1] == cursor_position[1] && v[2] > cursor_position[2]}) + if len(potential_matches) > 0 + let start_position = reduce(potential_matches, + \ {a, b -> s:closest_position(cursor_position, a, b)}) + endif + endif + + if type(start_position) == type(v:null) + " No match found yet, try to find one on lines above the cursor + let potential_matches = filter(copy(all_positions), + \ {v -> v[1] < cursor_position[1]}) + if len(potential_matches) > 0 + let start_position = reduce(potential_matches, + \ {a, b -> s:closest_position(cursor_position, a, b)}) + endif + endif + + if type(start_position) == type(v:null) + " No match found yet, try to find one on below after the cursor + let potential_matches = filter(copy(all_positions), + \ {v -> v[1] > cursor_position[1]}) + if len(potential_matches) > 0 + let start_position = reduce(potential_matches, + \ {a, b -> s:closest_position(cursor_position, a, b)}) + endif + endif + + if type(start_position) == type(v:null) + " The buffer must not contain any words - fall back to the cursor's + " position + let start_position = cursor_position + endif + + call setpos('.', start_position) call search('\>', 'c') let word_end = getpos('.') call setpos('.', start_position)