]> git.r.bdr.sh - rbdr/dotfiles/blame - vim/plugin/indent-object.vim
Merge branch 'master' of github.com:benbeltran/dotfiles
[rbdr/dotfiles] / vim / plugin / indent-object.vim
CommitLineData
0d23b6e5
BB
1"--------------------------------------------------------------------------------
2"
3" Copyright (c) 2010 Michael Smith <msmith@msmith.id.au>
4"
5" http://github.com/michaeljsmith/vim-indent-object
6"
7" Permission is hereby granted, free of charge, to any person obtaining a copy
8" of this software and associated documentation files (the "Software"), to
9" deal in the Software without restriction, including without limitation the
10" rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
11" sell copies of the Software, and to permit persons to whom the Software is
12" furnished to do so, subject to the following conditions:
13"
14" The above copyright notice and this permission notice shall be included in
15" all copies or substantial portions of the Software.
16"
17" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18" IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19" FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20" AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21" LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22" FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
23" IN THE SOFTWARE.
24"
25"--------------------------------------------------------------------------------
26
27" Mappings excluding line below.
28onoremap <silent>ai :<C-u>cal <Sid>HandleTextObjectMapping(0, 0, 0, [line("."), line("."), col("."), col(".")])<CR>
29onoremap <silent>ii :<C-u>cal <Sid>HandleTextObjectMapping(1, 0, 0, [line("."), line("."), col("."), col(".")])<CR>
30vnoremap <silent>ai :<C-u>cal <Sid>HandleTextObjectMapping(0, 0, 1, [line("'<"), line("'>"), col("'<"), col("'>")])<CR><Esc>gv
31vnoremap <silent>ii :<C-u>cal <Sid>HandleTextObjectMapping(1, 0, 1, [line("'<"), line("'>"), col("'<"), col("'>")])<CR><Esc>gv
32
33" Mappings including line below.
34onoremap <silent>aI :<C-u>cal <Sid>HandleTextObjectMapping(0, 1, 0, [line("."), line("."), col("."), col(".")])<CR>
35onoremap <silent>iI :<C-u>cal <Sid>HandleTextObjectMapping(1, 1, 0, [line("."), line("."), col("."), col(".")])<CR>
36vnoremap <silent>aI :<C-u>cal <Sid>HandleTextObjectMapping(0, 1, 1, [line("'<"), line("'>"), col("'<"), col("'>")])<CR><Esc>gv
37vnoremap <silent>iI :<C-u>cal <Sid>HandleTextObjectMapping(1, 1, 1, [line("'<"), line("'>"), col("'<"), col("'>")])<CR><Esc>gv
38
39let s:l0 = -1
40let s:l1 = -1
41let s:c0 = -1
42let s:c1 = -1
43
44function! <Sid>TextObject(inner, incbelow, vis, range, count)
45
46 " Record the current state of the visual region.
47 let vismode = "V"
48
49 " Detect if this is a completely new visual selction session.
50 let new_vis = 0
51 let new_vis = new_vis || s:l0 != a:range[0]
52 let new_vis = new_vis || s:l1 != a:range[1]
53 let new_vis = new_vis || s:c0 != a:range[2]
54 let new_vis = new_vis || s:c1 != a:range[3]
55
56 let s:l0 = a:range[0]
57 let s:l1 = a:range[1]
58 let s:c0 = a:range[2]
59 let s:c1 = a:range[3]
60
61 " Repeatedly increase the scope of the selection.
62 let itr_cnt = 0
63 let cnt = a:count
64 while cnt > 0
65
66 " Look for the minimum indentation in the current visual region.
67 let l = s:l0
68 let idnt_invalid = 1000
69 let idnt = idnt_invalid
70 while l <= s:l1
71 if !(getline(l) =~ "^\\s*$")
72 let idnt = min([idnt, indent(l)])
73 endif
74 let l += 1
75 endwhile
76
77 " Keep track of where the range should be expanded to.
78 let l_1 = s:l0
79 let l_1o = l_1
80 let l2 = s:l1
81 let l2o = l2
82
83 " If we are highlighting only blank lines, we may not have found a
84 " valid indent. In this case we need to look for the next and previous
85 " non blank lines and check which of those has the largest indent.
86 if idnt == idnt_invalid
87 let idnt = 0
88 let pnb = prevnonblank(s:l0)
89 if pnb
90 let idnt = max([idnt, indent(pnb)])
91 let l_1 = pnb
92 endif
93 let nnb = nextnonblank(s:l0)
94 if nnb
95 let idnt = max([idnt, indent(nnb)])
96 endif
97
98 " If we are in whitespace at the beginning of a block, skip over
99 " it when we are selecting the range. Similarly, if we are in
100 " whitespace at the end, ignore it.
101 if idnt > indent(pnb)
102 let l_1 = nnb
103 endif
104 if idnt > indent(nnb)
105 let l2 = pnb
106 endif
107 endif
108
109 " Search backward for the first line with less indent than the target
110 " indent (skipping blank lines).
111 let blnk = getline(l_1) =~ "^\\s*$"
112 while l_1 > 0 && ((idnt == 0 && !blnk) || (idnt != 0 && (blnk || indent(l_1) >= idnt)))
113 if !blnk || !a:inner
114 let l_1o = l_1
115 endif
116 let l_1 -= 1
117 let blnk = getline(l_1) =~ "^\\s*$"
118 endwhile
119
120 " Search forward for the first line with more indent than the target
121 " indent (skipping blank lines).
122 let line_cnt = line("$")
123 let blnk = getline(l2) =~ "^\\s*$"
124 while l2 <= line_cnt && ((idnt == 0 && !blnk) || (idnt != 0 && (blnk || indent(l2) >= idnt)))
125 if !blnk || !a:inner
126 let l2o = l2
127 endif
128 let l2 += 1
129 let blnk = getline(l2) =~ "^\\s*$"
130 endwhile
131
132 " Determine which of these extensions to include. Include neither if
133 " we are selecting an 'inner' object. Exclude the bottom unless are
134 " told to include it.
135 let idnt2 = max([indent(l_1), indent(l2)])
136 if indent(l_1) < idnt2 || a:inner
137 let l_1 = l_1o
138 endif
139 if indent(l2) < idnt2 || a:inner || !a:incbelow
140 let l2 = l2o
141 endif
142 let l_1 = max([l_1, 1])
143 let l2 = min([l2, line("$")])
144
145 " Extend the columns to the start and end.
146 " If inner is selected, set the final cursor pos to the start
147 " of the text in the line.
148 let c_1 = 1
149 if a:inner
150 let c_1 = match(getline(l_1), "\\c\\S") + 1
151 endif
152 let c2 = len(getline(l2))
153 if !a:inner
154 let c2 += 1
155 endif
156
157 " Make sure there's no change if we haven't really made a
158 " significant change in linewise mode - this makes sure that
159 " we can iteratively increase selection in linewise mode.
160 if itr_cnt == 0 && vismode ==# 'V' && s:l0 == l_1 && s:l1 == l2
161 let c_1 = s:c0
162 let c2 = s:c1
163 endif
164
165 " Check whether the visual region has changed.
166 let chg = 0
167 let chg = chg || s:l0 != l_1
168 let chg = chg || s:l1 != l2
169 let chg = chg || s:c0 != c_1
170 let chg = chg || s:c1 != c2
171
172 if vismode ==# 'V' && new_vis
173 let chg = 1
174 endif
175
176 " Update the vars.
177 let s:l0 = l_1
178 let s:l1 = l2
179 let s:c0 = c_1
180 let s:c1 = c2
181
182 " If there was no change, then don't decrement the count (it didn't
183 " count because it didn't do anything).
184 if chg
185 let cnt = cnt - 1
186 else
187 " Since this didn't work, push the selection back one char. This
188 " will have the effect of getting the enclosing block. Do it at
189 " the beginning rather than the end - the beginning is very likely
190 " to be only one indentation level different.
191 if s:l0 == 0
192 return
193 endif
194 let s:l0 -= 1
195 let s:c0 = len(getline(s:l0))
196 endif
197
198 let itr_cnt += 1
199
200 endwhile
201
202 " Apply the range we have found. Make sure to use the current visual mode.
203 call cursor(s:l0, s:c0)
204 exe "normal! " . vismode
205 call cursor(s:l1, s:c1)
206 normal! o
207
208 " Update these static variables - we need to keep these up-to-date between
209 " invocations because it's the only way we can detect whether it's a new
210 " visual mode. We need to know if it's a new visual mode because otherwise
211 " if there's a single line block in visual line mode and we select it with
212 " "V", we can't tell whether it's already been selected using Vii.
213 exe "normal! \<Esc>"
214 let s:l0 = line("'<")
215 let s:l1 = line("'>")
216 let s:c0 = col("'<")
217 let s:c1 = col("'>")
218 normal gv
219
220endfunction
221
222function! <Sid>HandleTextObjectMapping(inner, incbelow, vis, range)
223 call <Sid>TextObject(a:inner, a:incbelow, a:vis, a:range, v:count1)
224endfunction