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
|
"--------------------------------------------------------------------------------
"
" Copyright (c) 2010 Michael Smith <msmith@msmith.id.au>
"
" http://github.com/michaeljsmith/vim-indent-object
"
" Permission is hereby granted, free of charge, to any person obtaining a copy
" of this software and associated documentation files (the "Software"), to
" deal in the Software without restriction, including without limitation the
" rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
" sell copies of the Software, and to permit persons to whom the Software is
" furnished to do so, subject to the following conditions:
"
" The above copyright notice and this permission notice shall be included in
" all copies or substantial portions of the Software.
"
" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
" IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
" FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
" AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
" LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
" FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
" IN THE SOFTWARE.
"
"--------------------------------------------------------------------------------
" Mappings excluding line below.
onoremap <silent>ai :<C-u>cal <Sid>HandleTextObjectMapping(0, 0, 0, [line("."), line("."), col("."), col(".")])<CR>
onoremap <silent>ii :<C-u>cal <Sid>HandleTextObjectMapping(1, 0, 0, [line("."), line("."), col("."), col(".")])<CR>
vnoremap <silent>ai :<C-u>cal <Sid>HandleTextObjectMapping(0, 0, 1, [line("'<"), line("'>"), col("'<"), col("'>")])<CR><Esc>gv
vnoremap <silent>ii :<C-u>cal <Sid>HandleTextObjectMapping(1, 0, 1, [line("'<"), line("'>"), col("'<"), col("'>")])<CR><Esc>gv
" Mappings including line below.
onoremap <silent>aI :<C-u>cal <Sid>HandleTextObjectMapping(0, 1, 0, [line("."), line("."), col("."), col(".")])<CR>
onoremap <silent>iI :<C-u>cal <Sid>HandleTextObjectMapping(1, 1, 0, [line("."), line("."), col("."), col(".")])<CR>
vnoremap <silent>aI :<C-u>cal <Sid>HandleTextObjectMapping(0, 1, 1, [line("'<"), line("'>"), col("'<"), col("'>")])<CR><Esc>gv
vnoremap <silent>iI :<C-u>cal <Sid>HandleTextObjectMapping(1, 1, 1, [line("'<"), line("'>"), col("'<"), col("'>")])<CR><Esc>gv
let s:l0 = -1
let s:l1 = -1
let s:c0 = -1
let s:c1 = -1
function! <Sid>TextObject(inner, incbelow, vis, range, count)
" Record the current state of the visual region.
let vismode = "V"
" Detect if this is a completely new visual selction session.
let new_vis = 0
let new_vis = new_vis || s:l0 != a:range[0]
let new_vis = new_vis || s:l1 != a:range[1]
let new_vis = new_vis || s:c0 != a:range[2]
let new_vis = new_vis || s:c1 != a:range[3]
let s:l0 = a:range[0]
let s:l1 = a:range[1]
let s:c0 = a:range[2]
let s:c1 = a:range[3]
" Repeatedly increase the scope of the selection.
let itr_cnt = 0
let cnt = a:count
while cnt > 0
" Look for the minimum indentation in the current visual region.
let l = s:l0
let idnt_invalid = 1000
let idnt = idnt_invalid
while l <= s:l1
if !(getline(l) =~ "^\\s*$")
let idnt = min([idnt, indent(l)])
endif
let l += 1
endwhile
" Keep track of where the range should be expanded to.
let l_1 = s:l0
let l_1o = l_1
let l2 = s:l1
let l2o = l2
" If we are highlighting only blank lines, we may not have found a
" valid indent. In this case we need to look for the next and previous
" non blank lines and check which of those has the largest indent.
if idnt == idnt_invalid
let idnt = 0
let pnb = prevnonblank(s:l0)
if pnb
let idnt = max([idnt, indent(pnb)])
let l_1 = pnb
endif
let nnb = nextnonblank(s:l0)
if nnb
let idnt = max([idnt, indent(nnb)])
endif
" If we are in whitespace at the beginning of a block, skip over
" it when we are selecting the range. Similarly, if we are in
" whitespace at the end, ignore it.
if idnt > indent(pnb)
let l_1 = nnb
endif
if idnt > indent(nnb)
let l2 = pnb
endif
endif
" Search backward for the first line with less indent than the target
" indent (skipping blank lines).
let blnk = getline(l_1) =~ "^\\s*$"
while l_1 > 0 && ((idnt == 0 && !blnk) || (idnt != 0 && (blnk || indent(l_1) >= idnt)))
if !blnk || !a:inner
let l_1o = l_1
endif
let l_1 -= 1
let blnk = getline(l_1) =~ "^\\s*$"
endwhile
" Search forward for the first line with more indent than the target
" indent (skipping blank lines).
let line_cnt = line("$")
let blnk = getline(l2) =~ "^\\s*$"
while l2 <= line_cnt && ((idnt == 0 && !blnk) || (idnt != 0 && (blnk || indent(l2) >= idnt)))
if !blnk || !a:inner
let l2o = l2
endif
let l2 += 1
let blnk = getline(l2) =~ "^\\s*$"
endwhile
" Determine which of these extensions to include. Include neither if
" we are selecting an 'inner' object. Exclude the bottom unless are
" told to include it.
let idnt2 = max([indent(l_1), indent(l2)])
if indent(l_1) < idnt2 || a:inner
let l_1 = l_1o
endif
if indent(l2) < idnt2 || a:inner || !a:incbelow
let l2 = l2o
endif
let l_1 = max([l_1, 1])
let l2 = min([l2, line("$")])
" Extend the columns to the start and end.
" If inner is selected, set the final cursor pos to the start
" of the text in the line.
let c_1 = 1
if a:inner
let c_1 = match(getline(l_1), "\\c\\S") + 1
endif
let c2 = len(getline(l2))
if !a:inner
let c2 += 1
endif
" Make sure there's no change if we haven't really made a
" significant change in linewise mode - this makes sure that
" we can iteratively increase selection in linewise mode.
if itr_cnt == 0 && vismode ==# 'V' && s:l0 == l_1 && s:l1 == l2
let c_1 = s:c0
let c2 = s:c1
endif
" Check whether the visual region has changed.
let chg = 0
let chg = chg || s:l0 != l_1
let chg = chg || s:l1 != l2
let chg = chg || s:c0 != c_1
let chg = chg || s:c1 != c2
if vismode ==# 'V' && new_vis
let chg = 1
endif
" Update the vars.
let s:l0 = l_1
let s:l1 = l2
let s:c0 = c_1
let s:c1 = c2
" If there was no change, then don't decrement the count (it didn't
" count because it didn't do anything).
if chg
let cnt = cnt - 1
else
" Since this didn't work, push the selection back one char. This
" will have the effect of getting the enclosing block. Do it at
" the beginning rather than the end - the beginning is very likely
" to be only one indentation level different.
if s:l0 == 0
return
endif
let s:l0 -= 1
let s:c0 = len(getline(s:l0))
endif
let itr_cnt += 1
endwhile
" Apply the range we have found. Make sure to use the current visual mode.
call cursor(s:l0, s:c0)
exe "normal! " . vismode
call cursor(s:l1, s:c1)
normal! o
" Update these static variables - we need to keep these up-to-date between
" invocations because it's the only way we can detect whether it's a new
" visual mode. We need to know if it's a new visual mode because otherwise
" if there's a single line block in visual line mode and we select it with
" "V", we can't tell whether it's already been selected using Vii.
exe "normal! \<Esc>"
let s:l0 = line("'<")
let s:l1 = line("'>")
let s:c0 = col("'<")
let s:c1 = col("'>")
normal gv
endfunction
function! <Sid>HandleTextObjectMapping(inner, incbelow, vis, range)
call <Sid>TextObject(a:inner, a:incbelow, a:vis, a:range, v:count1)
endfunction
|