source:
issm/oecreview/Archive/14312-15392/ISSM-14736-14737.diff
Last change on this file was 15393, checked in by , 12 years ago | |
---|---|
File size: 31.3 KB |
-
../trunk-jpl/externalpackages/vim/addons/vim/plugin/matchit.vim
1 " matchit.vim: (global plugin) Extended "%" matching 2 " Last Change: Fri Jan 25 10:00 AM 2008 EST 3 " Maintainer: Benji Fisher PhD <benji@member.AMS.org> 4 " Version: 1.13.2, for Vim 6.3+ 5 " URL: http://www.vim.org/script.php?script_id=39 6 7 " Documentation: 8 " The documentation is in a separate file, matchit.txt . 9 10 " Credits: 11 " Vim editor by Bram Moolenaar (Thanks, Bram!) 12 " Original script and design by Raul Segura Acevedo 13 " Support for comments by Douglas Potts 14 " Support for back references and other improvements by Benji Fisher 15 " Support for many languages by Johannes Zellner 16 " Suggestions for improvement, bug reports, and support for additional 17 " languages by Jordi-Albert Batalla, Neil Bird, Servatius Brandt, Mark 18 " Collett, Stephen Wall, Dany St-Amant, Yuheng Xie, and Johannes Zellner. 19 20 " Debugging: 21 " If you'd like to try the built-in debugging commands... 22 " :MatchDebug to activate debugging for the current buffer 23 " This saves the values of several key script variables as buffer-local 24 " variables. See the MatchDebug() function, below, for details. 25 26 " TODO: I should think about multi-line patterns for b:match_words. 27 " This would require an option: how many lines to scan (default 1). 28 " This would be useful for Python, maybe also for *ML. 29 " TODO: Maybe I should add a menu so that people will actually use some of 30 " the features that I have implemented. 31 " TODO: Eliminate the MultiMatch function. Add yet another argument to 32 " Match_wrapper() instead. 33 " TODO: Allow :let b:match_words = '\(\(foo\)\(bar\)\):\3\2:end\1' 34 " TODO: Make backrefs safer by using '\V' (very no-magic). 35 " TODO: Add a level of indirection, so that custom % scripts can use my 36 " work but extend it. 37 38 " allow user to prevent loading 39 " and prevent duplicate loading 40 if exists("loaded_matchit") || &cp 41 finish 42 endif 43 let loaded_matchit = 1 44 let s:last_mps = "" 45 let s:last_words = ":" 46 47 let s:save_cpo = &cpo 48 set cpo&vim 49 50 nnoremap <silent> % :<C-U>call <SID>Match_wrapper('',1,'n') <CR> 51 nnoremap <silent> g% :<C-U>call <SID>Match_wrapper('',0,'n') <CR> 52 vnoremap <silent> % :<C-U>call <SID>Match_wrapper('',1,'v') <CR>m'gv`` 53 vnoremap <silent> g% :<C-U>call <SID>Match_wrapper('',0,'v') <CR>m'gv`` 54 onoremap <silent> % v:<C-U>call <SID>Match_wrapper('',1,'o') <CR> 55 onoremap <silent> g% v:<C-U>call <SID>Match_wrapper('',0,'o') <CR> 56 57 " Analogues of [{ and ]} using matching patterns: 58 nnoremap <silent> [% :<C-U>call <SID>MultiMatch("bW", "n") <CR> 59 nnoremap <silent> ]% :<C-U>call <SID>MultiMatch("W", "n") <CR> 60 vmap [% <Esc>[%m'gv`` 61 vmap ]% <Esc>]%m'gv`` 62 " vnoremap <silent> [% :<C-U>call <SID>MultiMatch("bW", "v") <CR>m'gv`` 63 " vnoremap <silent> ]% :<C-U>call <SID>MultiMatch("W", "v") <CR>m'gv`` 64 onoremap <silent> [% v:<C-U>call <SID>MultiMatch("bW", "o") <CR> 65 onoremap <silent> ]% v:<C-U>call <SID>MultiMatch("W", "o") <CR> 66 67 " text object: 68 vmap a% <Esc>[%v]% 69 70 " Auto-complete mappings: (not yet "ready for prime time") 71 " TODO Read :help write-plugin for the "right" way to let the user 72 " specify a key binding. 73 " let g:match_auto = '<C-]>' 74 " let g:match_autoCR = '<C-CR>' 75 " if exists("g:match_auto") 76 " execute "inoremap " . g:match_auto . ' x<Esc>"=<SID>Autocomplete()<CR>Pls' 77 " endif 78 " if exists("g:match_autoCR") 79 " execute "inoremap " . g:match_autoCR . ' <CR><C-R>=<SID>Autocomplete()<CR>' 80 " endif 81 " if exists("g:match_gthhoh") 82 " execute "inoremap " . g:match_gthhoh . ' <C-O>:call <SID>Gthhoh()<CR>' 83 " endif " gthhoh = "Get the heck out of here!" 84 85 let s:notslash = '\\\@<!\%(\\\\\)*' 86 87 function! s:Match_wrapper(word, forward, mode) range 88 " In s:CleanUp(), :execute "set" restore_options . 89 let restore_options = (&ic ? " " : " no") . "ignorecase" 90 if exists("b:match_ignorecase") 91 let &ignorecase = b:match_ignorecase 92 endif 93 let restore_options = " ve=" . &ve . restore_options 94 set ve= 95 " If this function was called from Visual mode, make sure that the cursor 96 " is at the correct end of the Visual range: 97 if a:mode == "v" 98 execute "normal! gv\<Esc>" 99 endif 100 " In s:CleanUp(), we may need to check whether the cursor moved forward. 101 let startline = line(".") 102 let startcol = col(".") 103 " Use default behavior if called with a count. 104 if v:count 105 exe "normal! " . v:count . "%" 106 return s:CleanUp(restore_options, a:mode, startline, startcol) 107 end 108 109 " First step: if not already done, set the script variables 110 " s:do_BR flag for whether there are backrefs 111 " s:pat parsed version of b:match_words 112 " s:all regexp based on s:pat and the default groups 113 " 114 if !exists("b:match_words") || b:match_words == "" 115 let match_words = "" 116 " Allow b:match_words = "GetVimMatchWords()" . 117 elseif b:match_words =~ ":" 118 let match_words = b:match_words 119 else 120 execute "let match_words =" b:match_words 121 endif 122 " Thanks to Preben "Peppe" Guldberg and Bram Moolenaar for this suggestion! 123 if (match_words != s:last_words) || (&mps != s:last_mps) || 124 \ exists("b:match_debug") 125 let s:last_words = match_words 126 let s:last_mps = &mps 127 " The next several lines were here before 128 " BF started messing with this script. 129 " quote the special chars in 'matchpairs', replace [,:] with \| and then 130 " append the builtin pairs (/*, */, #if, #ifdef, #else, #elif, #endif) 131 " let default = substitute(escape(&mps, '[$^.*~\\/?]'), '[,:]\+', 132 " \ '\\|', 'g').'\|\/\*\|\*\/\|#if\>\|#ifdef\>\|#else\>\|#elif\>\|#endif\>' 133 let default = escape(&mps, '[$^.*~\\/?]') . (strlen(&mps) ? "," : "") . 134 \ '\/\*:\*\/,#if\%(def\)\=:#else\>:#elif\>:#endif\>' 135 " s:all = pattern with all the keywords 136 let match_words = match_words . (strlen(match_words) ? "," : "") . default 137 if match_words !~ s:notslash . '\\\d' 138 let s:do_BR = 0 139 let s:pat = match_words 140 else 141 let s:do_BR = 1 142 let s:pat = s:ParseWords(match_words) 143 endif 144 let s:all = substitute(s:pat, s:notslash . '\zs[,:]\+', '\\|', 'g') 145 let s:all = '\%(' . s:all . '\)' 146 " let s:all = '\%(' . substitute(s:all, '\\\ze[,:]', '', 'g') . '\)' 147 if exists("b:match_debug") 148 let b:match_pat = s:pat 149 endif 150 endif 151 152 " Second step: set the following local variables: 153 " matchline = line on which the cursor started 154 " curcol = number of characters before match 155 " prefix = regexp for start of line to start of match 156 " suffix = regexp for end of match to end of line 157 " Require match to end on or after the cursor and prefer it to 158 " start on or before the cursor. 159 let matchline = getline(startline) 160 if a:word != '' 161 " word given 162 if a:word !~ s:all 163 echohl WarningMsg|echo 'Missing rule for word:"'.a:word.'"'|echohl NONE 164 return s:CleanUp(restore_options, a:mode, startline, startcol) 165 endif 166 let matchline = a:word 167 let curcol = 0 168 let prefix = '^\%(' 169 let suffix = '\)$' 170 " Now the case when "word" is not given 171 else " Find the match that ends on or after the cursor and set curcol. 172 let regexp = s:Wholematch(matchline, s:all, startcol-1) 173 let curcol = match(matchline, regexp) 174 " If there is no match, give up. 175 if curcol == -1 176 return s:CleanUp(restore_options, a:mode, startline, startcol) 177 endif 178 let endcol = matchend(matchline, regexp) 179 let suf = strlen(matchline) - endcol 180 let prefix = (curcol ? '^.*\%' . (curcol + 1) . 'c\%(' : '^\%(') 181 let suffix = (suf ? '\)\%' . (endcol + 1) . 'c.*$' : '\)$') 182 endif 183 if exists("b:match_debug") 184 let b:match_match = matchstr(matchline, regexp) 185 let b:match_col = curcol+1 186 endif 187 188 " Third step: Find the group and single word that match, and the original 189 " (backref) versions of these. Then, resolve the backrefs. 190 " Set the following local variable: 191 " group = colon-separated list of patterns, one of which matches 192 " = ini:mid:fin or ini:fin 193 " 194 " Reconstruct the version with unresolved backrefs. 195 let patBR = substitute(match_words.',', 196 \ s:notslash.'\zs[,:]*,[,:]*', ',', 'g') 197 let patBR = substitute(patBR, s:notslash.'\zs:\{2,}', ':', 'g') 198 " Now, set group and groupBR to the matching group: 'if:endif' or 199 " 'while:endwhile' or whatever. A bit of a kluge: s:Choose() returns 200 " group . "," . groupBR, and we pick it apart. 201 let group = s:Choose(s:pat, matchline, ",", ":", prefix, suffix, patBR) 202 let i = matchend(group, s:notslash . ",") 203 let groupBR = strpart(group, i) 204 let group = strpart(group, 0, i-1) 205 " Now, matchline =~ prefix . substitute(group,':','\|','g') . suffix 206 if s:do_BR " Do the hard part: resolve those backrefs! 207 let group = s:InsertRefs(groupBR, prefix, group, suffix, matchline) 208 endif 209 if exists("b:match_debug") 210 let b:match_wholeBR = groupBR 211 let i = matchend(groupBR, s:notslash . ":") 212 let b:match_iniBR = strpart(groupBR, 0, i-1) 213 endif 214 215 " Fourth step: Set the arguments for searchpair(). 216 let i = matchend(group, s:notslash . ":") 217 let j = matchend(group, '.*' . s:notslash . ":") 218 let ini = strpart(group, 0, i-1) 219 let mid = substitute(strpart(group, i,j-i-1), s:notslash.'\zs:', '\\|', 'g') 220 let fin = strpart(group, j) 221 "Un-escape the remaining , and : characters. 222 let ini = substitute(ini, s:notslash . '\zs\\\(:\|,\)', '\1', 'g') 223 let mid = substitute(mid, s:notslash . '\zs\\\(:\|,\)', '\1', 'g') 224 let fin = substitute(fin, s:notslash . '\zs\\\(:\|,\)', '\1', 'g') 225 " searchpair() requires that these patterns avoid \(\) groups. 226 let ini = substitute(ini, s:notslash . '\zs\\(', '\\%(', 'g') 227 let mid = substitute(mid, s:notslash . '\zs\\(', '\\%(', 'g') 228 let fin = substitute(fin, s:notslash . '\zs\\(', '\\%(', 'g') 229 " Set mid. This is optimized for readability, not micro-efficiency! 230 if a:forward && matchline =~ prefix . fin . suffix 231 \ || !a:forward && matchline =~ prefix . ini . suffix 232 let mid = "" 233 endif 234 " Set flag. This is optimized for readability, not micro-efficiency! 235 if a:forward && matchline =~ prefix . fin . suffix 236 \ || !a:forward && matchline !~ prefix . ini . suffix 237 let flag = "bW" 238 else 239 let flag = "W" 240 endif 241 " Set skip. 242 if exists("b:match_skip") 243 let skip = b:match_skip 244 elseif exists("b:match_comment") " backwards compatibility and testing! 245 let skip = "r:" . b:match_comment 246 else 247 let skip = 's:comment\|string' 248 endif 249 let skip = s:ParseSkip(skip) 250 if exists("b:match_debug") 251 let b:match_ini = ini 252 let b:match_tail = (strlen(mid) ? mid.'\|' : '') . fin 253 endif 254 255 " Fifth step: actually start moving the cursor and call searchpair(). 256 " Later, :execute restore_cursor to get to the original screen. 257 let restore_cursor = virtcol(".") . "|" 258 normal! g0 259 let restore_cursor = line(".") . "G" . virtcol(".") . "|zs" . restore_cursor 260 normal! H 261 let restore_cursor = "normal!" . line(".") . "Gzt" . restore_cursor 262 execute restore_cursor 263 call cursor(0, curcol + 1) 264 " normal! 0 265 " if curcol 266 " execute "normal!" . curcol . "l" 267 " endif 268 if skip =~ 'synID' && !(has("syntax") && exists("g:syntax_on")) 269 let skip = "0" 270 else 271 execute "if " . skip . "| let skip = '0' | endif" 272 endif 273 let sp_return = searchpair(ini, mid, fin, flag, skip) 274 let final_position = "call cursor(" . line(".") . "," . col(".") . ")" 275 " Restore cursor position and original screen. 276 execute restore_cursor 277 normal! m' 278 if sp_return > 0 279 execute final_position 280 endif 281 return s:CleanUp(restore_options, a:mode, startline, startcol, mid.'\|'.fin) 282 endfun 283 284 " Restore options and do some special handling for Operator-pending mode. 285 " The optional argument is the tail of the matching group. 286 fun! s:CleanUp(options, mode, startline, startcol, ...) 287 execute "set" a:options 288 " Open folds, if appropriate. 289 if a:mode != "o" 290 if &foldopen =~ "percent" 291 normal! zv 292 endif 293 " In Operator-pending mode, we want to include the whole match 294 " (for example, d%). 295 " This is only a problem if we end up moving in the forward direction. 296 elseif (a:startline < line(".")) || 297 \ (a:startline == line(".") && a:startcol < col(".")) 298 if a:0 299 " Check whether the match is a single character. If not, move to the 300 " end of the match. 301 let matchline = getline(".") 302 let currcol = col(".") 303 let regexp = s:Wholematch(matchline, a:1, currcol-1) 304 let endcol = matchend(matchline, regexp) 305 if endcol > currcol " This is NOT off by one! 306 execute "normal!" . (endcol - currcol) . "l" 307 endif 308 endif " a:0 309 endif " a:mode != "o" && etc. 310 return 0 311 endfun 312 313 " Example (simplified HTML patterns): if 314 " a:groupBR = '<\(\k\+\)>:</\1>' 315 " a:prefix = '^.\{3}\(' 316 " a:group = '<\(\k\+\)>:</\(\k\+\)>' 317 " a:suffix = '\).\{2}$' 318 " a:matchline = "123<tag>12" or "123</tag>12" 319 " then extract "tag" from a:matchline and return "<tag>:</tag>" . 320 fun! s:InsertRefs(groupBR, prefix, group, suffix, matchline) 321 if a:matchline !~ a:prefix . 322 \ substitute(a:group, s:notslash . '\zs:', '\\|', 'g') . a:suffix 323 return a:group 324 endif 325 let i = matchend(a:groupBR, s:notslash . ':') 326 let ini = strpart(a:groupBR, 0, i-1) 327 let tailBR = strpart(a:groupBR, i) 328 let word = s:Choose(a:group, a:matchline, ":", "", a:prefix, a:suffix, 329 \ a:groupBR) 330 let i = matchend(word, s:notslash . ":") 331 let wordBR = strpart(word, i) 332 let word = strpart(word, 0, i-1) 333 " Now, a:matchline =~ a:prefix . word . a:suffix 334 if wordBR != ini 335 let table = s:Resolve(ini, wordBR, "table") 336 else 337 " let table = "----------" 338 let table = "" 339 let d = 0 340 while d < 10 341 if tailBR =~ s:notslash . '\\' . d 342 " let table[d] = d 343 let table = table . d 344 else 345 let table = table . "-" 346 endif 347 let d = d + 1 348 endwhile 349 endif 350 let d = 9 351 while d 352 if table[d] != "-" 353 let backref = substitute(a:matchline, a:prefix.word.a:suffix, 354 \ '\'.table[d], "") 355 " Are there any other characters that should be escaped? 356 let backref = escape(backref, '*,:') 357 execute s:Ref(ini, d, "start", "len") 358 let ini = strpart(ini, 0, start) . backref . strpart(ini, start+len) 359 let tailBR = substitute(tailBR, s:notslash . '\zs\\' . d, 360 \ escape(backref, '\\'), 'g') 361 endif 362 let d = d-1 363 endwhile 364 if exists("b:match_debug") 365 if s:do_BR 366 let b:match_table = table 367 let b:match_word = word 368 else 369 let b:match_table = "" 370 let b:match_word = "" 371 endif 372 endif 373 return ini . ":" . tailBR 374 endfun 375 376 " Input a comma-separated list of groups with backrefs, such as 377 " a:groups = '\(foo\):end\1,\(bar\):end\1' 378 " and return a comma-separated list of groups with backrefs replaced: 379 " return '\(foo\):end\(foo\),\(bar\):end\(bar\)' 380 fun! s:ParseWords(groups) 381 let groups = substitute(a:groups.",", s:notslash.'\zs[,:]*,[,:]*', ',', 'g') 382 let groups = substitute(groups, s:notslash . '\zs:\{2,}', ':', 'g') 383 let parsed = "" 384 while groups =~ '[^,:]' 385 let i = matchend(groups, s:notslash . ':') 386 let j = matchend(groups, s:notslash . ',') 387 let ini = strpart(groups, 0, i-1) 388 let tail = strpart(groups, i, j-i-1) . ":" 389 let groups = strpart(groups, j) 390 let parsed = parsed . ini 391 let i = matchend(tail, s:notslash . ':') 392 while i != -1 393 " In 'if:else:endif', ini='if' and word='else' and then word='endif'. 394 let word = strpart(tail, 0, i-1) 395 let tail = strpart(tail, i) 396 let i = matchend(tail, s:notslash . ':') 397 let parsed = parsed . ":" . s:Resolve(ini, word, "word") 398 endwhile " Now, tail has been used up. 399 let parsed = parsed . "," 400 endwhile " groups =~ '[^,:]' 401 let parsed = substitute(parsed, ',$', '', '') 402 return parsed 403 endfun 404 405 " TODO I think this can be simplified and/or made more efficient. 406 " TODO What should I do if a:start is out of range? 407 " Return a regexp that matches all of a:string, such that 408 " matchstr(a:string, regexp) represents the match for a:pat that starts 409 " as close to a:start as possible, before being preferred to after, and 410 " ends after a:start . 411 " Usage: 412 " let regexp = s:Wholematch(getline("."), 'foo\|bar', col(".")-1) 413 " let i = match(getline("."), regexp) 414 " let j = matchend(getline("."), regexp) 415 " let match = matchstr(getline("."), regexp) 416 fun! s:Wholematch(string, pat, start) 417 let group = '\%(' . a:pat . '\)' 418 let prefix = (a:start ? '\(^.*\%<' . (a:start + 2) . 'c\)\zs' : '^') 419 let len = strlen(a:string) 420 let suffix = (a:start+1 < len ? '\(\%>'.(a:start+1).'c.*$\)\@=' : '$') 421 if a:string !~ prefix . group . suffix 422 let prefix = '' 423 endif 424 return prefix . group . suffix 425 endfun 426 427 " No extra arguments: s:Ref(string, d) will 428 " find the d'th occurrence of '\(' and return it, along with everything up 429 " to and including the matching '\)'. 430 " One argument: s:Ref(string, d, "start") returns the index of the start 431 " of the d'th '\(' and any other argument returns the length of the group. 432 " Two arguments: s:Ref(string, d, "foo", "bar") returns a string to be 433 " executed, having the effect of 434 " :let foo = s:Ref(string, d, "start") 435 " :let bar = s:Ref(string, d, "len") 436 fun! s:Ref(string, d, ...) 437 let len = strlen(a:string) 438 if a:d == 0 439 let start = 0 440 else 441 let cnt = a:d 442 let match = a:string 443 while cnt 444 let cnt = cnt - 1 445 let index = matchend(match, s:notslash . '\\(') 446 if index == -1 447 return "" 448 endif 449 let match = strpart(match, index) 450 endwhile 451 let start = len - strlen(match) 452 if a:0 == 1 && a:1 == "start" 453 return start - 2 454 endif 455 let cnt = 1 456 while cnt 457 let index = matchend(match, s:notslash . '\\(\|\\)') - 1 458 if index == -2 459 return "" 460 endif 461 " Increment if an open, decrement if a ')': 462 let cnt = cnt + (match[index]=="(" ? 1 : -1) " ')' 463 " let cnt = stridx('0(', match[index]) + cnt 464 let match = strpart(match, index+1) 465 endwhile 466 let start = start - 2 467 let len = len - start - strlen(match) 468 endif 469 if a:0 == 1 470 return len 471 elseif a:0 == 2 472 return "let " . a:1 . "=" . start . "| let " . a:2 . "=" . len 473 else 474 return strpart(a:string, start, len) 475 endif 476 endfun 477 478 " Count the number of disjoint copies of pattern in string. 479 " If the pattern is a literal string and contains no '0' or '1' characters 480 " then s:Count(string, pattern, '0', '1') should be faster than 481 " s:Count(string, pattern). 482 fun! s:Count(string, pattern, ...) 483 let pat = escape(a:pattern, '\\') 484 if a:0 > 1 485 let foo = substitute(a:string, '[^'.a:pattern.']', "a:1", "g") 486 let foo = substitute(a:string, pat, a:2, "g") 487 let foo = substitute(foo, '[^' . a:2 . ']', "", "g") 488 return strlen(foo) 489 endif 490 let result = 0 491 let foo = a:string 492 let index = matchend(foo, pat) 493 while index != -1 494 let result = result + 1 495 let foo = strpart(foo, index) 496 let index = matchend(foo, pat) 497 endwhile 498 return result 499 endfun 500 501 " s:Resolve('\(a\)\(b\)', '\(c\)\2\1\1\2') should return table.word, where 502 " word = '\(c\)\(b\)\(a\)\3\2' and table = '-32-------'. That is, the first 503 " '\1' in target is replaced by '\(a\)' in word, table[1] = 3, and this 504 " indicates that all other instances of '\1' in target are to be replaced 505 " by '\3'. The hard part is dealing with nesting... 506 " Note that ":" is an illegal character for source and target, 507 " unless it is preceded by "\". 508 fun! s:Resolve(source, target, output) 509 let word = a:target 510 let i = matchend(word, s:notslash . '\\\d') - 1 511 let table = "----------" 512 while i != -2 " There are back references to be replaced. 513 let d = word[i] 514 let backref = s:Ref(a:source, d) 515 " The idea is to replace '\d' with backref. Before we do this, 516 " replace any \(\) groups in backref with :1, :2, ... if they 517 " correspond to the first, second, ... group already inserted 518 " into backref. Later, replace :1 with \1 and so on. The group 519 " number w+b within backref corresponds to the group number 520 " s within a:source. 521 " w = number of '\(' in word before the current one 522 let w = s:Count( 523 \ substitute(strpart(word, 0, i-1), '\\\\', '', 'g'), '\(', '1') 524 let b = 1 " number of the current '\(' in backref 525 let s = d " number of the current '\(' in a:source 526 while b <= s:Count(substitute(backref, '\\\\', '', 'g'), '\(', '1') 527 \ && s < 10 528 if table[s] == "-" 529 if w + b < 10 530 " let table[s] = w + b 531 let table = strpart(table, 0, s) . (w+b) . strpart(table, s+1) 532 endif 533 let b = b + 1 534 let s = s + 1 535 else 536 execute s:Ref(backref, b, "start", "len") 537 let ref = strpart(backref, start, len) 538 let backref = strpart(backref, 0, start) . ":". table[s] 539 \ . strpart(backref, start+len) 540 let s = s + s:Count(substitute(ref, '\\\\', '', 'g'), '\(', '1') 541 endif 542 endwhile 543 let word = strpart(word, 0, i-1) . backref . strpart(word, i+1) 544 let i = matchend(word, s:notslash . '\\\d') - 1 545 endwhile 546 let word = substitute(word, s:notslash . '\zs:', '\\', 'g') 547 if a:output == "table" 548 return table 549 elseif a:output == "word" 550 return word 551 else 552 return table . word 553 endif 554 endfun 555 556 " Assume a:comma = ",". Then the format for a:patterns and a:1 is 557 " a:patterns = "<pat1>,<pat2>,..." 558 " a:1 = "<alt1>,<alt2>,..." 559 " If <patn> is the first pattern that matches a:string then return <patn> 560 " if no optional arguments are given; return <patn>,<altn> if a:1 is given. 561 fun! s:Choose(patterns, string, comma, branch, prefix, suffix, ...) 562 let tail = (a:patterns =~ a:comma."$" ? a:patterns : a:patterns . a:comma) 563 let i = matchend(tail, s:notslash . a:comma) 564 if a:0 565 let alttail = (a:1 =~ a:comma."$" ? a:1 : a:1 . a:comma) 566 let j = matchend(alttail, s:notslash . a:comma) 567 endif 568 let current = strpart(tail, 0, i-1) 569 if a:branch == "" 570 let currpat = current 571 else 572 let currpat = substitute(current, s:notslash . a:branch, '\\|', 'g') 573 endif 574 while a:string !~ a:prefix . currpat . a:suffix 575 let tail = strpart(tail, i) 576 let i = matchend(tail, s:notslash . a:comma) 577 if i == -1 578 return -1 579 endif 580 let current = strpart(tail, 0, i-1) 581 if a:branch == "" 582 let currpat = current 583 else 584 let currpat = substitute(current, s:notslash . a:branch, '\\|', 'g') 585 endif 586 if a:0 587 let alttail = strpart(alttail, j) 588 let j = matchend(alttail, s:notslash . a:comma) 589 endif 590 endwhile 591 if a:0 592 let current = current . a:comma . strpart(alttail, 0, j-1) 593 endif 594 return current 595 endfun 596 597 " Call this function to turn on debugging information. Every time the main 598 " script is run, buffer variables will be saved. These can be used directly 599 " or viewed using the menu items below. 600 if !exists(":MatchDebug") 601 command! -nargs=0 MatchDebug call s:Match_debug() 602 endif 603 604 fun! s:Match_debug() 605 let b:match_debug = 1 " Save debugging information. 606 " pat = all of b:match_words with backrefs parsed 607 amenu &Matchit.&pat :echo b:match_pat<CR> 608 " match = bit of text that is recognized as a match 609 amenu &Matchit.&match :echo b:match_match<CR> 610 " curcol = cursor column of the start of the matching text 611 amenu &Matchit.&curcol :echo b:match_col<CR> 612 " wholeBR = matching group, original version 613 amenu &Matchit.wh&oleBR :echo b:match_wholeBR<CR> 614 " iniBR = 'if' piece, original version 615 amenu &Matchit.ini&BR :echo b:match_iniBR<CR> 616 " ini = 'if' piece, with all backrefs resolved from match 617 amenu &Matchit.&ini :echo b:match_ini<CR> 618 " tail = 'else\|endif' piece, with all backrefs resolved from match 619 amenu &Matchit.&tail :echo b:match_tail<CR> 620 " fin = 'endif' piece, with all backrefs resolved from match 621 amenu &Matchit.&word :echo b:match_word<CR> 622 " '\'.d in ini refers to the same thing as '\'.table[d] in word. 623 amenu &Matchit.t&able :echo '0:' . b:match_table . ':9'<CR> 624 endfun 625 626 " Jump to the nearest unmatched "(" or "if" or "<tag>" if a:spflag == "bW" 627 " or the nearest unmatched "</tag>" or "endif" or ")" if a:spflag == "W". 628 " Return a "mark" for the original position, so that 629 " let m = MultiMatch("bW", "n") ... execute m 630 " will return to the original position. If there is a problem, do not 631 " move the cursor and return "", unless a count is given, in which case 632 " go up or down as many levels as possible and again return "". 633 " TODO This relies on the same patterns as % matching. It might be a good 634 " idea to give it its own matching patterns. 635 fun! s:MultiMatch(spflag, mode) 636 if !exists("b:match_words") || b:match_words == "" 637 return "" 638 end 639 let restore_options = (&ic ? "" : "no") . "ignorecase" 640 if exists("b:match_ignorecase") 641 let &ignorecase = b:match_ignorecase 642 endif 643 let startline = line(".") 644 let startcol = col(".") 645 646 " First step: if not already done, set the script variables 647 " s:do_BR flag for whether there are backrefs 648 " s:pat parsed version of b:match_words 649 " s:all regexp based on s:pat and the default groups 650 " This part is copied and slightly modified from s:Match_wrapper(). 651 let default = escape(&mps, '[$^.*~\\/?]') . (strlen(&mps) ? "," : "") . 652 \ '\/\*:\*\/,#if\%(def\)\=:#else\>:#elif\>:#endif\>' 653 " Allow b:match_words = "GetVimMatchWords()" . 654 if b:match_words =~ ":" 655 let match_words = b:match_words 656 else 657 execute "let match_words =" b:match_words 658 endif 659 if (match_words != s:last_words) || (&mps != s:last_mps) || 660 \ exists("b:match_debug") 661 let s:last_words = match_words 662 let s:last_mps = &mps 663 if match_words !~ s:notslash . '\\\d' 664 let s:do_BR = 0 665 let s:pat = match_words 666 else 667 let s:do_BR = 1 668 let s:pat = s:ParseWords(match_words) 669 endif 670 let s:all = '\%(' . substitute(s:pat . (strlen(s:pat)?",":"") . default, 671 \ '[,:]\+','\\|','g') . '\)' 672 if exists("b:match_debug") 673 let b:match_pat = s:pat 674 endif 675 endif 676 677 " Second step: figure out the patterns for searchpair() 678 " and save the screen, cursor position, and 'ignorecase'. 679 " - TODO: A lot of this is copied from s:Match_wrapper(). 680 " - maybe even more functionality should be split off 681 " - into separate functions! 682 let cdefault = (s:pat =~ '[^,]$' ? "," : "") . default 683 let open = substitute(s:pat . cdefault, 684 \ s:notslash . '\zs:.\{-}' . s:notslash . ',', '\\),\\(', 'g') 685 let open = '\(' . substitute(open, s:notslash . '\zs:.*$', '\\)', '') 686 let close = substitute(s:pat . cdefault, 687 \ s:notslash . '\zs,.\{-}' . s:notslash . ':', '\\),\\(', 'g') 688 let close = substitute(close, '^.\{-}' . s:notslash . ':', '\\(', '') . '\)' 689 if exists("b:match_skip") 690 let skip = b:match_skip 691 elseif exists("b:match_comment") " backwards compatibility and testing! 692 let skip = "r:" . b:match_comment 693 else 694 let skip = 's:comment\|string' 695 endif 696 let skip = s:ParseSkip(skip) 697 " let restore_cursor = line(".") . "G" . virtcol(".") . "|" 698 " normal! H 699 " let restore_cursor = "normal!" . line(".") . "Gzt" . restore_cursor 700 let restore_cursor = virtcol(".") . "|" 701 normal! g0 702 let restore_cursor = line(".") . "G" . virtcol(".") . "|zs" . restore_cursor 703 normal! H 704 let restore_cursor = "normal!" . line(".") . "Gzt" . restore_cursor 705 execute restore_cursor 706 707 " Third step: call searchpair(). 708 " Replace '\('--but not '\\('--with '\%(' and ',' with '\|'. 709 let openpat = substitute(open, '\(\\\@<!\(\\\\\)*\)\@<=\\(', '\\%(', 'g') 710 let openpat = substitute(openpat, ',', '\\|', 'g') 711 let closepat = substitute(close, '\(\\\@<!\(\\\\\)*\)\@<=\\(', '\\%(', 'g') 712 let closepat = substitute(closepat, ',', '\\|', 'g') 713 if skip =~ 'synID' && !(has("syntax") && exists("g:syntax_on")) 714 let skip = '0' 715 else 716 execute "if " . skip . "| let skip = '0' | endif" 717 endif 718 mark ' 719 let level = v:count1 720 while level 721 if searchpair(openpat, '', closepat, a:spflag, skip) < 1 722 call s:CleanUp(restore_options, a:mode, startline, startcol) 723 return "" 724 endif 725 let level = level - 1 726 endwhile 727 728 " Restore options and return a string to restore the original position. 729 call s:CleanUp(restore_options, a:mode, startline, startcol) 730 return restore_cursor 731 endfun 732 733 " Search backwards for "if" or "while" or "<tag>" or ... 734 " and return "endif" or "endwhile" or "</tag>" or ... . 735 " For now, this uses b:match_words and the same script variables 736 " as s:Match_wrapper() . Later, it may get its own patterns, 737 " either from a buffer variable or passed as arguments. 738 " fun! s:Autocomplete() 739 " echo "autocomplete not yet implemented :-(" 740 " if !exists("b:match_words") || b:match_words == "" 741 " return "" 742 " end 743 " let startpos = s:MultiMatch("bW") 744 " 745 " if startpos == "" 746 " return "" 747 " endif 748 " " - TODO: figure out whether 'if' or '<tag>' matched, and construct 749 " " - the appropriate closing. 750 " let matchline = getline(".") 751 " let curcol = col(".") - 1 752 " " - TODO: Change the s:all argument if there is a new set of match pats. 753 " let regexp = s:Wholematch(matchline, s:all, curcol) 754 " let suf = strlen(matchline) - matchend(matchline, regexp) 755 " let prefix = (curcol ? '^.\{' . curcol . '}\%(' : '^\%(') 756 " let suffix = (suf ? '\).\{' . suf . '}$' : '\)$') 757 " " Reconstruct the version with unresolved backrefs. 758 " let patBR = substitute(b:match_words.',', '[,:]*,[,:]*', ',', 'g') 759 " let patBR = substitute(patBR, ':\{2,}', ':', "g") 760 " " Now, set group and groupBR to the matching group: 'if:endif' or 761 " " 'while:endwhile' or whatever. 762 " let group = s:Choose(s:pat, matchline, ",", ":", prefix, suffix, patBR) 763 " let i = matchend(group, s:notslash . ",") 764 " let groupBR = strpart(group, i) 765 " let group = strpart(group, 0, i-1) 766 " " Now, matchline =~ prefix . substitute(group,':','\|','g') . suffix 767 " if s:do_BR 768 " let group = s:InsertRefs(groupBR, prefix, group, suffix, matchline) 769 " endif 770 " " let g:group = group 771 " 772 " " - TODO: Construct the closing from group. 773 " let fake = "end" . expand("<cword>") 774 " execute startpos 775 " return fake 776 " endfun 777 778 " Close all open structures. "Get the heck out of here!" 779 " fun! s:Gthhoh() 780 " let close = s:Autocomplete() 781 " while strlen(close) 782 " put=close 783 " let close = s:Autocomplete() 784 " endwhile 785 " endfun 786 787 " Parse special strings as typical skip arguments for searchpair(): 788 " s:foo becomes (current syntax item) =~ foo 789 " S:foo becomes (current syntax item) !~ foo 790 " r:foo becomes (line before cursor) =~ foo 791 " R:foo becomes (line before cursor) !~ foo 792 fun! s:ParseSkip(str) 793 let skip = a:str 794 if skip[1] == ":" 795 if skip[0] == "s" 796 let skip = "synIDattr(synID(line('.'),col('.'),1),'name') =~? '" . 797 \ strpart(skip,2) . "'" 798 elseif skip[0] == "S" 799 let skip = "synIDattr(synID(line('.'),col('.'),1),'name') !~? '" . 800 \ strpart(skip,2) . "'" 801 elseif skip[0] == "r" 802 let skip = "strpart(getline('.'),0,col('.'))=~'" . strpart(skip,2). "'" 803 elseif skip[0] == "R" 804 let skip = "strpart(getline('.'),0,col('.'))!~'" . strpart(skip,2). "'" 805 endif 806 endif 807 return skip 808 endfun 809 810 let &cpo = s:save_cpo 811 812 " vim:sts=2:sw=2:
Note:
See TracBrowser
for help on using the repository browser.