diff options
Diffstat (limited to 'runtime/macros')
38 files changed, 2972 insertions, 0 deletions
diff --git a/runtime/macros/README.txt b/runtime/macros/README.txt new file mode 100644 index 000000000..61066c5a5 --- /dev/null +++ b/runtime/macros/README.txt @@ -0,0 +1,22 @@ +The macros in the maze, hanoi and urm directories can be used to test Vim for +vi compatibility. They have been written for vi to show its unlimited +possibilities. The life macros can be used for performance comparisons. + +hanoi Macros that solve the tower of hanoi problem. +life Macros that run Conway's game of life. +maze Macros that solve a maze (amazing!). +urm Macros that simulate a simple computer: "Universal Register Machine" + + +The other files contain some handy utilities. They also serve as examples for +how to use Vi and Vim functionality. + +dvorak for when you use a Dvorak keyboard +justify.vim user function for justifying text +matchit.vim + matchit.txt make % match if-fi, HTML tags, and much more +less.sh + less.vim make Vim work like less (or more) +shellmenu.vim menus for editing shell scripts in the GUI version +swapmous.vim swap left and right mouse buttons + +This one is only for Unix. It can be found in the extra archive: +file_select.vim macros that make a handy file selector diff --git a/runtime/macros/README.txt.info b/runtime/macros/README.txt.info Binary files differnew file mode 100644 index 000000000..d173e72e7 --- /dev/null +++ b/runtime/macros/README.txt.info diff --git a/runtime/macros/dvorak b/runtime/macros/dvorak new file mode 100644 index 000000000..0c7588870 --- /dev/null +++ b/runtime/macros/dvorak @@ -0,0 +1,164 @@ +When using a dvorak keyboard this file may be of help to you. +These mappings have been made by Lawrence Kesteloot <kesteloo@cs.unc.edu>. +What they do is that the most often used keys, like hjkl, are put in a more +easy to use position. +It may take some time to learn using this. + +Put these lines in your .vimrc: +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +" Key to go into dvorak mode: +map ,d :source ~/.dvorak +" Key to get out of dvorak mode: +map ,q :source ~/.qwerty +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + +write these lines into the file ~/.dvorak: +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +" Dvorak keyboard, only in insert mode and ex mode. +" You may want to add a list of map's too. +imap! a a +imap! b x +imap! c j +imap! d e +imap! e . +imap! f u +imap! g i +imap! h d +imap! i c +imap! j h +imap! k t +imap! l n +imap! m m +imap! n b +imap! o r +imap! p l +imap! q ' +imap! r p +imap! s o +imap! t y +imap! u g +imap! v k +imap! w , +imap! x q +imap! y f +imap! z ; +imap! ; s +imap! ' - +imap! " _ +imap! , w +imap! . v +imap! / z +imap! A A +imap! B X +imap! C J +imap! D E +imap! E > +imap! F U +imap! G I +imap! H D +imap! I C +imap! J H +imap! K T +imap! L N +imap! M M +imap! N B +imap! O R +imap! P L +imap! Q " +imap! R P +imap! S O +imap! T Y +imap! U G +imap! V K +imap! W < +imap! X Q +imap! Y F +imap! Z : +imap! < W +imap! > V +imap! ? Z +imap! : S +imap! [ / +imap! ] = +imap! { ? +imap! } + +imap! - [ +imap! _ { +imap! = ] +imap! + } +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + +write these lines into the file ~/.qwerty +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +" Qwerty keyboard +unmap! a +unmap! b +unmap! c +unmap! d +unmap! e +unmap! f +unmap! g +unmap! h +unmap! i +unmap! j +unmap! k +unmap! l +unmap! m +unmap! n +unmap! o +unmap! p +unmap! q +unmap! r +unmap! s +unmap! t +unmap! u +unmap! v +unmap! w +unmap! x +unmap! y +unmap! z +unmap! ; +unmap! ' +unmap! \" +unmap! , +unmap! . +unmap! / +unmap! A +unmap! B +unmap! C +unmap! D +unmap! E +unmap! F +unmap! G +unmap! H +unmap! I +unmap! J +unmap! K +unmap! L +unmap! M +unmap! N +unmap! O +unmap! P +unmap! Q +unmap! R +unmap! S +unmap! T +unmap! U +unmap! V +unmap! W +unmap! X +unmap! Y +unmap! Z +unmap! < +unmap! > +unmap! ? +unmap! : +unmap! [ +unmap! ] +unmap! { +unmap! } +unmap! - +unmap! _ +unmap! = +unmap! + +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- diff --git a/runtime/macros/hanoi.info b/runtime/macros/hanoi.info Binary files differnew file mode 100644 index 000000000..8072e5678 --- /dev/null +++ b/runtime/macros/hanoi.info diff --git a/runtime/macros/hanoi/click.me b/runtime/macros/hanoi/click.me new file mode 100644 index 000000000..24f178bfe --- /dev/null +++ b/runtime/macros/hanoi/click.me @@ -0,0 +1,14 @@ + + +See Vim solve the towers of Hanoi! + +Instructions: + type ":so hanoi.vim<RETURN>" to load the macros + type "g" to start it + +and watch it go. + + to quit type ":q!<RETURN>" +to interrupt type CTRL-C + +(This text will disappear as soon as you type "g") diff --git a/runtime/macros/hanoi/click.me.info b/runtime/macros/hanoi/click.me.info Binary files differnew file mode 100644 index 000000000..f38cb5a0a --- /dev/null +++ b/runtime/macros/hanoi/click.me.info diff --git a/runtime/macros/hanoi/hanoi.vim b/runtime/macros/hanoi/hanoi.vim new file mode 100644 index 000000000..834d6f2c4 --- /dev/null +++ b/runtime/macros/hanoi/hanoi.vim @@ -0,0 +1,64 @@ +set remap +set noterse +set wrapscan +" to set the height of the tower, change the digit in the following +" two lines to the height you want (select from 1 to 9) +map t 7 +map! t 7 +map L 1G/t
X/^0
$P1GJ$An$BGC0e$X0E0F$X/T
@f
@h
$A1GJ@f0l$Xn$PU +map g IL + +map J /^0[^t]*$
+map X x +map P p +map U L +map A "fyl +map B "hyl +map C "fp +map e "fy2l +map E "hp +map F "hy2l + +" initialisations: +" KM cleanup buffer +" Y create tower of desired height +" NOQ copy it and inster a T +" NO copy this one +" S change last char into a $ +" R change last char in previous line into a n +" T insert two lines containing a zero +" V add a last line containing a backslash +map I KMYNOQNOSkRTV + +"create empty line +map K 1Go + +"delete to end of file +map M dG + +"yank one line +map N yy + +"put +map O p + +"delete more than height-of-tower characters +map q tllD + +"create a tower of desired height +map Y o0123456789Z0q + +"insert a T in column 1 +map Q 0iT + +"substitute last character with a n +map R $rn + +"substitute last character with a $ +map S $r$ + +"insert two lines containing a zero +map T ko0
0
+ +"add a backslash at the end +map V Go/ diff --git a/runtime/macros/hanoi/poster b/runtime/macros/hanoi/poster new file mode 100644 index 000000000..dd03b2613 --- /dev/null +++ b/runtime/macros/hanoi/poster @@ -0,0 +1,36 @@ +Article 2913 of alt.sources: +Path: oce-rd1!hp4nl!mcsun!uunet!munnari.oz.au!metro!cluster!swift!softway!otc!gregm +From: gregm@otc.otca.oz.au (Greg McFarlane) +Newsgroups: comp.sources.d,alt.sources,comp.editors +Subject: VI SOLVES HANOI +Message-ID: <2323@otc.otca.oz> +Date: 19 Feb 91 01:32:14 GMT +Sender: news@otc.otca.oz +Reply-To: gregm@otc.otca.oz.au (Greg McFarlane) +Organization: OTC Development Unit, Australia +Lines: 80 +Xref: oce-rd1 comp.sources.d:5702 alt.sources:2913 comp.editors:2313 + +Submitted-by: gregm@otc.otca.oz.au +Archive-name: hanoi.vi.macros/part01 + +Everyone seems to be writing stupid Tower of Hanoi programs. +Well, here is the stupidest of them all: the hanoi solving vi macros. + +Save this article, unshar it, and run uudecode on hanoi.vi.macros.uu. +This will give you the macro file hanoi.vi.macros. +Then run vi (with no file: just type "vi") and type: + :so hanoi.vi.macros + g +and watch it go. + +The default height of the tower is 7 but can be easily changed by editing +the macro file. + +The disks aren't actually shown in this version, only numbers representing +each disk, but I believe it is possible to write some macros to show the +disks moving about as well. Any takers? + +(For maze solving macros, see alt.sources or comp.editors) + +Greg diff --git a/runtime/macros/hanoi/poster.info b/runtime/macros/hanoi/poster.info Binary files differnew file mode 100644 index 000000000..780fa35ed --- /dev/null +++ b/runtime/macros/hanoi/poster.info diff --git a/runtime/macros/justify.vim b/runtime/macros/justify.vim new file mode 100644 index 000000000..41d0591fe --- /dev/null +++ b/runtime/macros/justify.vim @@ -0,0 +1,316 @@ +" Function to left and rigt align text. +" +" Written by: Preben "Peppe" Guldberg <c928400@student.dtu.dk> +" Created: 980806 14:13 (or around that time anyway) +" Revised: 001103 00:36 (See "Revisions" below) + + +" function Justify( [ textwidth [, maxspaces [, indent] ] ] ) +" +" Justify() will left and right align a line by filling in an +" appropriate amount of spaces. Extra spaces are added to existing +" spaces starting from the right side of the line. As an example, the +" following documentation has been justified. +" +" The function takes the following arguments: + +" textwidth argument +" ------------------ +" If not specified, the value of the 'textwidth' option is used. If +" 'textwidth' is zero a value of 80 is used. +" +" Additionally the arguments 'tw' and '' are accepted. The value of +" 'textwidth' will be used. These are handy, if you just want to specify +" the maxspaces argument. + +" maxspaces argument +" ------------------ +" If specified, alignment will only be done, if the longest space run +" after alignment is no longer than maxspaces. +" +" An argument of '' is accepted, should the user like to specify all +" arguments. +" +" To aid user defined commands, negative values are accepted aswell. +" Using a negative value specifies the default behaviour: any length of +" space runs will be used to justify the text. + +" indent argument +" --------------- +" This argument specifies how a line should be indented. The default is +" to keep the current indentation. +" +" Negative values: Keep current amount of leading whitespace. +" Positive values: Indent all lines with leading whitespace using this +" amount of whitespace. +" +" Note that the value 0, needs to be quoted as a string. This value +" leads to a left flushed text. +" +" Additionally units of 'shiftwidth'/'sw' and 'tabstop'/'ts' may be +" added. In this case, if the value of indent is positive, the amount of +" whitespace to be added will be multiplied by the value of the +" 'shiftwidth' and 'tabstop' settings. If these units are used, the +" argument must be given as a string, eg. Justify('','','2sw'). +" +" If the values of 'sw' or 'tw' are negative, they are treated as if +" they were 0, which means that the text is flushed left. There is no +" check if a negative number prefix is used to change the sign of a +" negative 'sw' or 'ts' value. +" +" As with the other arguments, '' may be used to get the default +" behaviour. + + +" Notes: +" +" If the line, adjusted for space runs and leading/trailing whitespace, +" is wider than the used textwidth, the line will be left untouched (no +" whitespace removed). This should be equivalent to the behaviour of +" :left, :right and :center. +" +" If the resulting line is shorter than the used textwidth it is left +" untouched. +" +" All space runs in the line are truncated before the alignment is +" carried out. +" +" If you have set 'noexpandtab', :retab! is used to replace space runs +" with whitespace using the value of 'tabstop'. This should be +" conformant with :left, :right and :center. +" +" If joinspaces is set, an extra space is added after '.', '?' and '!'. +" If 'cpooptions' include 'j', extra space is only added after '.'. +" (This may on occasion conflict with maxspaces.) + + +" Related mappings: +" +" Mappings that will align text using the current text width, using at +" most four spaces in a space run and keeping current indentation. +nmap _j :%call Justify('tw',4)<CR> +vmap _j :call Justify('tw',4)<CR> +" +" Mappings that will remove space runs and format lines (might be useful +" prior to aligning the text). +nmap ,gq :%s/\s\+/ /g<CR>gq1G +vmap ,gq :s/\s\+/ /g<CR>gvgq + + +" User defined command: +" +" The following is an ex command that works as a shortcut to the Justify +" function. Arguments to Justify() can be added after the command. +com! -range -nargs=* Justify <line1>,<line2>call Justify(<f-args>) +" +" The following commands are all equivalent: +" +" 1. Simplest use of Justify(): +" :call Justify() +" :Justify +" +" 2. The _j mapping above via the ex command: +" :%Justify tw 4 +" +" 3. Justify visualised text at 72nd column while indenting all +" previously indented text two shiftwidths +" :'<,'>call Justify(72,'','2sw') +" :'<,'>Justify 72 -1 2sw +" +" This documentation has been justified using the following command: +":se et|kz|1;/^" function Justify(/+,'z-g/^" /s/^" //|call Justify(70,3)|s/^/" / + +" Revisions: +" 001103: If 'joinspaces' was set, calculations could be wrong. +" Tabs at start of line could also lead to errors. +" Use setline() instead of "exec 's/foo/bar/' - safer. +" Cleaned up the code a bit. +" +" Todo: Convert maps to the new script specific form + +" Error function +function! Justify_error(message) + echohl Error + echo "Justify([tw, [maxspaces [, indent]]]): " . a:message + echohl None +endfunction + + +" Now for the real thing +function! Justify(...) range + + if a:0 > 3 + call Justify_error("Too many arguments (max 3)") + return 1 + endif + + " Set textwidth (accept 'tw' and '' as arguments) + if a:0 >= 1 + if a:1 =~ '^\(tw\)\=$' + let tw = &tw + elseif a:1 =~ '^\d\+$' + let tw = a:1 + else + call Justify_error("tw must be a number (>0), '' or 'tw'") + return 2 + endif + else + let tw = &tw + endif + if tw == 0 + let tw = 80 + endif + + " Set maximum number of spaces between WORDs + if a:0 >= 2 + if a:2 == '' + let maxspaces = tw + elseif a:2 =~ '^-\d\+$' + let maxspaces = tw + elseif a:2 =~ '^\d\+$' + let maxspaces = a:2 + else + call Justify_error("maxspaces must be a number or ''") + return 3 + endif + else + let maxspaces = tw + endif + if maxspaces <= 1 + call Justify_error("maxspaces should be larger than 1") + return 4 + endif + + " Set the indentation style (accept sw and ts units) + let indent_fix = '' + if a:0 >= 3 + if (a:3 == '') || a:3 =~ '^-[1-9]\d*\(shiftwidth\|sw\|tabstop\|ts\)\=$' + let indent = -1 + elseif a:3 =~ '^-\=0\(shiftwidth\|sw\|tabstop\|ts\)\=$' + let indent = 0 + elseif a:3 =~ '^\d\+\(shiftwidth\|sw\|tabstop\|ts\)\=$' + let indent = substitute(a:3, '\D', '', 'g') + elseif a:3 =~ '^\(shiftwidth\|sw\|tabstop\|ts\)$' + let indent = 1 + else + call Justify_error("indent: a number with 'sw'/'ts' unit") + return 5 + endif + if indent >= 0 + while indent > 0 + let indent_fix = indent_fix . ' ' + let indent = indent - 1 + endwhile + let indent_sw = 0 + if a:3 =~ '\(shiftwidth\|sw\)' + let indent_sw = &sw + elseif a:3 =~ '\(tabstop\|ts\)' + let indent_sw = &ts + endif + let indent_fix2 = '' + while indent_sw > 0 + let indent_fix2 = indent_fix2 . indent_fix + let indent_sw = indent_sw - 1 + endwhile + let indent_fix = indent_fix2 + endif + else + let indent = -1 + endif + + " Avoid substitution reports + let save_report = &report + set report=1000000 + + " Check 'joinspaces' and 'cpo' + if &js == 1 + if &cpo =~ 'j' + let join_str = '\(\. \)' + else + let join_str = '\([.!?!] \)' + endif + endif + + let cur = a:firstline + while cur <= a:lastline + + let str_orig = getline(cur) + let save_et = &et + set et + exec cur . "retab" + let &et = save_et + let str = getline(cur) + + let indent_str = indent_fix + let indent_n = strlen(indent_str) + " Shall we remember the current indentation + if indent < 0 + let indent_orig = matchstr(str_orig, '^\s*') + if strlen(indent_orig) > 0 + let indent_str = indent_orig + let indent_n = strlen(matchstr(str, '^\s*')) + endif + endif + + " Trim trailing, leading and running whitespace + let str = substitute(str, '\s\+$', '', '') + let str = substitute(str, '^\s\+', '', '') + let str = substitute(str, '\s\+', ' ', 'g') + let str_n = strlen(str) + + " Possible addition of space after punctuation + if exists("join_str") + let str = substitute(str, join_str, '\1 ', 'g') + endif + let join_n = strlen(str) - str_n + + " Can extraspaces be added? + " Note that str_n may be less than strlen(str) [joinspaces above] + if strlen(str) < tw - indent_n && str_n > 0 + " How many spaces should be added + let s_add = tw - str_n - indent_n - join_n + let s_nr = strlen(substitute(str, '\S', '', 'g') ) - join_n + let s_dup = s_add / s_nr + let s_mod = s_add % s_nr + + " Test if the changed line fits with tw + if 0 <= (str_n + (maxspaces - 1)*s_nr + indent_n) - tw + + " Duplicate spaces + while s_dup > 0 + let str = substitute(str, '\( \+\)', ' \1', 'g') + let s_dup = s_dup - 1 + endwhile + + " Add extra spaces from the end + while s_mod > 0 + let str = substitute(str, '\(\(\s\+\S\+\)\{' . s_mod . '}\)$', ' \1', '') + let s_mod = s_mod - 1 + endwhile + + " Indent the line + if indent_n > 0 + let str = substitute(str, '^', indent_str, '' ) + endif + + " Replace the line + call setline(cur, str) + + " Convert to whitespace + if &et == 0 + exec cur . 'retab!' + endif + + endif " Change of line + endif " Possible change + + let cur = cur + 1 + endwhile + + norm ^ + + let &report = save_report + +endfunction + +" EOF vim: tw=78 ts=8 sw=4 sts=4 noet ai diff --git a/runtime/macros/less.sh b/runtime/macros/less.sh new file mode 100755 index 000000000..256936b5e --- /dev/null +++ b/runtime/macros/less.sh @@ -0,0 +1,9 @@ +#!/bin/sh +# Shell script to start Vim with less.vim. +# Read stdin if no arguments were given. + +if test $# = 0; then + vim --cmd 'let no_plugin_maps = 1' -c 'runtime! macros/less.vim' - +else + vim --cmd 'let no_plugin_maps = 1' -c 'runtime! macros/less.vim' "$@" +fi diff --git a/runtime/macros/less.vim b/runtime/macros/less.vim new file mode 100644 index 000000000..48ee7ab11 --- /dev/null +++ b/runtime/macros/less.vim @@ -0,0 +1,244 @@ +" Vim script to work like "less" +" Maintainer: Bram Moolenaar <Bram@vim.org> +" Last Change: 2004 Feb 19 + +" Avoid loading this file twice, allow the user to define his own script. +if exists("loaded_less") + finish +endif +let loaded_less = 1 + +" If not reading from stdin, skip files that can't be read. +" Exit if there is no file at all. +if argc() > 0 + let s:i = 0 + while 1 + if filereadable(argv(s:i)) + if s:i != 0 + sleep 3 + endif + break + endif + if isdirectory(argv(s:i)) + echomsg "Skipping directory " . argv(s:i) + elseif getftime(argv(s:i)) < 0 + echomsg "Skipping non-existing file " . argv(s:i) + else + echomsg "Skipping unreadable file " . argv(s:i) + endif + echo "\n" + let s:i = s:i + 1 + if s:i == argc() + quit + endif + next + endwhile +endif + +set nocp +syntax on +set so=0 +set hlsearch +set incsearch +nohlsearch +" Don't remember file names and positions +set viminfo= +set nows +" Inhibit screen updates while searching +let s:lz = &lz +set lz + +" Used after each command: put cursor at end and display position +if &wrap + noremap <SID>L L0:redraw<CR>:file<CR> + au VimEnter * normal! L0 +else + noremap <SID>L Lg0:redraw<CR>:file<CR> + au VimEnter * normal! Lg0 +endif + +" When reading from stdin don't consider the file modified. +au VimEnter * set nomod + +" Can't modify the text +set noma + +" Give help +noremap h :call <SID>Help()<CR> +map H h +fun! s:Help() + echo "<Space> One page forward b One page backward" + echo "d Half a page forward u Half a page backward" + echo "<Enter> One line forward k One line backward" + echo "G End of file g Start of file" + echo "N% percentage in file" + echo "\n" + echo "/pattern Search for pattern ?pattern Search backward for pattern" + echo "n next pattern match N Previous pattern match" + echo "\n" + echo ":n<Enter> Next file :p<Enter> Previous file" + echo "\n" + echo "q Quit v Edit file" + let i = input("Hit Enter to continue") +endfun + +" Scroll one page forward +noremap <script> <Space> :call <SID>NextPage()<CR><SID>L +map <C-V> <Space> +map f <Space> +map <C-F> <Space> +map z <Space> +map <Esc><Space> <Space> +fun! s:NextPage() + if line(".") == line("$") + if argidx() + 1 >= argc() + quit + endif + next + 1 + else + exe "normal! \<C-F>" + endif +endfun + +" Re-read file and page forward "tail -f" +map F :e<CR>G<SID>L:sleep 1<CR>F + +" Scroll half a page forward +noremap <script> d <C-D><SID>L +map <C-D> d + +" Scroll one line forward +noremap <script> <CR> <C-E><SID>L +map <C-N> <CR> +map e <CR> +map <C-E> <CR> +map j <CR> +map <C-J> <CR> + +" Scroll one page backward +noremap <script> b <C-B><SID>L +map <C-B> b +map w b +map <Esc>v b + +" Scroll half a page backward +noremap <script> u <C-U><SID>L +noremap <script> <C-U> <C-U><SID>L + +" Scroll one line backward +noremap <script> k <C-Y><SID>L +map y k +map <C-Y> k +map <C-P> k +map <C-K> k + +" Redraw +noremap <script> r <C-L><SID>L +noremap <script> <C-R> <C-L><SID>L +noremap <script> R <C-L><SID>L + +" Start of file +noremap <script> g gg<SID>L +map < g +map <Esc>< g + +" End of file +noremap <script> G G<SID>L +map > G +map <Esc>> G + +" Go to percentage +noremap <script> % %<SID>L +map p % + +" Search +noremap <script> / H$:call <SID>Forward()<CR>/ +if &wrap + noremap <script> ? H0:call <SID>Backward()<CR>? +else + noremap <script> ? Hg0:call <SID>Backward()<CR>? +endif + +fun! s:Forward() + " Searching forward + noremap <script> n H$nzt<SID>L + if &wrap + noremap <script> N H0Nzt<SID>L + else + noremap <script> N Hg0Nzt<SID>L + endif + cnoremap <script> <CR> <CR>:cunmap <lt>CR><CR>zt<SID>L +endfun + +fun! s:Backward() + " Searching backward + if &wrap + noremap <script> n H0nzt<SID>L + else + noremap <script> n Hg0nzt<SID>L + endif + noremap <script> N H$Nzt<SID>L + cnoremap <script> <CR> <CR>:cunmap <lt>CR><CR>zt<SID>L +endfun + +call s:Forward() + +" Quitting +noremap q :q<CR> + +" Switch to editing (switch off less mode) +map v :silent call <SID>End()<CR> +fun! s:End() + set ma + if exists(s:lz) + let &lz = s:lz + endif + unmap h + unmap H + unmap <Space> + unmap <C-V> + unmap f + unmap <C-F> + unmap z + unmap <Esc><Space> + unmap F + unmap d + unmap <C-D> + unmap <CR> + unmap <C-N> + unmap e + unmap <C-E> + unmap j + unmap <C-J> + unmap b + unmap <C-B> + unmap w + unmap <Esc>v + unmap u + unmap <C-U> + unmap k + unmap y + unmap <C-Y> + unmap <C-P> + unmap <C-K> + unmap r + unmap <C-R> + unmap R + unmap g + unmap < + unmap <Esc>< + unmap G + unmap > + unmap <Esc>> + unmap % + unmap p + unmap n + unmap N + unmap q + unmap v + unmap / + unmap ? +endfun + +" vim: sw=2 diff --git a/runtime/macros/life/click.me b/runtime/macros/life/click.me new file mode 100644 index 000000000..c2ed4691a --- /dev/null +++ b/runtime/macros/life/click.me @@ -0,0 +1,9 @@ + +To run the "Conway's game of life" macros: + + 1. Type ":so life.vim". This loads the macros. + 2. Type "g" to run the macros. + 3. Type CTRL-C to interrupt. + 4. Type ":q!" to get out. + +See life.vim for more advanced usage. diff --git a/runtime/macros/life/click.me.info b/runtime/macros/life/click.me.info Binary files differnew file mode 100644 index 000000000..f5756501f --- /dev/null +++ b/runtime/macros/life/click.me.info diff --git a/runtime/macros/life/life.vim b/runtime/macros/life/life.vim new file mode 100644 index 000000000..adf4e2081 --- /dev/null +++ b/runtime/macros/life/life.vim @@ -0,0 +1,260 @@ +" Macros to play Conway's Game of Life in vi +" Version 1.0m: edges wrap +" by Eli-the-Bearded (eli@netusa.net), Sept 1996 +" This file may be free distributed so long as these credits remain unchanged. +" +" Modified by Bram Moolenaar (Bram@vim.org), 1996 Sept 10 +" - Made it quite a bit faster, but now needs search patterns in the text +" - Changed the order of mappings to top-down. +" - Made "g" run the whole thing, "C" run one generation. +" - Added support for any uppercase character instead of 'X' +" +" Rules: +" If a germ has 0 or 1 live neighbors it dies of loneliness +" If a germ has 2 or 3 live neighbors it survives +" If a germ has 4 to 8 live neighbors it dies of starvation +" If an empty box has 3 live neighbors a new germ is born +" +" A new born germ is an "A". Every generation it gets older: B, C, etc. +" A germ dies of old age when it reaches "Z". +" +" Notice the rules do not mention edges. This version has the edges wrap +" around. I have an earlier version that offers the option of live edges or +" dead edges. Email me if you are interested. -Eli- +" +" Note: This is slow! One generation may take up to ten minutes (depends on +" your computer and the vi version). +" +" Quite a lot of the messy stuff is to work around the vi error "Can't yank +" inside global/macro". Still doesn't work for all versions of vi. +" +" To use these macros: +" +" vi start vi/vim +" +" :so life.mac Source this file +" +" g 'g'o! runs everything until interrupted: "IR". +" +" I Initialize everything. A board will be drawn at the end +" of the current buffer. All line references in these macros +" are relative to the end of the file and playing the game +" can be done safely with any file as the current buffer. +" +" Change the left field with spaces and uppercase letters to suit +" your taste. +" +" C 'C'ompute one generation. +" + idem, time running one generation. +" R 'R'un 'C'ompute until interrupted. +" i<nr><Esc>z Make a number the only thing on the current line and use +" 'z' to time that many generations. +" +" Time to run 30 generations on my 233 AMD K6 (FreeBSD 3.0): +" vim 5.4 xterm 51 sec +" gvim 5.4 Athena 42 sec +" gvim 5.4 Motif 42 sec +" gvim 5.4 GTK 50 sec +" nvi 1.79 xterm 58 sec +" vi 3.7 xterm 2 min 30 sec +" Elvis 2.1 xterm 7 min 50 sec +" Elvis 2.1 X11 6 min 31 sec +" +" Time to run 30 generations on my 850 AMD Duron (FreeBSD 4.2): +" vim 5.8 xterm 21 sec +" vim 6.0 xterm 24 sec +" vim 6.0 Motif 32 sec +" nvi 1.79 xterm 29 sec +" vi 3.7 xterm 32 sec +" elvis 2.1.4 xterm 34 sec +" +" And now the macros, more or less in top-down order. +" +" ----- macros that can be used by the human ----- +" +" 'g'o: 'I'nitialize and then 'R'un 'C'ompute recursively (used by the human) +map g IR +" +" +" 'R'un 'C'ompute recursively (used by the human and 'g'o) +map R CV +" work around "tail recursion" problem in vi, "V" == "R". +map V R +" +" +" 'I'nitialize the board (used by the human and 'g'o) +map I G)0)0)0)0)1)0)0)2)0)0)0)0,ok,-11k,-,R,IIN +" +" +" 'C'ompute next generation (used by the human and others) +map C T>>>>>>>>B& +" +" +" Time running one generation (used by the human) +map + <1C<2 +" +" +" Time running N generations, where N is the number on the current line. +" (used by the human) +map z ,^,&,*,&<1,*<2 +" +" ----- END of macros that can be used by the human ----- +" +" ----- Initialisation ----- +" +map ,- :s/./-/g
+map ,o oPut 'X's in the left box, then hit 'C' or 'R' +map ,R 03stop +" +" Write a new line (used by 'I'nitialize board) +map )0 o- --....................--....................- +map )1 o- VIM --....................--....................- +map )2 o- LIVES --....................--....................- +" +" +" Initialisation of the pattern/command to execute for working out a square. +" Pattern is: "#<germ><count>" +" where <germ> is " " if the current germ is dead, "X" when living. +" <count> is the number of living neighbours (including current germ) +" expressed in X's +" +map ,Il8 O#XXXXXXXXXX .`a22lr +map ,Id8 o# XXXXXXXX .`a22lr +map ,Il7 o#XXXXXXXXX .`a22lr +map ,Id7 o# XXXXXXX .`a22lr +map ,Il6 o#XXXXXXXX .`a22lr +map ,Id6 o# XXXXXX .`a22lr +map ,Il5 o#XXXXXXX .`a22lr +map ,Id5 o# XXXXX .`a22lr +map ,Il4 o#XXXXXX .`a22lr +map ,Id4 o# XXXX .`a22lr +map ,Il3 o#XXXXX .,a +map ,Id3 o# XXX .`a22lrA +map ,Il2 o#XXXX .,a +map ,Id2 o# XX .`a22lr +map ,Il1 o#XXX .`a22lr +map ,Id1 o# X .`a22lr +map ,Il0 o#XX .`a22lr +map ,Id0 o# .`a22lr +" +" Patterns used to replace a germ with it's next generation +map ,Iaa o=AB =BC =CD =DE =EF =FG =GH =HI =IJ =JK =KL =LM =MN =NO =OP =PQ =QR +map ,Iab o=RS =ST =TU =UV =VW =WX =XY =YZ =Z +" +" Insert the searched patterns above the board +map ,IIN G?^top
,Il8,Id8,Il7,Id7,Il6,Id6,Il5,Id5,Il4,Id4,Il3,Id3,Il2,Id2,Il1,Id1,Il0,Id0,Iaa,Iab +" +" ----- END of Initialisation ----- +" +" ----- Work out one line ----- +" +" Work out 'T'op line (used by show next) +map T G,c2k,!9k,@,#j>2k,$j +" +" Work out 'B'ottom line (used by show next) +map B ,%k>,$ +" +" Work out a line (used by show next, work out top and bottom lines) +map > 0 LWWWWWWWWWWWWWWWWWW,rj +" +" Refresh board (used by show next) +map & :%s/^\(-[ A-Z]*-\)\(-[ A-Z]*-\)\(-[.]*-\)$/\2\3\3/
+" +" +" Work around vi multiple yank/put in a single macro limitation +" (used by work out top and/or bottom line) +map ,$ dd +map ,% "cp +map ,! "byy +map ,@ "cyy +map ,# "bP +map ,c c$ +" +" ----- END of Work out one line ----- +" +" ----- Work out one square ----- +" +" The next three work out a square: put all nine chars around the current +" character on the bottom line (the bottom line must be empty when starting). +" +" 'W'ork out a center square (used by work out line) +map W makh,3`ah,3`ajh,3( +" +" +" Work out a 'L'eft square (used by work out line) +map L makf-h,1`ak,2`af-h,1`a,2`ajf-h,1`aj,2( +" +" +" Work out a 'R'ight square (used by work out line) +map ,r makh,2`akF-l,1`ah,2`aF-l,1`ajh,2`ajF-l,1( +" +" 'M'ove a character to the end of the file (used by all work out square +" macros) +" +map ,1 y G$p +map ,2 2y G$p +map ,3 3y G$p +" +" +" ----- END of Work out one square ----- +" +" ----- Work out one germ ----- +" +" Generate an edit command that depends on the number of living in the last +" line, and then run the edit command. (used by work out square). +" Leaves the cursor on the next character to be processed. +" +map ( ,s,i,X0i?^#A
0,df.l,Y21h +" +" Delete 's'paces (deads); +" The number of remaining characters is the number of living neighbours. +map ,s :.g/ /s///g
+" +" Insert current character in the last line +map ,i `ay GP +" +" Replace any uppercase letter with 'X'; +map ,X :.g/[A-Z]/s//X/g
+" +" Delete and execute the rest of the line +map ,d "qd$@q +" +" Yank and execute the rest of the line +map ,Y "qy$@q +" +" Yank the character under the cursor +map ,j y +" +" Put the current cut buffer after the cursor +map ,m p +" +" Delete the character under the cursor +map ,n x +" +" Replace a character by it's next, A --> B, B --> C, etc. +map ,a `a,jGi?=,ma
0,dll,j`a21l,ml,nh +" +" ----- END of Work out one germ ----- +" +" ----- timing macros ----- +" +" Get current date (used by time a generation) +map << :r!date
+map <1 G?^top
O<< +map <2 G?^top
k<< +" +" +" Turn number on current line into edit command (used by time N generations) +map ,^ AiC +" +" +" Delete current line and save current line (used by time N generations) +map ,& 0"gd$ +" +" +" Run saved line (used by time N generations) +map ,* @g +" +" ----- END of timing macros ----- +" +" End of the macros. diff --git a/runtime/macros/matchit.txt b/runtime/macros/matchit.txt new file mode 100644 index 000000000..8ea4de5f2 --- /dev/null +++ b/runtime/macros/matchit.txt @@ -0,0 +1,402 @@ +*matchit.txt* Extended "%" matching + +For instructions on installing this file, type + :help matchit-install +inside Vim. + +For Vim version 6.3. Last change: 2004 May 12 + + + VIM REFERENCE MANUAL by Benji Fisher + +*matchit* *matchit.vim* + +1. Extended matching with "%" |matchit-intro| +2. Activation |matchit-activate| +3. Configuration |matchit-configure| +4. Supporting a New Language |matchit-newlang| +5. Known Bugs and Limitations |matchit-bugs| + +The functionality mentioned here is a plugin, see |add-plugin|. +This plugin is only available if 'compatible' is not set. +You can avoid loading this plugin by setting the "loaded_matchit" variable +in your |vimrc| file: > + :let loaded_matchit = 1 + +{Vi does not have any of this} + +============================================================================== +1. Extended matching with "%" *matchit-intro* + + *matchit-%* +% Cycle forward through matching groups, such as "if", "else", "endif", + as specified by |b:match_words|. + + *g%* *v_g%* *o_g%* +g% Cycle backwards through matching groups, as specified by + |b:match_words|. For example, go from "endif" to "else" to "if". + + *[%* *v_[%* *o_[%* +[% Go to [count] previous unmatched group, as specified by + |b:match_words|. Similar to |[{|. + + *]%* *v_]%* *o_]%* +]% Go to [count] next unmatched group, as specified by + |b:match_words|. Similar to |]}|. + + *v_a%* +a% In Visual mode, select the matching group, as specified by + |b:match_words|, containing the cursor. Similar to |v_a[|. + A [count] is ignored, and only the first character of the closing + pattern is selected. + +In Vim, as in plain vi, the percent key, |%|, jumps the cursor from a brace, +bracket, or paren to its match. This can be configured with the 'matchpairs' +option. The matchit plugin extends this in several ways: + + You can match whole words, such as "if" and "endif", not just + single characters. You can also specify a |regular-expression|. + You can define groups with more than two words, such as "if", + "else", "endif". Banging on the "%" key will cycle from the "if" to + the first "else", the next "else", ..., the closing "endif", and back + to the opening "if". Nested structures are skipped. Using |g%| goes + in the reverse direction. + By default, words inside comments and strings are ignored, unless + the cursor is inside a comment or string when you type "%". If the + only thing you want to do is modify the behavior of "%" so that it + behaves this way, you can > + :let b:match_words = &matchpairs +< +See |matchit-details| for details on what the script does, and |b:match_words| +for how to specify matching patterns. + +MODES: *matchit-modes* *matchit-v_%* *matchit-o_%* + +Mostly, % and related motions (|g%| and |[%| and |]%|) work just like built-in +|motion| commands in |Operator-pending| and |Visual| modes. However, you +cannot make these motions |linewise| or |characterwise|, since the |:omap|s +that define them start with "v" in order to make the default behavior +inclusive. (See |o_v|.) In other words, "dV%" will not work. The +work-around is to go through Visual mode: "V%d" will work. + +LANGUAGES: *matchit-languages* + +Currently, the following languages are supported: Ada, ASP with VBS, Csh, +DTD, Entity, Essbase, Fortran, HTML, JSP (same as HTML), LaTeX, Lua, Pascal, +SGML, Shell, Tcsh, Vim, XML. Other languages may already have support via +|filetype-plugin|s. + +To support a new language, see |matchit-newlang| below. + +DETAILS: *matchit-details* *matchit-parse* + +Here is an outline of what matchit.vim does each time you hit the "%" key. If +there are |backref|s in |b:match_words| then the first step is to produce a +version in which these back references have been eliminated; if there are no +|backref|s then this step is skipped. This step is called parsing. For +example, "\(foo\|bar\):end\1" is parsed to yield +"\(foo\|bar\):end\(foo\|bar\)". This can get tricky, especially if there are +nested groups. If debugging is turned on, the parsed version is saved as +|b:match_pat|. + + *matchit-choose* +Next, the script looks for a word on the current line that matches the pattern +just constructed. It includes the patterns from the 'matchpairs' option. +The goal is to do what you expect, which turns out to be a little complicated. +The script follows these rules: + + Insist on a match that ends on or after the cursor. + Prefer a match that includes the cursor position (that is, one that + starts on or before the cursor). + Prefer a match that starts as close to the cursor as possible. + Prefer a match in |b:match_words| to a match in 'matchpairs'. + If more than one pattern in |b:match_words| matches, choose the one + that is listed first. + +Examples: + + Suppose you > + :let b:match_words = '<:>,<tag>:</tag>' +< and hit "%" with the cursor on or before the "<" in "a <tag> is born". + The pattern '<' comes first, so it is preferred over '<tag>', which + also matches. If the cursor is on the "t", however, then '<tag>' is + preferred, because this matches a bit of text containing the cursor. + If the two groups of patterns were reversed then '<' would never be + preferred. + + Suppose you > + :let b:match_words = 'if:end if' +< (Note the space!) and hit "%" with the cursor at the end of "end if". + Then "if" matches, which is probably not what you want, but if the + cursor starts on the "end " then "end if" is chosen. (You can avoid + this problem by using a more complicated pattern.) + +If there is no match, the script falls back on the usual behavior of |%|. If +debugging is turned on, the matched bit of text is saved as |b:match_match| +and the cursor column of the start of the match is saved as |b:match_col|. + +Next, the script looks through |b:match_words| (original and parsed versions) +for the group and pattern that match. If debugging is turned on, the group is +saved as |b:match_ini| (the first pattern) and |b:match_tail| (the rest). If +there are |backref|s then, in addition, the matching pattern is saved as +|b:match_word| and a table of translations is saved as |b:match_table|. If +there are |backref|s, these are determined from the matching pattern and +|b:match_match| and substituted into each pattern in the matching group. + +The script decides whether to search forwards or backwards and chooses +arguments for the |searchpair()| function. Then, the cursor is moved to the +start of the match, and |searchpair()| is called. By default, matching +structures inside strings and comments are ignored. This can be changed by +setting |b:match_skip|. + +============================================================================== +2. Activation *matchit-activate* + +You can use this script as a plugin, by copying it to your plugin directory. +See |add-global-plugin| for instructions. You can also add a line to your +|vimrc| file, such as > + :source $VIMRUNTIME/macros/matchit.vim +or > + :runtime macros/matchit.vim +Either way, the script should start working the next time you start up Vim. + +The script does nothing unless it finds a |buffer-variable| named +|b:match_words|. The script contains autocommands that set this variable for +various file types: see |matchit-languages| above. For a new language, you +can add autocommands to the script or to your vimrc file, but the recommended +method is to add a line such as > + let b:match_words = '\<foo\>:\<bar\>' +to the |filetype-plugin| for your language. See |b:match_words| below for how +this variable is interpreted. + +TROUBLESHOOTING *matchit-troubleshoot* + +The script should work in most installations of Vim. It may not work if Vim +was compiled with a minimal feature set, for example if the |+syntax| option +was not enabled. If your Vim has support for syntax compiled in, but you do +not have |syntax| highlighting turned on, matchit.vim should work, but it may +fail to skip matching groups in comments and strings. If the |filetype| +mechanism is turned off, the |b:match_words| variable will probably not be +defined automatically. + +============================================================================== +3. Configuration *matchit-configure* + +There are several variables that govern the behavior of matchit.vim. Note +that these are variables local to the buffer, not options, so use |:let| to +define them, not |:set|. Some of these variables have values that matter; for +others, it only matters whether the variable has been defined. All of these +can be defined in the |filetype-plugin| or autocommand that defines +|b:match_words| or "on the fly." + +The main variable is |b:match_words|. It is described in the section below on +supporting a new language. + + *MatchError* *matchit-hl* *matchit-highlight* +MatchError is the highlight group for error messages from the script. By +default, it is linked to WarningMsg. If you do not want to be bothered by +error messages, you can define this to be something invisible. For example, +if you use the GUI version of Vim and your command line is normally white, you +can do > + :hi MatchError guifg=white guibg=white +< + *b:match_ignorecase* +If you > + :let b:match_ignorecase = 1 +then matchit.vim acts as if 'ignorecase' is set: for example, "end" and "END" +are equivalent. If you > + :let b:match_ignorecase = 0 +then matchit.vim treats "end" and "END" differently. (There will be no +b:match_infercase option unless someone requests it.) + + *b:match_debug* +Define b:match_debug if you want debugging information to be saved. See +|matchit-debug|, below. + + *b:match_skip* +If b:match_skip is defined, it is passed as the skip argument to +|searchpair()|. This controls when matching structures are skipped, or +ignored. By default, they are ignored inside comments and strings, as +determined by the |syntax| mechanism. (If syntax highlighting is turned off, +nothing is skipped.) You can set b:match_skip to a string, which evaluates to +a non-zero, numerical value if the match is to be skipped or zero if the match +should not be skipped. In addition, the following special values are +supported by matchit.vim: + s:foo becomes (current syntax item) =~ foo + S:foo becomes (current syntax item) !~ foo + r:foo becomes (line before cursor) =~ foo + R:foo becomes (line before cursor) !~ foo +(The "s" is meant to suggest "syntax", and the "r" is meant to suggest +"regular expression".) + +Examples: + + You can get the default behavior with > + :let b:match_skip = 's:comment\|string' +< + If you want to skip matching structures unless they are at the start + of the line (ignoring whitespace) then you can > + :let b:match_skip = 'R:^\s*' +< Do not do this if strings or comments can span several lines, since + the normal syntax checking will not be done if you set b:match_skip. + + In LaTeX, since "%" is used as the comment character, you can > + :let b:match_skip = 'r:%' +< Unfortunately, this will skip anything after "\%", an escaped "%". To + allow for this, and also "\\%" (an excaped backslash followed by the + comment character) you can > + :let b:match_skip = 'r:\(^\|[^\\]\)\(\\\\\)*%' +< + See the $VIMRUNTIME/syntax/vim.vim for an example that uses both + syntax and a regular expression. + +============================================================================== +4. Supporting a New Language *matchit-newlang* + *b:match_words* +In order for matchit.vim to support a new language, you must define a suitable +pattern for |b:match_words|. You may also want to set some of the +|matchit-configure| variables, as described above. If your language has a +complicated syntax, or many keywords, you will need to know something about +Vim's |regular-expression|s. + +The format for |b:match_words| is similar to that of the 'matchpairs' option: +it is a comma (,)-separated list of groups; each group is a colon(:)-separated +list of patterns (regular expressions). It is OK to have only one group; the +effect is undefined if a group has only one pattern. A simple example is > + :let b:match_words = '\<if\>:\<endif\>,' + \ . '\<while\>:\<continue\>:\<break\>:\<endwhile\>' +(In Vim regular expressions, |\<| and |\>| denote word boundaries. Thus "if" +matches the end of "endif" but "\<if\>" does not.) Then banging on the "%" +key will bounce the cursor between "if" and the matching "endif"; and from +"while" to any matching "continue" or "break", then to the matching "endwhile" +and back to the "while". It is almost always easier to use |literal-string|s +(single quotes) as above: '\<if\>' rather than "\\<if\\>" and so on. + +Exception: If the ":" character does not appear in b:match_words, then it is +treated as an expression to be evaluated. For example, > + :let b:match_words = 'GetMatchWords()' +allows you to define a function. This can return a different string depending +on the current syntax, for example. + +Once you have defined the appropriate value of |b:match_words|, you will +probably want to have this set automatically each time you edit the +appropriate file type. The recommended way to do this is by adding the +definition to a |filetype-plugin| file. + +Tips: Be careful that your initial pattern does not match your final pattern. +See the example above for the use of word-boundary expressions. It is usually +better to use ".\{-}" (as many as necessary) instead of ".*" (as many as +possible). See |\{-|. For example, in the string "<tag>label</tag>", "<.*>" +matches the whole string whereas "<.\{-}>" and "<[^>]*>" match "<tag>" and +"</tag>". + + *matchit-spaces* *matchit-s:notend* +If "if" is to be paired with "end if" (Note the space!) then word boundaries +are not enough. Instead, define a regular expression s:notend that will match +anything but "end" and use it as follows: > + :let s:notend = '\%(\<end\s\+\)\@<!' + :let b:match_words = s:notend . '\<if\>:\<end\s\+if\>' +< *matchit-s:sol* +This is a simplified version of what is done for Ada. The s:notend is a +|script-variable|. Similarly, you may want to define a start-of-line regular +expression > + :let s:sol = '\%(^\|;\)\s*' +if keywords are only recognized after the start of a line or after a +semicolon (;), with optional white space. + + *matchit-backref* *matchit-\1* +In any group, the expressions |\1|, |\2|, ..., |\9| refer to parts of the +INITIAL pattern enclosed in |\(|escaped parentheses|\)|. These are referred +to as back references, or backrefs. For example, > + :let b:match_words = '\<b\(o\+\)\>:\(h\)\1\>' +means that "bo" pairs with "ho" and "boo" pairs with "hoo" and so on. Note +that "\1" does not refer to the "\(h\)" in this example. If you have +"\(nested \(parentheses\)\) then "\d" refers to the d-th "\(" and everything +up to and including the matching "\)": in "\(nested\(parentheses\)\)", "\1" +refers to everything and "\2" refers to "\(parentheses\)". If you use a +variable such as |s:notend| or |s:sol| in the previous paragraph then remember +to count any "\(" patterns in this variable. You do not have to count groups +defined by |\%(\)|. + +It should be possible to resolve back references from any pattern in the +group. For example, > + :let b:match_words = '\(foo\)\(bar\):more\1:and\2:end\1\2' +would not work because "\2" cannot be determined from "morefoo" and "\1" +cannot be determined from "andbar". On the other hand, > + :let b:match_words = '\(\(foo\)\(bar\)\):\3\2:end\1' +should work (and have the same effect as "foobar:barfoo:endfoobar"), although +this has not been thoroughly tested. + +You can use |zero-width| patterns such as |\@<=| and |\zs|. (The latter has +not been thouroughly tested in matchit.vim.) For example, if the keyword "if" +must occur at the start of the line, with optional white space, you might use +the pattern "\(^\s*\)\@<=if" so that the cursor will end on the "i" instead of +at the start of the line. For another example, if HTML had only one tag then +one could > + :let b:match_words = '<:>,<\@<=tag>:<\@<=/tag>' +so that "%" can bounce between matching "<" and ">" pairs or (starting on +"tag" or "/tag") between matching tags. Without the |\@<=|, the script would +bounce from "tag" to the "<" in "</tag>", and another "%" would not take you +back to where you started. + +DEBUGGING *matchit-debug* *:MatchDebug* + +If you are having trouble figuring out the appropriate definition of +|b:match_words| then you can take advantage of the same information I use when +debugging the script. This is especially true if you are not sure whether +your patterns or my script are at fault! To make this more convenient, I have +made the command :MatchDebug, which defines the variable |b:match_debug| and +creates a Matchit menu. This menu makes it convenient to check the values of +the variables described below. You will probably also want to read +|matchit-details| above. + +Defining the variable |b:match_debug| causes the script to set the following +variables, each time you hit the "%" key. Several of these are only defined +if |b:match_words| includes |backref|s. + + *b:match_pat* +The b:match_pat variable is set to |b:match_words| with |backref|s parsed. + *b:match_match* +The b:match_match variable is set to the bit of text that is recognized as a +match. + *b:match_col* +The b:match_col variable is set to the cursor column of the start of the +matching text. + *b:match_wholeBR* +The b:match_wholeBR variable is set to the comma-separated group of patterns +that matches, with |backref|s unparsed. + *b:match_iniBR* +The b:match_iniBR variable is set to the first pattern in |b:match_wholeBR|. + *b:match_ini* +The b:match_ini variable is set to the first pattern in |b:match_wholeBR|, +with |backref|s resolved from |b:match_match|. + *b:match_tail* +The b:match_tail variable is set to the remaining patterns in +|b:match_wholeBR|, with |backref|s resolved from |b:match_match|. + *b:match_word* +The b:match_word variable is set to the pattern from |b:match_wholeBR| that +matches |b:match_match|. + *b:match_table* +The back reference '\'.d refers to the same thing as '\'.b:match_table[d] in +|b:match_word|. + +============================================================================== +5. Known Bugs and Limitations *matchit-bugs* + +Just because I know about a bug does not mean that it is on my todo list. I +try to respond to reports of bugs that cause real problems. If it does not +cause serious problems, or if there is a work-around, a bug may sit there for +a while. Moral: if a bug (known or not) bothers you, let me know. + +The various |:vmap|s defined in the script (%, |g%|, |[%|, |]%|, |a%|) may +have undesired effects in Select mode |Select-mode-mapping|. At least, if you +want to replace the selection with any character in "ag%[]" there will be a +pause of |'updatetime'| first. + +It would be nice if "\0" were recognized as the entire pattern. That is, it +would be nice if "foo:\end\0" had the same effect as "\(foo\):\end\1". I may +try to implement this in a future version. (This is not so easy to arrange as +you might think!) + +============================================================================== +vim:tw=78:fo=tcq2: diff --git a/runtime/macros/matchit.vim b/runtime/macros/matchit.vim new file mode 100644 index 000000000..296099474 --- /dev/null +++ b/runtime/macros/matchit.vim @@ -0,0 +1,806 @@ +" matchit.vim: (global plugin) Extended "%" matching +" Last Change: Sat May 15 11:00 AM 2004 EDT +" Maintainer: Benji Fisher PhD <benji@member.AMS.org> +" Version: 1.9, for Vim 6.3 +" URL: http://www.vim.org/script.php?script_id=39 + +" Documentation: +" The documentation is in a separate file, matchit.txt . + +" Credits: +" Vim editor by Bram Moolenaar (Thanks, Bram!) +" Original script and design by Raul Segura Acevedo +" Support for comments by Douglas Potts +" Support for back references and other improvements by Benji Fisher +" Support for many languages by Johannes Zellner +" Suggestions for improvement, bug reports, and support for additional +" languages by Jordi-Albert Batalla, Neil Bird, Servatius Brandt, Mark +" Collett, Stephen Wall, Dany St-Amant, and Johannes Zellner. + +" Debugging: +" If you'd like to try the built-in debugging commands... +" :MatchDebug to activate debugging for the current buffer +" This saves the values of several key script variables as buffer-local +" variables. See the MatchDebug() function, below, for details. + +" TODO: I should think about multi-line patterns for b:match_words. +" This would require an option: how many lines to scan (default 1). +" This would be useful for Python, maybe also for *ML. +" TODO: Maybe I should add a menu so that people will actually use some of +" the features that I have implemented. +" TODO: Eliminate the MultiMatch function. Add yet another argument to +" Match_wrapper() instead. +" TODO: Allow :let b:match_words = '\(\(foo\)\(bar\)\):\3\2:end\1' +" TODO: Make backrefs safer by using '\V' (very no-magic). +" TODO: Add a level of indirection, so that custom % scripts can use my +" work but extend it. + +" allow user to prevent loading +" and prevent duplicate loading +if exists("loaded_matchit") || &cp + finish +endif +let loaded_matchit = 1 +let s:last_mps = "" +let s:last_words = "" + +let s:save_cpo = &cpo +set cpo&vim + +nnoremap <silent> % :<C-U>call <SID>Match_wrapper('',1,'n') <CR> +nnoremap <silent> g% :<C-U>call <SID>Match_wrapper('',0,'n') <CR> +vnoremap <silent> % :<C-U>call <SID>Match_wrapper('',1,'v') <CR>m'gv`` +vnoremap <silent> g% :<C-U>call <SID>Match_wrapper('',0,'v') <CR>m'gv`` +onoremap <silent> % v:<C-U>call <SID>Match_wrapper('',1,'o') <CR> +onoremap <silent> g% v:<C-U>call <SID>Match_wrapper('',0,'o') <CR> + +" Analogues of [{ and ]} using matching patterns: +nnoremap <silent> [% :<C-U>call <SID>MultiMatch("bW", "n") <CR> +nnoremap <silent> ]% :<C-U>call <SID>MultiMatch("W", "n") <CR> +vmap [% <Esc>[%m'gv`` +vmap ]% <Esc>]%m'gv`` +" vnoremap <silent> [% :<C-U>call <SID>MultiMatch("bW", "v") <CR>m'gv`` +" vnoremap <silent> ]% :<C-U>call <SID>MultiMatch("W", "v") <CR>m'gv`` +onoremap <silent> [% v:<C-U>call <SID>MultiMatch("bW", "o") <CR> +onoremap <silent> ]% v:<C-U>call <SID>MultiMatch("W", "o") <CR> + +" text object: +vmap a% <Esc>[%v]% + +" Auto-complete mappings: (not yet "ready for prime time") +" TODO Read :help write-plugin for the "right" way to let the user +" specify a key binding. +" let g:match_auto = '<C-]>' +" let g:match_autoCR = '<C-CR>' +" if exists("g:match_auto") +" execute "inoremap " . g:match_auto . ' x<Esc>"=<SID>Autocomplete()<CR>Pls' +" endif +" if exists("g:match_autoCR") +" execute "inoremap " . g:match_autoCR . ' <CR><C-R>=<SID>Autocomplete()<CR>' +" endif +" if exists("g:match_gthhoh") +" execute "inoremap " . g:match_gthhoh . ' <C-O>:call <SID>Gthhoh()<CR>' +" endif " gthhoh = "Get the heck out of here!" + +let s:notslash = '\\\@<!\%(\\\\\)*' + +function! s:Match_wrapper(word, forward, mode) range + " In s:CleanUp(), :execute "set" restore_options . + let restore_options = (&ic ? " " : " no") . "ignorecase" + if exists("b:match_ignorecase") + let &ignorecase = b:match_ignorecase + endif + let restore_options = " ve=" . &ve . restore_options + set ve= + " If this function was called from Visual mode, make sure that the cursor + " is at the correct end of the Visual range: + if a:mode == "v" + execute "normal! gv\<Esc>" + endif + " In s:CleanUp(), we may need to check whether the cursor moved forward. + let startline = line(".") + let startcol = col(".") + " Use default behavior if called with a count or if no patterns are defined. + if v:count + exe "normal! " . v:count . "%" + return s:CleanUp(restore_options, a:mode, startline, startcol) + elseif !exists("b:match_words") || b:match_words == "" + silent! normal! % + return s:CleanUp(restore_options, a:mode, startline, startcol) + end + + " First step: if not already done, set the script variables + " s:do_BR flag for whether there are backrefs + " s:pat parsed version of b:match_words + " s:all regexp based on s:pat and the default groups + " + " Allow b:match_words = "GetVimMatchWords()" . + if b:match_words =~ ":" + let match_words = b:match_words + else + execute "let match_words =" b:match_words + endif +" Thanks to Preben "Peppe" Guldberg and Bram Moolenaar for this suggestion! + if (match_words != s:last_words) || (&mps != s:last_mps) || + \ exists("b:match_debug") + let s:last_words = match_words + let s:last_mps = &mps + if match_words !~ s:notslash . '\\\d' + let s:do_BR = 0 + let s:pat = match_words + else + let s:do_BR = 1 + let s:pat = s:ParseWords(match_words) + endif + " The next several lines were here before + " BF started messing with this script. + " quote the special chars in 'matchpairs', replace [,:] with \| and then + " append the builtin pairs (/*, */, #if, #ifdef, #else, #elif, #endif) + " let default = substitute(escape(&mps, '[$^.*~\\/?]'), '[,:]\+', + " \ '\\|', 'g').'\|\/\*\|\*\/\|#if\>\|#ifdef\>\|#else\>\|#elif\>\|#endif\>' + let default = escape(&mps, '[$^.*~\\/?]') . (strlen(&mps) ? "," : "") . + \ '\/\*:\*\/,#if\%(def\)\=:#else\>:#elif\>:#endif\>' + " s:all = pattern with all the keywords + let s:all = s:pat . (strlen(s:pat) ? "," : "") . default + let s:all = substitute(s:all, s:notslash . '\zs[,:]\+', '\\|', 'g') + let s:all = '\%(' . s:all . '\)' + " let s:all = '\%(' . substitute(s:all, '\\\ze[,:]', '', 'g') . '\)' + if exists("b:match_debug") + let b:match_pat = s:pat + endif + endif + + " Second step: set the following local variables: + " matchline = line on which the cursor started + " curcol = number of characters before match + " prefix = regexp for start of line to start of match + " suffix = regexp for end of match to end of line + " Require match to end on or after the cursor and prefer it to + " start on or before the cursor. + let matchline = getline(startline) + if a:word != '' + " word given + if a:word !~ s:all + echohl WarningMsg|echo 'Missing rule for word:"'.a:word.'"'|echohl NONE + return s:CleanUp(restore_options, a:mode, startline, startcol) + endif + let matchline = a:word + let curcol = 0 + let prefix = '^\%(' + let suffix = '\)$' + " Now the case when "word" is not given + else " Find the match that ends on or after the cursor and set curcol. + let regexp = s:Wholematch(matchline, s:all, startcol-1) + let curcol = match(matchline, regexp) + let suf = strlen(matchline) - matchend(matchline, regexp) + let prefix = (curcol ? '^.\{' . curcol . '}\%(' : '^\%(') + let suffix = (suf ? '\).\{' . suf . '}$' : '\)$') + " If the match comes from the defaults, bail out. + if matchline !~ prefix . + \ substitute(s:pat, s:notslash.'\zs[,:]\+', '\\|', 'g') . suffix + silent! norm! % + return s:CleanUp(restore_options, a:mode, startline, startcol) + endif + endif + if exists("b:match_debug") + let b:match_match = matchstr(matchline, regexp) + let b:match_col = curcol+1 + endif + + " Third step: Find the group and single word that match, and the original + " (backref) versions of these. Then, resolve the backrefs. + " Set the following local variable: + " group = colon-separated list of patterns, one of which matches + " = ini:mid:fin or ini:fin + " + " Reconstruct the version with unresolved backrefs. + let patBR = substitute(match_words.',', + \ s:notslash.'\zs[,:]*,[,:]*', ',', 'g') + let patBR = substitute(patBR, s:notslash.'\zs:\{2,}', ':', 'g') + " Now, set group and groupBR to the matching group: 'if:endif' or + " 'while:endwhile' or whatever. A bit of a kluge: s:Choose() returns + " group . "," . groupBR, and we pick it apart. + let group = s:Choose(s:pat, matchline, ",", ":", prefix, suffix, patBR) + let i = matchend(group, s:notslash . ",") + let groupBR = strpart(group, i) + let group = strpart(group, 0, i-1) + " Now, matchline =~ prefix . substitute(group,':','\|','g') . suffix + if s:do_BR " Do the hard part: resolve those backrefs! + let group = s:InsertRefs(groupBR, prefix, group, suffix, matchline) + endif + if exists("b:match_debug") + let b:match_wholeBR = groupBR + let i = matchend(groupBR, s:notslash . ":") + let b:match_iniBR = strpart(groupBR, 0, i-1) + endif + + " Fourth step: Set the arguments for searchpair(). + let i = matchend(group, s:notslash . ":") + let j = matchend(group, '.*' . s:notslash . ":") + let ini = strpart(group, 0, i-1) + let mid = substitute(strpart(group, i,j-i-1), s:notslash.'\zs:', '\\|', 'g') + let fin = strpart(group, j) + " searchpair() requires that these patterns avoid \(\) groups. + let ini = substitute(ini, s:notslash . '\zs\\(', '\\%(', 'g') + let mid = substitute(mid, s:notslash . '\zs\\(', '\\%(', 'g') + let fin = substitute(fin, s:notslash . '\zs\\(', '\\%(', 'g') + " Set mid. This is optimized for readability, not micro-efficiency! + if a:forward && matchline =~ prefix . fin . suffix + \ || !a:forward && matchline =~ prefix . ini . suffix + let mid = "" + endif + " Set flag. This is optimized for readability, not micro-efficiency! + if a:forward && matchline =~ prefix . fin . suffix + \ || !a:forward && matchline !~ prefix . ini . suffix + let flag = "bW" + else + let flag = "W" + endif + " Set skip. + if exists("b:match_skip") + let skip = b:match_skip + elseif exists("b:match_comment") " backwards compatibility and testing! + let skip = "r:" . b:match_comment + else + let skip = 's:comment\|string' + endif + let skip = s:ParseSkip(skip) + if exists("b:match_debug") + let b:match_ini = ini + let b:match_tail = (strlen(mid) ? mid.'\|' : '') . fin + endif + + " Fifth step: actually start moving the cursor and call searchpair(). + " Later, :execute restore_cursor to get to the original screen. + let restore_cursor = virtcol(".") . "|" + normal! g0 + let restore_cursor = line(".") . "G" . virtcol(".") . "|zs" . restore_cursor + normal! H + let restore_cursor = "normal!" . line(".") . "Gzt" . restore_cursor + execute restore_cursor + normal! 0 + if curcol + execute "normal!" . curcol . "l" + endif + if skip =~ 'synID' && !(has("syntax") && exists("g:syntax_on")) + let skip = "0" + else + execute "if " . skip . "| let skip = '0' | endif" + endif + let sp_return = searchpair(ini, mid, fin, flag, skip) + let final_position = "call cursor(" . line(".") . "," . col(".") . ")" + " Restore cursor position and original screen. + execute restore_cursor + normal! m' + if sp_return > 0 + execute final_position + endif + return s:CleanUp(restore_options, a:mode, startline, startcol, mid.'\|'.fin) +endfun + +" Restore options and do some special handling for Operator-pending mode. +" The optional argument is the tail of the matching group. +fun! s:CleanUp(options, mode, startline, startcol, ...) + execute "set" a:options + " Open folds, if appropriate. + if a:mode != "o" + if &foldopen =~ "percent" + normal! zv + endif + " In Operator-pending mode, we want to include the whole match + " (for example, d%). + " This is only a problem if we end up moving in the forward direction. + elseif (a:startline < line(".")) || + \ (a:startline == line(".") && a:startcol < col(".")) + if a:0 + " Check whether the match is a single character. If not, move to the + " end of the match. + let matchline = getline(".") + let currcol = col(".") + let regexp = s:Wholematch(matchline, a:1, currcol-1) + let endcol = matchend(matchline, regexp) + if endcol > currcol " This is NOT off by one! + execute "normal!" . (endcol - currcol) . "l" + endif + endif " a:0 + endif " a:mode != "o" && etc. + return 0 +endfun + +" Example (simplified HTML patterns): if +" a:groupBR = '<\(\k\+\)>:</\1>' +" a:prefix = '^.\{3}\(' +" a:group = '<\(\k\+\)>:</\(\k\+\)>' +" a:suffix = '\).\{2}$' +" a:matchline = "123<tag>12" or "123</tag>12" +" then extract "tag" from a:matchline and return "<tag>:</tag>" . +fun! s:InsertRefs(groupBR, prefix, group, suffix, matchline) + if a:matchline !~ a:prefix . + \ substitute(a:group, s:notslash . '\zs:', '\\|', 'g') . a:suffix + return a:group + endif + let i = matchend(a:groupBR, s:notslash . ':') + let ini = strpart(a:groupBR, 0, i-1) + let tailBR = strpart(a:groupBR, i) + let word = s:Choose(a:group, a:matchline, ":", "", a:prefix, a:suffix, + \ a:groupBR) + let i = matchend(word, s:notslash . ":") + let wordBR = strpart(word, i) + let word = strpart(word, 0, i-1) + " Now, a:matchline =~ a:prefix . word . a:suffix + if wordBR != ini + let table = s:Resolve(ini, wordBR, "table") + else + " let table = "----------" + let table = "" + let d = 0 + while d < 10 + if tailBR =~ s:notslash . '\\' . d + " let table[d] = d + let table = table . d + else + let table = table . "-" + endif + let d = d + 1 + endwhile + endif + let d = 9 + while d + if table[d] != "-" + let backref = substitute(a:matchline, a:prefix.word.a:suffix, + \ '\'.table[d], "") + " Are there any other characters that should be escaped? + let backref = escape(backref, '*,:') + execute s:Ref(ini, d, "start", "len") + let ini = strpart(ini, 0, start) . backref . strpart(ini, start+len) + let tailBR = substitute(tailBR, s:notslash . '\zs\\' . d, + \ escape(backref, '\\'), 'g') + endif + let d = d-1 + endwhile + if exists("b:match_debug") + if s:do_BR + let b:match_table = table + let b:match_word = word + else + let b:match_table = "" + let b:match_word = "" + endif + endif + return ini . ":" . tailBR +endfun + +" Input a comma-separated list of groups with backrefs, such as +" a:groups = '\(foo\):end\1,\(bar\):end\1' +" and return a comma-separated list of groups with backrefs replaced: +" return '\(foo\):end\(foo\),\(bar\):end\(bar\)' +fun! s:ParseWords(groups) + let groups = substitute(a:groups.",", s:notslash.'\zs[,:]*,[,:]*', ',', 'g') + let groups = substitute(groups, s:notslash . '\zs:\{2,}', ':', 'g') + let parsed = "" + while groups =~ '[^,:]' + let i = matchend(groups, s:notslash . ':') + let j = matchend(groups, s:notslash . ',') + let ini = strpart(groups, 0, i-1) + let tail = strpart(groups, i, j-i-1) . ":" + let groups = strpart(groups, j) + let parsed = parsed . ini + let i = matchend(tail, s:notslash . ':') + while i != -1 + " In 'if:else:endif', ini='if' and word='else' and then word='endif'. + let word = strpart(tail, 0, i-1) + let tail = strpart(tail, i) + let i = matchend(tail, s:notslash . ':') + let parsed = parsed . ":" . s:Resolve(ini, word, "word") + endwhile " Now, tail has been used up. + let parsed = parsed . "," + endwhile " groups =~ '[^,:]' + return parsed +endfun + +" TODO I think this can be simplified and/or made more efficient. +" TODO What should I do if a:start is out of range? +" Return a regexp that matches all of a:string, such that +" matchstr(a:string, regexp) represents the match for a:pat that starts +" as close to a:start as possible, before being preferred to after, and +" ends after a:start . +" Usage: +" let regexp = s:Wholematch(getline("."), 'foo\|bar', col(".")-1) +" let i = match(getline("."), regexp) +" let j = matchend(getline("."), regexp) +" let match = matchstr(getline("."), regexp) +fun! s:Wholematch(string, pat, start) + let group = '\%(' . a:pat . '\)' + let prefix = (a:start ? '\(^.\{,' . a:start . '}\)\zs' : '^') + let len = strlen(a:string) + let suffix = (a:start+1 < len ? '\(.\{,'.(len-a:start-1).'}$\)\@=' : '$') + if a:string !~ prefix . group . suffix + let prefix = '' + endif + return prefix . group . suffix +endfun + +" No extra arguments: s:Ref(string, d) will +" find the d'th occurrence of '\(' and return it, along with everything up +" to and including the matching '\)'. +" One argument: s:Ref(string, d, "start") returns the index of the start +" of the d'th '\(' and any other argument returns the length of the group. +" Two arguments: s:Ref(string, d, "foo", "bar") returns a string to be +" executed, having the effect of +" :let foo = s:Ref(string, d, "start") +" :let bar = s:Ref(string, d, "len") +fun! s:Ref(string, d, ...) + let len = strlen(a:string) + if a:d == 0 + let start = 0 + else + let cnt = a:d + let match = a:string + while cnt + let cnt = cnt - 1 + let index = matchend(match, s:notslash . '\\(') + if index == -1 + return "" + endif + let match = strpart(match, index) + endwhile + let start = len - strlen(match) + if a:0 == 1 && a:1 == "start" + return start - 2 + endif + let cnt = 1 + while cnt + let index = matchend(match, s:notslash . '\\(\|\\)') - 1 + if index == -2 + return "" + endif + " Increment if an open, decrement if a ')': + let cnt = cnt + (match[index]=="(" ? 1 : -1) " ')' + " let cnt = stridx('0(', match[index]) + cnt + let match = strpart(match, index+1) + endwhile + let start = start - 2 + let len = len - start - strlen(match) + endif + if a:0 == 1 + return len + elseif a:0 == 2 + return "let " . a:1 . "=" . start . "| let " . a:2 . "=" . len + else + return strpart(a:string, start, len) + endif +endfun + +" Count the number of disjoint copies of pattern in string. +" If the pattern is a literal string and contains no '0' or '1' characters +" then s:Count(string, pattern, '0', '1') should be faster than +" s:Count(string, pattern). +fun! s:Count(string, pattern, ...) + let pat = escape(a:pattern, '\\') + if a:0 > 1 + let foo = substitute(a:string, '[^'.a:pattern.']', "a:1", "g") + let foo = substitute(a:string, pat, a:2, "g") + let foo = substitute(foo, '[^' . a:2 . ']', "", "g") + return strlen(foo) + endif + let result = 0 + let foo = a:string + let index = matchend(foo, pat) + while index != -1 + let result = result + 1 + let foo = strpart(foo, index) + let index = matchend(foo, pat) + endwhile + return result +endfun + +" s:Resolve('\(a\)\(b\)', '\(c\)\2\1\1\2') should return table.word, where +" word = '\(c\)\(b\)\(a\)\3\2' and table = '-32-------'. That is, the first +" '\1' in target is replaced by '\(a\)' in word, table[1] = 3, and this +" indicates that all other instances of '\1' in target are to be replaced +" by '\3'. The hard part is dealing with nesting... +" Note that ":" is an illegal character for source and target, +" unless it is preceded by "\". +fun! s:Resolve(source, target, output) + let word = a:target + let i = matchend(word, s:notslash . '\\\d') - 1 + let table = "----------" + while i != -2 " There are back references to be replaced. + let d = word[i] + let backref = s:Ref(a:source, d) + " The idea is to replace '\d' with backref. Before we do this, + " replace any \(\) groups in backref with :1, :2, ... if they + " correspond to the first, second, ... group already inserted + " into backref. Later, replace :1 with \1 and so on. The group + " number w+b within backref corresponds to the group number + " s within a:source. + " w = number of '\(' in word before the current one + let w = s:Count( + \ substitute(strpart(word, 0, i-1), '\\\\', '', 'g'), '\(', '1') + let b = 1 " number of the current '\(' in backref + let s = d " number of the current '\(' in a:source + while b <= s:Count(substitute(backref, '\\\\', '', 'g'), '\(', '1') + \ && s < 10 + if table[s] == "-" + if w + b < 10 + " let table[s] = w + b + let table = strpart(table, 0, s) . (w+b) . strpart(table, s+1) + endif + let b = b + 1 + let s = s + 1 + else + execute s:Ref(backref, b, "start", "len") + let ref = strpart(backref, start, len) + let backref = strpart(backref, 0, start) . ":". table[s] + \ . strpart(backref, start+len) + let s = s + s:Count(substitute(ref, '\\\\', '', 'g'), '\(', '1') + endif + endwhile + let word = strpart(word, 0, i-1) . backref . strpart(word, i+1) + let i = matchend(word, s:notslash . '\\\d') - 1 + endwhile + let word = substitute(word, s:notslash . '\zs:', '\\', 'g') + if a:output == "table" + return table + elseif a:output == "word" + return word + else + return table . word + endif +endfun + +" Assume a:comma = ",". Then the format for a:patterns and a:1 is +" a:patterns = "<pat1>,<pat2>,..." +" a:1 = "<alt1>,<alt2>,..." +" If <patn> is the first pattern that matches a:string then return <patn> +" if no optional arguments are given; return <patn>,<altn> if a:1 is given. +fun! s:Choose(patterns, string, comma, branch, prefix, suffix, ...) + let tail = (a:patterns =~ a:comma."$" ? a:patterns : a:patterns . a:comma) + let i = matchend(tail, s:notslash . a:comma) + if a:0 + let alttail = (a:1 =~ a:comma."$" ? a:1 : a:1 . a:comma) + let j = matchend(alttail, s:notslash . a:comma) + endif + let current = strpart(tail, 0, i-1) + if a:branch == "" + let currpat = current + else + let currpat = substitute(current, a:branch, '\\|', 'g') + endif + while a:string !~ a:prefix . currpat . a:suffix + let tail = strpart(tail, i) + let i = matchend(tail, s:notslash . a:comma) + if i == -1 + return -1 + endif + let current = strpart(tail, 0, i-1) + if a:branch == "" + let currpat = current + else + let currpat = substitute(current, a:branch, '\\|', 'g') + endif + if a:0 + let alttail = strpart(alttail, j) + let j = matchend(alttail, s:notslash . a:comma) + endif + endwhile + if a:0 + let current = current . a:comma . strpart(alttail, 0, j-1) + endif + return current +endfun + +" Call this function to turn on debugging information. Every time the main +" script is run, buffer variables will be saved. These can be used directly +" or viewed using the menu items below. +if !exists(":MatchDebug") + command! -nargs=0 MatchDebug call s:Match_debug() +endif + +fun! s:Match_debug() + let b:match_debug = 1 " Save debugging information. + " pat = all of b:match_words with backrefs parsed + amenu &Matchit.&pat :echo b:match_pat<CR> + " match = bit of text that is recognized as a match + amenu &Matchit.&match :echo b:match_match<CR> + " curcol = cursor column of the start of the matching text + amenu &Matchit.&curcol :echo b:match_col<CR> + " wholeBR = matching group, original version + amenu &Matchit.wh&oleBR :echo b:match_wholeBR<CR> + " iniBR = 'if' piece, original version + amenu &Matchit.ini&BR :echo b:match_iniBR<CR> + " ini = 'if' piece, with all backrefs resolved from match + amenu &Matchit.&ini :echo b:match_ini<CR> + " tail = 'else\|endif' piece, with all backrefs resolved from match + amenu &Matchit.&tail :echo b:match_tail<CR> + " fin = 'endif' piece, with all backrefs resolved from match + amenu &Matchit.&word :echo b:match_word<CR> + " '\'.d in ini refers to the same thing as '\'.table[d] in word. + amenu &Matchit.t&able :echo '0:' . b:match_table . ':9'<CR> +endfun + +" Jump to the nearest unmatched "(" or "if" or "<tag>" if a:spflag == "bW" +" or the nearest unmatched "</tag>" or "endif" or ")" if a:spflag == "W". +" Return a "mark" for the original position, so that +" let m = MultiMatch("bW", "n") ... execute m +" will return to the original position. If there is a problem, do not +" move the cursor and return "", unless a count is given, in which case +" go up or down as many levels as possible and again return "". +" TODO This relies on the same patterns as % matching. It might be a good +" idea to give it its own matching patterns. +fun! s:MultiMatch(spflag, mode) + if !exists("b:match_words") || b:match_words == "" + return "" + end + let restore_options = (&ic ? "" : "no") . "ignorecase" + if exists("b:match_ignorecase") + let &ignorecase = b:match_ignorecase + endif + let startline = line(".") + let startcol = col(".") + + " First step: if not already done, set the script variables + " s:do_BR flag for whether there are backrefs + " s:pat parsed version of b:match_words + " s:all regexp based on s:pat and the default groups + " This part is copied and slightly modified from s:Match_wrapper(). + let default = escape(&mps, '[$^.*~\\/?]') . (strlen(&mps) ? "," : "") . + \ '\/\*:\*\/,#if\%(def\)\=:$else\>:#elif\>:#endif\>' + " Allow b:match_words = "GetVimMatchWords()" . + if b:match_words =~ ":" + let match_words = b:match_words + else + execute "let match_words =" b:match_words + endif + if (match_words != s:last_words) || (&mps != s:last_mps) || + \ exists("b:match_debug") + let s:last_words = match_words + let s:last_mps = &mps + if match_words !~ s:notslash . '\\\d' + let s:do_BR = 0 + let s:pat = match_words + else + let s:do_BR = 1 + let s:pat = s:ParseWords(match_words) + endif + let s:all = '\%(' . substitute(s:pat . (strlen(s:pat)?",":"") . default, + \ '[,:]\+','\\|','g') . '\)' + if exists("b:match_debug") + let b:match_pat = s:pat + endif + endif + + " Second step: figure out the patterns for searchpair() + " and save the screen, cursor position, and 'ignorecase'. + " - TODO: A lot of this is copied from s:Match_wrapper(). + " - maybe even more functionality should be split off + " - into separate functions! + let cdefault = (s:pat =~ '[^,]$' ? "," : "") . default + let open = substitute(s:pat . cdefault, ':[^,]*,', '\\),\\(', 'g') + let open = '\(' . substitute(open, ':[^,]*$', '\\)', '') + let close = substitute(s:pat . cdefault, ',[^,]*:', '\\),\\(', 'g') + let close = substitute(close, '[^,]*:', '\\(', '') . '\)' + if exists("b:match_skip") + let skip = b:match_skip + elseif exists("b:match_comment") " backwards compatibility and testing! + let skip = "r:" . b:match_comment + else + let skip = 's:comment\|string' + endif + let skip = s:ParseSkip(skip) + " let restore_cursor = line(".") . "G" . virtcol(".") . "|" + " normal! H + " let restore_cursor = "normal!" . line(".") . "Gzt" . restore_cursor + let restore_cursor = virtcol(".") . "|" + normal! g0 + let restore_cursor = line(".") . "G" . virtcol(".") . "|zs" . restore_cursor + normal! H + let restore_cursor = "normal!" . line(".") . "Gzt" . restore_cursor + execute restore_cursor + + " Third step: call searchpair(). + " Replace '\('--but not '\\('--with '\%(' and ',' with '\|'. + let openpat = substitute(open, '\(\\\@<!\(\\\\\)*\)\@<=\\(', '\\%(', 'g') + let openpat = substitute(openpat, ',', '\\|', 'g') + let closepat = substitute(close, '\(\\\@<!\(\\\\\)*\)\@<=\\(', '\\%(', 'g') + let closepat = substitute(closepat, ',', '\\|', 'g') + if skip =~ 'synID' && !(has("syntax") && exists("g:syntax_on")) + let skip = '0' + else + execute "if " . skip . "| let skip = '0' | endif" + endif + mark ' + let level = v:count1 + while level + if searchpair(openpat, '', closepat, a:spflag, skip) < 1 + call s:CleanUp(restore_options, a:mode, startline, startcol) + return "" + endif + let level = level - 1 + endwhile + + " Restore options and return a string to restore the original position. + call s:CleanUp(restore_options, a:mode, startline, startcol) + return restore_cursor +endfun + +" Search backwards for "if" or "while" or "<tag>" or ... +" and return "endif" or "endwhile" or "</tag>" or ... . +" For now, this uses b:match_words and the same script variables +" as s:Match_wrapper() . Later, it may get its own patterns, +" either from a buffer variable or passed as arguments. +" fun! s:Autocomplete() +" echo "autocomplete not yet implemented :-(" +" if !exists("b:match_words") || b:match_words == "" +" return "" +" end +" let startpos = s:MultiMatch("bW") +" +" if startpos == "" +" return "" +" endif +" " - TODO: figure out whether 'if' or '<tag>' matched, and construct +" " - the appropriate closing. +" let matchline = getline(".") +" let curcol = col(".") - 1 +" " - TODO: Change the s:all argument if there is a new set of match pats. +" let regexp = s:Wholematch(matchline, s:all, curcol) +" let suf = strlen(matchline) - matchend(matchline, regexp) +" let prefix = (curcol ? '^.\{' . curcol . '}\%(' : '^\%(') +" let suffix = (suf ? '\).\{' . suf . '}$' : '\)$') +" " Reconstruct the version with unresolved backrefs. +" let patBR = substitute(b:match_words.',', '[,:]*,[,:]*', ',', 'g') +" let patBR = substitute(patBR, ':\{2,}', ':', "g") +" " Now, set group and groupBR to the matching group: 'if:endif' or +" " 'while:endwhile' or whatever. +" let group = s:Choose(s:pat, matchline, ",", ":", prefix, suffix, patBR) +" let i = matchend(group, s:notslash . ",") +" let groupBR = strpart(group, i) +" let group = strpart(group, 0, i-1) +" " Now, matchline =~ prefix . substitute(group,':','\|','g') . suffix +" if s:do_BR +" let group = s:InsertRefs(groupBR, prefix, group, suffix, matchline) +" endif +" " let g:group = group +" +" " - TODO: Construct the closing from group. +" let fake = "end" . expand("<cword>") +" execute startpos +" return fake +" endfun + +" Close all open structures. "Get the heck out of here!" +" fun! s:Gthhoh() +" let close = s:Autocomplete() +" while strlen(close) +" put=close +" let close = s:Autocomplete() +" endwhile +" endfun + +" Parse special strings as typical skip arguments for searchpair(): +" s:foo becomes (current syntax item) =~ foo +" S:foo becomes (current syntax item) !~ foo +" r:foo becomes (line before cursor) =~ foo +" R:foo becomes (line before cursor) !~ foo +fun! s:ParseSkip(str) + let skip = a:str + if skip[1] == ":" + if skip[0] == "s" + let skip = "synIDattr(synID(line('.'),col('.'),1),'name') =~? '" . + \ strpart(skip,2) . "'" + elseif skip[0] == "S" + let skip = "synIDattr(synID(line('.'),col('.'),1),'name') !~? '" . + \ strpart(skip,2) . "'" + elseif skip[0] == "r" + let skip = "strpart(getline('.'),0,col('.'))=~'" . strpart(skip,2). "'" + elseif skip[0] == "R" + let skip = "strpart(getline('.'),0,col('.'))!~'" . strpart(skip,2). "'" + endif + endif + return skip +endfun + +let &cpo = s:save_cpo + +" vim:sts=2:sw=2: diff --git a/runtime/macros/maze.info b/runtime/macros/maze.info Binary files differnew file mode 100644 index 000000000..2ad27db5a --- /dev/null +++ b/runtime/macros/maze.info diff --git a/runtime/macros/maze/Makefile b/runtime/macros/maze/Makefile new file mode 100644 index 000000000..c34e115d9 --- /dev/null +++ b/runtime/macros/maze/Makefile @@ -0,0 +1,7 @@ +# It's simple... + +maze: mazeansi.c + cc -o maze mazeansi.c + +mazeclean: mazeclean.c + cc -o mazeclean mazeclean.c diff --git a/runtime/macros/maze/README.txt b/runtime/macros/maze/README.txt new file mode 100644 index 000000000..be8e8ef20 --- /dev/null +++ b/runtime/macros/maze/README.txt @@ -0,0 +1,49 @@ +To run the maze macros with Vim: + + vim -u maze_mac maze_5.78 + press "g" + +The "-u maze.mac" loads the maze macros and skips loading your .vimrc, which +may contain settings and mappings that get in the way. + + +The original README: + +To prove that you can do anything in vi, I wrote a couple of macros that +allows vi to solve mazes. It will solve any maze produced by maze.c +that was posted to the net recently. + +Just follow this recipe and SEE FOR YOURSELF. + 1. run uudecode on the file "maze.vi.macros.uu" to + produce the file "maze.vi.macros" + (If you can't wait to see the action, jump to step 4) + 2. compile maze.c with "cc -o maze maze.c" + 3. run maze > maze.out and input a small number (for example 10 if + you are on a fast machine, 3-5 if slow) which + is the size of the maze to produce + 4. edit the maze (vi maze.out) + 5. include the macros with the vi command: + :so maze.vi.macros + 6. type the letter "g" (for "go") and watch vi solve the maze + 7. when vi solves the maze, you will see why it lies + 8. now look at maze.vi.macros and all will be revealed + +Tested on a sparc, a sun and a pyramid (although maze.c will not compile +on the pyramid). + +Anyone who can't get the maze.c file to compile, get a new compiler, +try maze.ansi.c which was also posted to the net. +If you can get it to compile but the maze comes out looking like a fence +and not a maze and you are using SysV or DOS replace the "27" on the +last line of maze.c by "11" +Thanks to John Tromp (tromp@piring.cwi.nl) for maze.c. +Thanks to antonyc@nntp-server.caltech.edu (Bill T. Cat) for maze.ansi.c. + +Any donations should be in unmarked small denomination bills :^)=. + + ACSnet: gregm@otc.otca.oz.au +Greg McFarlane UUCP: {uunet,mcvax}!otc.otca.oz.au!gregm +|||| OTC || Snail: OTC R&D GPO Box 7000, Sydney 2001, Australia + Phone: +61 2 287 3139 Fax: +61 2 287 3299 + + diff --git a/runtime/macros/maze/README.txt.info b/runtime/macros/maze/README.txt.info Binary files differnew file mode 100644 index 000000000..263ddcd2c --- /dev/null +++ b/runtime/macros/maze/README.txt.info diff --git a/runtime/macros/maze/main.aap b/runtime/macros/maze/main.aap new file mode 100644 index 000000000..ee9af21de --- /dev/null +++ b/runtime/macros/maze/main.aap @@ -0,0 +1,4 @@ +# Aap recipe to build the maze program +:program maze : mazeansi.c + +:program mazeclean : mazeclean.c diff --git a/runtime/macros/maze/maze.c b/runtime/macros/maze/maze.c new file mode 100644 index 000000000..b91745311 --- /dev/null +++ b/runtime/macros/maze/maze.c @@ -0,0 +1,7 @@ +char*M,A,Z,E=40,J[40],T[40];main(C){for(*J=A=scanf(M="%d",&C); +-- E; J[ E] =T +[E ]= E) printf("._"); for(;(A-=Z=!Z) || (printf("\n|" +) , A = 39 ,C -- +) ; Z || printf (M ))M[Z]=Z[A-(E =A[J-Z])&&!C +& A == T[ A] +|6<<27<rand()||!C&!Z?J[T[E]=T[A]]=E,J[T[A]=A-Z]=A,"_.":" |"];} diff --git a/runtime/macros/maze/maze_5.78 b/runtime/macros/maze/maze_5.78 new file mode 100644 index 000000000..dbe3d278b --- /dev/null +++ b/runtime/macros/maze/maze_5.78 @@ -0,0 +1,16 @@ +._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._ +| ._| . . ._| | |_._._. . ._|_._._._._. ._|_. ._|_._. ._| . ._|_. | . ._._. | +| ._|_| |_. | | | | ._._|_._|_._. . |_. | | | ._._| |_._._| | ._. ._| . . |_| +|_._._._. | ._|_. ._._._. | | ._. |_._. . | ._._| |_. | ._._._. |_. | |_|_| | +| | . |_._| . ._._._| ._._. ._._| | | |_| . | |_. . ._|_| ._._. |_._|_| . | | +|_._|_._._._|_._._._|_|_._._._|_._|_._._._|_._._._|_._._._|_._._._._._._|_._| + +See Vim solve a maze! + + type ":so maze_mac<RETURN>" to load the macros + + type "g" to start + +to interrupt type "<CTRL-C>" + to quit type ":q!<RETURN>" + diff --git a/runtime/macros/maze/maze_5.78.info b/runtime/macros/maze/maze_5.78.info Binary files differnew file mode 100644 index 000000000..fd65cef72 --- /dev/null +++ b/runtime/macros/maze/maze_5.78.info diff --git a/runtime/macros/maze/maze_mac b/runtime/macros/maze/maze_mac new file mode 100644 index 000000000..621aeec2b --- /dev/null +++ b/runtime/macros/maze/maze_mac @@ -0,0 +1,271 @@ +" These macros 'solve' any maze produced by the a-maze-ing maze.c program. +" +" First, a bit of maze theory. +" If you were put into a maze, a guaranteed method of finding your way +" out of the maze is to put your left hand onto a wall and just keep walking, +" never taking your hand off the wall. This technique is only guaranteed to +" work if the maze does not have any 'islands', or if the 'exit' is on the +" same island as your starting point. These conditions hold for the mazes +" under consideration. +" +" Assuming that the maze is made up of horizontal and vertical walls spaced +" one step apart and that you can move either north, south, east or west, +" then you can automate this procedure by carrying out the following steps. +" +" 1. Put yourself somewhere in the maze near a wall. +" 2. Check if you have a wall on your left. If so, go to step 4. +" 3. There is no wall on your left, so turn on the spot to your left and step +" forward by one step and repeat step 2. +" 4. Check what is directly in front of you. If it is a wall, turn on the +" spot to your right by 90 degrees and repeat step 4. +" 5. There is no wall in front of you, so step forward one step and +" go to step 2. +" +" In this way you will cover all the corridors of the maze (until you get back +" to where you started from, if you do not stop). +" +" By examining a maze produced by the maze.c program you will see that +" each square of the maze is one character high and two characters wide. +" To go north or south, you move by a one character step, but to move east or +" west you move by a two character step. Also note that in any position +" there are four places where walls could be put - to the north, to the south, +" to the east and to the west. +" A wall exists to the north of you if the character to the north of +" you is a _ (otherwise it is a space). +" A wall exists to the east of you if the character to the east of you +" is a | (otherwise it is a .). +" A wall exists to the west of you if the character to the west of you +" is a | (otherwise it is a .). +" A wall exists to the south of you if the character where you are +" is a _ (otherwise it is a space). +" +" Note the difference for direction south, where we must examine the character +" where the cursor is rather than an adjacent cell. +" +" If you were implementing the above procedure is a normal computer language +" you could use a loop with if statements and continue statements, +" However, these constructs are not available in vi macros so I have used +" a state machine with 8 states. Each state signifies the direction you +" are going in and whether or not you have checked if there is a wall on +" your left. +" +" The transition from state to state and the actions taken on each transition +" are given in the state table below. +" The names of the states are N1, N2, S1, S2, E1, E2, W1, W2, where each letter +" stands for a direction of the compass, the number 1 indicates that the we +" have not yet checked to see if there is a wall on our left and the number 2 +" indicates that we have checked and there is a wall on our left. +" +" For each state we must consider the existence or not of a wall in a +" particular direction. This direction is given in the following table. +" +" NextChar table: +" state direction vi commands +" N1 W hF +" N2 N kF +" S1 E lF +" S2 S F +" E1 N kF +" E2 E lF +" W1 S F +" W2 W hF +" +" where F is a macro which yanks the character under the cursor into +" the NextChar register (n). +" +" State table: +" In the 'vi commands' column is given the actions to carry out when in +" this state and the NextChar is as given. The commands k, j, ll, hh move +" the current position north, south, east and west respectively. The +" command mm is used as a no-op command. +" In the 'next state' column is given the new state of the machine after +" the action is carried out. +" +" current state NextChar vi commands next state +" N1 . hh W1 +" N1 | mm N2 +" N2 _ mm E1 +" N2 space k N1 +" S1 . ll E1 +" S1 | mm S2 +" S2 _ mm W1 +" S2 space j S1 +" E1 space k N1 +" E1 _ mm E2 +" E2 | mm S1 +" E2 . ll E1 +" W1 space j S1 +" W1 _ mm W2 +" W2 | mm N1 +" W2 . hh W1 +" +" +" Complaint about vi macros: +" It seems that you cannot have more than one 'undo-able' vi command +" in the one macro, so you have to make lots of little macros and +" put them together. +" +" I'll explain what I mean by an example. Edit a file and +" type ':map Q rXY'. This should map the Q key to 'replace the +" character under the cursor with X and yank the line'. +" But when I type Q, vi tells me 'Can't yank inside global/macro' and +" goes into ex mode. However if I type ':map Q rXT' and ':map T Y', +" everything is OK. I`m doing all this on a Sparcstation. +" If anyone reading this has an answer to this problem, the author would +" love to find out. Mail to gregm@otc.otca.oz.au. +" +" The macros: +" The macro to run the maze solver is 'g'. This simply calls two other +" macros: I, to initialise everything, and L, to loop forever running +" through the state table. +" Both of these macros are long sequences of calls to other macros. All +" of these other macros are quite simple and so to understand how this +" works, all you need to do is examine macros I and L and learn what they +" do (a simple sequence of vi actions) and how L loops (by calling U, which +" simply calls L again). +" +" Macro I sets up the state table and NextChar table at the end of the file. +" Macro L then searches these tables to find out what actions to perform and +" what state changes to make. +" +" The entries in the state table all begin with a key consisting of the +" letter 's', the current state and the NextChar. After this is the +" action to take in this state and after this is the next state to change to. +" +" The entries in the NextChar table begin with a key consisting of the +" letter 'n' and the current state. After this is the action to take to +" obtain NextChar - the character that must be examined to change state. +" +" One way to see what each part of the macros is doing is to type in the +" body of the macros I and L manually (instead of typing 'g') and see +" what happens at each step. +" +" Good luck. +" +" Registers used by the macros: +" s (State) - holds the state the machine is in +" c (Char) - holds the character under the current position +" m (Macro) - holds a vi command string to be executed later +" n (NextChar) - holds the character we must examine to change state +" r (Second Macro) - holds a second vi command string to be executed later +" +set remap +set nomagic +set noterse +set wrapscan +" +"================================================================ +" g - go runs the whole show +" I - initialise +" L - then loop forever +map g IL +" +"================================================================ +" I - initialise everything before running the loop +" G$?.^M - find the last . in the maze +" ^ - replace it with an X (the goal) +" GYKeDP - print the state table and next char table at the end of the file +" 0S - initialise the state of the machine to E1 +" 2Gl - move to the top left cell of the maze +map I G$?.
^GYKeDP0S2Gl +" +"================================================================ +" L - the loop which is executed forever +" Q - save the current character in the Char register +" A - replace the current character with an 'O' +" ma - mark the current position with mark 'a' +" GNB - on bottom line, create a command to search the NextChar table +" for the current state +" 0M0E@m^M - yank the command into the Macro register and execute it +" wX - we have now found the entry in the table, now yank the +" following word into the Macro register +" `a@m - go back to the current position and execute the macro, this will +" yank the NextChar in register n +" GT$B$R - on bottom line, create a command to search the state table +" for the current state and NextChar +" 0M0E@m^M - yank the command into the Macro register and execute it +" 2WS - we have now found the entry in the table, now yank the +" next state into the State macro +" bX - and yank the action corresponding to this state table entry +" into the Macro register +" GVJ - on bottom line, create a command to restore the current character +" 0H - and save the command into the second Macro register +" `a@r - go back to the current position and exectute the macro to restore +" the current character +" @m - execute the action associated with this state +" U - and repeat +map L QAmaGNB0M0E@m
wX`a@mGT$B$R0M0E@m
2WSbXGVJ0H`a@r@mU +" +"================================================================ +" U - no tail recursion allowed in vi macros so cheat and set U = L +map U L +" +"================================================================ +" S - yank the next two characters into the State register +map S "sy2l +" +"================================================================ +" Q - save the current character in the Char register +map Q "cyl +" +"================================================================ +" A - replace the current character with an 'O' +map A rO +" +"================================================================ +" N - replace this line with the string 'n' +map N C/n +" +"================================================================ +" B - put the current state +map B "sp +" +"================================================================ +" M - yank this line into the Macro register +map M "my$ +" +"================================================================ +" E - delete to the end of the line +map E d$ +" +"================================================================ +" X - yank this word into the Macro register +map X "myt +" +"================================================================ +" T - replace this line with the string 's' +map T C/s +" +"================================================================ +" R - put NextChar +map R "np +" +"================================================================ +" V - add the letter 'r' (the replace vi command) +map V ar +" +"================================================================ +" J - restore the current character +map J "cp +" +"================================================================ +" H - yank this line into the second Macro register +map H "ry$ +" +"================================================================ +" F - yank NextChar (this macro is called from the Macro register) +map F "nyl +" +"================================================================ +" ^ - replace the current character with an 'X' +map ^ rX +" +"================================================================ +" YKeDP - create the state table, NextChar table and initial state +" Note that you have to escape the bar character, since it is special to +" the map command (it indicates a new line). +map Y osE1 k N1 sE1_ mm E2 sE2| mm S1 sE2. ll E1 +map K osW1 j S1 sW1_ mm W2 sW2| mm N1 sW2. hh W1 +map e osN1. hh W1 sN1| mm N2 sN2 k N1 sN2_ mm E1 +map D osS1. ll E1 sS1| mm S2 sS2 j S1 sS2_ mm W1 +map P onE1 kF nE2 lF nW1 G$JF nW2 hF nN1 hF nN2 kF nS1 lF nS2 G$JF
E1 diff --git a/runtime/macros/maze/mazeansi.c b/runtime/macros/maze/mazeansi.c new file mode 100644 index 000000000..b8e5e796c --- /dev/null +++ b/runtime/macros/maze/mazeansi.c @@ -0,0 +1,7 @@ +char*M,A,Z,E=40,J[80],T[3];main(C){for(M=J+E,*J=A=scanf("%d",& +C) ;-- E;J [E ]=M +[E ]= E) printf("._"); for(;(A-=Z=!Z) || (printf("\n|" +) , A = 39 ,C -- +) ; Z || printf (T ))T[Z]=Z[A-(E =A[J-Z])&&!C +& A == M[ A] +|6<<27<rand()||!C&!Z?J[M[E]=M[A]]=E,J[M[A]=A-Z]=A,"_.":" |"];} diff --git a/runtime/macros/maze/mazeclean.c b/runtime/macros/maze/mazeclean.c new file mode 100644 index 000000000..04dcebd22 --- /dev/null +++ b/runtime/macros/maze/mazeclean.c @@ -0,0 +1,22 @@ +/* + * Cleaned-up version of the maze program. + * Doesn't look as nice, but should work with all C compilers. + * Sascha Wilde, October 2003 + */ +#include <stdio.h> +#include <stdlib.h> + +char *M, A, Z, E = 40, line[80], T[3]; +int +main (C) +{ + for (M = line + E, *line = A = scanf ("%d", &C); --E; line[E] = M[E] = E) + printf ("._"); + for (; (A -= Z = !Z) || (printf ("\n|"), A = 39, C--); Z || printf (T)) + T[Z] = Z[A - (E = A[line - Z]) && !C + & A == M[A] + | RAND_MAX/3 < rand () + || !C & !Z ? line[M[E] = M[A]] = E, line[M[A] = A - Z] = + A, "_." : " |"]; + return 0; +} diff --git a/runtime/macros/maze/poster b/runtime/macros/maze/poster new file mode 100644 index 000000000..9114f598d --- /dev/null +++ b/runtime/macros/maze/poster @@ -0,0 +1,37 @@ +Article 2846 of alt.sources: +Path: oce-rd1!hp4nl!mcsun!uunet!munnari.oz.au!metro!otc!gregm +From: gregm@otc.otca.oz.au (Greg McFarlane) +Newsgroups: alt.sources +Subject: VI SOLVES MAZE (commented macros) +Message-ID: <2289@otc.otca.oz> +Date: 10 Feb 91 23:31:02 GMT +Sender: news@otc.otca.oz +Reply-To: gregm@otc.otca.oz.au (Greg McFarlane) +Organization: OTC Development Unit, Australia +Lines: 464 + +Submitted-by: gregm@otc.otca.oz.au +Archive-name: maze_solving_vi_macros + +A real working model. See it walk the maze in front of your very own eyes. + +To prove that you can do anything in vi, I wrote a couple of macros that +allows vi to solve mazes. It will solve any maze produced by maze.c +that was posted to the alt.sources last month. (Maze.c is also included +in this posting as well as an example of its output.) + +The uncommented version of the macros was sent to alt.sources last month. +However, so many people mailed me requesting the commented version of the +macros that I decided to post it. I have made some modifications to the +original macros to make them easier to follow and also after I learnt +that you can escape the special meaning of '|' in macros by using '^V|'. + +Save this article and unshar it. Then read maze.README. + +After studying these macros, anyone who cannot write an emacs emulator +in vi macros should just curl up and :q!. + +Coming soon to a newsgroup near you: "Vi macros solve Tower of Hanoi", +and a repost of the original "Turing Machine implemented in Vi macros" + +Anyone who has a version of these macros for edlin or nroff, please post. diff --git a/runtime/macros/maze/poster.info b/runtime/macros/maze/poster.info Binary files differnew file mode 100644 index 000000000..9a478d6a6 --- /dev/null +++ b/runtime/macros/maze/poster.info diff --git a/runtime/macros/shellmenu.vim b/runtime/macros/shellmenu.vim new file mode 100644 index 000000000..6175d1d9a --- /dev/null +++ b/runtime/macros/shellmenu.vim @@ -0,0 +1,94 @@ +" When you're writing shell scripts and you are in doubt which test to use, +" which shell environment variables are defined, what the syntax of the case +" statement is, and you need to invoke 'man sh'? +" +" Your problems are over now! +" +" Attached is a Vim script file for turning gvim into a shell script editor. +" It may also be used as an example how to use menus in Vim. +" +" Written by: Lennart Schultz <les@dmi.min.dk> + +imenu Stmts.for for in
do
doneki kk0elli +imenu Stmts.case case in
) ;;
esacbki k0elli +imenu Stmts.if if
then
fiki kk0elli +imenu Stmts.if-else if
then
else
fiki kki kk0elli +imenu Stmts.elif elif
then
ki kk0elli +imenu Stmts.while while
do
doneki kk0elli +imenu Stmts.break break +imenu Stmts.continue continue +imenu Stmts.function () {
}ki k0i +imenu Stmts.return return +imenu Stmts.return-true return 0 +imenu Stmts.return-false return 1 +imenu Stmts.exit exit +imenu Stmts.shift shift +imenu Stmts.trap trap +imenu Test.existence [ -e ]hi +imenu Test.existence - file [ -f ]hi +imenu Test.existence - file (not empty) [ -s ]hi +imenu Test.existence - directory [ -d ]hi +imenu Test.existence - executable [ -x ]hi +imenu Test.existence - readable [ -r ]hi +imenu Test.existence - writable [ -w ]hi +imenu Test.String is empty [ x = "x$" ]hhi +imenu Test.String is not empty [ x != "x$" ]hhi +imenu Test.Strings is equal [ "" = "" ]hhhhhhhi +imenu Test.Strings is not equal [ "" != "" ]hhhhhhhhi +imenu Test.Values is greater than [ -gt ]hhhhhhi +imenu Test.Values is greater equal [ -ge ]hhhhhhi +imenu Test.Values is equal [ -eq ]hhhhhhi +imenu Test.Values is not equal [ -ne ]hhhhhhi +imenu Test.Values is less than [ -lt ]hhhhhhi +imenu Test.Values is less equal [ -le ]hhhhhhi +imenu ParmSub.Substitute word if parm not set ${:-}hhi +imenu ParmSub.Set parm to word if not set ${:=}hhi +imenu ParmSub.Substitute word if parm set else nothing ${:+}hhi +imenu ParmSub.If parm not set print word and exit ${:?}hhi +imenu SpShVars.Number of positional parameters ${#} +imenu SpShVars.All positional parameters (quoted spaces) ${*} +imenu SpShVars.All positional parameters (unquoted spaces) ${@} +imenu SpShVars.Flags set ${-} +imenu SpShVars.Return code of last command ${?} +imenu SpShVars.Process number of this shell ${$} +imenu SpShVars.Process number of last background command ${!} +imenu Environ.HOME ${HOME} +imenu Environ.PATH ${PATH} +imenu Environ.CDPATH ${CDPATH} +imenu Environ.MAIL ${MAIL} +imenu Environ.MAILCHECK ${MAILCHECK} +imenu Environ.PS1 ${PS1} +imenu Environ.PS2 ${PS2} +imenu Environ.IFS ${IFS} +imenu Environ.SHACCT ${SHACCT} +imenu Environ.SHELL ${SHELL} +imenu Environ.LC_CTYPE ${LC_CTYPE} +imenu Environ.LC_MESSAGES ${LC_MESSAGES} +imenu Builtins.cd cd +imenu Builtins.echo echo +imenu Builtins.eval eval +imenu Builtins.exec exec +imenu Builtins.export export +imenu Builtins.getopts getopts +imenu Builtins.hash hash +imenu Builtins.newgrp newgrp +imenu Builtins.pwd pwd +imenu Builtins.read read +imenu Builtins.readonly readonly +imenu Builtins.return return +imenu Builtins.times times +imenu Builtins.type type +imenu Builtins.umask umask +imenu Builtins.wait wait +imenu Set.set set +imenu Set.unset unset +imenu Set.mark modified or modified variables set -a +imenu Set.exit when command returns non-zero exit code set -e +imenu Set.Disable file name generation set -f +imenu Set.remember function commands set -h +imenu Set.All keyword arguments are placed in the environment set -k +imenu Set.Read commands but do not execute them set -n +imenu Set.Exit after reading and executing one command set -t +imenu Set.Treat unset variables as an error when substituting set -u +imenu Set.Print shell input lines as they are read set -v +imenu Set.Print commands and their arguments as they are executed set -x diff --git a/runtime/macros/swapmous.vim b/runtime/macros/swapmous.vim new file mode 100644 index 000000000..8b85be050 --- /dev/null +++ b/runtime/macros/swapmous.vim @@ -0,0 +1,22 @@ +" These macros swap the left and right mouse buttons (for left handed) +" Don't forget to do ":set mouse=a" or the mouse won't work at all +noremap <LeftMouse> <RightMouse> +noremap <2-LeftMouse> <2-RightMouse> +noremap <3-LeftMouse> <3-RightMouse> +noremap <4-LeftMouse> <4-RightMouse> +noremap <LeftDrag> <RightDrag> +noremap <LeftRelease> <RightRelease> +noremap <RightMouse> <LeftMouse> +noremap <2-RightMouse> <2-LeftMouse> +noremap <3-RightMouse> <3-LeftMouse> +noremap <4-RightMouse> <4-LeftMouse> +noremap <RightDrag> <LeftDrag> +noremap <RightRelease> <LeftRelease> +noremap g<LeftMouse> <C-RightMouse> +noremap g<RightMouse> <C-LeftMouse> +noremap! <LeftMouse> <RightMouse> +noremap! <LeftDrag> <RightDrag> +noremap! <LeftRelease> <RightRelease> +noremap! <RightMouse> <LeftMouse> +noremap! <RightDrag> <LeftDrag> +noremap! <RightRelease> <LeftRelease> diff --git a/runtime/macros/urm.info b/runtime/macros/urm.info Binary files differnew file mode 100644 index 000000000..65e82348e --- /dev/null +++ b/runtime/macros/urm.info diff --git a/runtime/macros/urm/README.txt b/runtime/macros/urm/README.txt new file mode 100644 index 000000000..a1ecc658c --- /dev/null +++ b/runtime/macros/urm/README.txt @@ -0,0 +1,47 @@ +This is another proof that Vim is perfectly compatible with Vi. +The URM macro package was written by Rudolf Koenig ("Rudi") +(rudolf@koeniglich.de) for hpux-vi in August 1991. + +Getting started: + +type +in your shell: vim urm<RETURN> +in vim: :so urm.vim<RETURN> +in vim: * (to load the registers and boot the URM-machine :-) +in vim: g (for 'go') and watch the fun. Per default, 3 and 4 + are multiplied. Watch the Program counter, it is + visible as a komma moving around. + +This is a "standard URM" (Universal register machine) interpreter. The URM +concept is used in theoretical computer science to aid in theorem proving. +Here it proves that vim is a general problem solver (if you bring enough +patience). + +The interpreter begins with register 1 (not 0), without macros and more-lines +capability. A dot marks the end of a program. (Bug: there must be a space +after the dot.) + +The registers are the first few lines, beginning with a '>' . +The program is the first line after the registers. +You should always initialize the registers required by the program. + +Output register: line 2 +Input registers: line 2 to ... + +Commands: +a<n> increment register <n> +s<n> decrement register <n> +<x>;<y> execute command <x> and then <y> +(<x>)<n> execute command <x> while register <n> is nonzero +. ("dot blank") halt the machine. + +Examples: + +Add register 2 to register 3: + (a2;s3)3. +Multiply register 2 with register 3: + (a4;a5;s2)2; ((a2;s4)4; s3; (a1;a4;s5)5; (a5;s1)1)3. + +There are more (complicated) examples in the file examples. +Note, undo may take a while after a division. + diff --git a/runtime/macros/urm/README.txt.info b/runtime/macros/urm/README.txt.info Binary files differnew file mode 100644 index 000000000..1337937c9 --- /dev/null +++ b/runtime/macros/urm/README.txt.info diff --git a/runtime/macros/urm/examples b/runtime/macros/urm/examples new file mode 100644 index 000000000..9907d4aef --- /dev/null +++ b/runtime/macros/urm/examples @@ -0,0 +1,16 @@ +Note that enough temporary registers should be provided for each example. +All should be initialised to 0. + +Initial register values for benchmarking: 0,8,3,0,... + +Performed on a Xenix 386/16: +Operation [sec, kbyte tmp space]: program + +Asym. Diff.[ 7, 4]: (s2;s3)3. +Abs. Diff. [90,81]: (a1;a4;s2)2; (a2;s1)1; (a1;a5;s3)3; (a3;s1)1; (s2;s3)3; (s5;s4)4; (a2;s5)5. +Add [ 7, 4]: (a2;s3)3. +Mult [227, 161]: (a4;a5;s2)2; ((a2;s4)4; s3; (a1;a4;s5)5; (a5;s1)1)3. +Copy [ 48, 25]: (a1;a3;s2)2; (a2;s1)1. +sign [ 30, 17]: (a3;s2)2; (a2;(s3)3)3. +!sign[ 36, 28]: (a3;s2)2; (a2;(s3)3)3; a3; (s3;s2)2; (s3;a2)3. +Div [630,1522]: (a9;s2)2; (a2;a10;s3)3; (a3;s2)2; (a2;(s3)3)3; a3; (s3;s2)2; (s3;a2)3; (a2)2;(a2;s9)9;(a3;s10)10; (a9;a10;s2)2; (a11;a12;s3)3; (a2;s12)12; (a3;s9)9; (s2;s3)3; (a3;s2)2; (a2;(s3)3)3; a3; (s3;s2)2; (s3;a2)3; (a1;s2)2; (a2;s10)10; (a3;s11)11; ((a12;a13;s3)3; (a3;s13)13; (s2;s3)3; (a3;s12)12; a14; (s1)1; (a9;a10;s2)2; (a11;a12;s3)3; (a2;s12)12; (a3;s9)9; (s2;s3)3; (a3;s2)2; (a2;(s3)3)3; a3; (s3;s2)2; (s3;a2)3; (a1;s2)2; (a2;s10)10; (a3;s11)11)1; (s2)2; (a2;s14)14. diff --git a/runtime/macros/urm/urm b/runtime/macros/urm/urm new file mode 100644 index 000000000..9cbefb70a --- /dev/null +++ b/runtime/macros/urm/urm @@ -0,0 +1,22 @@ +>0 +>3 +>4 +>0 +>0 +>0 +(a4;a5;s2)2; ((a2;s4)4; s3; (a1;a4;s5)5; (a5;s1)1)3. +_________ +O ; =xp ( =x%hp ) @l a @db s @dt . =x0xkdd:ready _end_ +o 0 1 2 3 4 5 6 7 8 9 0 +_________ +INIT main(k), l, b, c, t, u, q, d +
"kT
"lT
"bT
"cT
"tT
"uT
"qT
"dT +=lF'wa/O
fpaw"zdt hp@z0"xD@x@k +=2ldwhp'wiGT'wp0P0"yD@ya =xlwP >0 =x%p I k/>0
ww"ydt 0D@y +'wa/o
fwF'wpi`ar`aF'wffp0"vD@v0"vDp03x@v'wa @c 0 0 0I f0w"wdt 0D@w +`ahmaF'wa 'aa1 > @b 0p0f>w"vdt 0D@v +'wa/o
wfbF'wpi`ar`aF'wffp0"vD@v0"vDp03x@v'wa @u 9 0 0I f9w"wdt 0D@w +`ahmaF'wa `alr0 > @q 0p0f>w"vdt 0D@v +`ahy2l'wa `ax >1 @t 0p0/>1
ww"idt 0D@i +=xwhpbldwhp'wpaG$ma0"yD@y@ + diff --git a/runtime/macros/urm/urm.vim b/runtime/macros/urm/urm.vim new file mode 100644 index 000000000..310818078 --- /dev/null +++ b/runtime/macros/urm/urm.vim @@ -0,0 +1,5 @@ +map * 1G/INIT
j"iT@i1G/INIT
dG +map g 1G/^[(as;.]
i
>,mkkmw@k +map T y$ +map F yl +map = 'kf, |