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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
|
" Text object creation {{{
" XXX: use virtualedit here, it should greatly simplify things
let s:text_object_number = 0
function Textobj(char, callback)
let s:text_object_number += 1
function s:textobj_{s:text_object_number}(inner, operator, count, callback)
try
let pos = getpos('.')
sandbox let [startline, startcol, endline, endcol] = function(a:callback)(a:inner, a:count)
catch /no-match/
return
finally
call setpos('.', pos)
endtry
if startline == endline
let objlength = endcol - startcol + 1
else
let lines = getline(startline + 1, endline - 1)
let lines = [strpart(getline(startline), startcol - 1)] +
\ lines +
\ [strpart(getline(endline), 0, endcol)]
let objlength = 0
for line in lines
let objlength += strlen(line) + 1
endfor
let objlength -= 1
endif
let whichwrap = &whichwrap
set whichwrap+=s
if startcol > strlen(getline(startline))
let startcol = 1
let startline += 1
let objlength -= 1
endif
call cursor(startline, startcol)
exe 'normal! '.a:operator.objlength.' '
if a:operator == 'c'
normal! l
startinsert
elseif a:operator == 'v'
exe "normal! \<BS>"
endif
let &whichwrap = whichwrap
endfunction
exe 'onoremap <silent>a'.a:char.' <Esc>:call <SID>textobj_'.s:text_object_number.'(0, v:operator, v:prevcount, "'.a:callback.'")<CR>'
exe 'onoremap <silent>i'.a:char.' <Esc>:call <SID>textobj_'.s:text_object_number.'(1, v:operator, v:prevcount, "'.a:callback.'")<CR>'
exe 'xnoremap <silent>a'.a:char.' <Esc>:call <SID>textobj_'.s:text_object_number.'(0, "v", v:prevcount, "'.a:callback.'")<CR>'
exe 'xnoremap <silent>i'.a:char.' <Esc>:call <SID>textobj_'.s:text_object_number.'(1, "v", v:prevcount, "'.a:callback.'")<CR>'
endfunction
" }}}
" Text object definitions {{{
" / for regex {{{
function s:textobj_regex(inner, count)
let pos = getpos('.')
let line = strpart(getline(pos[1]), 0, pos[2])
let lines = getline(1, pos[1] - 1) + [line]
let linenum = pos[1]
for line in reverse(lines)
let objstart = match(line, '.*\zs\\\@<!/') + 1
if objstart != 0
break
endif
let linenum -= 1
endfor
if objstart == 0
throw 'no-match'
endif
let objstart += a:inner
let objstartline = linenum
let line = strpart(getline(pos[1]), pos[2] - 1)
let lines = [line] + getline(pos[1] + 1, line('$'))
let linenum = pos[1]
for line in lines
let objend = match(line, '\\\@<!/') + 1
if objend != 0
if linenum == pos[1]
" have to account for the possibility of a split escape
" sequence
if objend == 1
if getline(pos[1])[pos[2] - 2] == '\'
let objend = match(line, '\\\@<!/', 1) + 1
if objend == 0
let linenum += 1
continue
endif
else
" if we're sitting on a /, don't do anything, since it's
" impossible to know which direction to look
throw 'no-match'
endif
endif
let objend += pos[2] - 1
endif
break
endif
let linenum += 1
endfor
if objend == 0
throw 'no-match'
endif
let objend -= a:inner
let objendline = linenum
return [objstartline, objstart, objendline, objend]
endfunction
" }}}
" f for folds {{{
function s:textobj_fold(inner, count)
if foldlevel(line('.')) == 0
throw 'no-match'
endif
exe 'normal! '.a:count.'[z'
let startline = line('.') + a:inner
normal! ]z
let endline = line('.') - a:inner
return [startline, 1, endline, strlen(getline(endline))]
endfunction
" }}}
" , for function arguments {{{
function s:textobj_arg(inner, count)
let pos = getpos('.')
let curchar = getline(pos[1])[pos[2] - 1]
if curchar == ','
if getline(pos[1])[pos[2] - 2] =~ '\s'
normal! gE
else
exe "normal! \<BS>"
endif
return s:textobj_arg(a:inner, a:count)
elseif curchar =~ '\s'
normal! W
return s:textobj_arg(a:inner, a:count)
endif
let line = strpart(getline(pos[1]), 0, pos[2])
let lines = getline(1, pos[1] - 1) + [line]
let linenum = pos[1]
for line in reverse(lines)
let argbegin = matchend(line, '.*\%(,\s*\|(\)') + 1
if argbegin != 0
while argbegin > strlen(line)
let linenum += 1
let line = getline(linenum)
let argbegin = matchend(line, '^\s*') + 1
endwhile
break
endif
let linenum -= 1
endfor
if argbegin == 0
throw 'no-match'
endif
let argstartline = linenum
let line = strpart(getline(pos[1]), pos[2] - 1)
let lines = [line] + getline(pos[1] + 1, line('$'))
let linenum = pos[1]
for line in lines
let argend = match(line, '\zs.\?\%(,\|)\)') + 1
if argend != 0
if linenum == pos[1]
let argend += pos[2] - 1
endif
if argend == 1 && getline(linenum)[argend - 1] == ')'
let linenum -= 1
let argend = strlen(getline(linenum))
endif
break
endif
let linenum += 1
endfor
if argend == 0
throw 'no-match'
endif
let argendline = linenum
if a:inner == 0
let endline = getline(argendline)
let startline = getline(argstartline)
if argend >= strlen(endline)
let argend = 0
let argendline += 1
let endline = getline(argendline)
endif
if endline[argend] == ')' && startline[argbegin - 2] != '('
let argbegin = match(strpart(startline, 0, argbegin - 1), '\s*$')
while argbegin == 0
let argstartline -= 1
let startline = getline(argstartline)
let argbegin = strlen(startline)
endwhile
elseif endline[argend] != ')'
let argend += matchend(strpart(endline, argend + 1), '^\s*') + 1
if startline[argbegin - 2] == '('
for line in [strpart(endline, argend)] +
\ getline(argendline + 1, line('$'))
let argincr = matchend(line, '\s*\ze\S')
if argincr != -1
let argend += argincr
break
endif
let argendline += 1
let argend = 0
endfor
endif
endif
if argend >= strlen(endline)
if argendline == argstartline
let newbegin = matchend(strpart(endline, 0, argbegin), '.*,')
if newbegin != -1
let argbegin = newbegin + 1
endif
endif
let argend = 0
let argendline += 1
endif
endif
return [argstartline, argbegin, argendline, argend]
endfunction
" }}}
" }}}
" Text object creation {{{
if exists("g:Textobj_regex_enable") && g:Textobj_regex_enable
call Textobj('/', "<SID>textobj_regex")
endif
if exists("g:Textobj_fold_enable") && g:Textobj_fold_enable
call Textobj('f', "<SID>textobj_fold")
endif
if exists("g:Textobj_arg_enable") && g:Textobj_arg_enable
call Textobj(',', "<SID>textobj_arg")
endif
" }}}
|