Skip to content

Commit 7401b52

Browse files
Benji Fishervim-scripts
Benji Fisher
authored andcommitted
Version 0.4: Initial upload
0 parents  commit 7401b52

File tree

2 files changed

+228
-0
lines changed

2 files changed

+228
-0
lines changed

Diff for: README

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
This is a mirror of http://www.vim.org/scripts/script.php?script_id=386
2+
3+
This script redefines the % motion so that (in addition to its usual behavior)
4+
it cycles through if/elif/else, try/except/catch, for/continue/break, and
5+
while/continue/break structures. The script also
6+
defines g% to cycle in the opposite direction. Two other motions, [% and ]%,
7+
go to the start and end of the current block, respectively.
8+
9+
All of these motions should work in Normal, Visual, and Operator-pending
10+
modes. For example, d]% should delete (characterwise) until the end of the
11+
current block; v]%d should do the same, going through Visual mode so that
12+
you can see what is being deleted; and V]%d makes it linewise.

Diff for: ftplugin/python_match.vim

+216
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
" Python filetype plugin for matching with % key
2+
" Language: Python (ft=python)
3+
" Last Change: 2002 August 15
4+
" Maintainer: Benji Fisher, Ph.D. <[email protected]>
5+
" Version: 0.4, for Vim 6.1
6+
7+
" allow user to prevent loading and prevent duplicate loading
8+
if exists("b:loaded_py_match") || &cp
9+
finish
10+
endif
11+
let b:loaded_py_match = 1
12+
13+
let s:save_cpo = &cpo
14+
set cpo&vim
15+
16+
" % for if -> elif -> else -> if, g% for else -> elif -> if -> else
17+
nnoremap <buffer> <silent> % :<C-U>call <SID>PyMatch('%','n') <CR>
18+
vnoremap <buffer> <silent> % :<C-U>call <SID>PyMatch('%','v') <CR>m'gv``
19+
onoremap <buffer> <silent> % v:<C-U>call <SID>PyMatch('%','o') <CR>
20+
nnoremap <buffer> <silent> g% :<C-U>call <SID>PyMatch('g%','n') <CR>
21+
vnoremap <buffer> <silent> g% :<C-U>call <SID>PyMatch('g%','v') <CR>m'gv``
22+
onoremap <buffer> <silent> g% v:<C-U>call <SID>PyMatch('g%','o') <CR>
23+
" Move to the start ([%) or end (]%) of the current block.
24+
nnoremap <buffer> <silent> [% :<C-U>call <SID>PyMatch('[%', 'n') <CR>
25+
vmap <buffer> [% <Esc>[%m'gv``
26+
onoremap <buffer> <silent> [% v:<C-U>call <SID>PyMatch('[%', 'o') <CR>
27+
nnoremap <buffer> <silent> ]% :<C-U>call <SID>PyMatch(']%', 'n') <CR>
28+
vmap <buffer> ]% <Esc>]%m'gv``
29+
onoremap <buffer> <silent> ]% v:<C-U>call <SID>PyMatch(']%', 'o') <CR>
30+
31+
" The rest of the file needs to be :sourced only once per session.
32+
if exists("s:loaded_functions") || &cp
33+
finish
34+
endif
35+
let s:loaded_functions = 1
36+
37+
" One problem with matching in Python is that so many parts are optional.
38+
" I deal with this by matching on any known key words at the start of the
39+
" line, if they have the same indent.
40+
"
41+
" Recognize try, except, finally and if, elif, else .
42+
" keywords that start a block:
43+
let s:ini = 'try\|if'
44+
" keywords that continue or end a block:
45+
let s:tail = 'except\|finally'
46+
let s:tail = s:tail . '\|elif\|else'
47+
" all keywords:
48+
let s:all = s:ini . '\|' . s:tail
49+
50+
function! s:PyMatch(type, mode) range
51+
" If this function was called from Visual mode, make sure that the cursor
52+
" is at the correct end of the Visual range:
53+
if a:mode == "v"
54+
execute "normal! gv\<Esc>"
55+
endif
56+
57+
let startline = line(".") " Do not change these: needed for s:CleanUp()
58+
let startcol = col(".")
59+
" In case we start on a comment line, ...
60+
if a:type[0] =~ '[][]'
61+
let currline = s:NonComment(+1, startline-1)
62+
else
63+
let currline = startline
64+
endif
65+
let startindent = indent(currline)
66+
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)
71+
endif
72+
73+
" If called as % or g%, decide whether to bail out.
74+
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)
80+
endif
81+
endif
82+
83+
" If called as %, look down for "elif" or "else" or up for "if".
84+
if a:type == '%'
85+
let next = s:NonComment(+1, currline)
86+
while next != 0 && indent(next) > startindent
87+
let next = s:NonComment(+1, next)
88+
endwhile
89+
if indent(next) == startindent && getline(next) =~ '^\s*\%('.s:tail.'\)'
90+
execute next
91+
return s:CleanUp('', a:mode, startline, startcol, '$')
92+
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, '$')
100+
endif
101+
let next = s:NonComment(-1, next)
102+
endwhile
103+
" If we are still here, there is an error in the file. Let's do nothing.
104+
endif
105+
106+
" 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
117+
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, '$')
133+
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)
149+
endwhile
150+
endif
151+
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
161+
endif
162+
let currline = nextline
163+
let nextline = s:NonComment(+1, currline)
164+
endwhile
165+
" nextline is in the next block or after EOF, so go to currline:
166+
execute currline
167+
return s:CleanUp('', a:mode, startline, startcol, '$')
168+
endif
169+
endfun
170+
171+
" Return the line number of the next non-comment, or 0 if there is none.
172+
" Start at the current line unless the optional second argument is given.
173+
" The direction is specified by a:inc (normally +1 or -1 ;
174+
" no test for a:inc == 0, which may lead to an infinite loop).
175+
fun! s:NonComment(inc, ...)
176+
if a:0 > 0
177+
let next = a:1 + a:inc
178+
else
179+
let next = line(".") + a:inc
180+
endif
181+
while 0 < next && next <= line("$")
182+
if getline(next) !~ '^\s*\(#\|$\)'
183+
return next
184+
endif
185+
let next = next + a:inc
186+
endwhile
187+
return 0 " If the while loop finishes, we fell off the end of the file.
188+
endfun
189+
190+
" Restore options and do some special handling for Operator-pending mode.
191+
" The optional argument is the tail of the matching group.
192+
fun! s:CleanUp(options, mode, startline, startcol, ...)
193+
if strlen(a:options)
194+
execute "set" a:options
195+
endif
196+
" Open folds, if appropriate.
197+
if a:mode != "o"
198+
if &foldopen =~ "percent"
199+
normal! zv
200+
endif
201+
" In Operator-pending mode, we want to include the whole match
202+
" (for example, d%).
203+
" 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(".")
206+
if a:0
207+
" If we want to include the whole line then a:1 should be '$' .
208+
silent! call search(a:1)
209+
endif
210+
endif " a:mode != "o" && etc.
211+
return 0
212+
endfun
213+
214+
let &cpo = s:save_cpo
215+
216+
" vim:sts=2:sw=2:ff=unix:

0 commit comments

Comments
 (0)