forked from Julian/vim-textobj-variable-segment
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathvariable_segment.vim
139 lines (121 loc) · 4.62 KB
/
variable_segment.vim
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
" 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']
" 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)
call search(a:right_boundary, 'c')
for _ in range(v:count1 - 1)
if getpos('.') != word_end
call search(a:right_boundary)
endif
endfor
let end_position = getpos('.')
return ['v', start_position, end_position]
endfunction
function! s:select_a()
let right_boundaries = ['_', '\l\u', '\u\u\l', '\a\d', '\d\a', '\k\>']
let right_boundary = join(right_boundaries, '\|')
let [type, start_position, end_position] = s:select('a', right_boundary)
let [_, start_line, start_column, _] = start_position
call search('\k\>', 'c')
if end_position == getpos('.') &&
\ getline(start_line)[start_column - 2] =~# '_'
let start_position[2] -= 1
endif
let was_small_camel = match(expand('<cword>'), '^_*\l.*\u') != -1
if was_small_camel
call search('\<', 'bc')
let [_, _, word_start, _] = getpos('.')
if start_column - 2 <= word_start ||
\ getline(start_line)[:start_column - 2] =~# '^_*$'
call setpos('.', end_position)
let l:tildeop = &tildeop
set notildeop
normal! l~
let &tildeop = l:tildeop
endif
endif
return [type, start_position, end_position]
endfunction
function! s:select_i()
let right_boundaries = ['\k_', '\l\u', '\u\u\l', '\a\d', '\d\a', '\k\>']
return s:select('i', join(right_boundaries, '\|'))
endfunction
function! textobj#variable_segment#select_i() abort
return s:select_i()
endfunction
function! textobj#variable_segment#select_a() abort
return s:select_a()
endfunction