1
1
" Python filetype plugin for matching with % key
2
2
" Language: Python (ft=python)
3
- " Last Change: 2002 August 15
3
+ " Last Change: Thu 02 Oct 2003 12:12:20 PM EDT
4
4
" Maintainer: Benji Fisher, Ph.D. <[email protected] >
5
- " Version: 0.4, for Vim 6.1
5
+ " Version: 0.5, for Vim 6.1
6
+ " URL: http://www.vim.org/scripts/script.php?script_id=386
6
7
7
8
" allow user to prevent loading and prevent duplicate loading
8
9
if exists (" b:loaded_py_match" ) || &cp
@@ -22,10 +23,10 @@ vnoremap <buffer> <silent> g% :<C-U>call <SID>PyMatch('g%','v') <CR>m'gv``
22
23
onoremap <buffer> <silent> g% v:<C-U> call <SID> PyMatch('g%','o') <CR>
23
24
" Move to the start ([%) or end (]%) of the current block.
24
25
nnoremap <buffer> <silent> [% :<C-U> call <SID> PyMatch('[%', 'n') <CR>
25
- vmap <buffer> [% <Esc> [% m'gv``
26
+ vnoremap <buffer> <silent> [% : <C-U> call <SID> PyMatch('[%','v') <CR> m'gv``
26
27
onoremap <buffer> <silent> [% v:<C-U> call <SID> PyMatch('[%', 'o') <CR>
27
28
nnoremap <buffer> <silent> ]% :<C-U> call <SID> PyMatch(']%', 'n') <CR>
28
- vmap <buffer> ]% <Esc> ]% m'gv``
29
+ vnoremap <buffer> <silent> ]% : <C-U> call <SID> PyMatch(']%','v') <CR> m'gv``
29
30
onoremap <buffer> <silent> ]% v:<C-U> call <SID> PyMatch(']%', 'o') <CR>
30
31
31
32
" The rest of the file needs to be :sourced only once per session.
@@ -40,132 +41,157 @@ let s:loaded_functions = 1
40
41
"
41
42
" Recognize try, except, finally and if, elif, else .
42
43
" keywords that start a block:
43
- let s: ini = ' try\|if'
44
+ let s: ini1 = ' try\|if'
45
+ " These are special, because the matching words may not have the same indent:
46
+ let s: ini2 = ' for\|while'
44
47
" keywords that continue or end a block:
45
- let s: tail = ' except\|finally'
46
- let s: tail = s: tail . ' \|elif\|else'
48
+ let s: tail1 = ' except\|finally'
49
+ let s: tail1 = s: tail1 . ' \|elif\|else'
50
+ " These go with s:ini2 :
51
+ let s: tail2 = ' break\|continue'
47
52
" all keywords:
48
- let s: all = s: ini . ' \|' . s: tail
53
+ let s: all1 = s: ini1 . ' \|' . s: tail1
54
+ let s: all2 = s: ini2 . ' \|' . s: tail2
49
55
50
- function ! s: PyMatch (type , mode ) range
56
+ fun ! s: PyMatch (type , mode ) range
57
+ " I have to do this before the :normal gv...
58
+ let cnt = v: count1
51
59
" If this function was called from Visual mode, make sure that the cursor
52
60
" is at the correct end of the Visual range:
53
61
if a: mode == " v"
54
62
execute " normal! gv\<Esc> "
55
63
endif
64
+ " Use default behavior if called as % with a count.
65
+ if a: type == " %" && v: count
66
+ exe " normal! " . v: count . " %"
67
+ return s: CleanUp (' ' , a: mode )
68
+ endif
56
69
57
- let startline = line (" ." ) " Do not change these: needed for s:CleanUp()
58
- let startcol = col (" ." )
70
+ " Do not change these: needed for s:CleanUp()
71
+ let s: startline = line (" ." )
72
+ let s: startcol = col (" ." )
59
73
" In case we start on a comment line, ...
60
- if a: type[ 0 ] = ~ ' [][] '
61
- let currline = s: NonComment (+ 1 , startline- 1 )
74
+ if a: type == ' [% ' || a: type == ' ]% '
75
+ let currline = s: NonComment (+ 1 , s: startline- 1 )
62
76
else
63
- let currline = startline
77
+ let currline = s: startline
64
78
endif
65
79
let startindent = indent (currline)
80
+ " Set a mark before jumping.
81
+ normal ! m '
66
82
67
- " Use default behavior if called as % with a count.
68
- if a: type == " %" && v: count
69
- exe " normal! " . v: count . " %"
70
- return s: CleanUp (' ' , a: mode , startline, startcol)
83
+ " If called as [%, find the start of the current block.
84
+ " If called as ]%, find the end of the current block.
85
+ if a: type == ' [%' || a: type == ' ]%'
86
+ while cnt > 0
87
+ let currline = (a: type == ' [%' ) ?
88
+ \ s: StartOfBlock (currline) : s: EndOfBlock (currline)
89
+ let cnt = cnt - 1
90
+ endwhile
91
+ execute currline
92
+ return s: CleanUp (' ' , a: mode , ' $' )
71
93
endif
72
94
73
95
" If called as % or g%, decide whether to bail out.
74
96
if a: type == ' %' || a: type == ' g%'
75
- let text = getline (" ." )
76
- if strpart (text, 0 , col (" ." )) = ~ ' \S\s' || text !~ ' ^\s*\%(' . s: all .' \)'
77
- " cursor not on the first WORD or no keyword so bail out
78
- normal ! %
79
- return s: CleanUp (' ' , a: mode , startline, startcol)
97
+ let text = getline (currline)
98
+ if strpart (text, 0 , col (" ." )) = ~ ' \S\s'
99
+ \ || text !~ ' ^\s*\%(' . s: all1 . ' \|' . s: all2 . ' \)'
100
+ " cursor not on the first WORD or no keyword so bail out
101
+ if a: type == ' %'
102
+ normal ! %
103
+ endif
104
+ return s: CleanUp (' ' , a: mode )
105
+ endif
106
+ " If it matches s:all2, we need to find the "for" or "while".
107
+ if text = ~ ' ^\s*\%(' . s: all2 . ' \)'
108
+ let topline = currline
109
+ while getline (topline) !~ ' ^\s*\%(' . s: ini2 . ' \)'
110
+ let temp = s: StartOfBlock (topline)
111
+ if temp == topline " there is no enclosing block.
112
+ return s: CleanUp (' ' , a: mode )
113
+ endif
114
+ let topline = temp
115
+ endwhile
116
+ let topindent = indent (topline)
80
117
endif
81
118
endif
82
119
83
120
" If called as %, look down for "elif" or "else" or up for "if".
84
- if a: type == ' %'
121
+ if a: type == ' %' && text = ~ ' ^\s*\%( ' . s: all1 . ' \) '
85
122
let next = s: NonComment (+ 1 , currline)
86
- while next != 0 && indent (next ) > startindent
123
+ while next > 0 && indent (next ) > startindent
87
124
let next = s: NonComment (+ 1 , next )
88
125
endwhile
89
- if indent (next ) == startindent && getline (next ) = ~ ' ^\s*\%(' .s: tail .' \)'
90
- execute next
91
- return s: CleanUp (' ' , a: mode , startline, startcol, ' $' )
126
+ if next == 0 || indent (next ) < startindent
127
+ \ || getline (next ) !~ ' ^\s*\%(' . s: tail1 . ' \)'
128
+ " There are no "tail1" keywords below startline in this block. Go to
129
+ " the start of the block.
130
+ let next = (text = ~ ' ^\s*\%(' . s: ini1 . ' \)' ) ?
131
+ \ currline : s: StartOfBlock (currline)
92
132
endif
93
- " If we are still here, then there are no "tail" keywords below us in this
94
- " block. Search upwards for the start of the block.
95
- let next = currline
96
- while next != 0 && indent (next ) >= startindent
97
- if indent (next ) == startindent && getline (next ) = ~ ' ^\s*\%(' .s: ini .' \)'
98
- execute next
99
- return s: CleanUp (' ' , a: mode , startline, startcol, ' $' )
133
+ execute next
134
+ return s: CleanUp (' ' , a: mode , ' $' )
135
+ endif
136
+
137
+ " If called as %, look down for "break" or "continue" or up for
138
+ " "for" or "while".
139
+ if a: type == ' %' && text = ~ ' ^\s*\%(' . s: all2 . ' \)'
140
+ let next = s: NonComment (+ 1 , currline)
141
+ while next > 0 && indent (next ) > topindent
142
+ \ && getline (next ) !~ ' ^\s*\%(' . s: tail2 . ' \)'
143
+ " Skip over nested "for" or "while" blocks:
144
+ if getline (next ) = ~ ' ^\s*\%(' . s: ini2 . ' \)'
145
+ let next = s: EndOfBlock (next )
100
146
endif
101
- let next = s: NonComment (- 1 , next )
147
+ let next = s: NonComment (+ 1 , next )
102
148
endwhile
103
- " If we are still here, there is an error in the file. Let's do nothing.
149
+ if indent (next ) > topindent && getline (next ) = ~ ' ^\s*\%(' . s: tail2 . ' \)'
150
+ execute next
151
+ else " There are no " tail2" keywords below v:startline, so go to topline.
152
+ execute topline
153
+ endif
154
+ return s: CleanUp (' ' , a: mode , ' $' )
104
155
endif
105
156
106
157
" If called as g%, look up for "if" or "elif" or "else" or down for any.
107
- if a: type == ' g%'
108
- if getline (currline) = ~ ' ^\s*\(' . s: tail . ' \)'
109
- let next = s: NonComment (-1 , currline)
110
- while next != 0 && indent (next ) > startindent
111
- let next = s: NonComment (-1 , next )
112
- endwhile
113
- if indent (next ) == startindent && getline (next ) = ~ ' ^\s*\%(' .s: all .' \)'
114
- execute next
115
- return s: CleanUp (' ' , a: mode , startline, startcol, ' $' )
116
- endif
158
+ if a: type == ' g%' && text = ~ ' ^\s*\%(' . s: all1 .' \)'
159
+ " If we started at the top of the block, go down to the end of the block.
160
+ if text = ~ ' ^\s*\(' . s: ini1 . ' \)'
161
+ let next = s: EndOfBlock (currline)
117
162
else
118
- " We started at the top of the block.
119
- " Search down for the end of the block.
120
- let next = s: NonComment (+ 1 , currline)
121
- while next != 0 && indent (next ) >= startindent
122
- if indent (next ) == startindent
123
- if getline (next ) = ~ ' ^\s*\(' .s: tail .' \)'
124
- let currline = next
125
- else
126
- break
127
- endif
128
- endif
129
- let next = s: NonComment (+ 1 , next )
130
- endwhile
131
- execute currline
132
- return s: CleanUp (' ' , a: mode , startline, startcol, ' $' )
163
+ let next = s: NonComment (-1 , currline)
133
164
endif
134
- endif
135
-
136
- " If called as [%, find the start of the current block.
137
- if a: type == ' [%'
138
- let tailflag = (getline (currline) = ~ ' ^\s*\(' . s: tail . ' \)' )
139
- let prevline = s: NonComment (-1 , currline)
140
- while prevline > 0
141
- if indent (prevline) < startindent ||
142
- \ tailflag && indent (prevline) == startindent &&
143
- \ getline (prevline) = ~ ' ^\s*\(' . s: ini . ' \)'
144
- " Found the start of block, so go there!
145
- execute prevline
146
- return s: CleanUp (' ' , a: mode , startline, startcol, ' $' )
147
- endif
148
- let prevline = s: NonComment (-1 , prevline)
165
+ while next > 0 && indent (next ) > startindent
166
+ let next = s: NonComment (-1 , next )
149
167
endwhile
168
+ if indent (next ) == startindent && getline (next ) = ~ ' ^\s*\%(' .s: all1 .' \)'
169
+ execute next
170
+ endif
171
+ return s: CleanUp (' ' , a: mode , ' $' )
150
172
endif
151
173
152
- " If called as ]%, find the end of the current block.
153
- if a: type == ' ]%'
154
- let nextline = s: NonComment (+ 1 , currline)
155
- let startofblock = (indent (nextline) > startindent)
156
- while nextline > 0
157
- if indent (nextline) < startindent ||
158
- \ startofblock && indent (nextline) == startindent &&
159
- \ getline (nextline) !~ ' ^\s*\(' . s: tail . ' \)'
160
- break
174
+ " If called as g%, look up for "for" or "while" or down for any.
175
+ if a: type == ' g%' && text = ~ ' ^\s*\%(' . s: all2 . ' \)'
176
+ " Start at topline . If we started on a "for" or "while" then topline is
177
+ " the same as currline, and we want the last "break" or "continue" in the
178
+ " block. Otherwise, we want the last one before currline.
179
+ let botline = (topline == currline) ? line (" $" ) + 1 : currline
180
+ let currline = topline
181
+ let next = s: NonComment (+ 1 , currline)
182
+ while next < botline && indent (next ) > topindent
183
+ if getline (next ) = ~ ' ^\s*\%(' . s: tail2 . ' \)'
184
+ let currline = next
185
+ elseif getline (next ) = ~ ' ^\s*\%(' . s: ini2 . ' \)'
186
+ " Skip over nested "for" or "while" blocks:
187
+ let next = s: EndOfBlock (next )
161
188
endif
162
- let currline = nextline
163
- let nextline = s: NonComment (+ 1 , currline)
189
+ let next = s: NonComment (+ 1 , next )
164
190
endwhile
165
- " nextline is in the next block or after EOF, so go to currline:
166
191
execute currline
167
- return s: CleanUp (' ' , a: mode , startline, startcol, ' $' )
192
+ return s: CleanUp (' ' , a: mode , ' $' )
168
193
endif
194
+
169
195
endfun
170
196
171
197
" Return the line number of the next non-comment, or 0 if there is none.
@@ -187,9 +213,54 @@ fun! s:NonComment(inc, ...)
187
213
return 0 " If the while loop finishes, we fell off the end of the file.
188
214
endfun
189
215
216
+ " Return the line number of the top of the block containing Line a:start .
217
+ " For most lines, this is the first previous line with smaller indent.
218
+ " For lines starting with "except", "finally", "elif", or "else", this is the
219
+ " first previous line starting with "try" or "if".
220
+ fun ! s: StartOfBlock (start )
221
+ let startindent = indent (a: start )
222
+ let tailflag = (getline (a: start ) = ~ ' ^\s*\(' . s: tail1 . ' \)' )
223
+ let prevline = s: NonComment (-1 , a: start )
224
+ while prevline > 0
225
+ if indent (prevline) < startindent ||
226
+ \ tailflag && indent (prevline) == startindent &&
227
+ \ getline (prevline) = ~ ' ^\s*\(' . s: ini1 . ' \)'
228
+ " Found the start of block!
229
+ return prevline
230
+ endif
231
+ let prevline = s: NonComment (-1 , prevline)
232
+ endwhile
233
+ " If the loop completes, then s:NonComment() returned 0, so we are at the
234
+ " top.
235
+ return a: start
236
+ endfun
237
+
238
+ " Return the line number of the end of the block containing Line a:start .
239
+ " For most lines, this is the line before the next line with smaller indent.
240
+ " For lines that begin a block, go to the end of that block, with special
241
+ " treatment for "if" and "try" blocks.
242
+ fun ! s: EndOfBlock (start )
243
+ let startindent = indent (a: start )
244
+ let currline = a: start
245
+ let nextline = s: NonComment (+ 1 , currline)
246
+ let startofblock = (indent (nextline) > startindent) ||
247
+ \ getline (currline) = ~ ' ^\s*\(' . s: ini1 . ' \)'
248
+ while nextline > 0
249
+ if indent (nextline) < startindent ||
250
+ \ startofblock && indent (nextline) == startindent &&
251
+ \ getline (nextline) !~ ' ^\s*\(' . s: tail1 . ' \)'
252
+ break
253
+ endif
254
+ let currline = nextline
255
+ let nextline = s: NonComment (+ 1 , currline)
256
+ endwhile
257
+ " nextline is in the next block or after EOF, so return currline:
258
+ return currline
259
+ endfun
260
+
190
261
" Restore options and do some special handling for Operator-pending mode.
191
262
" The optional argument is the tail of the matching group.
192
- fun ! s: CleanUp (options , mode , startline, startcol, ... )
263
+ fun ! s: CleanUp (options , mode , ... )
193
264
if strlen (a: options )
194
265
execute " set" a: options
195
266
endif
@@ -201,13 +272,13 @@ fun! s:CleanUp(options, mode, startline, startcol, ...)
201
272
" In Operator-pending mode, we want to include the whole match
202
273
" (for example, d%).
203
274
" This is only a problem if we end up moving in the forward direction.
204
- elseif a : startline < line (" ." ) ||
205
- \ a : startline == line (" ." ) && a : startcol < col (" ." )
275
+ elseif s : startline < line (" ." ) ||
276
+ \ s : startline == line (" ." ) && s : startcol < col (" ." )
206
277
if a: 0
207
278
" If we want to include the whole line then a:1 should be '$' .
208
279
silent ! call search (a: 1 )
209
280
endif
210
- endif " a:mode != " o " && etc.
281
+ endif " a:mode != " o "
211
282
return 0
212
283
endfun
213
284
0 commit comments