# HG changeset patch # User Steve Losh # Date 1276120428 14400 # Node ID 309b770e8a8d4bf269a5a0aa546c194f97f21a97 # Parent 1b7aa8ec75fb5a6743de4459b6079f3699a52c8a# Parent 24c78d478e3664db84a4091945932c6b10834daa Merge. diff -r 1b7aa8ec75fb -r 309b770e8a8d vim/.gvimrc --- a/vim/.gvimrc Wed Jun 09 17:53:21 2010 -0400 +++ b/vim/.gvimrc Wed Jun 09 17:53:48 2010 -0400 @@ -5,7 +5,7 @@ if has("gui_macvim") macmenu &File.New\ Tab key= - map PeepOpen + map t PeepOpen end -let g:sparkupExecuteMapping = '' \ No newline at end of file +let g:sparkupExecuteMapping = '' diff -r 1b7aa8ec75fb -r 309b770e8a8d vim/.vimrc --- a/vim/.vimrc Wed Jun 09 17:53:21 2010 -0400 +++ b/vim/.vimrc Wed Jun 09 17:53:48 2010 -0400 @@ -19,11 +19,12 @@ set wildmode=list:longest set visualbell set cursorline +set ttyfast " Backups -set nobackup -set nowritebackup -set directory=$HOME/.vim/tmp//,. +set backupdir=~/tmp,/tmp " backups (~) +set directory=~/tmp,/tmp " swap files +set backup " enable backups " Leader let mapleader = "," @@ -40,7 +41,8 @@ set incsearch set showmatch set hlsearch -map c :let @/='' +set gdefault +map :let @/='' " Soft/hard wrapping set wrap @@ -85,7 +87,8 @@ map :resize +10 map :vertical resize +10 -" Use F1 to fold/unfold +" Folding +set foldlevelstart=1 nnoremap za vnoremap za @@ -126,3 +129,6 @@ if has("gui_running") highlight SpellBad term=underline gui=undercurl guisp=Orange endif + +" Yankring +nnoremap :YRShow diff -r 1b7aa8ec75fb -r 309b770e8a8d vim/autoload/repeat.vim --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vim/autoload/repeat.vim Wed Jun 09 17:53:48 2010 -0400 @@ -0,0 +1,72 @@ +" repeat.vim - Let the repeat command repeat plugin maps +" Maintainer: Tim Pope +" Version: 1.0 + +" Installation: +" Place in either ~/.vim/plugin/repeat.vim (to load at start up) or +" ~/.vim/autoload/repeat.vim (to load automatically as needed). +" +" Developers: +" Basic usage is as follows: +" +" silent! call repeat#set("\MappingToRepeatCommand",3) +" +" The first argument is the mapping that will be invoked when the |.| key is +" pressed. Typically, it will be the same as the mapping the user invoked. +" This sequence will be stuffed into the input queue literally. Thus you must +" encode special keys by prefixing them with a backslash inside double quotes. +" +" The second argument is the default count. This is the number that will be +" prefixed to the mapping if no explicit numeric argument was given. The +" value of the v:count variable is usually correct and it will be used if the +" second parameter is omitted. If your mapping doesn't accept a numeric +" argument and you never want to receive one, pass a value of -1. +" +" Make sure to call the repeat#set function _after_ making changes to the +" file. + +if exists("g:loaded_repeat") || &cp || v:version < 700 + finish +endif +let g:loaded_repeat = 1 + +let g:repeat_tick = -1 + +function! repeat#set(sequence,...) + silent exe "norm! \"=''\p" + let g:repeat_sequence = a:sequence + let g:repeat_count = a:0 ? a:1 : v:count + let g:repeat_tick = b:changedtick +endfunction + +function! s:repeat(count) + if g:repeat_tick == b:changedtick + let c = g:repeat_count + let s = g:repeat_sequence + let cnt = c == -1 ? "" : (a:count ? a:count : (c ? c : '')) + call feedkeys(cnt . s) + else + call feedkeys((a:count ? a:count : '') . '.', 'n') + endif +endfunction + +function! s:wrap(command,count) + let preserve = (g:repeat_tick == b:changedtick) + exe 'norm! '.(a:count ? a:count : '').a:command + if preserve + let g:repeat_tick = b:changedtick + endif +endfunction + +nnoremap . :call repeat(v:count) +nnoremap u :call wrap('u',v:count) +nnoremap U :call wrap('U',v:count) +nnoremap :call wrap("\C-R>",v:count) + +augroup repeatPlugin + autocmd! + autocmd BufLeave,BufWritePre,BufReadPre * let g:repeat_tick = (g:repeat_tick == b:changedtick || g:repeat_tick == 0) ? 0 : -1 + autocmd BufEnter,BufWritePost * if g:repeat_tick == 0|let g:repeat_tick = b:changedtick|endif +augroup END + +" vim:set ft=vim et sw=4 sts=4: diff -r 1b7aa8ec75fb -r 309b770e8a8d vim/bundle/nerdcommenter/.gitignore --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vim/bundle/nerdcommenter/.gitignore Wed Jun 09 17:53:48 2010 -0400 @@ -0,0 +1,2 @@ +*~ +*.swp diff -r 1b7aa8ec75fb -r 309b770e8a8d vim/bundle/nerdcommenter/Rakefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vim/bundle/nerdcommenter/Rakefile Wed Jun 09 17:53:48 2010 -0400 @@ -0,0 +1,18 @@ +desc "Copy the vim/doc files into ~/.vim" +task :deploy_local do + run "cp plugin/NERD_commenter.vim ~/.vim/plugin" + run "cp doc/NERD_commenter.txt ~/.vim/doc" +end + + +desc "Create a zip archive for release to vim.org" +task :zip do + abort "NERD_commenter.zip already exists, aborting" if File.exist?("NERD_commenter.zip") + run "zip NERD_commenter.zip plugin/NERD_commenter.vim doc/NERD_commenter.txt" +end + +def run(cmd) + puts "Executing: #{cmd}" + system cmd +end + diff -r 1b7aa8ec75fb -r 309b770e8a8d vim/bundle/nerdcommenter/doc/NERD_commenter.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vim/bundle/nerdcommenter/doc/NERD_commenter.txt Wed Jun 09 17:53:48 2010 -0400 @@ -0,0 +1,991 @@ +*NERD_commenter.txt* Plugin for commenting code + + + NERD COMMENTER REFERENCE MANUAL~ + + + + + +============================================================================== +CONTENTS *NERDCommenterContents* + + 1.Intro...................................|NERDCommenter| + 2.Functionality provided..................|NERDComFunctionality| + 2.1 Functionality Summary.............|NERDComFunctionalitySummary| + 2.2 Functionality Details.............|NERDComFunctionalityDetails| + 2.2.1 Comment map.................|NERDComComment| + 2.2.2 Nested comment map..........|NERDComNestedComment| + 2.2.3 Toggle comment map..........|NERDComToggleComment| + 2.2.4 Minimal comment map.........|NERDComMinimalComment| + 2.2.5 Invert comment map..........|NERDComInvertComment| + 2.2.6 Sexy comment map............|NERDComSexyComment| + 2.2.7 Yank comment map............|NERDComYankComment| + 2.2.8 Comment to EOL map..........|NERDComEOLComment| + 2.2.9 Append com to line map......|NERDComAppendComment| + 2.2.10 Insert comment map.........|NERDComInsertComment| + 2.2.11 Use alternate delims map...|NERDComAltDelim| + 2.2.12 Comment aligned maps.......|NERDComAlignedComment| + 2.2.13 Uncomment line map.........|NERDComUncommentLine| + 2.3 Supported filetypes...............|NERDComFiletypes| + 2.4 Sexy Comments.....................|NERDComSexyComments| + 2.5 The NERDComment function..........|NERDComNERDComment| + 3.Options.................................|NERDComOptions| + 3.1 Options summary...................|NERDComOptionsSummary| + 3.2 Options details...................|NERDComOptionsDetails| + 3.3 Default delimiter Options.........|NERDComDefaultDelims| + 4. Customising key mappings...............|NERDComMappings| + 5. Issues with the script.................|NERDComIssues| + 5.1 Delimiter detection heuristics....|NERDComHeuristics| + 5.2 Nesting issues....................|NERDComNesting| + 6.About.. ............................|NERDComAbout| + 7.Changelog...............................|NERDComChangelog| + 8.Credits.................................|NERDComCredits| + 9.License.................................|NERDComLicense| + +============================================================================== +1. Intro *NERDCommenter* + +The NERD commenter provides many different commenting operations and styles +which are invoked via key mappings and a menu. These operations are available +for most filetypes. + +There are also options that allow to tweak the commenting engine to your +taste. + +============================================================================== +2. Functionality provided *NERDComFunctionality* + +------------------------------------------------------------------------------ +2.1 Functionality summary *NERDComFunctionalitySummary* + +The following key mappings are provided by default (there is also a menu +with items corresponding to all the mappings below): + +[count],cc |NERDComComment| +Comment out the current line or text selected in visual mode. + + +[count],cn |NERDComNestedComment| +Same as ,cc but forces nesting. + + +[count],c |NERDComToggleComment| +Toggles the comment state of the selected line(s). If the topmost selected +line is commented, all selected lines are uncommented and vice versa. + + +[count],cm |NERDComMinimalComment| +Comments the given lines using only one set of multipart delimiters. + + +[count],ci |NERDComInvertComment| +Toggles the comment state of the selected line(s) individually. + + +[count],cs |NERDComSexyComment| +Comments out the selected lines ``sexily'' + + +[count],cy |NERDComYankComment| +Same as ,cc except that the commented line(s) are yanked first. + + +,c$ |NERDComEOLComment| +Comments the current line from the cursor to the end of line. + + +,cA |NERDComAppendComment| +Adds comment delimiters to the end of line and goes into insert mode between +them. + + +|NERDComInsertComment| +Adds comment delimiters at the current cursor position and inserts between. +Disabled by default. + + +,ca |NERDComAltDelim| +Switches to the alternative set of delimiters. + + +[count],cl +[count],cb |NERDComAlignedComment| +Same as |NERDComComment| except that the delimiters are aligned down the +left side (,cl) or both sides (,cb). + + +[count],cu |NERDComUncommentLine| +Uncomments the selected line(s). + +------------------------------------------------------------------------------ +2.2 Functionality details *NERDComFunctionalityDetails* + +------------------------------------------------------------------------------ +2.2.1 Comment map *NERDComComment* + +Default mapping: [count],cc +Mapped to: NERDCommenterComment +Applicable modes: normal visual visual-line visual-block. + + +Comments out the current line. If multiple lines are selected in visual-line +mode, they are all commented out. If some text is selected in visual or +visual-block mode then the script will try to comment out the exact text that +is selected using multi-part delimiters if they are available. + +If a [count] is given in normal mode, the mapping works as though that many +lines were selected in visual-line mode. + +------------------------------------------------------------------------------ +2.2.2 Nested comment map *NERDComNestedComment* + +Default mapping: [count],cn +Mapped to: NERDCommenterNest +Applicable modes: normal visual visual-line visual-block. + +Performs nested commenting. Works the same as ,cc except that if a line is +already commented then it will be commented again. + +If |'NERDUsePlaceHolders'| is set then the previous comment delimiters will +be replaced by place-holder delimiters if needed. Otherwise the nested +comment will only be added if the current commenting delimiters have no right +delimiter (to avoid syntax errors) + +If a [count] is given in normal mode, the mapping works as though that many +lines were selected in visual-line mode. + +Related options: +|'NERDDefaultNesting'| + +------------------------------------------------------------------------------ +2.2.3 Toggle comment map *NERDComToggleComment* + +Default mapping: [count],c +Mapped to: NERDCommenterToggle +Applicable modes: normal visual-line. + +Toggles commenting of the lines selected. The behaviour of this mapping +depends on whether the first line selected is commented or not. If so, all +selected lines are uncommented and vice versa. + +With this mapping, a line is only considered to be commented if it starts with +a left delimiter. + +If a [count] is given in normal mode, the mapping works as though that many +lines were selected in visual-line mode. + +------------------------------------------------------------------------------ +2.2.4 Minimal comment map *NERDComMinimalComment* + +Default mapping: [count],cm +Mapped to: NERDCommenterMinimal +Applicable modes: normal visual-line. + +Comments the selected lines using one set of multipart delimiters if possible. + +For example: if you are programming in c and you select 5 lines and press ,cm +then a '/*' will be placed at the start of the top line and a '*/' will be +placed at the end of the last line. + +Sets of multipart comment delimiters that are between the top and bottom +selected lines are replaced with place holders (see |'NERDLPlace'|) if +|'NERDUsePlaceHolders'| is set for the current filetype. If it is not, then +the comment will be aborted if place holders are required to prevent illegal +syntax. + +If a [count] is given in normal mode, the mapping works as though that many +lines were selected in visual-line mode. + +------------------------------------------------------------------------------ +2.2.5 Invert comment map *NERDComInvertComment* + +Default mapping: ,ci +Mapped to: NERDCommenterInvert +Applicable modes: normal visual-line. + +Inverts the commented state of each selected line. If the a selected line is +commented then it is uncommented and vice versa. Each line is examined and +commented/uncommented individually. + +With this mapping, a line is only considered to be commented if it starts with +a left delimiter. + +If a [count] is given in normal mode, the mapping works as though that many +lines were selected in visual-line mode. + +------------------------------------------------------------------------------ +2.2.6 Sexy comment map *NERDComSexyComment* + +Default mapping: [count],cs +Mapped to: NERDCommenterSexy +Applicable modes: normal, visual-line. + +Comments the selected line(s) ``sexily''... see |NERDComSexyComments| for +a description of what sexy comments are. Can only be done on filetypes for +which there is at least one set of multipart comment delimiters specified. + +Sexy comments cannot be nested and lines inside a sexy comment cannot be +commented again. + +If a [count] is given in normal mode, the mapping works as though that many +lines were selected in visual-line mode. + +Related options: +|'NERDCompactSexyComs'| + +------------------------------------------------------------------------------ +2.2.7 Yank comment map *NERDComYankComment* + +Default mapping: [count],cy +Mapped to: NERDCommenterYank +Applicable modes: normal visual visual-line visual-block. + +Same as ,cc except that it yanks the line(s) that are commented first. + +------------------------------------------------------------------------------ +2.2.8 Comment to EOL map *NERDComEOLComment* + +Default mapping: ,c$ +Mapped to: NERDCommenterToEOL +Applicable modes: normal. + +Comments the current line from the current cursor position up to the end of +the line. + +------------------------------------------------------------------------------ +2.2.9 Append com to line map *NERDComAppendComment* + +Default mapping: ,cA +Mapped to: NERDCommenterAppend +Applicable modes: normal. + +Appends comment delimiters to the end of the current line and goes +to insert mode between the new delimiters. + +------------------------------------------------------------------------------ +2.2.10 Insert comment map *NERDComInsertComment* + +Default mapping: disabled by default. +Map it to: NERDCommenterInInsert +Applicable modes: insert. + +Adds comment delimiters at the current cursor position and inserts +between them. + +NOTE: prior to version 2.1.17 this was mapped to ctrl-c. To restore this +mapping add > + let NERDComInsertMap='' +< +to your vimrc. + +------------------------------------------------------------------------------ +2.2.11 Use alternate delims map *NERDComAltDelim* + +Default mapping: ,ca +Mapped to: NERDCommenterAltDelims +Applicable modes: normal. + +Changes to the alternative commenting style if one is available. For example, +if the user is editing a c++ file using // comments and they hit ,ca +then they will be switched over to /**/ comments. + +See also |NERDComDefaultDelims| + +------------------------------------------------------------------------------ +2.2.12 Comment aligned maps *NERDComAlignedComment* + +Default mappings: [count],cl [count],cb +Mapped to: NERDCommenterAlignLeft + NERDCommenterAlignBoth +Applicable modes: normal visual-line. + +Same as ,cc except that the comment delimiters are aligned on the left side or +both sides respectively. These comments are always nested if the line(s) are +already commented. + +If a [count] is given in normal mode, the mapping works as though that many +lines were selected in visual-line mode. + +------------------------------------------------------------------------------ +2.2.13 Uncomment line map *NERDComUncommentLine* + +Default mapping: [count],cu +Mapped to: NERDCommenterUncomment +Applicable modes: normal visual visual-line visual-block. + +Uncomments the current line. If multiple lines are selected in +visual mode then they are all uncommented. + +When uncommenting, if the line contains multiple sets of delimiters then the +``outtermost'' pair of delimiters will be removed. + +The script uses a set of heurisics to distinguish ``real'' delimiters from +``fake'' ones when uncommenting. See |NERDComIssues| for details. + +If a [count] is given in normal mode, the mapping works as though that many +lines were selected in visual-line mode. + +Related options: +|'NERDRemoveAltComs'| +|'NERDRemoveExtraSpaces'| + +------------------------------------------------------------------------------ +2.3 Supported filetypes *NERDComFiletypes* + +Filetypes that can be commented by this plugin: +abaqus abc acedb ada ahdl amiga aml ampl ant apache apachestyle asm68k asm asn +aspvbs atlas autohotkey autoit automake ave awk basic b bc bdf bib bindzone +bst btm caos catalog c cfg cg ch changelog cl clean clipper cmake conf config +context cpp crontab cs csc csp css cterm cupl csv cvs dcl debchangelog +debcontrol debsources def diff django docbk dns dosbatch dosini dot dracula +dsl dtd dtml dylan ecd eiffel elf elmfilt erlang eruby eterm expect exports +fetchmail fgl focexec form fortran foxpro fstab fvwm fx gdb gdmo geek +gentoo-package-keywords' gentoo-package-mask' gentoo-package-use' gnuplot +gtkrc haskell hb h help hercules hog html htmldjango htmlos ia64 icon idlang +idl indent inform inittab ishd iss ist jam java javascript jess jgraph +jproperties jproperties jsp kconfig kix kscript lace lex lftp lifelines lilo +lisp lite lotos lout lprolog lscript lss lua lynx m4 mail make maple masm +master matlab mel mf mib mma model moduala. modula2 modula3 monk mush muttrc +named nasm nastran natural ncf netdict netrw nqc nroff nsis objc ocaml occam +omlet omnimark openroad opl ora otl ox pascal passwd pcap pccts perl pfmain +php phtml pic pike pilrc pine plaintex plm plsql po postscr pov povini ppd +ppwiz procmail progress prolog psf ptcap python python qf radiance ratpoison r +rc readline rebol registry remind rexx robots rpl rtf ruby sa samba sas sass +sather scheme scilab screen scsh sdl sed selectbuf sgml sgmldecl sgmllnx sh +sicad simula sinda skill slang sl slrnrc sm smarty smil smith sml snnsnet +snnspat snnsres snobol4 spec specman spice sql sqlforms sqlj sqr squid st stp +strace svn systemverilog tads taglist tags tak tasm tcl terminfo tex text +plaintex texinfo texmf tf tidy tli trasys tsalt tsscl tssgm uc uil vb verilog +verilog_systemverilog vgrindefs vhdl vim viminfo virata vo_base vrml vsejcl +webmacro wget winbatch wml wvdial xdefaults xf86conf xhtml xkb xmath xml +xmodmap xpm2 xpm xslt yacc yaml z8a + +If a language is not in the list of hardcoded supported filetypes then the +&commentstring vim option is used. + +------------------------------------------------------------------------------ +2.4 Sexy Comments *NERDComSexyComments* +These are comments that use one set of multipart comment delimiters as well as +one other marker symbol. For example: > + /* + * This is a c style sexy comment + * So there! + */ + + /* This is a c style sexy comment + * So there! + * But this one is ``compact'' style */ +< +Here the multipart delimiters are /* and */ and the marker is *. + +------------------------------------------------------------------------------ +2.5 The NERDComment function *NERDComNERDComment* + +All of the NERD commenter mappings and menu items invoke a single function +which delegates the commenting work to other functions. This function is +public and has the prototype: > + function! NERDComment(isVisual, type) +< +The arguments to this function are simple: + - isVisual: if you wish to do any kind of visual comment then set this to + 1 and the function will use the '< and '> marks to find the comment + boundries. If set to 0 then the function will operate on the current + line. + - type: is used to specify what type of commenting operation is to be + performed, and it can be one of the following: "sexy", "invert", + "minimal", "toggle", "alignLeft", "alignBoth", "norm", "nested", + "toEOL", "append", "insert", "uncomment", "yank" + +For example, if you typed > + :call NERDComment(1, 'sexy') +< +then the script would do a sexy comment on the last visual selection. + + +============================================================================== +3. Options *NERDComOptions* + +------------------------------------------------------------------------------ +3.1 Options summary *NERDComOptionsSummary* + +|'loaded_nerd_comments'| Turns off the script. +|'NERDAllowAnyVisualDelims'| Allows multipart alternative delims to + be used when commenting in + visual/visual-block mode. +|'NERDBlockComIgnoreEmpty'| Forces right delims to be placed when + doing visual-block comments. +|'NERDCommentWholeLinesInVMode'| Changes behaviour of visual comments. +|'NERDCreateDefaultMappings'| Turn the default mappings on/off. +|'NERDDefaultNesting'| Tells the script to use nested comments + by default. +|'NERDMenuMode'| Specifies how the NERD commenter menu + will appear (if at all). +|'NERDLPlace'| Specifies what to use as the left + delimiter placeholder when nesting + comments. +|'NERDUsePlaceHolders'| Specifies which filetypes may use + placeholders when nesting comments. +|'NERDRemoveAltComs'| Tells the script whether to remove + alternative comment delimiters when + uncommenting. +|'NERDRemoveExtraSpaces'| Tells the script to always remove the + extra spaces when uncommenting + (regardless of whether NERDSpaceDelims + is set) +|'NERDRPlace'| Specifies what to use as the right + delimiter placeholder when nesting + comments. +|'NERDSpaceDelims'| Specifies whether to add extra spaces + around delimiters when commenting, and + whether to remove them when + uncommenting. +|'NERDCompactSexyComs'| Specifies whether to use the compact + style sexy comments. + +------------------------------------------------------------------------------ +3.3 Options details *NERDComOptionsDetails* + +To enable any of the below options you should put the given line in your +~/.vimrc + + *'loaded_nerd_comments'* +If this script is driving you insane you can turn it off by setting this +option > + let loaded_nerd_comments=1 +< +------------------------------------------------------------------------------ + *'NERDAllowAnyVisualDelims'* +Values: 0 or 1. +Default: 1. + +If set to 1 then, when doing a visual or visual-block comment (but not a +visual-line comment), the script will choose the right delimiters to use for +the comment. This means either using the current delimiters if they are +multipart or using the alternative delimiters if THEY are multipart. For +example if we are editing the following java code: > + float foo = 1221; + float bar = 324; + System.out.println(foo * bar); +< +If we are using // comments and select the "foo" and "bar" in visual-block +mode, as shown left below (where '|'s are used to represent the visual-block +boundary), and comment it then the script will use the alternative delims as +shown on the right: > + + float |foo| = 1221; float /*foo*/ = 1221; + float |bar| = 324; float /*bar*/ = 324; + System.out.println(foo * bar); System.out.println(foo * bar); +< +------------------------------------------------------------------------------ + *'NERDBlockComIgnoreEmpty'* +Values: 0 or 1. +Default: 1. + +This option affects visual-block mode commenting. If this option is turned +on, lines that begin outside the right boundary of the selection block will be +ignored. + +For example, if you are commenting this chunk of c code in visual-block mode +(where the '|'s are used to represent the visual-block boundary) > + #include + #include + #include + |int| main(){ + | | printf("SUCK THIS\n"); + | | while(1){ + | | fork(); + | | } + |} | +< +If NERDBlockComIgnoreEmpty=0 then this code will become: > + #include + #include + #include + /*int*/ main(){ + /* */ printf("SUCK THIS\n"); + /* */ while(1){ + /* */ fork(); + /* */ } + /*} */ +< +Otherwise, the code block would become: > + #include + #include + #include + /*int*/ main(){ + printf("SUCK THIS\n"); + while(1){ + fork(); + } + /*} */ +< +------------------------------------------------------------------------------ + *'NERDCommentWholeLinesInVMode'* +Values: 0, 1 or 2. +Default: 0. + +By default the script tries to comment out exactly what is selected in visual +mode (v). For example if you select and comment the following c code (using | +to represent the visual boundary): > + in|t foo = 3; + int bar =| 9; + int baz = foo + bar; +< +This will result in: > + in/*t foo = 3;*/ + /*int bar =*/ 9; + int baz = foo + bar; +< +But some people prefer it if the whole lines are commented like: > + /*int foo = 3;*/ + /*int bar = 9;*/ + int baz = foo + bar; +< +If you prefer the second option then stick this line in your vimrc: > + let NERDCommentWholeLinesInVMode=1 +< + +If the filetype you are editing only has no multipart delimiters (for example +a shell script) and you hadnt set this option then the above would become > + in#t foo = 3; + #int bar = 9; +< +(where # is the comment delimiter) as this is the closest the script can +come to commenting out exactly what was selected. If you prefer for whole +lines to be commented out when there is no multipart delimiters but the EXACT +text that was selected to be commented out if there IS multipart delimiters +then stick the following line in your vimrc: > + let NERDCommentWholeLinesInVMode=2 +< + +Note that this option does not affect the behaviour of commenting in +|visual-block| mode. + +------------------------------------------------------------------------------ + *'NERDCreateDefaultMappings'* +Values: 0 or 1. +Default: 1. + +If set to 0, none of the default mappings will be created. + +See also |NERDComMappings|. + +------------------------------------------------------------------------------ + *'NERDRemoveAltComs'* +Values: 0 or 1. +Default: 1. + +When uncommenting a line (for a filetype with an alternative commenting style) +this option tells the script whether to look for, and remove, comment +delimiters of the alternative style. + +For example, if you are editing a c++ file using // style comments and you go +,cu on this line: > + /* This is a c++ comment baby! */ +< +It will not be uncommented if the NERDRemoveAltComs is set to 0. + +------------------------------------------------------------------------------ + *'NERDRemoveExtraSpaces'* +Values: 0 or 1. +Default: 1. + +By default, the NERD commenter will remove spaces around comment delimiters if +either: +1. |'NERDSpaceDelims'| is set to 1. +2. NERDRemoveExtraSpaces is set to 1. + +This means that if we have the following lines in a c code file: > + /* int foo = 5; */ + /* int bar = 10; */ + int baz = foo + bar +< +If either of the above conditions hold then if these lines are uncommented +they will become: > + int foo = 5; + int bar = 10; + int baz = foo + bar +< +Otherwise they would become: > + int foo = 5; + int bar = 10; + int baz = foo + bar +< +If you want the spaces to be removed only if |'NERDSpaceDelims'| is set then +set NERDRemoveExtraSpaces to 0. + +------------------------------------------------------------------------------ + *'NERDLPlace'* + *'NERDRPlace'* +Values: arbitrary string. +Default: + NERDLPlace: "[>" + NERDRPlace: "<]" + +These options are used to control the strings used as place-holder delimiters. +Place holder delimiters are used when performing nested commenting when the +filetype supports commenting styles with both left and right delimiters. +To set these options use lines like: > + let NERDLPlace="FOO" + let NERDRPlace="BAR" +< +Following the above example, if we have line of c code: > + /* int horse */ +< +and we comment it with ,cn it will be changed to: > + /*FOO int horse BAR*/ +< +When we uncomment this line it will go back to what it was. + +------------------------------------------------------------------------------ + *'NERDMenuMode'* +Values: 0, 1, 2, 3. +Default: 3 + +This option can take 4 values: + "0": Turns the menu off. + "1": Turns the 'comment' menu on with no menu shortcut. + "2": Turns the 'comment 'menu on with -c as the shortcut. + "3": Turns the 'Plugin -> comment' menu on with -c as the shortcut. + +------------------------------------------------------------------------------ + *'NERDUsePlaceHolders'* +Values: 0 or 1. +Default 1. + +This option is used to specify whether place-holder delimiters should be used +when creating a nested comment. + +------------------------------------------------------------------------------ + *'NERDSpaceDelims'* +Values: 0 or 1. +Default 0. + +Some people prefer a space after the left delimiter and before the right +delimiter like this: > + /* int foo=2; */ +< +as opposed to this: > + /*int foo=2;*/ +< +If you want spaces to be added then set NERDSpaceDelims to 1 in your vimrc. + +See also |'NERDRemoveExtraSpaces'|. + +------------------------------------------------------------------------------ + *'NERDCompactSexyComs'* +Values: 0 or 1. +Default 0. + +Some people may want their sexy comments to be like this: > + /* Hi There! + * This is a sexy comment + * in c */ +< +As opposed to like this: > + /* + * Hi There! + * This is a sexy comment + * in c + */ +< +If this option is set to 1 then the top style will be used. + +------------------------------------------------------------------------------ + *'NERDDefaultNesting'* +Values: 0 or 1. +Default 1. + +When this option is set to 1, comments are nested automatically. That is, if +you hit ,cc on a line that is already commented it will be commented again + +------------------------------------------------------------------------------ +3.3 Default delimiter customisation *NERDComDefaultDelims* + +If you want the NERD commenter to use the alternative delimiters for a +specific filetype by default then put a line of this form into your vimrc: > + let NERD__alt_style=1 +< +Example: java uses // style comments by default, but you want it to default to +/* */ style comments instead. You would put this line in your vimrc: > + let NERD_java_alt_style=1 +< + +See |NERDComAltDelim| for switching commenting styles at runtime. + +============================================================================== +4. Key mapping customisation *NERDComMappings* + +To change a mapping just map another key combo to the internal mapping. +For example, to remap the |NERDComComment| mapping to ",omg" you would put +this line in your vimrc: > + map ,omg NERDCommenterComment +< +This will stop the corresponding default mappings from being created. + +See the help for the mapping in question to see which mapping to +map to. + +See also |'NERDCreateDefaultMappings'|. + +============================================================================== +5. Issues with the script *NERDComIssues* + + +------------------------------------------------------------------------------ +5.1 Delimiter detection heuristics *NERDComHeuristics* + +Heuristics are used to distinguish the real comment delimiters + +Because we have comment mappings that place delimiters in the middle of lines, +removing comment delimiters is a bit tricky. This is because if comment +delimiters appear in a line doesnt mean they really ARE delimiters. For +example, Java uses // comments but the line > + System.out.println("//"); +< +clearly contains no real comment delimiters. + +To distinguish between ``real'' comment delimiters and ``fake'' ones we use a +set of heuristics. For example, one such heuristic states that any comment +delimiter that has an odd number of non-escaped " characters both preceding +and following it on the line is not a comment because it is probably part of a +string. These heuristics, while usually pretty accurate, will not work for all +cases. + +------------------------------------------------------------------------------ +5.2 Nesting issues *NERDComNesting* + +If we have some line of code like this: > + /*int foo */ = /*5 + 9;*/ +< +This will not be uncommented legally. The NERD commenter will remove the +"outter most" delimiters so the line will become: > + int foo */ = /*5 + 9; +< +which almost certainly will not be what you want. Nested sets of comments will +uncomment fine though. Eg: > + /*int/* foo =*/ 5 + 9;*/ +< +will become: > + int/* foo =*/ 5 + 9; +< +(Note that in the above examples I have deliberately not used place holders +for simplicity) + +============================================================================== +6. About *NERDComAbout* + +The author of the NERD commenter is Martyzillatron --- the half robot, half +dinosaur bastard son of Megatron and Godzilla. He enjoys destroying +metropolises and eating tourist busses. + +Drop him a line at martin_grenfell at msn.com. He would love to hear from you. +its a lonely life being the worlds premier terror machine. How would you feel +if your face looked like a toaster and a t-rex put together? :( + +The latest stable versions can be found at + http://www.vim.org/scripts/script.php?script_id=1218 + +The latest dev versions are on github + http://github.com/scrooloose/nerdcommenter + +============================================================================== +8. Changelog *NERDComChangelog* + +2.2.2 + - remove the NERDShutup option and the message is suppresses, this makes + the plugin silently rely on &commentstring for unknown filetypes. + - add support for dhcpd, limits, ntp, resolv, rgb, sysctl, udevconf and + udevrules. Thanks to Thilo Six. + - match filetypes case insensitively + - add support for mp (metapost), thanks to Andrey Skvortsov. + - add support for htmlcheetah, thanks to Simon Hengel. + - add support for javacc, thanks to Matt Tolton. + - make <%# %> the default delims for eruby, thanks to tpope. + - add support for javascript.jquery, thanks to Ivan Devat. + - add support for cucumber and pdf. Fix sass and railslog delims, + thanks to tpope + +2.2.1 + - add support for newlisp and clojure, thanks to Matthew Lee Hinman. + - fix automake comments, thanks to Elias Pipping + - make haml comments default to -# with / as the alternative delimiter, + thanks to tpope + - add support for actionscript and processing thanks to Edwin Benavides + - add support for ps1 (powershell), thanks to Jason Mills + - add support for hostsaccess, thanks to Thomas Rowe + - add support for CVScommit + - add support for asciidoc, git and gitrebase. Thanks to Simon Ruderich. + - use # for gitcommit comments, thanks to Simon Ruderich. + - add support for mako and genshi, thanks to Keitheis. + - add support for conkyrc, thanks to David + - add support for SVNannotate, thanks to Miguel Jaque Barbero. + - add support for sieve, thanks to Stefan Walk + - add support for objj, thanks to Adam Thorsen. + +2.2.0 + - rewrote the mappings system to be more "standard". + - removed all the mapping options. Now, mappings to mappings are + used + - see :help NERDComMappings, and :help NERDCreateDefaultMappings for + more info + - remove "prepend comments" and "right aligned comments". + - add support for applescript, calbire, man, SVNcommit, potwiki, txt2tags and SVNinfo. + Thanks to nicothakis, timberke, sgronblo, mntnoe, Bernhard Grotz, John + O'Shea, François and Giacomo Mariani respectively. + - bugfix for haskell delimiters. Thanks to mntnoe. +2.1.18 + - add support for llvm. Thanks to nicothakis. + - add support for xquery. Thanks to Phillip Kovalev. +2.1.17 + - fixed haskell delimiters (hackily). Thanks to Elias Pipping. + - add support for mailcap. Thanks to Pascal Brueckner. + - add support for stata. Thanks to Jerónimo Carballo. + - applied a patch from ewfalor to fix an error in the help file with the + NERDMapleader doc + - disable the insert mode ctrl-c mapping by default, see :help + NERDComInsertComment if you wish to restore it + +============================================================================== +8. Credits *NERDComCredits* + +Thanks to the follow people for suggestions and patches: + +Nick Brettell +Matthew Hawkins +Mathieu Clabaut +Greg Searle +Nguyen +Litchi +Jorge Scandaliaris +Shufeng Zheng +Martin Stubenschrott +Markus Erlmann +Brent Rice +Richard Willis +Igor Prischepoff +Harry +David Bourgeois +Eike Von Seggern +Torsten Blix +Alexander Bosecke +Stefano Zacchiroli +Norick Chen +Joseph Barker +Gary Church +Tim Carey-Smith +Markus Klinik +Anders +Seth Mason +James Hales +Heptite +Cheng Fang +Yongwei Wu +David Miani +Jeremy Hinegardner +Marco +Ingo Karkat +Zhang Shuhan +tpope +Ben Schmidt +David Fishburn +Erik Falor +JaGoTerr +Elias Pipping +mntnoe +Mark S. + + +Thanks to the following people for sending me new filetypes to support: + +The hackers The filetypes~ +Sam R verilog +Jonathan Derque context, plaintext and mail +Vigil fetchmail +Michael Brunner kconfig +Antono Vasiljev netdict +Melissa Reid omlet +Ilia N Ternovich quickfix +John O'Shea RTF, SVNcommitlog and vcscommit, SVNCommit +Anders occam +Mark Woodward csv +fREW gentoo-package-mask, + gentoo-package-keywords, + gentoo-package-use, and vo_base +Alexey verilog_systemverilog, systemverilog +Lizendir fstab +Michael Böhler autoit, autohotkey and docbk +Aaron Small cmake +Ramiro htmldjango and django +Stefano Zacchiroli debcontrol, debchangelog, mkd +Alex Tarkovsky ebuild and eclass +Jorge Rodrigues gams +Rainer Müller Objective C +Jason Mills Groovy, ps1 +Normandie Azucena vera +Florian Apolloner ldif +David Fishburn lookupfile +Niels Aan de Brugh rst +Don Hatlestad ahk +Christophe Benz Desktop and xsd +Eyolf Østrem lilypond, bbx and lytex +Ingo Karkat dosbatch +Nicolas Weber markdown, objcpp +tinoucas gentoo-conf-d +Greg Weber D, haml +Bruce Sherrod velocity +timberke cobol, calibre +Aaron Schaefer factor +Mr X asterisk, mplayerconf +Kuchma Michael plsql +Brett Warneke spectre +Pipp lhaskell +Renald Buter scala +Vladimir Lomov asymptote +Marco mrxvtrc, aap +nicothakis SVNAnnotate, CVSAnnotate, SVKAnnotate, + SVNdiff, gitAnnotate, gitdiff, dtrace + llvm, applescript +Chen Xing Wikipedia +Jacobo Diaz dakota, patran +Li Jin gentoo-env-d, gentoo-init-d, + gentoo-make-conf, grub, modconf, sudoers +SpookeyPeanut rib +Greg Jandl pyrex/cython +Christophe Benz services, gitcommit +A Pontus vimperator +Stromnov slice, bzr +Martin Kustermann pamconf +Indriði Einarsson mason +Chris map +Krzysztof A. Adamski group +Pascal Brueckner mailcap +Jerónimo Carballo stata +Phillip Kovalev xquery +Bernhard Grotz potwiki +sgronblo man +François txt2tags +Giacomo Mariani SVNinfo +Matthew Lee Hinman newlisp, clojure +Elias Pipping automake +Edwin Benavides actionscript, processing +Thomas Rowe hostsaccess +Simon Ruderich asciidoc, git, gitcommit, gitrebase +Keitheis mako, genshi +David conkyrc +Miguel Jaque Barbero SVNannotate +Stefan Walk sieve +Adam Thorsen objj +Thilo Six dhcpd, limits, ntp, resolv, rgb, sysctl, + udevconf, udevrules +Andrey Skvortsov mp +Simon Hengel htmlcheetah +Matt Tolton javacc +Ivan Devat javascript.jquery +tpope cucumber,pdf +============================================================================== +9. License *NERDComLicense* + +The NERD commenter is released under the wtfpl. +See http://sam.zoy.org/wtfpl/COPYING. diff -r 1b7aa8ec75fb -r 309b770e8a8d vim/bundle/nerdcommenter/plugin/NERD_commenter.vim --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vim/bundle/nerdcommenter/plugin/NERD_commenter.vim Wed Jun 09 17:53:48 2010 -0400 @@ -0,0 +1,3146 @@ +" ============================================================================ +" File: NERD_commenter.vim +" Description: vim global plugin that provides easy code commenting +" Maintainer: Martin Grenfell +" Version: 2.2.2 +" Last Change: 30th March, 2008 +" License: This program is free software. It comes without any warranty, +" to the extent permitted by applicable law. You can redistribute +" it and/or modify it under the terms of the Do What The Fuck You +" Want To Public License, Version 2, as published by Sam Hocevar. +" See http://sam.zoy.org/wtfpl/COPYING for more details. +" +" ============================================================================ + +" Section: script init stuff {{{1 +if exists("loaded_nerd_comments") + finish +endif +if v:version < 700 + echoerr "NERDCommenter: this plugin requires vim >= 7. DOWNLOAD IT! You'll thank me later!" + finish +endif +let loaded_nerd_comments = 1 + +" Function: s:InitVariable() function {{{2 +" This function is used to initialise a given variable to a given value. The +" variable is only initialised if it does not exist prior +" +" Args: +" -var: the name of the var to be initialised +" -value: the value to initialise var to +" +" Returns: +" 1 if the var is set, 0 otherwise +function s:InitVariable(var, value) + if !exists(a:var) + exec 'let ' . a:var . ' = ' . "'" . a:value . "'" + return 1 + endif + return 0 +endfunction + +" Section: space string init{{{2 +" When putting spaces after the left delim and before the right we use +" s:spaceStr for the space char. This way we can make it add anything after +" the left and before the right by modifying this variable +let s:spaceStr = ' ' +let s:lenSpaceStr = strlen(s:spaceStr) + +" Section: variable init calls {{{2 +call s:InitVariable("g:NERDAllowAnyVisualDelims", 1) +call s:InitVariable("g:NERDBlockComIgnoreEmpty", 0) +call s:InitVariable("g:NERDCommentWholeLinesInVMode", 0) +call s:InitVariable("g:NERDCompactSexyComs", 0) +call s:InitVariable("g:NERDCreateDefaultMappings", 1) +call s:InitVariable("g:NERDDefaultNesting", 1) +call s:InitVariable("g:NERDMenuMode", 3) +call s:InitVariable("g:NERDLPlace", "[>") +call s:InitVariable("g:NERDUsePlaceHolders", 1) +call s:InitVariable("g:NERDRemoveAltComs", 1) +call s:InitVariable("g:NERDRemoveExtraSpaces", 1) +call s:InitVariable("g:NERDRPlace", "<]") +call s:InitVariable("g:NERDSpaceDelims", 0) +call s:InitVariable("g:NERDDelimiterRequests", 1) + + + +let s:NERDFileNameEscape="[]#*$%'\" ?`!&();<>\\" + +" Section: Comment mapping functions, autocommands and commands {{{1 +" ============================================================================ +" Section: Comment enabler autocommands {{{2 +" ============================================================================ + +augroup commentEnablers + + "if the user enters a buffer or reads a buffer then we gotta set up + "the comment delimiters for that new filetype + autocmd BufEnter,BufRead * :call s:SetUpForNewFiletype(&filetype, 0) + + "if the filetype of a buffer changes, force the script to reset the + "delims for the buffer + autocmd Filetype * :call s:SetUpForNewFiletype(&filetype, 1) +augroup END + + +" Function: s:SetUpForNewFiletype(filetype) function {{{2 +" This function is responsible for setting up buffer scoped variables for the +" given filetype. +" +" These variables include the comment delimiters for the given filetype and calls +" MapDelimiters or MapDelimitersWithAlternative passing in these delimiters. +" +" Args: +" -filetype: the filetype to set delimiters for +" -forceReset: 1 if the delimiters should be reset if they have already be +" set for this buffer. +" +function s:SetUpForNewFiletype(filetype, forceReset) + "if we have already set the delimiters for this buffer then dont go thru + "it again + if !a:forceReset && exists("b:NERDLeft") && b:NERDLeft != '' + return + endif + + let b:NERDSexyComMarker = '' + + "check the filetype against all known filetypes to see if we have + "hardcoded the comment delimiters to use + if a:filetype ==? "" + call s:MapDelimiters('', '') + elseif a:filetype ==? "aap" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "abc" + call s:MapDelimiters('%', '') + elseif a:filetype ==? "acedb" + call s:MapDelimitersWithAlternative('//','', '/*','*/') + elseif a:filetype ==? "actionscript" + call s:MapDelimitersWithAlternative('//','', '/*','*/') + elseif a:filetype ==? "ada" + call s:MapDelimitersWithAlternative('--','', '-- ', '') + elseif a:filetype ==? "ahdl" + call s:MapDelimiters('--', '') + elseif a:filetype ==? "ahk" + call s:MapDelimitersWithAlternative(';', '', '/*', '*/') + elseif a:filetype ==? "amiga" + call s:MapDelimiters(';', '') + elseif a:filetype ==? "aml" + call s:MapDelimiters('/*', '') + elseif a:filetype ==? "ampl" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "apache" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "apachestyle" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "asciidoc" + call s:MapDelimiters('//', '') + elseif a:filetype ==? "applescript" + call s:MapDelimitersWithAlternative('--', '', '(*', '*)') + elseif a:filetype ==? "asm68k" + call s:MapDelimiters(';', '') + elseif a:filetype ==? "asm" + call s:MapDelimitersWithAlternative(';', '', '#', '') + elseif a:filetype ==? "asn" + call s:MapDelimiters('--', '') + elseif a:filetype ==? "aspvbs" + call s:MapDelimiters('''', '') + elseif a:filetype ==? "asterisk" + call s:MapDelimiters(';', '') + elseif a:filetype ==? "asy" + call s:MapDelimiters('//', '') + elseif a:filetype ==? "atlas" + call s:MapDelimiters('C','$') + elseif a:filetype ==? "autohotkey" + call s:MapDelimiters(';','') + elseif a:filetype ==? "autoit" + call s:MapDelimiters(';','') + elseif a:filetype ==? "ave" + call s:MapDelimiters("'",'') + elseif a:filetype ==? "awk" + call s:MapDelimiters('#','') + elseif a:filetype ==? "basic" + call s:MapDelimitersWithAlternative("'",'', 'REM ', '') + elseif a:filetype ==? "bbx" + call s:MapDelimiters('%', '') + elseif a:filetype ==? "bc" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "bib" + call s:MapDelimiters('%','') + elseif a:filetype ==? "bindzone" + call s:MapDelimiters(';', '') + elseif a:filetype ==? "bst" + call s:MapDelimiters('%', '') + elseif a:filetype ==? "btm" + call s:MapDelimiters('::', '') + elseif a:filetype ==? "caos" + call s:MapDelimiters('*', '') + elseif a:filetype ==? "calibre" + call s:MapDelimiters('//','') + elseif a:filetype ==? "catalog" + call s:MapDelimiters('--','--') + elseif a:filetype ==? "c" + call s:MapDelimitersWithAlternative('/*','*/', '//', '') + elseif a:filetype ==? "cfg" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "cg" + call s:MapDelimitersWithAlternative('//','', '/*','*/') + elseif a:filetype ==? "ch" + call s:MapDelimitersWithAlternative('//','', '/*','*/') + elseif a:filetype ==? "cl" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "clean" + call s:MapDelimitersWithAlternative('//','', '/*','*/') + elseif a:filetype ==? "clipper" + call s:MapDelimitersWithAlternative('//','', '/*','*/') + elseif a:filetype ==? "clojure" + call s:MapDelimiters(';', '') + elseif a:filetype ==? "cmake" + call s:MapDelimiters('#','') + elseif a:filetype ==? "conkyrc" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "cpp" + call s:MapDelimitersWithAlternative('//','', '/*','*/') + elseif a:filetype ==? "crontab" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "cs" + call s:MapDelimitersWithAlternative('//','', '/*','*/') + elseif a:filetype ==? "csp" + call s:MapDelimiters('--', '') + elseif a:filetype ==? "cterm" + call s:MapDelimiters('*', '') + elseif a:filetype ==? "cucumber" + call s:MapDelimiters('#','') + elseif a:filetype ==? "cvs" + call s:MapDelimiters('CVS:','') + elseif a:filetype ==? "d" + call s:MapDelimitersWithAlternative('//','', '/*','*/') + elseif a:filetype ==? "dcl" + call s:MapDelimiters('$!', '') + elseif a:filetype ==? "dakota" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "debcontrol" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "debsources" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "def" + call s:MapDelimiters(';', '') + elseif a:filetype ==? "desktop" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "dhcpd" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "diff" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "django" + call s:MapDelimitersWithAlternative('', '{#', '#}') + elseif a:filetype ==? "docbk" + call s:MapDelimiters('') + elseif a:filetype ==? "dns" + call s:MapDelimiters(';', '') + elseif a:filetype ==? "dosbatch" + call s:MapDelimitersWithAlternative('REM ','', '::', '') + elseif a:filetype ==? "dosini" + call s:MapDelimiters(';', '') + elseif a:filetype ==? "dot" + call s:MapDelimitersWithAlternative('//','', '/*','*/') + elseif a:filetype ==? "dracula" + call s:MapDelimiters(';', '') + elseif a:filetype ==? "dsl" + call s:MapDelimiters(';', '') + elseif a:filetype ==? "dtml" + call s:MapDelimiters('','') + elseif a:filetype ==? "dylan" + call s:MapDelimitersWithAlternative('//','', '/*','*/') + elseif a:filetype ==? 'ebuild' + call s:MapDelimiters('#', '') + elseif a:filetype ==? "ecd" + call s:MapDelimiters('#', '') + elseif a:filetype ==? 'eclass' + call s:MapDelimiters('#', '') + elseif a:filetype ==? "eiffel" + call s:MapDelimiters('--', '') + elseif a:filetype ==? "elf" + call s:MapDelimiters("'", '') + elseif a:filetype ==? "elmfilt" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "erlang" + call s:MapDelimiters('%', '') + elseif a:filetype ==? "eruby" + call s:MapDelimitersWithAlternative('<%#', '%>', '') + elseif a:filetype ==? "expect" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "exports" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "factor" + call s:MapDelimitersWithAlternative('! ', '', '!# ', '') + elseif a:filetype ==? "fgl" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "focexec" + call s:MapDelimiters('-*', '') + elseif a:filetype ==? "form" + call s:MapDelimiters('*', '') + elseif a:filetype ==? "foxpro" + call s:MapDelimiters('*', '') + elseif a:filetype ==? "fstab" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "fvwm" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "fx" + call s:MapDelimitersWithAlternative('//','', '/*','*/') + elseif a:filetype ==? "gams" + call s:MapDelimiters('*', '') + elseif a:filetype ==? "gdb" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "gdmo" + call s:MapDelimiters('--', '') + elseif a:filetype ==? "geek" + call s:MapDelimiters('GEEK_COMMENT:', '') + elseif a:filetype ==? "genshi" + call s:MapDelimitersWithAlternative('', '{#', '#}') + elseif a:filetype ==? "gentoo-conf-d" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "gentoo-env-d" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "gentoo-init-d" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "gentoo-make-conf" + call s:MapDelimiters('#', '') + elseif a:filetype ==? 'gentoo-package-keywords' + call s:MapDelimiters('#', '') + elseif a:filetype ==? 'gentoo-package-mask' + call s:MapDelimiters('#', '') + elseif a:filetype ==? 'gentoo-package-use' + call s:MapDelimiters('#', '') + elseif a:filetype ==? 'gitcommit' + call s:MapDelimiters('#', '') + elseif a:filetype ==? 'gitconfig' + call s:MapDelimiters(';', '') + elseif a:filetype ==? 'gitrebase' + call s:MapDelimiters('#', '') + elseif a:filetype ==? "gnuplot" + call s:MapDelimiters('#','') + elseif a:filetype ==? "groovy" + call s:MapDelimitersWithAlternative('//','', '/*','*/') + elseif a:filetype ==? "gtkrc" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "haskell" + call s:MapDelimitersWithAlternative('{-','-}', '--', '') + elseif a:filetype ==? "hb" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "h" + call s:MapDelimitersWithAlternative('//','', '/*','*/') + elseif a:filetype ==? "haml" + call s:MapDelimitersWithAlternative('-#', '', '/', '') + elseif a:filetype ==? "hercules" + call s:MapDelimitersWithAlternative('//','', '/*','*/') + elseif a:filetype ==? "hog" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "hostsaccess" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "htmlcheetah" + call s:MapDelimiters('##','') + elseif a:filetype ==? "htmldjango" + call s:MapDelimitersWithAlternative('', '{#', '#}') + elseif a:filetype ==? "htmlos" + call s:MapDelimiters('#','/#') + elseif a:filetype ==? "ia64" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "icon" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "idlang" + call s:MapDelimiters(';', '') + elseif a:filetype ==? "idl" + call s:MapDelimitersWithAlternative('//','', '/*','*/') + elseif a:filetype ==? "inform" + call s:MapDelimiters('!', '') + elseif a:filetype ==? "inittab" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "ishd" + call s:MapDelimitersWithAlternative('//','', '/*','*/') + elseif a:filetype ==? "iss" + call s:MapDelimiters(';', '') + elseif a:filetype ==? "ist" + call s:MapDelimiters('%', '') + elseif a:filetype ==? "java" + call s:MapDelimitersWithAlternative('//','', '/*','*/') + elseif a:filetype ==? "javacc" + call s:MapDelimitersWithAlternative('//','', '/*','*/') + elseif a:filetype ==? "javascript" + call s:MapDelimitersWithAlternative('//','', '/*','*/') + elseif a:filetype == "javascript.jquery" + call s:MapDelimitersWithAlternative('//','', '/*','*/') + elseif a:filetype ==? "jess" + call s:MapDelimiters(';', '') + elseif a:filetype ==? "jgraph" + call s:MapDelimiters('(*','*)') + elseif a:filetype ==? "jproperties" + call s:MapDelimiters('#','') + elseif a:filetype ==? "jsp" + call s:MapDelimiters('<%--', '--%>') + elseif a:filetype ==? "kix" + call s:MapDelimiters(';', '') + elseif a:filetype ==? "kscript" + call s:MapDelimitersWithAlternative('//','', '/*','*/') + elseif a:filetype ==? "lace" + call s:MapDelimiters('--', '') + elseif a:filetype ==? "ldif" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "lilo" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "lilypond" + call s:MapDelimiters('%', '') + elseif a:filetype ==? "liquid" + call s:MapDelimiters('{%', '%}') + elseif a:filetype ==? "lisp" + call s:MapDelimitersWithAlternative(';','', '#|', '|#') + elseif a:filetype ==? "llvm" + call s:MapDelimiters(';','') + elseif a:filetype ==? "lotos" + call s:MapDelimiters('(*','*)') + elseif a:filetype ==? "lout" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "lprolog" + call s:MapDelimiters('%', '') + elseif a:filetype ==? "lscript" + call s:MapDelimiters("'", '') + elseif a:filetype ==? "lss" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "lua" + call s:MapDelimitersWithAlternative('--','', '--[[', ']]') + elseif a:filetype ==? "lynx" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "lytex" + call s:MapDelimiters('%', '') + elseif a:filetype ==? "mail" + call s:MapDelimiters('> ','') + elseif a:filetype ==? "mako" + call s:MapDelimiters('##', '') + elseif a:filetype ==? "man" + call s:MapDelimiters('."', '') + elseif a:filetype ==? "map" + call s:MapDelimiters('%', '') + elseif a:filetype ==? "maple" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "markdown" + call s:MapDelimiters('') + elseif a:filetype ==? "masm" + call s:MapDelimiters(';', '') + elseif a:filetype ==? "mason" + call s:MapDelimiters('<% #', '%>') + elseif a:filetype ==? "master" + call s:MapDelimiters('$', '') + elseif a:filetype ==? "matlab" + call s:MapDelimiters('%', '') + elseif a:filetype ==? "mel" + call s:MapDelimitersWithAlternative('//','', '/*','*/') + elseif a:filetype ==? "mib" + call s:MapDelimiters('--', '') + elseif a:filetype ==? "mkd" + call s:MapDelimiters('>', '') + elseif a:filetype ==? "mma" + call s:MapDelimiters('(*','*)') + elseif a:filetype ==? "model" + call s:MapDelimiters('$','$') + elseif a:filetype =~ "moduala." + call s:MapDelimiters('(*','*)') + elseif a:filetype ==? "modula2" + call s:MapDelimiters('(*','*)') + elseif a:filetype ==? "modula3" + call s:MapDelimiters('(*','*)') + elseif a:filetype ==? "monk" + call s:MapDelimiters(';', '') + elseif a:filetype ==? "mush" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "named" + call s:MapDelimitersWithAlternative('//','', '/*','*/') + elseif a:filetype ==? "nasm" + call s:MapDelimiters(';', '') + elseif a:filetype ==? "nastran" + call s:MapDelimiters('$', '') + elseif a:filetype ==? "natural" + call s:MapDelimiters('/*', '') + elseif a:filetype ==? "ncf" + call s:MapDelimiters(';', '') + elseif a:filetype ==? "newlisp" + call s:MapDelimiters(';','') + elseif a:filetype ==? "nroff" + call s:MapDelimiters('\"', '') + elseif a:filetype ==? "nsis" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "ntp" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "objc" + call s:MapDelimitersWithAlternative('//','', '/*','*/') + elseif a:filetype ==? "objcpp" + call s:MapDelimitersWithAlternative('//','', '/*','*/') + elseif a:filetype ==? "objj" + call s:MapDelimitersWithAlternative('//','', '/*','*/') + elseif a:filetype ==? "ocaml" + call s:MapDelimiters('(*','*)') + elseif a:filetype ==? "occam" + call s:MapDelimiters('--','') + elseif a:filetype ==? "omlet" + call s:MapDelimiters('(*','*)') + elseif a:filetype ==? "omnimark" + call s:MapDelimiters(';', '') + elseif a:filetype ==? "openroad" + call s:MapDelimiters('//', '') + elseif a:filetype ==? "opl" + call s:MapDelimiters("REM", "") + elseif a:filetype ==? "ora" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "ox" + call s:MapDelimiters('//', '') + elseif a:filetype ==? "pascal" + call s:MapDelimitersWithAlternative('{','}', '(*', '*)') + elseif a:filetype ==? "patran" + call s:MapDelimitersWithAlternative('$','','/*', '*/') + elseif a:filetype ==? "pcap" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "pccts" + call s:MapDelimitersWithAlternative('//','', '/*','*/') + elseif a:filetype ==? "pdf" + call s:MapDelimiters('%', '') + elseif a:filetype ==? "pfmain" + call s:MapDelimiters('//', '') + elseif a:filetype ==? "php" + call s:MapDelimitersWithAlternative('//','','/*', '*/') + elseif a:filetype ==? "pic" + call s:MapDelimiters(';', '') + elseif a:filetype ==? "pike" + call s:MapDelimitersWithAlternative('//','', '/*','*/') + elseif a:filetype ==? "pilrc" + call s:MapDelimitersWithAlternative('//','', '/*','*/') + elseif a:filetype ==? "pine" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "plm" + call s:MapDelimitersWithAlternative('//','', '/*','*/') + elseif a:filetype ==? "plsql" + call s:MapDelimitersWithAlternative('--', '', '/*', '*/') + elseif a:filetype ==? "po" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "postscr" + call s:MapDelimiters('%', '') + elseif a:filetype ==? "pov" + call s:MapDelimitersWithAlternative('//','', '/*','*/') + elseif a:filetype ==? "povini" + call s:MapDelimiters(';', '') + elseif a:filetype ==? "ppd" + call s:MapDelimiters('%', '') + elseif a:filetype ==? "ppwiz" + call s:MapDelimiters(';;', '') + elseif a:filetype ==? "processing" + call s:MapDelimitersWithAlternative('//','', '/*','*/') + elseif a:filetype ==? "prolog" + call s:MapDelimitersWithAlternative('%','','/*','*/') + elseif a:filetype ==? "ps1" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "psf" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "ptcap" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "python" + call s:MapDelimiters('#','') + elseif a:filetype ==? "radiance" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "ratpoison" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "r" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "rc" + call s:MapDelimitersWithAlternative('//','', '/*','*/') + elseif a:filetype ==? "rebol" + call s:MapDelimiters(';', '') + elseif a:filetype ==? "registry" + call s:MapDelimiters(';', '') + elseif a:filetype ==? "remind" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "resolv" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "rgb" + call s:MapDelimiters('!', '') + elseif a:filetype ==? "rib" + call s:MapDelimiters('#','') + elseif a:filetype ==? "robots" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "sa" + call s:MapDelimiters('--','') + elseif a:filetype ==? "samba" + call s:MapDelimitersWithAlternative(';','', '#', '') + elseif a:filetype ==? "sass" + call s:MapDelimitersWithAlternative('//','', '/*', '') + elseif a:filetype ==? "sather" + call s:MapDelimiters('--', '') + elseif a:filetype ==? "scala" + call s:MapDelimitersWithAlternative('//','', '/*','*/') + elseif a:filetype ==? "scilab" + call s:MapDelimiters('//', '') + elseif a:filetype ==? "scsh" + call s:MapDelimiters(';', '') + elseif a:filetype ==? "sed" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "sgmldecl" + call s:MapDelimiters('--','--') + elseif a:filetype ==? "sgmllnx" + call s:MapDelimiters('') + elseif a:filetype ==? "sicad" + call s:MapDelimiters('*', '') + elseif a:filetype ==? "simula" + call s:MapDelimitersWithAlternative('%', '', '--', '') + elseif a:filetype ==? "sinda" + call s:MapDelimiters('$', '') + elseif a:filetype ==? "skill" + call s:MapDelimiters(';', '') + elseif a:filetype ==? "slang" + call s:MapDelimiters('%', '') + elseif a:filetype ==? "slice" + call s:MapDelimitersWithAlternative('//','', '/*','*/') + elseif a:filetype ==? "slrnrc" + call s:MapDelimiters('%', '') + elseif a:filetype ==? "sm" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "smarty" + call s:MapDelimiters('{*', '*}') + elseif a:filetype ==? "smil" + call s:MapDelimiters('') + elseif a:filetype ==? "smith" + call s:MapDelimiters(';', '') + elseif a:filetype ==? "sml" + call s:MapDelimiters('(*','*)') + elseif a:filetype ==? "snnsnet" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "snnspat" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "snnsres" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "snobol4" + call s:MapDelimiters('*', '') + elseif a:filetype ==? "spec" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "specman" + call s:MapDelimiters('//', '') + elseif a:filetype ==? "spectre" + call s:MapDelimitersWithAlternative('//', '', '*', '') + elseif a:filetype ==? "spice" + call s:MapDelimiters('$', '') + elseif a:filetype ==? "sql" + call s:MapDelimiters('--', '') + elseif a:filetype ==? "sqlforms" + call s:MapDelimiters('--', '') + elseif a:filetype ==? "sqlj" + call s:MapDelimiters('--', '') + elseif a:filetype ==? "sqr" + call s:MapDelimiters('!', '') + elseif a:filetype ==? "squid" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "st" + call s:MapDelimiters('"','') + elseif a:filetype ==? "stp" + call s:MapDelimiters('--', '') + elseif a:filetype ==? "systemverilog" + call s:MapDelimitersWithAlternative('//','', '/*','*/') + elseif a:filetype ==? "tads" + call s:MapDelimitersWithAlternative('//','', '/*','*/') + elseif a:filetype ==? "tags" + call s:MapDelimiters(';', '') + elseif a:filetype ==? "tak" + call s:MapDelimiters('$', '') + elseif a:filetype ==? "tasm" + call s:MapDelimiters(';', '') + elseif a:filetype ==? "tcl" + call s:MapDelimiters('#','') + elseif a:filetype ==? "texinfo" + call s:MapDelimiters("@c ", "") + elseif a:filetype ==? "texmf" + call s:MapDelimiters('%', '') + elseif a:filetype ==? "tf" + call s:MapDelimiters(';', '') + elseif a:filetype ==? "tidy" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "tli" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "trasys" + call s:MapDelimiters("$", "") + elseif a:filetype ==? "tsalt" + call s:MapDelimitersWithAlternative('//','', '/*','*/') + elseif a:filetype ==? "tsscl" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "tssgm" + call s:MapDelimiters("comment = '","'") + elseif a:filetype ==? "txt2tags" + call s:MapDelimiters('%','') + elseif a:filetype ==? "uc" + call s:MapDelimitersWithAlternative('//','', '/*','*/') + elseif a:filetype ==? "uil" + call s:MapDelimiters('!', '') + elseif a:filetype ==? "vb" + call s:MapDelimiters("'","") + elseif a:filetype ==? "velocity" + call s:MapDelimitersWithAlternative("##","", '#*', '*#') + elseif a:filetype ==? "vera" + call s:MapDelimitersWithAlternative('/*','*/','//','') + elseif a:filetype ==? "verilog" + call s:MapDelimitersWithAlternative('//','', '/*','*/') + elseif a:filetype ==? "verilog_systemverilog" + call s:MapDelimitersWithAlternative('//','', '/*','*/') + elseif a:filetype ==? "vgrindefs" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "vhdl" + call s:MapDelimiters('--', '') + elseif a:filetype ==? "vimperator" + call s:MapDelimiters('"','') + elseif a:filetype ==? "virata" + call s:MapDelimiters('%', '') + elseif a:filetype ==? "vrml" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "vsejcl" + call s:MapDelimiters('/*', '') + elseif a:filetype ==? "webmacro" + call s:MapDelimiters('##', '') + elseif a:filetype ==? "wget" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "Wikipedia" + call s:MapDelimiters('') + elseif a:filetype ==? "winbatch" + call s:MapDelimiters(';', '') + elseif a:filetype ==? "wml" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "wvdial" + call s:MapDelimiters(';', '') + elseif a:filetype ==? "xdefaults" + call s:MapDelimiters('!', '') + elseif a:filetype ==? "xkb" + call s:MapDelimiters('//', '') + elseif a:filetype ==? "xmath" + call s:MapDelimiters('#', '') + elseif a:filetype ==? "xpm2" + call s:MapDelimiters('!', '') + elseif a:filetype ==? "xquery" + call s:MapDelimiters('(:',':)') + elseif a:filetype ==? "z8a" + call s:MapDelimiters(';', '') + + else + + "extract the delims from &commentstring + let left= substitute(&commentstring, '\([^ \t]*\)\s*%s.*', '\1', '') + let right= substitute(&commentstring, '.*%s\s*\(.*\)', '\1', 'g') + call s:MapDelimiters(left,right) + + endif +endfunction + +" Function: s:MapDelimiters(left, right) function {{{2 +" This function is a wrapper for s:MapDelimiters(left, right, leftAlt, rightAlt, useAlt) and is called when there +" is no alternative comment delimiters for the current filetype +" +" Args: +" -left: the left comment delimiter +" -right: the right comment delimiter +function s:MapDelimiters(left, right) + call s:MapDelimitersWithAlternative(a:left, a:right, "", "") +endfunction + +" Function: s:MapDelimitersWithAlternative(left, right, leftAlt, rightAlt) function {{{2 +" this function sets up the comment delimiter buffer variables +" +" Args: +" -left: the string defining the comment start delimiter +" -right: the string defining the comment end delimiter +" -leftAlt: the string for the alternative comment style defining the comment start delimiter +" -rightAlt: the string for the alternative comment style defining the comment end delimiter +function s:MapDelimitersWithAlternative(left, right, leftAlt, rightAlt) + if !exists('g:NERD_' . &filetype . '_alt_style') + let b:NERDLeft = a:left + let b:NERDRight = a:right + let b:NERDLeftAlt = a:leftAlt + let b:NERDRightAlt = a:rightAlt + else + let b:NERDLeft = a:leftAlt + let b:NERDRight = a:rightAlt + let b:NERDLeftAlt = a:left + let b:NERDRightAlt = a:right + endif +endfunction + +" Function: s:SwitchToAlternativeDelimiters(printMsgs) function {{{2 +" This function is used to swap the delimiters that are being used to the +" alternative delimiters for that filetype. For example, if a c++ file is +" being edited and // comments are being used, after this function is called +" /**/ comments will be used. +" +" Args: +" -printMsgs: if this is 1 then a message is echoed to the user telling them +" if this function changed the delimiters or not +function s:SwitchToAlternativeDelimiters(printMsgs) + "if both of the alternative delimiters are empty then there is no + "alternative comment style so bail out + if b:NERDLeftAlt == "" && b:NERDRightAlt == "" + if a:printMsgs + call s:NerdEcho("Cannot use alternative delimiters, none are specified", 0) + endif + return 0 + endif + + "save the current delimiters + let tempLeft = b:NERDLeft + let tempRight = b:NERDRight + + "swap current delimiters for alternative + let b:NERDLeft = b:NERDLeftAlt + let b:NERDRight = b:NERDRightAlt + + "set the previously current delimiters to be the new alternative ones + let b:NERDLeftAlt = tempLeft + let b:NERDRightAlt = tempRight + + "tell the user what comment delimiters they are now using + if a:printMsgs + let leftNoEsc = b:NERDLeft + let rightNoEsc = b:NERDRight + call s:NerdEcho("Now using " . leftNoEsc . " " . rightNoEsc . " to delimit comments", 1) + endif + + return 1 +endfunction + +" Section: Comment delimiter add/removal functions {{{1 +" ============================================================================ +" Function: s:AppendCommentToLine(){{{2 +" This function appends comment delimiters at the EOL and places the cursor in +" position to start typing the comment +function s:AppendCommentToLine() + let left = s:GetLeft(0,1,0) + let right = s:GetRight(0,1,0) + + " get the len of the right delim + let lenRight = strlen(right) + + let isLineEmpty = strlen(getline(".")) == 0 + let insOrApp = (isLineEmpty==1 ? 'i' : 'A') + + "stick the delimiters down at the end of the line. We have to format the + "comment with spaces as appropriate + execute ":normal! " . insOrApp . (isLineEmpty ? '' : ' ') . left . right . " " + + " if there is a right delimiter then we gotta move the cursor left + " by the len of the right delimiter so we insert between the delimiters + if lenRight > 0 + let leftMoveAmount = lenRight + execute ":normal! " . leftMoveAmount . "h" + endif + startinsert +endfunction + +" Function: s:CommentBlock(top, bottom, lSide, rSide, forceNested ) {{{2 +" This function is used to comment out a region of code. This region is +" specified as a bounding box by arguments to the function. +" +" Args: +" -top: the line number for the top line of code in the region +" -bottom: the line number for the bottom line of code in the region +" -lSide: the column number for the left most column in the region +" -rSide: the column number for the right most column in the region +" -forceNested: a flag indicating whether comments should be nested +function s:CommentBlock(top, bottom, lSide, rSide, forceNested ) + " we need to create local copies of these arguments so we can modify them + let top = a:top + let bottom = a:bottom + let lSide = a:lSide + let rSide = a:rSide + + "if the top or bottom line starts with tabs we have to adjust the left and + "right boundaries so that they are set as though the tabs were spaces + let topline = getline(top) + let bottomline = getline(bottom) + if s:HasLeadingTabs(topline, bottomline) + + "find out how many tabs are in the top line and adjust the left + "boundary accordingly + let numTabs = s:NumberOfLeadingTabs(topline) + if lSide < numTabs + let lSide = &ts * lSide + else + let lSide = (lSide - numTabs) + (&ts * numTabs) + endif + + "find out how many tabs are in the bottom line and adjust the right + "boundary accordingly + let numTabs = s:NumberOfLeadingTabs(bottomline) + let rSide = (rSide - numTabs) + (&ts * numTabs) + endif + + "we must check that bottom IS actually below top, if it is not then we + "swap top and bottom. Similarly for left and right. + if bottom < top + let temp = top + let top = bottom + let bottom = top + endif + if rSide < lSide + let temp = lSide + let lSide = rSide + let rSide = temp + endif + + "if the current delimiters arent multipart then we will switch to the + "alternative delims (if THEY are) as the comment will be better and more + "accurate with multipart delims + let switchedDelims = 0 + if !s:Multipart() && g:NERDAllowAnyVisualDelims && s:AltMultipart() + let switchedDelims = 1 + call s:SwitchToAlternativeDelimiters(0) + endif + + "start the commenting from the top and keep commenting till we reach the + "bottom + let currentLine=top + while currentLine <= bottom + + "check if we are allowed to comment this line + if s:CanCommentLine(a:forceNested, currentLine) + + "convert the leading tabs into spaces + let theLine = getline(currentLine) + let lineHasLeadTabs = s:HasLeadingTabs(theLine) + if lineHasLeadTabs + let theLine = s:ConvertLeadingTabsToSpaces(theLine) + endif + + "dont comment lines that begin after the right boundary of the + "block unless the user has specified to do so + if theLine !~ '^ \{' . rSide . '\}' || !g:NERDBlockComIgnoreEmpty + + "attempt to place the cursor in on the left of the boundary box, + "then check if we were successful, if not then we cant comment this + "line + call setline(currentLine, theLine) + if s:CanPlaceCursor(currentLine, lSide) + + let leftSpaced = s:GetLeft(0,1,0) + let rightSpaced = s:GetRight(0,1,0) + + "stick the left delimiter down + let theLine = strpart(theLine, 0, lSide-1) . leftSpaced . strpart(theLine, lSide-1) + + if s:Multipart() + "stick the right delimiter down + let theLine = strpart(theLine, 0, rSide+strlen(leftSpaced)) . rightSpaced . strpart(theLine, rSide+strlen(leftSpaced)) + + let firstLeftDelim = s:FindDelimiterIndex(b:NERDLeft, theLine) + let lastRightDelim = s:LastIndexOfDelim(b:NERDRight, theLine) + + if firstLeftDelim != -1 && lastRightDelim != -1 + let searchStr = strpart(theLine, 0, lastRightDelim) + let searchStr = strpart(searchStr, firstLeftDelim+strlen(b:NERDLeft)) + + "replace the outter most delims in searchStr with + "place-holders + let theLineWithPlaceHolders = s:ReplaceDelims(b:NERDLeft, b:NERDRight, g:NERDLPlace, g:NERDRPlace, searchStr) + + "add the right delimiter onto the line + let theLine = strpart(theLine, 0, firstLeftDelim+strlen(b:NERDLeft)) . theLineWithPlaceHolders . strpart(theLine, lastRightDelim) + endif + endif + endif + endif + + "restore tabs if needed + if lineHasLeadTabs + let theLine = s:ConvertLeadingSpacesToTabs(theLine) + endif + + call setline(currentLine, theLine) + endif + + let currentLine = currentLine + 1 + endwhile + + "if we switched delims then we gotta go back to what they were before + if switchedDelims == 1 + call s:SwitchToAlternativeDelimiters(0) + endif +endfunction + +" Function: s:CommentLines(forceNested, alignLeft, alignRight, firstLine, lastLine) {{{2 +" This function comments a range of lines. +" +" Args: +" -forceNested: a flag indicating whether the called is requesting the comment +" to be nested if need be +" -align: should be "left" or "both" or "none" +" -firstLine/lastLine: the top and bottom lines to comment +function s:CommentLines(forceNested, align, firstLine, lastLine) + " we need to get the left and right indexes of the leftmost char in the + " block of of lines and the right most char so that we can do alignment of + " the delimiters if the user has specified + let leftAlignIndx = s:LeftMostIndx(a:forceNested, 0, a:firstLine, a:lastLine) + let rightAlignIndx = s:RightMostIndx(a:forceNested, 0, a:firstLine, a:lastLine) + + " gotta add the length of the left delimiter onto the rightAlignIndx cos + " we'll be adding a left delim to the line + let rightAlignIndx = rightAlignIndx + strlen(s:GetLeft(0,1,0)) + + " now we actually comment the lines. Do it line by line + let currentLine = a:firstLine + while currentLine <= a:lastLine + + " get the next line, check commentability and convert spaces to tabs + let theLine = getline(currentLine) + let lineHasLeadingTabs = s:HasLeadingTabs(theLine) + let theLine = s:ConvertLeadingTabsToSpaces(theLine) + if s:CanCommentLine(a:forceNested, currentLine) + "if the user has specified forceNesting then we check to see if we + "need to switch delimiters for place-holders + if a:forceNested && g:NERDUsePlaceHolders + let theLine = s:SwapOutterMultiPartDelimsForPlaceHolders(theLine) + endif + + " find out if the line is commented using normal delims and/or + " alternate ones + let isCommented = s:IsCommented(b:NERDLeft, b:NERDRight, theLine) || s:IsCommented(b:NERDLeftAlt, b:NERDRightAlt, theLine) + + " check if we can comment this line + if !isCommented || g:NERDUsePlaceHolders || s:Multipart() + if a:align == "left" || a:align == "both" + let theLine = s:AddLeftDelimAligned(s:GetLeft(0,1,0), theLine, leftAlignIndx) + else + let theLine = s:AddLeftDelim(s:GetLeft(0,1,0), theLine) + endif + if a:align == "both" + let theLine = s:AddRightDelimAligned(s:GetRight(0,1,0), theLine, rightAlignIndx) + else + let theLine = s:AddRightDelim(s:GetRight(0,1,0), theLine) + endif + endif + endif + + " restore leading tabs if appropriate + if lineHasLeadingTabs + let theLine = s:ConvertLeadingSpacesToTabs(theLine) + endif + + " we are done with this line + call setline(currentLine, theLine) + let currentLine = currentLine + 1 + endwhile + +endfunction + +" Function: s:CommentLinesMinimal(firstLine, lastLine) {{{2 +" This function comments a range of lines in a minimal style. I +" +" Args: +" -firstLine/lastLine: the top and bottom lines to comment +function s:CommentLinesMinimal(firstLine, lastLine) + "check that minimal comments can be done on this filetype + if !s:HasMultipartDelims() + throw 'NERDCommenter.Delimiters exception: Minimal comments can only be used for filetypes that have multipart delimiters' + endif + + "if we need to use place holders for the comment, make sure they are + "enabled for this filetype + if !g:NERDUsePlaceHolders && s:DoesBlockHaveMultipartDelim(a:firstLine, a:lastLine) + throw 'NERDCommenter.Settings exception: Placeoholders are required but disabled.' + endif + + "get the left and right delims to smack on + let left = s:GetSexyComLeft(g:NERDSpaceDelims,0) + let right = s:GetSexyComRight(g:NERDSpaceDelims,0) + + "make sure all multipart delims on the lines are replaced with + "placeholders to prevent illegal syntax + let currentLine = a:firstLine + while(currentLine <= a:lastLine) + let theLine = getline(currentLine) + let theLine = s:ReplaceDelims(left, right, g:NERDLPlace, g:NERDRPlace, theLine) + call setline(currentLine, theLine) + let currentLine = currentLine + 1 + endwhile + + "add the delim to the top line + let theLine = getline(a:firstLine) + let lineHasLeadingTabs = s:HasLeadingTabs(theLine) + let theLine = s:ConvertLeadingTabsToSpaces(theLine) + let theLine = s:AddLeftDelim(left, theLine) + if lineHasLeadingTabs + let theLine = s:ConvertLeadingSpacesToTabs(theLine) + endif + call setline(a:firstLine, theLine) + + "add the delim to the bottom line + let theLine = getline(a:lastLine) + let lineHasLeadingTabs = s:HasLeadingTabs(theLine) + let theLine = s:ConvertLeadingTabsToSpaces(theLine) + let theLine = s:AddRightDelim(right, theLine) + if lineHasLeadingTabs + let theLine = s:ConvertLeadingSpacesToTabs(theLine) + endif + call setline(a:lastLine, theLine) +endfunction + +" Function: s:CommentLinesSexy(topline, bottomline) function {{{2 +" This function is used to comment lines in the 'Sexy' style. eg in c: +" /* +" * This is a sexy comment +" */ +" Args: +" -topline: the line num of the top line in the sexy comment +" -bottomline: the line num of the bottom line in the sexy comment +function s:CommentLinesSexy(topline, bottomline) + let left = s:GetSexyComLeft(0, 0) + let right = s:GetSexyComRight(0, 0) + + "check if we can do a sexy comment with the available delimiters + if left == -1 || right == -1 + throw 'NERDCommenter.Delimiters exception: cannot perform sexy comments with available delimiters.' + endif + + "make sure the lines arent already commented sexually + if !s:CanSexyCommentLines(a:topline, a:bottomline) + throw 'NERDCommenter.Nesting exception: cannot nest sexy comments' + endif + + + let sexyComMarker = s:GetSexyComMarker(0,0) + let sexyComMarkerSpaced = s:GetSexyComMarker(1,0) + + + " we jam the comment as far to the right as possible + let leftAlignIndx = s:LeftMostIndx(1, 1, a:topline, a:bottomline) + + "check if we should use the compact style i.e that the left/right + "delimiters should appear on the first and last lines of the code and not + "on separate lines above/below the first/last lines of code + if g:NERDCompactSexyComs + let spaceString = (g:NERDSpaceDelims ? s:spaceStr : '') + + "comment the top line + let theLine = getline(a:topline) + let lineHasTabs = s:HasLeadingTabs(theLine) + if lineHasTabs + let theLine = s:ConvertLeadingTabsToSpaces(theLine) + endif + let theLine = s:SwapOutterMultiPartDelimsForPlaceHolders(theLine) + let theLine = s:AddLeftDelimAligned(left . spaceString, theLine, leftAlignIndx) + if lineHasTabs + let theLine = s:ConvertLeadingSpacesToTabs(theLine) + endif + call setline(a:topline, theLine) + + "comment the bottom line + if a:bottomline != a:topline + let theLine = getline(a:bottomline) + let lineHasTabs = s:HasLeadingTabs(theLine) + if lineHasTabs + let theLine = s:ConvertLeadingTabsToSpaces(theLine) + endif + let theLine = s:SwapOutterMultiPartDelimsForPlaceHolders(theLine) + endif + let theLine = s:AddRightDelim(spaceString . right, theLine) + if lineHasTabs + let theLine = s:ConvertLeadingSpacesToTabs(theLine) + endif + call setline(a:bottomline, theLine) + else + + " add the left delimiter one line above the lines that are to be commented + call cursor(a:topline, 1) + execute 'normal! O' + let theLine = repeat(' ', leftAlignIndx) . left + + " Make sure tabs are respected + if !&expandtab + let theLine = s:ConvertLeadingSpacesToTabs(theLine) + endif + call setline(a:topline, theLine) + + " add the right delimiter after bottom line (we have to add 1 cos we moved + " the lines down when we added the left delim + call cursor(a:bottomline+1, 1) + execute 'normal! o' + let theLine = repeat(' ', leftAlignIndx) . repeat(' ', strlen(left)-strlen(sexyComMarker)) . right + + " Make sure tabs are respected + if !&expandtab + let theLine = s:ConvertLeadingSpacesToTabs(theLine) + endif + call setline(a:bottomline+2, theLine) + + endif + + " go thru each line adding the sexyComMarker marker to the start of each + " line in the appropriate place to align them with the comment delims + let currentLine = a:topline+1 + while currentLine <= a:bottomline + !g:NERDCompactSexyComs + " get the line and convert the tabs to spaces + let theLine = getline(currentLine) + let lineHasTabs = s:HasLeadingTabs(theLine) + if lineHasTabs + let theLine = s:ConvertLeadingTabsToSpaces(theLine) + endif + + let theLine = s:SwapOutterMultiPartDelimsForPlaceHolders(theLine) + + " add the sexyComMarker + let theLine = repeat(' ', leftAlignIndx) . repeat(' ', strlen(left)-strlen(sexyComMarker)) . sexyComMarkerSpaced . strpart(theLine, leftAlignIndx) + + if lineHasTabs + let theLine = s:ConvertLeadingSpacesToTabs(theLine) + endif + + + " set the line and move onto the next one + call setline(currentLine, theLine) + let currentLine = currentLine + 1 + endwhile + +endfunction + +" Function: s:CommentLinesToggle(forceNested, firstLine, lastLine) {{{2 +" Applies "toggle" commenting to the given range of lines +" +" Args: +" -forceNested: a flag indicating whether the called is requesting the comment +" to be nested if need be +" -firstLine/lastLine: the top and bottom lines to comment +function s:CommentLinesToggle(forceNested, firstLine, lastLine) + let currentLine = a:firstLine + while currentLine <= a:lastLine + + " get the next line, check commentability and convert spaces to tabs + let theLine = getline(currentLine) + let lineHasLeadingTabs = s:HasLeadingTabs(theLine) + let theLine = s:ConvertLeadingTabsToSpaces(theLine) + if s:CanToggleCommentLine(a:forceNested, currentLine) + + "if the user has specified forceNesting then we check to see if we + "need to switch delimiters for place-holders + if g:NERDUsePlaceHolders + let theLine = s:SwapOutterMultiPartDelimsForPlaceHolders(theLine) + endif + + let theLine = s:AddLeftDelim(s:GetLeft(0, 1, 0), theLine) + let theLine = s:AddRightDelim(s:GetRight(0, 1, 0), theLine) + endif + + " restore leading tabs if appropriate + if lineHasLeadingTabs + let theLine = s:ConvertLeadingSpacesToTabs(theLine) + endif + + " we are done with this line + call setline(currentLine, theLine) + let currentLine = currentLine + 1 + endwhile + +endfunction + +" Function: s:CommentRegion(topline, topCol, bottomLine, bottomCol) function {{{2 +" This function comments chunks of text selected in visual mode. +" It will comment exactly the text that they have selected. +" Args: +" -topLine: the line num of the top line in the sexy comment +" -topCol: top left col for this comment +" -bottomline: the line num of the bottom line in the sexy comment +" -bottomCol: the bottom right col for this comment +" -forceNested: whether the caller wants comments to be nested if the +" line(s) are already commented +function s:CommentRegion(topLine, topCol, bottomLine, bottomCol, forceNested) + + "switch delims (if we can) if the current set isnt multipart + let switchedDelims = 0 + if !s:Multipart() && s:AltMultipart() && !g:NERDAllowAnyVisualDelims + let switchedDelims = 1 + call s:SwitchToAlternativeDelimiters(0) + endif + + "if there is only one line in the comment then just do it + if a:topLine == a:bottomLine + call s:CommentBlock(a:topLine, a:bottomLine, a:topCol, a:bottomCol, a:forceNested) + + "there are multiple lines in the comment + else + "comment the top line + call s:CommentBlock(a:topLine, a:topLine, a:topCol, strlen(getline(a:topLine)), a:forceNested) + + "comment out all the lines in the middle of the comment + let topOfRange = a:topLine+1 + let bottomOfRange = a:bottomLine-1 + if topOfRange <= bottomOfRange + call s:CommentLines(a:forceNested, "none", topOfRange, bottomOfRange) + endif + + "comment the bottom line + let bottom = getline(a:bottomLine) + let numLeadingSpacesTabs = strlen(substitute(bottom, '^\([ \t]*\).*$', '\1', '')) + call s:CommentBlock(a:bottomLine, a:bottomLine, numLeadingSpacesTabs+1, a:bottomCol, a:forceNested) + + endif + + "stick the cursor back on the char it was on before the comment + call cursor(a:topLine, a:topCol + strlen(b:NERDLeft) + g:NERDSpaceDelims) + + "if we switched delims then we gotta go back to what they were before + if switchedDelims == 1 + call s:SwitchToAlternativeDelimiters(0) + endif + +endfunction + +" Function: s:InvertComment(firstLine, lastLine) function {{{2 +" Inverts the comments on the lines between and including the given line +" numbers i.e all commented lines are uncommented and vice versa +" Args: +" -firstLine: the top of the range of lines to be inverted +" -lastLine: the bottom of the range of lines to be inverted +function s:InvertComment(firstLine, lastLine) + + " go thru all lines in the given range + let currentLine = a:firstLine + while currentLine <= a:lastLine + let theLine = getline(currentLine) + + let sexyComBounds = s:FindBoundingLinesOfSexyCom(currentLine) + + " if the line is commented normally, uncomment it + if s:IsCommentedFromStartOfLine(b:NERDLeft, theLine) || s:IsCommentedFromStartOfLine(b:NERDLeftAlt, theLine) + call s:UncommentLines(currentLine, currentLine) + let currentLine = currentLine + 1 + + " check if the line is commented sexually + elseif !empty(sexyComBounds) + let numLinesBeforeSexyComRemoved = s:NumLinesInBuf() + call s:UncommentLinesSexy(sexyComBounds[0], sexyComBounds[1]) + + "move to the line after last line of the sexy comment + let numLinesAfterSexyComRemoved = s:NumLinesInBuf() + let currentLine = bottomBound - (numLinesBeforeSexyComRemoved - numLinesAfterSexyComRemoved) + 1 + + " the line isnt commented + else + call s:CommentLinesToggle(1, currentLine, currentLine) + let currentLine = currentLine + 1 + endif + + endwhile +endfunction + +" Function: NERDComment(isVisual, type) function {{{2 +" This function is a Wrapper for the main commenting functions +" +" Args: +" -isVisual: a flag indicating whether the comment is requested in visual +" mode or not +" -type: the type of commenting requested. Can be 'sexy', 'invert', +" 'minimal', 'toggle', 'alignLeft', 'alignBoth', 'norm', +" 'nested', 'toEOL', 'append', 'insert', 'uncomment', 'yank' +function! NERDComment(isVisual, type) range + " we want case sensitivity when commenting + let oldIgnoreCase = &ignorecase + set noignorecase + + if a:isVisual + let firstLine = line("'<") + let lastLine = line("'>") + let firstCol = col("'<") + let lastCol = col("'>") - (&selection == 'exclusive' ? 1 : 0) + else + let firstLine = a:firstline + let lastLine = a:lastline + endif + + let countWasGiven = (a:isVisual == 0 && firstLine != lastLine) + + let forceNested = (a:type == 'nested' || g:NERDDefaultNesting) + + if a:type == 'norm' || a:type == 'nested' + if a:isVisual && visualmode() == "" + call s:CommentBlock(firstLine, lastLine, firstCol, lastCol, forceNested) + elseif a:isVisual && visualmode() == "v" && (g:NERDCommentWholeLinesInVMode==0 || (g:NERDCommentWholeLinesInVMode==2 && s:HasMultipartDelims())) + call s:CommentRegion(firstLine, firstCol, lastLine, lastCol, forceNested) + else + call s:CommentLines(forceNested, "none", firstLine, lastLine) + endif + + elseif a:type == 'alignLeft' || a:type == 'alignBoth' + let align = "none" + if a:type == "alignLeft" + let align = "left" + elseif a:type == "alignBoth" + let align = "both" + endif + call s:CommentLines(forceNested, align, firstLine, lastLine) + + elseif a:type == 'invert' + call s:InvertComment(firstLine, lastLine) + + elseif a:type == 'sexy' + try + call s:CommentLinesSexy(firstLine, lastLine) + catch /NERDCommenter.Delimiters/ + call s:CommentLines(forceNested, "none", firstLine, lastLine) + catch /NERDCommenter.Nesting/ + call s:NerdEcho("Sexy comment aborted. Nested sexy cannot be nested", 0) + endtry + + elseif a:type == 'toggle' + let theLine = getline(firstLine) + + if s:IsInSexyComment(firstLine) || s:IsCommentedFromStartOfLine(b:NERDLeft, theLine) || s:IsCommentedFromStartOfLine(b:NERDLeftAlt, theLine) + call s:UncommentLines(firstLine, lastLine) + else + call s:CommentLinesToggle(forceNested, firstLine, lastLine) + endif + + elseif a:type == 'minimal' + try + call s:CommentLinesMinimal(firstLine, lastLine) + catch /NERDCommenter.Delimiters/ + call s:NerdEcho("Minimal comments can only be used for filetypes that have multipart delimiters.", 0) + catch /NERDCommenter.Settings/ + call s:NerdEcho("Place holders are required but disabled.", 0) + endtry + + elseif a:type == 'toEOL' + call s:SaveScreenState() + call s:CommentBlock(firstLine, firstLine, col("."), col("$")-1, 1) + call s:RestoreScreenState() + + elseif a:type == 'append' + call s:AppendCommentToLine() + + elseif a:type == 'insert' + call s:PlaceDelimitersAndInsBetween() + + elseif a:type == 'uncomment' + call s:UncommentLines(firstLine, lastLine) + + elseif a:type == 'yank' + if a:isVisual + normal! gvy + elseif countWasGiven + execute firstLine .','. lastLine .'yank' + else + normal! yy + endif + execute firstLine .','. lastLine .'call NERDComment('. a:isVisual .', "norm")' + endif + + let &ignorecase = oldIgnoreCase +endfunction + +" Function: s:PlaceDelimitersAndInsBetween() function {{{2 +" This is function is called to place comment delimiters down and place the +" cursor between them +function s:PlaceDelimitersAndInsBetween() + " get the left and right delimiters without any escape chars in them + let left = s:GetLeft(0, 1, 0) + let right = s:GetRight(0, 1, 0) + + let theLine = getline(".") + let lineHasLeadTabs = s:HasLeadingTabs(theLine) || (theLine =~ '^ *$' && !&expandtab) + + "convert tabs to spaces and adjust the cursors column to take this into + "account + let untabbedCol = s:UntabbedCol(theLine, col(".")) + call setline(line("."), s:ConvertLeadingTabsToSpaces(theLine)) + call cursor(line("."), untabbedCol) + + " get the len of the right delim + let lenRight = strlen(right) + + let isDelimOnEOL = col(".") >= strlen(getline(".")) + + " if the cursor is in the first col then we gotta insert rather than + " append the comment delimiters here + let insOrApp = (col(".")==1 ? 'i' : 'a') + + " place the delimiters down. We do it differently depending on whether + " there is a left AND right delimiter + if lenRight > 0 + execute ":normal! " . insOrApp . left . right + execute ":normal! " . lenRight . "h" + else + execute ":normal! " . insOrApp . left + + " if we are tacking the delim on the EOL then we gotta add a space + " after it cos when we go out of insert mode the cursor will move back + " one and the user wont be in position to type the comment. + if isDelimOnEOL + execute 'normal! a ' + endif + endif + normal! l + + "if needed convert spaces back to tabs and adjust the cursors col + "accordingly + if lineHasLeadTabs + let tabbedCol = s:TabbedCol(getline("."), col(".")) + call setline(line("."), s:ConvertLeadingSpacesToTabs(getline("."))) + call cursor(line("."), tabbedCol) + endif + + startinsert +endfunction + +" Function: s:RemoveDelimiters(left, right, line) {{{2 +" this function is called to remove the first left comment delimiter and the +" last right delimiter of the given line. +" +" The args left and right must be strings. If there is no right delimiter (as +" is the case for e.g vim file comments) them the arg right should be "" +" +" Args: +" -left: the left comment delimiter +" -right: the right comment delimiter +" -line: the line to remove the delimiters from +function s:RemoveDelimiters(left, right, line) + + let l:left = a:left + let l:right = a:right + let lenLeft = strlen(left) + let lenRight = strlen(right) + + let delimsSpaced = (g:NERDSpaceDelims || g:NERDRemoveExtraSpaces) + + let line = a:line + + "look for the left delimiter, if we find it, remove it. + let leftIndx = s:FindDelimiterIndex(a:left, line) + if leftIndx != -1 + let line = strpart(line, 0, leftIndx) . strpart(line, leftIndx+lenLeft) + + "if the user has specified that there is a space after the left delim + "then check for the space and remove it if it is there + if delimsSpaced && strpart(line, leftIndx, s:lenSpaceStr) == s:spaceStr + let line = strpart(line, 0, leftIndx) . strpart(line, leftIndx+s:lenSpaceStr) + endif + endif + + "look for the right delimiter, if we find it, remove it + let rightIndx = s:FindDelimiterIndex(a:right, line) + if rightIndx != -1 + let line = strpart(line, 0, rightIndx) . strpart(line, rightIndx+lenRight) + + "if the user has specified that there is a space before the right delim + "then check for the space and remove it if it is there + if delimsSpaced && strpart(line, rightIndx-s:lenSpaceStr, s:lenSpaceStr) == s:spaceStr && s:Multipart() + let line = strpart(line, 0, rightIndx-s:lenSpaceStr) . strpart(line, rightIndx) + endif + endif + + return line +endfunction + +" Function: s:UncommentLines(topLine, bottomLine) {{{2 +" This function uncomments the given lines +" +" Args: +" topLine: the top line of the visual selection to uncomment +" bottomLine: the bottom line of the visual selection to uncomment +function s:UncommentLines(topLine, bottomLine) + "make local copies of a:firstline and a:lastline and, if need be, swap + "them around if the top line is below the bottom + let l:firstline = a:topLine + let l:lastline = a:bottomLine + if firstline > lastline + let firstline = lastline + let lastline = a:topLine + endif + + "go thru each line uncommenting each line removing sexy comments + let currentLine = firstline + while currentLine <= lastline + + "check the current line to see if it is part of a sexy comment + let sexyComBounds = s:FindBoundingLinesOfSexyCom(currentLine) + if !empty(sexyComBounds) + + "we need to store the num lines in the buf before the comment is + "removed so we know how many lines were removed when the sexy com + "was removed + let numLinesBeforeSexyComRemoved = s:NumLinesInBuf() + + call s:UncommentLinesSexy(sexyComBounds[0], sexyComBounds[1]) + + "move to the line after last line of the sexy comment + let numLinesAfterSexyComRemoved = s:NumLinesInBuf() + let numLinesRemoved = numLinesBeforeSexyComRemoved - numLinesAfterSexyComRemoved + let currentLine = sexyComBounds[1] - numLinesRemoved + 1 + let lastline = lastline - numLinesRemoved + + "no sexy com was detected so uncomment the line as normal + else + call s:UncommentLinesNormal(currentLine, currentLine) + let currentLine = currentLine + 1 + endif + endwhile + +endfunction + +" Function: s:UncommentLinesSexy(topline, bottomline) {{{2 +" This function removes all the comment characters associated with the sexy +" comment spanning the given lines +" Args: +" -topline/bottomline: the top/bottom lines of the sexy comment +function s:UncommentLinesSexy(topline, bottomline) + let left = s:GetSexyComLeft(0,1) + let right = s:GetSexyComRight(0,1) + + + "check if it is even possible for sexy comments to exist with the + "available delimiters + if left == -1 || right == -1 + throw 'NERDCommenter.Delimiters exception: cannot uncomment sexy comments with available delimiters.' + endif + + let leftUnEsc = s:GetSexyComLeft(0,0) + let rightUnEsc = s:GetSexyComRight(0,0) + + let sexyComMarker = s:GetSexyComMarker(0, 1) + let sexyComMarkerUnEsc = s:GetSexyComMarker(0, 0) + + "the markerOffset is how far right we need to move the sexyComMarker to + "line it up with the end of the left delim + let markerOffset = strlen(leftUnEsc)-strlen(sexyComMarkerUnEsc) + + " go thru the intermediate lines of the sexy comment and remove the + " sexy comment markers (eg the '*'s on the start of line in a c sexy + " comment) + let currentLine = a:topline+1 + while currentLine < a:bottomline + let theLine = getline(currentLine) + + " remove the sexy comment marker from the line. We also remove the + " space after it if there is one and if appropriate options are set + let sexyComMarkerIndx = stridx(theLine, sexyComMarkerUnEsc) + if strpart(theLine, sexyComMarkerIndx+strlen(sexyComMarkerUnEsc), s:lenSpaceStr) == s:spaceStr && g:NERDSpaceDelims + let theLine = strpart(theLine, 0, sexyComMarkerIndx - markerOffset) . strpart(theLine, sexyComMarkerIndx+strlen(sexyComMarkerUnEsc)+s:lenSpaceStr) + else + let theLine = strpart(theLine, 0, sexyComMarkerIndx - markerOffset) . strpart(theLine, sexyComMarkerIndx+strlen(sexyComMarkerUnEsc)) + endif + + let theLine = s:SwapOutterPlaceHoldersForMultiPartDelims(theLine) + + let theLine = s:ConvertLeadingWhiteSpace(theLine) + + " move onto the next line + call setline(currentLine, theLine) + let currentLine = currentLine + 1 + endwhile + + " gotta make a copy of a:bottomline cos we modify the position of the + " last line it if we remove the topline + let bottomline = a:bottomline + + " get the first line so we can remove the left delim from it + let theLine = getline(a:topline) + + " if the first line contains only the left delim then just delete it + if theLine =~ '^[ \t]*' . left . '[ \t]*$' && !g:NERDCompactSexyComs + call cursor(a:topline, 1) + normal! dd + let bottomline = bottomline - 1 + + " topline contains more than just the left delim + else + + " remove the delim. If there is a space after it + " then remove this too if appropriate + let delimIndx = stridx(theLine, leftUnEsc) + if strpart(theLine, delimIndx+strlen(leftUnEsc), s:lenSpaceStr) == s:spaceStr && g:NERDSpaceDelims + let theLine = strpart(theLine, 0, delimIndx) . strpart(theLine, delimIndx+strlen(leftUnEsc)+s:lenSpaceStr) + else + let theLine = strpart(theLine, 0, delimIndx) . strpart(theLine, delimIndx+strlen(leftUnEsc)) + endif + let theLine = s:SwapOutterPlaceHoldersForMultiPartDelims(theLine) + call setline(a:topline, theLine) + endif + + " get the last line so we can remove the right delim + let theLine = getline(bottomline) + + " if the bottomline contains only the right delim then just delete it + if theLine =~ '^[ \t]*' . right . '[ \t]*$' + call cursor(bottomline, 1) + normal! dd + + " the last line contains more than the right delim + else + " remove the right delim. If there is a space after it and + " if the appropriate options are set then remove this too. + let delimIndx = s:LastIndexOfDelim(rightUnEsc, theLine) + if strpart(theLine, delimIndx+strlen(leftUnEsc), s:lenSpaceStr) == s:spaceStr && g:NERDSpaceDelims + let theLine = strpart(theLine, 0, delimIndx) . strpart(theLine, delimIndx+strlen(rightUnEsc)+s:lenSpaceStr) + else + let theLine = strpart(theLine, 0, delimIndx) . strpart(theLine, delimIndx+strlen(rightUnEsc)) + endif + + " if the last line also starts with a sexy comment marker then we + " remove this as well + if theLine =~ '^[ \t]*' . sexyComMarker + + " remove the sexyComMarker. If there is a space after it then + " remove that too + let sexyComMarkerIndx = stridx(theLine, sexyComMarkerUnEsc) + if strpart(theLine, sexyComMarkerIndx+strlen(sexyComMarkerUnEsc), s:lenSpaceStr) == s:spaceStr && g:NERDSpaceDelims + let theLine = strpart(theLine, 0, sexyComMarkerIndx - markerOffset ) . strpart(theLine, sexyComMarkerIndx+strlen(sexyComMarkerUnEsc)+s:lenSpaceStr) + else + let theLine = strpart(theLine, 0, sexyComMarkerIndx - markerOffset ) . strpart(theLine, sexyComMarkerIndx+strlen(sexyComMarkerUnEsc)) + endif + endif + + let theLine = s:SwapOutterPlaceHoldersForMultiPartDelims(theLine) + call setline(bottomline, theLine) + endif +endfunction + +" Function: s:UncommentLineNormal(line) {{{2 +" uncomments the given line and returns the result +" Args: +" -line: the line to uncomment +function s:UncommentLineNormal(line) + let line = a:line + + "get the comment status on the line so we know how it is commented + let lineCommentStatus = s:IsCommentedOuttermost(b:NERDLeft, b:NERDRight, b:NERDLeftAlt, b:NERDRightAlt, line) + + "it is commented with b:NERDLeft and b:NERDRight so remove these delims + if lineCommentStatus == 1 + let line = s:RemoveDelimiters(b:NERDLeft, b:NERDRight, line) + + "it is commented with b:NERDLeftAlt and b:NERDRightAlt so remove these delims + elseif lineCommentStatus == 2 && g:NERDRemoveAltComs + let line = s:RemoveDelimiters(b:NERDLeftAlt, b:NERDRightAlt, line) + + "it is not properly commented with any delims so we check if it has + "any random left or right delims on it and remove the outtermost ones + else + "get the positions of all delim types on the line + let indxLeft = s:FindDelimiterIndex(b:NERDLeft, line) + let indxLeftAlt = s:FindDelimiterIndex(b:NERDLeftAlt, line) + let indxRight = s:FindDelimiterIndex(b:NERDRight, line) + let indxRightAlt = s:FindDelimiterIndex(b:NERDRightAlt, line) + + "remove the outter most left comment delim + if indxLeft != -1 && (indxLeft < indxLeftAlt || indxLeftAlt == -1) + let line = s:RemoveDelimiters(b:NERDLeft, '', line) + elseif indxLeftAlt != -1 + let line = s:RemoveDelimiters(b:NERDLeftAlt, '', line) + endif + + "remove the outter most right comment delim + if indxRight != -1 && (indxRight < indxRightAlt || indxRightAlt == -1) + let line = s:RemoveDelimiters('', b:NERDRight, line) + elseif indxRightAlt != -1 + let line = s:RemoveDelimiters('', b:NERDRightAlt, line) + endif + endif + + + let indxLeft = s:FindDelimiterIndex(b:NERDLeft, line) + let indxLeftAlt = s:FindDelimiterIndex(b:NERDLeftAlt, line) + let indxLeftPlace = s:FindDelimiterIndex(g:NERDLPlace, line) + + let indxRightPlace = s:FindDelimiterIndex(g:NERDRPlace, line) + let indxRightAlt = s:FindDelimiterIndex(b:NERDRightAlt, line) + let indxRightPlace = s:FindDelimiterIndex(g:NERDRPlace, line) + + let right = b:NERDRight + let left = b:NERDLeft + if !s:Multipart() + let right = b:NERDRightAlt + let left = b:NERDLeftAlt + endif + + + "if there are place-holders on the line then we check to see if they are + "the outtermost delimiters on the line. If so then we replace them with + "real delimiters + if indxLeftPlace != -1 + if (indxLeftPlace < indxLeft || indxLeft==-1) && (indxLeftPlace < indxLeftAlt || indxLeftAlt==-1) + let line = s:ReplaceDelims(g:NERDLPlace, g:NERDRPlace, left, right, line) + endif + elseif indxRightPlace != -1 + if (indxRightPlace < indxLeft || indxLeft==-1) && (indxLeftPlace < indxLeftAlt || indxLeftAlt==-1) + let line = s:ReplaceDelims(g:NERDLPlace, g:NERDRPlace, left, right, line) + endif + + endif + + let line = s:ConvertLeadingWhiteSpace(line) + + return line +endfunction + +" Function: s:UncommentLinesNormal(topline, bottomline) {{{2 +" This function is called to uncomment lines that arent a sexy comment +" Args: +" -topline/bottomline: the top/bottom line numbers of the comment +function s:UncommentLinesNormal(topline, bottomline) + let currentLine = a:topline + while currentLine <= a:bottomline + let line = getline(currentLine) + call setline(currentLine, s:UncommentLineNormal(line)) + let currentLine = currentLine + 1 + endwhile +endfunction + + +" Section: Other helper functions {{{1 +" ============================================================================ + +" Function: s:AddLeftDelim(delim, theLine) {{{2 +" Args: +function s:AddLeftDelim(delim, theLine) + return substitute(a:theLine, '^\([ \t]*\)', '\1' . a:delim, '') +endfunction + +" Function: s:AddLeftDelimAligned(delim, theLine) {{{2 +" Args: +function s:AddLeftDelimAligned(delim, theLine, alignIndx) + + "if the line is not long enough then bung some extra spaces on the front + "so we can align the delim properly + let theLine = a:theLine + if strlen(theLine) < a:alignIndx + let theLine = repeat(' ', a:alignIndx - strlen(theLine)) + endif + + return strpart(theLine, 0, a:alignIndx) . a:delim . strpart(theLine, a:alignIndx) +endfunction + +" Function: s:AddRightDelim(delim, theLine) {{{2 +" Args: +function s:AddRightDelim(delim, theLine) + if a:delim == '' + return a:theLine + else + return substitute(a:theLine, '$', a:delim, '') + endif +endfunction + +" Function: s:AddRightDelimAligned(delim, theLine, alignIndx) {{{2 +" Args: +function s:AddRightDelimAligned(delim, theLine, alignIndx) + if a:delim == "" + return a:theLine + else + + " when we align the right delim we are just adding spaces + " so we get a string containing the needed spaces (it + " could be empty) + let extraSpaces = '' + let extraSpaces = repeat(' ', a:alignIndx-strlen(a:theLine)) + + " add the right delim + return substitute(a:theLine, '$', extraSpaces . a:delim, '') + endif +endfunction + +" Function: s:AltMultipart() {{{2 +" returns 1 if the alternative delims are multipart +function s:AltMultipart() + return b:NERDRightAlt != '' +endfunction + +" Function: s:CanCommentLine(forceNested, line) {{{2 +"This function is used to determine whether the given line can be commented. +"It returns 1 if it can be and 0 otherwise +" +" Args: +" -forceNested: a flag indicating whether the caller wants comments to be nested +" if the current line is already commented +" -lineNum: the line num of the line to check for commentability +function s:CanCommentLine(forceNested, lineNum) + let theLine = getline(a:lineNum) + + " make sure we don't comment lines that are just spaces or tabs or empty. + if theLine =~ "^[ \t]*$" + return 0 + endif + + "if the line is part of a sexy comment then just flag it... + if s:IsInSexyComment(a:lineNum) + return 0 + endif + + let isCommented = s:IsCommentedNormOrSexy(a:lineNum) + + "if the line isnt commented return true + if !isCommented + return 1 + endif + + "if the line is commented but nesting is allowed then return true + if a:forceNested && (!s:Multipart() || g:NERDUsePlaceHolders) + return 1 + endif + + return 0 +endfunction + +" Function: s:CanPlaceCursor(line, col) {{{2 +" returns 1 if the cursor can be placed exactly in the given position +function s:CanPlaceCursor(line, col) + let c = col(".") + let l = line(".") + call cursor(a:line, a:col) + let success = (line(".") == a:line && col(".") == a:col) + call cursor(l,c) + return success +endfunction + +" Function: s:CanSexyCommentLines(topline, bottomline) {{{2 +" Return: 1 if the given lines can be commented sexually, 0 otherwise +function s:CanSexyCommentLines(topline, bottomline) + " see if the selected regions have any sexy comments + let currentLine = a:topline + while(currentLine <= a:bottomline) + if s:IsInSexyComment(currentLine) + return 0 + endif + let currentLine = currentLine + 1 + endwhile + return 1 +endfunction +" Function: s:CanToggleCommentLine(forceNested, line) {{{2 +"This function is used to determine whether the given line can be toggle commented. +"It returns 1 if it can be and 0 otherwise +" +" Args: +" -lineNum: the line num of the line to check for commentability +function s:CanToggleCommentLine(forceNested, lineNum) + let theLine = getline(a:lineNum) + if (s:IsCommentedFromStartOfLine(b:NERDLeft, theLine) || s:IsCommentedFromStartOfLine(b:NERDLeftAlt, theLine)) && !a:forceNested + return 0 + endif + + " make sure we don't comment lines that are just spaces or tabs or empty. + if theLine =~ "^[ \t]*$" + return 0 + endif + + "if the line is part of a sexy comment then just flag it... + if s:IsInSexyComment(a:lineNum) + return 0 + endif + + return 1 +endfunction + +" Function: s:ConvertLeadingSpacesToTabs(line) {{{2 +" This function takes a line and converts all leading tabs on that line into +" spaces +" +" Args: +" -line: the line whose leading tabs will be converted +function s:ConvertLeadingSpacesToTabs(line) + let toReturn = a:line + while toReturn =~ '^\t*' . s:TabSpace() . '\(.*\)$' + let toReturn = substitute(toReturn, '^\(\t*\)' . s:TabSpace() . '\(.*\)$' , '\1\t\2' , "") + endwhile + + return toReturn +endfunction + + +" Function: s:ConvertLeadingTabsToSpaces(line) {{{2 +" This function takes a line and converts all leading spaces on that line into +" tabs +" +" Args: +" -line: the line whose leading spaces will be converted +function s:ConvertLeadingTabsToSpaces(line) + let toReturn = a:line + while toReturn =~ '^\( *\)\t' + let toReturn = substitute(toReturn, '^\( *\)\t', '\1' . s:TabSpace() , "") + endwhile + + return toReturn +endfunction + +" Function: s:ConvertLeadingWhiteSpace(line) {{{2 +" Converts the leading white space to tabs/spaces depending on &ts +" +" Args: +" -line: the line to convert +function s:ConvertLeadingWhiteSpace(line) + let toReturn = a:line + while toReturn =~ '^ *\t' + let toReturn = substitute(toReturn, '^ *\zs\t\ze', s:TabSpace(), "g") + endwhile + + if !&expandtab + let toReturn = s:ConvertLeadingSpacesToTabs(toReturn) + endif + + return toReturn +endfunction + + +" Function: s:CountNonESCedOccurances(str, searchstr, escChar) {{{2 +" This function counts the number of substrings contained in another string. +" These substrings are only counted if they are not escaped with escChar +" Args: +" -str: the string to look for searchstr in +" -searchstr: the substring to search for in str +" -escChar: the escape character which, when preceding an instance of +" searchstr, will cause it not to be counted +function s:CountNonESCedOccurances(str, searchstr, escChar) + "get the index of the first occurrence of searchstr + let indx = stridx(a:str, a:searchstr) + + "if there is an instance of searchstr in str process it + if indx != -1 + "get the remainder of str after this instance of searchstr is removed + let lensearchstr = strlen(a:searchstr) + let strLeft = strpart(a:str, indx+lensearchstr) + + "if this instance of searchstr is not escaped, add one to the count + "and recurse. If it is escaped, just recurse + if !s:IsEscaped(a:str, indx, a:escChar) + return 1 + s:CountNonESCedOccurances(strLeft, a:searchstr, a:escChar) + else + return s:CountNonESCedOccurances(strLeft, a:searchstr, a:escChar) + endif + endif +endfunction +" Function: s:DoesBlockHaveDelim(delim, top, bottom) {{{2 +" Returns 1 if the given block of lines has a delimiter (a:delim) in it +" Args: +" -delim: the comment delimiter to check the block for +" -top: the top line number of the block +" -bottom: the bottom line number of the block +function s:DoesBlockHaveDelim(delim, top, bottom) + let currentLine = a:top + while currentLine < a:bottom + let theline = getline(currentLine) + if s:FindDelimiterIndex(a:delim, theline) != -1 + return 1 + endif + let currentLine = currentLine + 1 + endwhile + return 0 +endfunction + +" Function: s:DoesBlockHaveMultipartDelim(top, bottom) {{{2 +" Returns 1 if the given block has a >= 1 multipart delimiter in it +" Args: +" -top: the top line number of the block +" -bottom: the bottom line number of the block +function s:DoesBlockHaveMultipartDelim(top, bottom) + if s:HasMultipartDelims() + if s:Multipart() + return s:DoesBlockHaveDelim(b:NERDLeft, a:top, a:bottom) || s:DoesBlockHaveDelim(b:NERDRight, a:top, a:bottom) + else + return s:DoesBlockHaveDelim(b:NERDLeftAlt, a:top, a:bottom) || s:DoesBlockHaveDelim(b:NERDRightAlt, a:top, a:bottom) + endif + endif + return 0 +endfunction + + +" Function: s:Esc(str) {{{2 +" Escapes all the tricky chars in the given string +function s:Esc(str) + let charsToEsc = '*/\."&$+' + return escape(a:str, charsToEsc) +endfunction + +" Function: s:FindDelimiterIndex(delimiter, line) {{{2 +" This function is used to get the string index of the input comment delimiter +" on the input line. If no valid comment delimiter is found in the line then +" -1 is returned +" Args: +" -delimiter: the delimiter we are looking to find the index of +" -line: the line we are looking for delimiter on +function s:FindDelimiterIndex(delimiter, line) + + "make sure the delimiter isnt empty otherwise we go into an infinite loop. + if a:delimiter == "" + return -1 + endif + + + let l:delimiter = a:delimiter + let lenDel = strlen(l:delimiter) + + "get the index of the first occurrence of the delimiter + let delIndx = stridx(a:line, l:delimiter) + + "keep looping thru the line till we either find a real comment delimiter + "or run off the EOL + while delIndx != -1 + + "if we are not off the EOL get the str before the possible delimiter + "in question and check if it really is a delimiter. If it is, return + "its position + if delIndx != -1 + if s:IsDelimValid(l:delimiter, delIndx, a:line) + return delIndx + endif + endif + + "we have not yet found a real comment delimiter so move past the + "current one we are lookin at + let restOfLine = strpart(a:line, delIndx + lenDel) + let distToNextDelim = stridx(restOfLine , l:delimiter) + + "if distToNextDelim is -1 then there is no more potential delimiters + "on the line so set delIndx to -1. Otherwise, move along the line by + "distToNextDelim + if distToNextDelim == -1 + let delIndx = -1 + else + let delIndx = delIndx + lenDel + distToNextDelim + endif + endwhile + + "there is no comment delimiter on this line + return -1 +endfunction + +" Function: s:FindBoundingLinesOfSexyCom(lineNum) {{{2 +" This function takes in a line number and tests whether this line number is +" the top/bottom/middle line of a sexy comment. If it is then the top/bottom +" lines of the sexy comment are returned +" Args: +" -lineNum: the line number that is to be tested whether it is the +" top/bottom/middle line of a sexy com +" Returns: +" A string that has the top/bottom lines of the sexy comment encoded in it. +" The format is 'topline,bottomline'. If a:lineNum turns out not to be the +" top/bottom/middle of a sexy comment then -1 is returned +function s:FindBoundingLinesOfSexyCom(lineNum) + + "find which delimiters to look for as the start/end delims of the comment + let left = '' + let right = '' + if s:Multipart() + let left = s:GetLeft(0,0,1) + let right = s:GetRight(0,0,1) + elseif s:AltMultipart() + let left = s:GetLeft(1,0,1) + let right = s:GetRight(1,0,1) + else + return [] + endif + + let sexyComMarker = s:GetSexyComMarker(0, 1) + + "initialise the top/bottom line numbers of the sexy comment to -1 + let top = -1 + let bottom = -1 + + let currentLine = a:lineNum + while top == -1 || bottom == -1 + let theLine = getline(currentLine) + + "check if the current line is the top of the sexy comment + if currentLine <= a:lineNum && theLine =~ '^[ \t]*' . left && theLine !~ '.*' . right && currentLine < s:NumLinesInBuf() + let top = currentLine + let currentLine = a:lineNum + + "check if the current line is the bottom of the sexy comment + elseif theLine =~ '^[ \t]*' . right && theLine !~ '.*' . left && currentLine > 1 + let bottom = currentLine + + "the right delimiter is on the same line as the last sexyComMarker + elseif theLine =~ '^[ \t]*' . sexyComMarker . '.*' . right + let bottom = currentLine + + "we have not found the top or bottom line so we assume currentLine is an + "intermediate line and look to prove otherwise + else + + "if the line doesnt start with a sexyComMarker then it is not a sexy + "comment + if theLine !~ '^[ \t]*' . sexyComMarker + return [] + endif + + endif + + "if top is -1 then we havent found the top yet so keep looking up + if top == -1 + let currentLine = currentLine - 1 + "if we have found the top line then go down looking for the bottom + else + let currentLine = currentLine + 1 + endif + + endwhile + + return [top, bottom] +endfunction + + +" Function: s:GetLeft(alt, space, esc) {{{2 +" returns the left/left-alternative delimiter +" Args: +" -alt: specifies whether to get left or left-alternative delim +" -space: specifies whether the delim should be spaced or not +" (the space string will only be added if NERDSpaceDelims is set) +" -esc: specifies whether the tricky chars in the delim should be ESCed +function s:GetLeft(alt, space, esc) + let delim = b:NERDLeft + + if a:alt + if b:NERDLeftAlt == '' + return '' + else + let delim = b:NERDLeftAlt + endif + endif + if delim == '' + return '' + endif + + if a:space && g:NERDSpaceDelims + let delim = delim . s:spaceStr + endif + + if a:esc + let delim = s:Esc(delim) + endif + + return delim +endfunction + +" Function: s:GetRight(alt, space, esc) {{{2 +" returns the right/right-alternative delimiter +" Args: +" -alt: specifies whether to get right or right-alternative delim +" -space: specifies whether the delim should be spaced or not +" (the space string will only be added if NERDSpaceDelims is set) +" -esc: specifies whether the tricky chars in the delim should be ESCed +function s:GetRight(alt, space, esc) + let delim = b:NERDRight + + if a:alt + if !s:AltMultipart() + return '' + else + let delim = b:NERDRightAlt + endif + endif + if delim == '' + return '' + endif + + if a:space && g:NERDSpaceDelims + let delim = s:spaceStr . delim + endif + + if a:esc + let delim = s:Esc(delim) + endif + + return delim +endfunction + + +" Function: s:GetSexyComMarker() {{{2 +" Returns the sexy comment marker for the current filetype. +" +" C style sexy comments are assumed if possible. If not then the sexy comment +" marker is the last char of the delimiter pair that has both left and right +" delims and has the longest left delim +" +" Args: +" -space: specifies whether the marker is to have a space string after it +" (the space string will only be added if NERDSpaceDelims is set) +" -esc: specifies whether the tricky chars in the marker are to be ESCed +function s:GetSexyComMarker(space, esc) + let sexyComMarker = b:NERDSexyComMarker + + "if there is no hardcoded marker then we find one + if sexyComMarker == '' + + "if the filetype has c style comments then use standard c sexy + "comments + if s:HasCStyleComments() + let sexyComMarker = '*' + else + "find a comment marker by getting the longest available left delim + "(that has a corresponding right delim) and taking the last char + let lenLeft = strlen(b:NERDLeft) + let lenLeftAlt = strlen(b:NERDLeftAlt) + let left = '' + let right = '' + if s:Multipart() && lenLeft >= lenLeftAlt + let left = b:NERDLeft + elseif s:AltMultipart() + let left = b:NERDLeftAlt + else + return -1 + endif + + "get the last char of left + let sexyComMarker = strpart(left, strlen(left)-1) + endif + endif + + if a:space && g:NERDSpaceDelims + let sexyComMarker = sexyComMarker . s:spaceStr + endif + + if a:esc + let sexyComMarker = s:Esc(sexyComMarker) + endif + + return sexyComMarker +endfunction + +" Function: s:GetSexyComLeft(space, esc) {{{2 +" Returns the left delimiter for sexy comments for this filetype or -1 if +" there is none. C style sexy comments are used if possible +" Args: +" -space: specifies if the delim has a space string on the end +" (the space string will only be added if NERDSpaceDelims is set) +" -esc: specifies whether the tricky chars in the string are ESCed +function s:GetSexyComLeft(space, esc) + let lenLeft = strlen(b:NERDLeft) + let lenLeftAlt = strlen(b:NERDLeftAlt) + let left = '' + + "assume c style sexy comments if possible + if s:HasCStyleComments() + let left = '/*' + else + "grab the longest left delim that has a right + if s:Multipart() && lenLeft >= lenLeftAlt + let left = b:NERDLeft + elseif s:AltMultipart() + let left = b:NERDLeftAlt + else + return -1 + endif + endif + + if a:space && g:NERDSpaceDelims + let left = left . s:spaceStr + endif + + if a:esc + let left = s:Esc(left) + endif + + return left +endfunction + +" Function: s:GetSexyComRight(space, esc) {{{2 +" Returns the right delimiter for sexy comments for this filetype or -1 if +" there is none. C style sexy comments are used if possible. +" Args: +" -space: specifies if the delim has a space string on the start +" (the space string will only be added if NERDSpaceDelims +" is specified for the current filetype) +" -esc: specifies whether the tricky chars in the string are ESCed +function s:GetSexyComRight(space, esc) + let lenLeft = strlen(b:NERDLeft) + let lenLeftAlt = strlen(b:NERDLeftAlt) + let right = '' + + "assume c style sexy comments if possible + if s:HasCStyleComments() + let right = '*/' + else + "grab the right delim that pairs with the longest left delim + if s:Multipart() && lenLeft >= lenLeftAlt + let right = b:NERDRight + elseif s:AltMultipart() + let right = b:NERDRightAlt + else + return -1 + endif + endif + + if a:space && g:NERDSpaceDelims + let right = s:spaceStr . right + endif + + if a:esc + let right = s:Esc(right) + endif + + return right +endfunction + +" Function: s:HasMultipartDelims() {{{2 +" Returns 1 iff the current filetype has at least one set of multipart delims +function s:HasMultipartDelims() + return s:Multipart() || s:AltMultipart() +endfunction + +" Function: s:HasLeadingTabs(...) {{{2 +" Returns 1 if any of the given strings have leading tabs +function s:HasLeadingTabs(...) + for s in a:000 + if s =~ '^\t.*' + return 1 + end + endfor + return 0 +endfunction +" Function: s:HasCStyleComments() {{{2 +" Returns 1 iff the current filetype has c style comment delimiters +function s:HasCStyleComments() + return (b:NERDLeft == '/*' && b:NERDRight == '*/') || (b:NERDLeftAlt == '/*' && b:NERDRightAlt == '*/') +endfunction + +" Function: s:IsCommentedNormOrSexy(lineNum) {{{2 +"This function is used to determine whether the given line is commented with +"either set of delimiters or if it is part of a sexy comment +" +" Args: +" -lineNum: the line number of the line to check +function s:IsCommentedNormOrSexy(lineNum) + let theLine = getline(a:lineNum) + + "if the line is commented normally return 1 + if s:IsCommented(b:NERDLeft, b:NERDRight, theLine) || s:IsCommented(b:NERDLeftAlt, b:NERDRightAlt, theLine) + return 1 + endif + + "if the line is part of a sexy comment return 1 + if s:IsInSexyComment(a:lineNum) + return 1 + endif + return 0 +endfunction + +" Function: s:IsCommented(left, right, line) {{{2 +"This function is used to determine whether the given line is commented with +"the given delimiters +" +" Args: +" -line: the line that to check if commented +" -left/right: the left and right delimiters to check for +function s:IsCommented(left, right, line) + "if the line isnt commented return true + if s:FindDelimiterIndex(a:left, a:line) != -1 && (s:FindDelimiterIndex(a:right, a:line) != -1 || !s:Multipart()) + return 1 + endif + return 0 +endfunction + +" Function: s:IsCommentedFromStartOfLine(left, line) {{{2 +"This function is used to determine whether the given line is commented with +"the given delimiters at the start of the line i.e the left delimiter is the +"first thing on the line (apart from spaces\tabs) +" +" Args: +" -line: the line that to check if commented +" -left: the left delimiter to check for +function s:IsCommentedFromStartOfLine(left, line) + let theLine = s:ConvertLeadingTabsToSpaces(a:line) + let numSpaces = strlen(substitute(theLine, '^\( *\).*$', '\1', '')) + let delimIndx = s:FindDelimiterIndex(a:left, theLine) + return delimIndx == numSpaces +endfunction + +" Function: s:IsCommentedOuttermost(left, right, leftAlt, rightAlt, line) {{{2 +" Finds the type of the outtermost delims on the line +" +" Args: +" -line: the line that to check if the outtermost comments on it are +" left/right +" -left/right: the left and right delimiters to check for +" -leftAlt/rightAlt: the left and right alternative delimiters to check for +" +" Returns: +" 0 if the line is not commented with either set of delims +" 1 if the line is commented with the left/right delim set +" 2 if the line is commented with the leftAlt/rightAlt delim set +function s:IsCommentedOuttermost(left, right, leftAlt, rightAlt, line) + "get the first positions of the left delims and the last positions of the + "right delims + let indxLeft = s:FindDelimiterIndex(a:left, a:line) + let indxLeftAlt = s:FindDelimiterIndex(a:leftAlt, a:line) + let indxRight = s:LastIndexOfDelim(a:right, a:line) + let indxRightAlt = s:LastIndexOfDelim(a:rightAlt, a:line) + + "check if the line has a left delim before a leftAlt delim + if (indxLeft <= indxLeftAlt || indxLeftAlt == -1) && indxLeft != -1 + "check if the line has a right delim after any rightAlt delim + if (indxRight > indxRightAlt && indxRight > indxLeft) || !s:Multipart() + return 1 + endif + + "check if the line has a leftAlt delim before a left delim + elseif (indxLeftAlt <= indxLeft || indxLeft == -1) && indxLeftAlt != -1 + "check if the line has a rightAlt delim after any right delim + if (indxRightAlt > indxRight && indxRightAlt > indxLeftAlt) || !s:AltMultipart() + return 2 + endif + else + return 0 + endif + + return 0 + +endfunction + + +" Function: s:IsDelimValid(delimiter, delIndx, line) {{{2 +" This function is responsible for determining whether a given instance of a +" comment delimiter is a real delimiter or not. For example, in java the +" // string is a comment delimiter but in the line: +" System.out.println("//"); +" it does not count as a comment delimiter. This function is responsible for +" distinguishing between such cases. It does so by applying a set of +" heuristics that are not fool proof but should work most of the time. +" +" Args: +" -delimiter: the delimiter we are validating +" -delIndx: the position of delimiter in line +" -line: the line that delimiter occurs in +" +" Returns: +" 0 if the given delimiter is not a real delimiter (as far as we can tell) , +" 1 otherwise +function s:IsDelimValid(delimiter, delIndx, line) + "get the delimiter without the escchars + let l:delimiter = a:delimiter + + "get the strings before and after the delimiter + let preComStr = strpart(a:line, 0, a:delIndx) + let postComStr = strpart(a:line, a:delIndx+strlen(delimiter)) + + "to check if the delimiter is real, make sure it isnt preceded by + "an odd number of quotes and followed by the same (which would indicate + "that it is part of a string and therefore is not a comment) + if !s:IsNumEven(s:CountNonESCedOccurances(preComStr, '"', "\\")) && !s:IsNumEven(s:CountNonESCedOccurances(postComStr, '"', "\\")) + return 0 + endif + if !s:IsNumEven(s:CountNonESCedOccurances(preComStr, "'", "\\")) && !s:IsNumEven(s:CountNonESCedOccurances(postComStr, "'", "\\")) + return 0 + endif + if !s:IsNumEven(s:CountNonESCedOccurances(preComStr, "`", "\\")) && !s:IsNumEven(s:CountNonESCedOccurances(postComStr, "`", "\\")) + return 0 + endif + + + "if the comment delimiter is escaped, assume it isnt a real delimiter + if s:IsEscaped(a:line, a:delIndx, "\\") + return 0 + endif + + "vim comments are so fuckin stupid!! Why the hell do they have comment + "delimiters that are used elsewhere in the syntax?!?! We need to check + "some conditions especially for vim + if &filetype == "vim" + if !s:IsNumEven(s:CountNonESCedOccurances(preComStr, '"', "\\")) + return 0 + endif + + "if the delimiter is on the very first char of the line or is the + "first non-tab/space char on the line then it is a valid comment delimiter + if a:delIndx == 0 || a:line =~ "^[ \t]\\{" . a:delIndx . "\\}\".*$" + return 1 + endif + + let numLeftParen =s:CountNonESCedOccurances(preComStr, "(", "\\") + let numRightParen =s:CountNonESCedOccurances(preComStr, ")", "\\") + + "if the quote is inside brackets then assume it isnt a comment + if numLeftParen > numRightParen + return 0 + endif + + "if the line has an even num of unescaped "'s then we can assume that + "any given " is not a comment delimiter + if s:IsNumEven(s:CountNonESCedOccurances(a:line, "\"", "\\")) + return 0 + endif + endif + + return 1 + +endfunction + +" Function: s:IsNumEven(num) {{{2 +" A small function the returns 1 if the input number is even and 0 otherwise +" Args: +" -num: the number to check +function s:IsNumEven(num) + return (a:num % 2) == 0 +endfunction + +" Function: s:IsEscaped(str, indx, escChar) {{{2 +" This function takes a string, an index into that string and an esc char and +" returns 1 if the char at the index is escaped (i.e if it is preceded by an +" odd number of esc chars) +" Args: +" -str: the string to check +" -indx: the index into str that we want to check +" -escChar: the escape char the char at indx may be ESCed with +function s:IsEscaped(str, indx, escChar) + "initialise numEscChars to 0 and look at the char before indx + let numEscChars = 0 + let curIndx = a:indx-1 + + "keep going back thru str until we either reach the start of the str or + "run out of esc chars + while curIndx >= 0 && strpart(a:str, curIndx, 1) == a:escChar + + "we have found another esc char so add one to the count and move left + "one char + let numEscChars = numEscChars + 1 + let curIndx = curIndx - 1 + + endwhile + + "if there is an odd num of esc chars directly before the char at indx then + "the char at indx is escaped + return !s:IsNumEven(numEscChars) +endfunction + +" Function: s:IsInSexyComment(line) {{{2 +" returns 1 if the given line number is part of a sexy comment +function s:IsInSexyComment(line) + return !empty(s:FindBoundingLinesOfSexyCom(a:line)) +endfunction + +" Function: s:IsSexyComment(topline, bottomline) {{{2 +" This function takes in 2 line numbers and returns 1 if the lines between and +" including the given line numbers are a sexy comment. It returns 0 otherwise. +" Args: +" -topline: the line that the possible sexy comment starts on +" -bottomline: the line that the possible sexy comment stops on +function s:IsSexyComment(topline, bottomline) + + "get the delim set that would be used for a sexy comment + let left = '' + let right = '' + if s:Multipart() + let left = b:NERDLeft + let right = b:NERDRight + elseif s:AltMultipart() + let left = b:NERDLeftAlt + let right = b:NERDRightAlt + else + return 0 + endif + + "swap the top and bottom line numbers around if need be + let topline = a:topline + let bottomline = a:bottomline + if bottomline < topline + topline = bottomline + bottomline = a:topline + endif + + "if there is < 2 lines in the comment it cannot be sexy + if (bottomline - topline) <= 0 + return 0 + endif + + "if the top line doesnt begin with a left delim then the comment isnt sexy + if getline(a:topline) !~ '^[ \t]*' . left + return 0 + endif + + "if there is a right delim on the top line then this isnt a sexy comment + if s:FindDelimiterIndex(right, getline(a:topline)) != -1 + return 0 + endif + + "if there is a left delim on the bottom line then this isnt a sexy comment + if s:FindDelimiterIndex(left, getline(a:bottomline)) != -1 + return 0 + endif + + "if the bottom line doesnt begin with a right delim then the comment isnt + "sexy + if getline(a:bottomline) !~ '^.*' . right . '$' + return 0 + endif + + let sexyComMarker = s:GetSexyComMarker(0, 1) + + "check each of the intermediate lines to make sure they start with a + "sexyComMarker + let currentLine = a:topline+1 + while currentLine < a:bottomline + let theLine = getline(currentLine) + + if theLine !~ '^[ \t]*' . sexyComMarker + return 0 + endif + + "if there is a right delim in an intermediate line then the block isnt + "a sexy comment + if s:FindDelimiterIndex(right, theLine) != -1 + return 0 + endif + + let currentLine = currentLine + 1 + endwhile + + "we have not found anything to suggest that this isnt a sexy comment so + return 1 + +endfunction + +" Function: s:LastIndexOfDelim(delim, str) {{{2 +" This function takes a string and a delimiter and returns the last index of +" that delimiter in string +" Args: +" -delim: the delimiter to look for +" -str: the string to look for delim in +function s:LastIndexOfDelim(delim, str) + let delim = a:delim + let lenDelim = strlen(delim) + + "set index to the first occurrence of delim. If there is no occurrence then + "bail + let indx = s:FindDelimiterIndex(delim, a:str) + if indx == -1 + return -1 + endif + + "keep moving to the next instance of delim in str till there is none left + while 1 + + "search for the next delim after the previous one + let searchStr = strpart(a:str, indx+lenDelim) + let indx2 = s:FindDelimiterIndex(delim, searchStr) + + "if we find a delim update indx to record the position of it, if we + "dont find another delim then indx is the last one so break out of + "this loop + if indx2 != -1 + let indx = indx + indx2 + lenDelim + else + break + endif + endwhile + + return indx + +endfunction + +" Function: s:LeftMostIndx(countCommentedLines, countEmptyLines, topline, bottomline) {{{2 +" This function takes in 2 line numbers and returns the index of the left most +" char (that is not a space or a tab) on all of these lines. +" Args: +" -countCommentedLines: 1 if lines that are commented are to be checked as +" well. 0 otherwise +" -countEmptyLines: 1 if empty lines are to be counted in the search +" -topline: the top line to be checked +" -bottomline: the bottom line to be checked +function s:LeftMostIndx(countCommentedLines, countEmptyLines, topline, bottomline) + + " declare the left most index as an extreme value + let leftMostIndx = 1000 + + " go thru the block line by line updating leftMostIndx + let currentLine = a:topline + while currentLine <= a:bottomline + + " get the next line and if it is allowed to be commented, or is not + " commented, check it + let theLine = getline(currentLine) + if a:countEmptyLines || theLine !~ '^[ \t]*$' + if a:countCommentedLines || (!s:IsCommented(b:NERDLeft, b:NERDRight, theLine) && !s:IsCommented(b:NERDLeftAlt, b:NERDRightAlt, theLine)) + " convert spaces to tabs and get the number of leading spaces for + " this line and update leftMostIndx if need be + let theLine = s:ConvertLeadingTabsToSpaces(theLine) + let leadSpaceOfLine = strlen( substitute(theLine, '\(^[ \t]*\).*$','\1','') ) + if leadSpaceOfLine < leftMostIndx + let leftMostIndx = leadSpaceOfLine + endif + endif + endif + + " move on to the next line + let currentLine = currentLine + 1 + endwhile + + if leftMostIndx == 1000 + return 0 + else + return leftMostIndx + endif +endfunction + +" Function: s:Multipart() {{{2 +" returns 1 if the current delims are multipart +function s:Multipart() + return b:NERDRight != '' +endfunction + +" Function: s:NerdEcho(msg, typeOfMsg) {{{2 +" Args: +" -msg: the message to echo +" -typeOfMsg: 0 = warning message +" 1 = normal message +function s:NerdEcho(msg, typeOfMsg) + if a:typeOfMsg == 0 + echohl WarningMsg + echo 'NERDCommenter:' . a:msg + echohl None + elseif a:typeOfMsg == 1 + echo 'NERDCommenter:' . a:msg + endif +endfunction + +" Function: s:NumberOfLeadingTabs(s) {{{2 +" returns the number of leading tabs in the given string +function s:NumberOfLeadingTabs(s) + return strlen(substitute(a:s, '^\(\t*\).*$', '\1', "")) +endfunction + +" Function: s:NumLinesInBuf() {{{2 +" Returns the number of lines in the current buffer +function s:NumLinesInBuf() + return line('$') +endfunction + +" Function: s:ReplaceDelims(toReplace1, toReplace2, replacor1, replacor2, str) {{{2 +" This function takes in a string, 2 delimiters in that string and 2 strings +" to replace these delimiters with. +" +" Args: +" -toReplace1: the first delimiter to replace +" -toReplace2: the second delimiter to replace +" -replacor1: the string to replace toReplace1 with +" -replacor2: the string to replace toReplace2 with +" -str: the string that the delimiters to be replaced are in +function s:ReplaceDelims(toReplace1, toReplace2, replacor1, replacor2, str) + let line = s:ReplaceLeftMostDelim(a:toReplace1, a:replacor1, a:str) + let line = s:ReplaceRightMostDelim(a:toReplace2, a:replacor2, line) + return line +endfunction + +" Function: s:ReplaceLeftMostDelim(toReplace, replacor, str) {{{2 +" This function takes a string and a delimiter and replaces the left most +" occurrence of this delimiter in the string with a given string +" +" Args: +" -toReplace: the delimiter in str that is to be replaced +" -replacor: the string to replace toReplace with +" -str: the string that contains toReplace +function s:ReplaceLeftMostDelim(toReplace, replacor, str) + let toReplace = a:toReplace + let replacor = a:replacor + "get the left most occurrence of toReplace + let indxToReplace = s:FindDelimiterIndex(toReplace, a:str) + + "if there IS an occurrence of toReplace in str then replace it and return + "the resulting string + if indxToReplace != -1 + let line = strpart(a:str, 0, indxToReplace) . replacor . strpart(a:str, indxToReplace+strlen(toReplace)) + return line + endif + + return a:str +endfunction + +" Function: s:ReplaceRightMostDelim(toReplace, replacor, str) {{{2 +" This function takes a string and a delimiter and replaces the right most +" occurrence of this delimiter in the string with a given string +" +" Args: +" -toReplace: the delimiter in str that is to be replaced +" -replacor: the string to replace toReplace with +" -str: the string that contains toReplace +" +function s:ReplaceRightMostDelim(toReplace, replacor, str) + let toReplace = a:toReplace + let replacor = a:replacor + let lenToReplace = strlen(toReplace) + + "get the index of the last delim in str + let indxToReplace = s:LastIndexOfDelim(toReplace, a:str) + + "if there IS a delimiter in str, replace it and return the result + let line = a:str + if indxToReplace != -1 + let line = strpart(a:str, 0, indxToReplace) . replacor . strpart(a:str, indxToReplace+strlen(toReplace)) + endif + return line +endfunction + +"FUNCTION: s:RestoreScreenState() {{{2 +" +"Sets the screen state back to what it was when s:SaveScreenState was last +"called. +" +function s:RestoreScreenState() + if !exists("t:NERDComOldTopLine") || !exists("t:NERDComOldPos") + throw 'NERDCommenter exception: cannot restore screen' + endif + + call cursor(t:NERDComOldTopLine, 0) + normal! zt + call setpos(".", t:NERDComOldPos) +endfunction + +" Function: s:RightMostIndx(countCommentedLines, countEmptyLines, topline, bottomline) {{{2 +" This function takes in 2 line numbers and returns the index of the right most +" char on all of these lines. +" Args: +" -countCommentedLines: 1 if lines that are commented are to be checked as +" well. 0 otherwise +" -countEmptyLines: 1 if empty lines are to be counted in the search +" -topline: the top line to be checked +" -bottomline: the bottom line to be checked +function s:RightMostIndx(countCommentedLines, countEmptyLines, topline, bottomline) + let rightMostIndx = -1 + + " go thru the block line by line updating rightMostIndx + let currentLine = a:topline + while currentLine <= a:bottomline + + " get the next line and see if it is commentable, otherwise it doesnt + " count + let theLine = getline(currentLine) + if a:countEmptyLines || theLine !~ '^[ \t]*$' + + if a:countCommentedLines || (!s:IsCommented(b:NERDLeft, b:NERDRight, theLine) && !s:IsCommented(b:NERDLeftAlt, b:NERDRightAlt, theLine)) + + " update rightMostIndx if need be + let theLine = s:ConvertLeadingTabsToSpaces(theLine) + let lineLen = strlen(theLine) + if lineLen > rightMostIndx + let rightMostIndx = lineLen + endif + endif + endif + + " move on to the next line + let currentLine = currentLine + 1 + endwhile + + return rightMostIndx +endfunction + +"FUNCTION: s:SaveScreenState() {{{2 +"Saves the current cursor position in the current buffer and the window +"scroll position +function s:SaveScreenState() + let t:NERDComOldPos = getpos(".") + let t:NERDComOldTopLine = line("w0") +endfunction + +" Function: s:SwapOutterMultiPartDelimsForPlaceHolders(line) {{{2 +" This function takes a line and swaps the outter most multi-part delims for +" place holders +" Args: +" -line: the line to swap the delims in +" +function s:SwapOutterMultiPartDelimsForPlaceHolders(line) + " find out if the line is commented using normal delims and/or + " alternate ones + let isCommented = s:IsCommented(b:NERDLeft, b:NERDRight, a:line) + let isCommentedAlt = s:IsCommented(b:NERDLeftAlt, b:NERDRightAlt, a:line) + + let line2 = a:line + + "if the line is commented and there is a right delimiter, replace + "the delims with place-holders + if isCommented && s:Multipart() + let line2 = s:ReplaceDelims(b:NERDLeft, b:NERDRight, g:NERDLPlace, g:NERDRPlace, a:line) + + "similarly if the line is commented with the alternative + "delimiters + elseif isCommentedAlt && s:AltMultipart() + let line2 = s:ReplaceDelims(b:NERDLeftAlt, b:NERDRightAlt, g:NERDLPlace, g:NERDRPlace, a:line) + endif + + return line2 +endfunction + +" Function: s:SwapOutterPlaceHoldersForMultiPartDelims(line) {{{2 +" This function takes a line and swaps the outtermost place holders for +" multi-part delims +" Args: +" -line: the line to swap the delims in +" +function s:SwapOutterPlaceHoldersForMultiPartDelims(line) + let left = '' + let right = '' + if s:Multipart() + let left = b:NERDLeft + let right = b:NERDRight + elseif s:AltMultipart() + let left = b:NERDLeftAlt + let right = b:NERDRightAlt + endif + + let line = s:ReplaceDelims(g:NERDLPlace, g:NERDRPlace, left, right, a:line) + return line +endfunction +" Function: s:TabbedCol(line, col) {{{2 +" Gets the col number for given line and existing col number. The new col +" number is the col number when all leading spaces are converted to tabs +" Args: +" -line:the line to get the rel col for +" -col: the abs col +function s:TabbedCol(line, col) + let lineTruncated = strpart(a:line, 0, a:col) + let lineSpacesToTabs = substitute(lineTruncated, s:TabSpace(), '\t', 'g') + return strlen(lineSpacesToTabs) +endfunction +"FUNCTION: s:TabSpace() {{{2 +"returns a string of spaces equal in length to &tabstop +function s:TabSpace() + let tabSpace = "" + let spacesPerTab = &tabstop + while spacesPerTab > 0 + let tabSpace = tabSpace . " " + let spacesPerTab = spacesPerTab - 1 + endwhile + return tabSpace +endfunction + +" Function: s:UnEsc(str, escChar) {{{2 +" This function removes all the escape chars from a string +" Args: +" -str: the string to remove esc chars from +" -escChar: the escape char to be removed +function s:UnEsc(str, escChar) + return substitute(a:str, a:escChar, "", "g") +endfunction + +" Function: s:UntabbedCol(line, col) {{{2 +" Takes a line and a col and returns the absolute column of col taking into +" account that a tab is worth 3 or 4 (or whatever) spaces. +" Args: +" -line:the line to get the abs col for +" -col: the col that doesnt take into account tabs +function s:UntabbedCol(line, col) + let lineTruncated = strpart(a:line, 0, a:col) + let lineTabsToSpaces = substitute(lineTruncated, '\t', s:TabSpace(), 'g') + return strlen(lineTabsToSpaces) +endfunction +" Section: Comment mapping setup {{{1 +" =========================================================================== + +" switch to/from alternative delimiters +nnoremap NERDCommenterAltDelims :call SwitchToAlternativeDelimiters(1) + +" comment out lines +nnoremap NERDCommenterComment :call NERDComment(0, "norm") +vnoremap NERDCommenterComment :call NERDComment(1, "norm") + +" toggle comments +nnoremap NERDCommenterToggle :call NERDComment(0, "toggle") +vnoremap NERDCommenterToggle :call NERDComment(1, "toggle") + +" minimal comments +nnoremap NERDCommenterMinimal :call NERDComment(0, "minimal") +vnoremap NERDCommenterMinimal :call NERDComment(1, "minimal") + +" sexy comments +nnoremap NERDCommenterSexy :call NERDComment(0, "sexy") +vnoremap NERDCommenterSexy :call NERDComment(1, "sexy") + +" invert comments +nnoremap NERDCommenterInvert :call NERDComment(0, "invert") +vnoremap NERDCommenterInvert :call NERDComment(1, "invert") + +" yank then comment +nmap NERDCommenterYank :call NERDComment(0, "yank") +vmap NERDCommenterYank :call NERDComment(1, "yank") + +" left aligned comments +nnoremap NERDCommenterAlignLeft :call NERDComment(0, "alignLeft") +vnoremap NERDCommenterAlignLeft :call NERDComment(1, "alignLeft") + +" left and right aligned comments +nnoremap NERDCommenterAlignBoth :call NERDComment(0, "alignBoth") +vnoremap NERDCommenterAlignBoth :call NERDComment(1, "alignBoth") + +" nested comments +nnoremap NERDCommenterNest :call NERDComment(0, "nested") +vnoremap NERDCommenterNest :call NERDComment(1, "nested") + +" uncomment +nnoremap NERDCommenterUncomment :call NERDComment(0, "uncomment") +vnoremap NERDCommenterUncomment :call NERDComment(1, "uncomment") + +" comment till the end of the line +nnoremap NERDCommenterToEOL :call NERDComment(0, "toEOL") + +" append comments +nmap NERDCommenterAppend :call NERDComment(0, "append") + +" insert comments +inoremap NERDCommenterInInsert :call NERDComment(0, "insert") + + +function! s:CreateMaps(target, combo) + if !hasmapto(a:target, 'n') + exec 'nmap ' . a:combo . ' ' . a:target + endif + + if !hasmapto(a:target, 'v') + exec 'vmap ' . a:combo . ' ' . a:target + endif +endfunction + +if g:NERDCreateDefaultMappings + call s:CreateMaps('NERDCommenterComment', ',cc') + call s:CreateMaps('NERDCommenterToggle', ',c') + call s:CreateMaps('NERDCommenterMinimal', ',cm') + call s:CreateMaps('NERDCommenterSexy', ',cs') + call s:CreateMaps('NERDCommenterInvert', ',ci') + call s:CreateMaps('NERDCommenterYank', ',cy') + call s:CreateMaps('NERDCommenterAlignLeft', ',cl') + call s:CreateMaps('NERDCommenterAlignBoth', ',cb') + call s:CreateMaps('NERDCommenterNest', ',cn') + call s:CreateMaps('NERDCommenterUncomment', ',cu') + call s:CreateMaps('NERDCommenterToEOL', ',c$') + call s:CreateMaps('NERDCommenterAppend', ',cA') + + if !hasmapto('NERDCommenterAltDelims', 'n') + nmap ,ca NERDCommenterAltDelims + endif +endif + + + +" Section: Menu item setup {{{1 +" =========================================================================== +"check if the user wants the menu to be displayed +if g:NERDMenuMode != 0 + + let menuRoot = "" + if g:NERDMenuMode == 1 + let menuRoot = 'comment' + elseif g:NERDMenuMode == 2 + let menuRoot = '&comment' + elseif g:NERDMenuMode == 3 + let menuRoot = '&Plugin.&comment' + endif + + function! s:CreateMenuItems(target, desc, root) + exec 'nmenu ' . a:root . '.' . a:desc . ' ' . a:target + exec 'vmenu ' . a:root . '.' . a:desc . ' ' . a:target + endfunction + call s:CreateMenuItems("NERDCommenterComment", 'Comment', menuRoot) + call s:CreateMenuItems("NERDCommenterToggle", 'Toggle', menuRoot) + call s:CreateMenuItems('NERDCommenterMinimal', 'Minimal', menuRoot) + call s:CreateMenuItems('NERDCommenterNest', 'Nested', menuRoot) + exec 'nmenu '. menuRoot .'.To\ EOL NERDCommenterToEOL' + call s:CreateMenuItems('NERDCommenterInvert', 'Invert', menuRoot) + call s:CreateMenuItems('NERDCommenterSexy', 'Sexy', menuRoot) + call s:CreateMenuItems('NERDCommenterYank', 'Yank\ then\ comment', menuRoot) + exec 'nmenu '. menuRoot .'.Append NERDCommenterAppend' + exec 'menu '. menuRoot .'.-Sep- :' + call s:CreateMenuItems('NERDCommenterAlignLeft', 'Left\ aligned', menuRoot) + call s:CreateMenuItems('NERDCommenterAlignBoth', 'Left\ and\ right\ aligned', menuRoot) + exec 'menu '. menuRoot .'.-Sep2- :' + call s:CreateMenuItems('NERDCommenterUncomment', 'Uncomment', menuRoot) + exec 'nmenu '. menuRoot .'.Switch\ Delimiters NERDCommenterAltDelims' + exec 'imenu '. menuRoot .'.Insert\ Comment\ Here NERDCommenterInInsert' + exec 'menu '. menuRoot .'.-Sep3- :' + exec 'menu '. menuRoot .'.Help :help NERDCommenterContents' +endif +" vim: set foldmethod=marker : diff -r 1b7aa8ec75fb -r 309b770e8a8d vim/bundle/yankring/doc/yankring.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vim/bundle/yankring/doc/yankring.txt Wed Jun 09 17:53:48 2010 -0400 @@ -0,0 +1,1412 @@ +*yankring.txt* For Vim version 7.0. + +Author: David Fishburn August 29, 2009 +Version: 10.0 + +For instructions on installing this file, type + :help add-local-help |add-local-help| inside Vim. + + +============================================================================== +1. Contents *yankring* *yankring-contents* + + 1. Contents...............................: |yankring-contents| + 2. Description............................: |yankring-description| + 3. Configuration..........................: |yankring-configure| + 3.1 Global Variables...................: |yankring-globals| + 3.2 Default Keys.......................: |yankring-mappings| + 3.3 Customizing Maps...................: |yankring-custom-maps| + 4. Using the YankRing Window..............: |yankring-window| + 5. Commands...............................: |yankring-commands| + 5.1 YRToggle..........................: |YRToggle| + 5.2 YRClear...........................: |YRClear| + 5.3 YRShow............................: |YRShow| + 5.5 YRGetElem.........................: |YRGetElem| + 5.6 YRGetMultiple.....................: |YRGetMultiple| + 5.7 YRPush............................: |YRPush| + 5.8 YRPop.............................: |YRPop| + 5.9 YRYankCount.......................: |YRYankCount| + 5.10 YRYankRange.......................: |YRYankRange| + 5.11 YRDeleteRange.....................: |YRDeleteRange| + 5.12 YRPaste...........................: |YRPaste| + 5.13 YRReplace.........................: |YRReplace| + 5.14 YRMapsCreate......................: |YRMapsCreate| + 5.15 YRMapsDelete......................: |YRMapsDelete| + 5.16 YRSearch..........................: |YRSearch| + 5.17 YRRunAfterMaps....................: |yankring-custom-maps| + 6. Tutorial...............................: |yankring-tutorial| + 6.1 YRShow............................: |YRShow-example| + 6.2 YRReplace.........................: |YRReplace-example| + 6.3 YRPush............................: |YRPush-example| + 6.4 YRClear...........................: |YRClear-example| + 6.8 YRPop.............................: |YRPop-example| + 6.9 Visual modes......................: |yankring-visual-example| + 6.10 Using ranges......................: |YRYankRange-example| + 6.11 :global...........................: |global-example| + 6.12 YRSearch..........................: |YRSearch-example| + 7. History................................: |yankring-history| + +============================================================================== +2. Description *yankring-description* + +Vim already maintains a list of numbered registers containing the last 9 +deletes. These previous deletes can be referenced using [register]p, so +"1p will paste the last delete, "2p the 2nd last delete. For more +information see |quote_number|. + +Vim does not provide any mechanism to reference previously yanked text. +In Emacs this feature is called the "kill ring". + +The YankRing plugin allows the user to configure the number of yanked +and deleted text. After text has been pasted, it can be replaced with +a previous value from the yankring. + +As of version 3.0, the yankring's content will persist (by default) +between starting and stopping Vim. + +The plugin can be toggled on and off, and supports: + Ranges + Registers + Counts + All visual modes + All motions + All text-objects + +If you have any suggestions for the improvement of this plugin, see the +yankring.vim file for my email address. Suggestions / bug reports are +always welcome. + +For details on the changes between versions see |yankring-history|. + +============================================================================== +3. Configuration *yankring-configure* + +The YankRing allows the user to choose which keys are to be assigned to +the various commands. By default, the YankRing chose keys identical +with Vim's standard behaviour/keys. + +3.1 Global Variables *yankring-globals* + +You can customize the YankRing by setting various global variables in +your |.vimrc|. +> + yankring_max_history +< Default: 100 + Controls how many elements to save in the yankring. > + let g:yankring_max_history = 100 + yankring_min_element_length +< Default: 1 + If the yanked element has a length less than this value + if will not be added to the YankRing. This can be useful if + you want to bypass single letter deletes by adding the + following to your .vimrc: > + let g:yankring_min_element_length = 2 + yankring_max_element_length +< Default: 1048576 (1M) + Will truncate a new entry to the specified maximum. If + g:yankring_max_element_length is set to 0, there is no limit. > + let g:yankring_max_element_length = 4194304 " 4M + yankring_max_display +< Default: 500 + When the YankRing window is opened, each element is displayed on a + separate line. Since each yank could be very large, the display of + the element is limited to the above default. > + let g:yankring_max_display = 70 + yankring_enabled +< Default: 1 + If you do not want to YankRing enabled by default, set this + variable in your |vimrc|. > + let g:yankring_enabled = 0 " Disables the yankring + yankring_persist +< Default: 1 + If you have enabled the storing of global variables in the |viminfo| + file, the YankRing will be default persist the contents of the ring + between starting and stopping Vim. To disable this feature: > + let g:yankring_persist = 0 + yankring_share_between_instances +< Default: 1 + By default, any instance of Vim will share the same yankring + history file. But if want each instance to have their own history + you can set this option to 0. Setting g:yankring_persist = 0 and + g:yankring_share_between_instances = 0 will ensure no 2 instances + of Vim share the same YankRing history AND the history is not + remembered the next time Vim is started. > + let g:yankring_share_between_instances = 0 + yankring_dot_repeat_yank +< Default: Based on the Vim cpoption setting + By default Vim will not repeat (using '.') yanking of text. This can + be controlled via the |'cpoptions'| setting. The YankRing now respects + the cpoptions setting, if 'y' is included and you press '.', the + previous yank command is repeated and added to the yankring. + You can also add this behaviour by setting this in your |vimrc|: > + let g:yankring_dot_repeat_yank = 1 + yankring_ignore_duplicate +< Default: 1 + Duplicates will not be added to the YankRing by default. If a + duplicate is found, that element will be moved to the top of the + yankring. This can be controlled by setting this in your |vimrc|: > + let g:yankring_ignore_duplicate = 0 + yankring_map_dot +< Default: 1 + If the '.' (repeat) command should be mapped by the yankring. Since + most of the normal commands yy,dd,dw,... are mapped by the yankring, + if g:yankring_map_dot is false the . operator will not repeat these + operations. The YankRing tracks several of the internal Vim registers + and decides whether an action was the result of the YankRing or an + action outside of it. If the previous action was a result of the + yankring, it will be executed again. If it was an action outside of + the yankring, it asks Vim to repeat the command. > + let g:yankring_map_dot = 1 + yankring_paste_using_g +< Default: 1 + By default [p] and [P] are mapped to interact with the yankring. This + option controls whether [gp] and [gP] are also mapped. Setting this + option to 0 will not create these maps. > + let g:yankring_paste_using_g = 1 + yankring_window_use_separate +< Default: 1 + This is a new feature as of the 2.0 release. The YankRing now uses a + separate split buffer to display the yankring. There are many buffer + specific maps that allow you to operate over the various elements from + within the yankring. Setting this option to 0, uses the 1.0 + interface. > + let g:yankring_window_use_separate = 0 + yankring_window_auto_close +< Default: 1 + By default once you choose an option in the YankRing buffer, the + action is performed and the buffer window is closed, returning you to + the original buffer. This option can be toggled from within the + YankRing buffer by pressing [a]. The YankRing buffer status line + visually indicates where auto close is enabled or disabled. There are + many times where you need to paste (or delete) many items from the + yankring. Pressing [a], disables auto close, allows you to paste many + items, and finally you can press [a] to re-enable auto close, followed + by [q] to quit the buffer window. > + let g:yankring_window_auto_close = 1 + yankring_window_use_horiz +< Default: 1 + When the YankRing window is opened, it uses a horizontal split at the + bottom of the Vim window. It can optionally use a vertical split by + setting this option to 0. > + let g:yankring_window_use_horiz = 0 " Use vertical split + yankring_window_height +< Default: 1 + If using a horizontal split, this option controls how high to make + the window. > + let g:yankring_window_height = 8 + yankring_window_width +< Default: 1 + If using a vertical split, this option controls how wide to make the + window. > + let g:yankring_window_width = 30 + yankring_window_use_bottom +< Default: 1 + If using a horizontal split, this option control whether the window is + opened at the top or bottom of the Vim window. Setting this option to + 0 forces the window to open at the top of the Vim window. > + let g:yankring_window_use_bottom = 1 + yankring_window_use_right +< Default: 1 + If using a vertical split, this option control whether the window is + opened on the left or right side of the Vim window. To force the + window to open on the left side, set this option to 0. > + let g:yankring_window_use_right = 1 + yankring_window_increment +< Default: 1 + If using a vertical split the default width of the vertical window may + be too narrow to view enough of the elements. Pressing [] will + increase the size of the window by this number of columns. Pressing + [] again will toggle it back to the original size. > + let g:yankring_window_increment = 50 + yankring_manage_numbered_reg +< Default: 0 + Vim already maintains a list of numbered registers containing the last + yanked item and the previous 9 deletes. These items can be referenced + using [register]p, so "0p will paste the last yank, "1p will paste the + last delete, "2p the 2nd last delete. For more information see + |quote_number|. + If you wish the YankRing to maintain these numbered registers so + the top 10 elements in the YankRing are in the numbered reqisters 0-9 + you can put the following in your |vimrc| > + let g:yankring_manage_numbered_reg = 1 + yankring_ignore_operator +< Default: 'g~ gu gU ! = gq g? > < zf g@' + There are a number of Vim operations which do not change any + registers, and therefore should not be captured by the yankring. + This list is used to ignore the appropriate operators. + You can put the following in your |vimrc| > + let g:yankring_ignore_operator = 'g~ gu gU ! = gq g? > < zf g@' + yankring_history_dir +< Default: $HOME + The YankRing stores the text in a file. This global variable + allows you to customize where the file(s) will be stored. + You can put the following in your |vimrc| > + let g:yankring_history_dir = '$VIM' + yankring_history_file +< Default: 'yankring_history' + The history filename prefix can be controlled by setting this + variable. + You can put the following in your |vimrc| > + let g:yankring_history_file = 'my_yankring_history_file' + yankring_clipboard_monitor +< Default: 1 + When flipping between applications I find I often copy text + and attempt to use it inside of Vim. This is typically easy + by simply using "+p, but there are times when I will repeatedly + want to use the same text later on. By default, the YankRing + will detect when Vim regains focus and check if the clipboard + has changed since it last checked. If so, it will add the contents + of the clipboard to the YankRing. To disable this feature + you can put the following in your |vimrc| > + let g:yankring_clipboard_monitor = 0 + yankring_paste_check_default_buffer +< Default: 1 + If the default register has changed without the YankRing registering + the change the YankRing will paste the top item from the history + rather than what is currently in the default register. + This option allows you to control the behaviour. Plugins can + intentionally change the default buffer which the YankRing has + no way to noticing. To disable this feature you can put the following + in your |vimrc| > + let g:yankring_paste_check_default_buffer = 0 + +< +3.2 Default Keys *yankring-mappings* + +You can choose to override the default keys by creating these global +variables in your |vimrc|. +> + yankring_n_keys +< n - normal mode + Default Vim 7.2: + 'Y D x X' + Default Vim 7.1 and below: + 'x yy dd yw dw ye de yE dE yiw diw yaw daw y$ d$ Y D yG dG ygg dgg' + + With the introduction of some new features in Vim 7.2 it is no longer + necessary to list all cmds which the YankRing will act upon. + The yankring_n_keys only lists actions which an omap cannot be used. + Using the yankring_separator, the above list is parsed and + individual mappings are created. For each of the above normal + commands the YankRing will include the text those commands + acted upon. There are many ways to accomplish the same result + in Vim, if you do not see a common key stroke you regularly use + simply add the following to your |vimrc| with the additional + keys you wished mapped. > + let g:yankring_n_keys = 'Y D x X' + yankring_o_keys +< o - omap mode + Default: + Standard motions: 'b B w W e E d y $ G ;' + Vim text objects: ' iw iW aw aW as is ap ip a] a[ i] i[' + 'a) a( ab i) i( ib a> a< i> i< at it ' + 'a} a{ aB i} i{ iB a" a'' a` i" i'' i`' + + As of Vim 7.2 omaps are used to capture changes to the registers + in Vim. All of the standard motion commands are captured. + New to YankRing 5.0 all default Vim text objects are also + captured. + Using the yankring_separator, the above list is parsed and + individual mappings are created. For each of the above normal + commands the YankRing will include the text those commands + acted upon. There are many ways to accomplish the same result + in Vim, if you do not see a common key stroke you regularly use + simply add the following to your |vimrc| with the additional + keys you wished mapped. > + let g:yankring_o_keys = 'b B w W e E d y $ G ; iw iW aw aW' + yankring_zap_keys +< Default: 'f F t T / ?' + omaps are enough for most operations except for f and t. + These motions prompt the user for a character or string which + they should act upon. These must be treated as a special case + in YankRing. > + let g:yankring_zap_keys = 'f t' + yankring_ignore_operator +< Default: 'g~ gu gU ! = gq g? > < zf g@' + There are certain motions which do not update any registers + in Vim. If the registers are not changed, there is nothing + the YankRing can capture. This list instructs the YankRing + to ignore any action for these keys. > + let g:yankring_ignore_operator = 'g~ gu gU' + yankring_v_key +< v - visual mode + Default: y + Yanks visually select text. > + yankring_del_v_key +< n - normal mode + Default: d + The visually select text is included in the YankRing and deleted. > + yankring_paste_n_bkey +< n - normal mode + b - before + Default: P + The default Vim paste key will retrieve from the yankring. This + will paste the text BEFORE the current position. + There is a special check to see if the text in the default paste + register is the same as what is in the current position of the + yankring. If it is not, we assume the user used some other + mechanism to yank text (ie yt). If this is the case + we paste the text in the default paste buffer. Using the + text can be replaced with the current entry from the yankring. + Since there are many ways to do things in Vim, this provides + the best integration. > + yankring_paste_n_akey +< n - normal mode + a - after + Default: p + The default Vim paste key will retrieve from the yankring. This + will paste the text AFTER the current position. + There is a special check to see if the text in the default paste + register is the same as what is in the current position of the + yankring. If it is not, we assume the user used some other + mechanism to yank text (ie yt). If this is the case + we paste the text in the default paste buffer. Using the + text can be replaced with the current entry from the yankring. + Since there are many ways to do things in Vim, this provides + the best integration. > + yankring_paste_v_key +< n - normal mode + Default: p + This will replace the visually select text with the contents + from the yankring. See yankring_paste_n_akey for additional + details. > + yankring_replace_n_pkey +< n - normal mode + Default: + If you do not want to open the YankRing window to choose your + selection, then you can paste (as usual) then use a YankRing + mapping to cycle through items in the YankRing. This is especially + useful if you know you recently used the text you are looking for. + If you wish to cycle through the yankring, replacing the previously + pasted text with the previous yanked text you can repeatedly press + (or whatever keystroke you choose to map it to). This map + moves backwards through the yankring, so you will retrieve your + most recent yank. + + I prefer not to use since I like using that key to cycle + through all the matches in the QuickFix window. You can add + something similar to this in your |.vimrc| to get similar + functionality. + + On Windows use the ALT-< character to move through the YankRing. + To determine what character # these are go into insert mode + in a new buffer. Press CTRL-V then ALT and the < key. + Leave insert mode, move the cursor onto the character + and press ga. This will display the decimal, hex and octal + representation of the character. In this case it is 172. > + if has('win32') + let g:yankring_replace_n_pkey = '' + let g:yankring_replace_n_nkey = '' + " Instead map these keys to moving through items in the quickfix window. + nnoremap :cp + nnoremap :cn + endif +< Other users have also stated that this will work: > + let g:yankring_replace_n_pkey = '' + let g:yankring_replace_n_nkey = '' + yankring_replace_n_nkey +< n - normal mode + Default: + If you do not want to open the YankRing window to choose your + selection, then you can paste (as usual) then use a YankRing + mapping to cycle through items in the YankRing. This is especially + useful if you know you recently used the text you are looking for. + If you wish to cycle through the yankring, replacing the previously + pasted text with the next yanked text you can repeatedly press + (or whatever keystroke you choose to map it to). This map + moves forwards through the YankRing, so you will retrieve your + most recent yank. + + I prefer not to use since I like using that key to cycle + through all the matches in the QuickFix window. You can add + something similar to this in your |.vimrc| to get similar + functionality. + + On Windows use the ALT-> character to move through the YankRing. + To determine what character # these are go into insert mode + in a new buffer. Press CTRL-V then ALT and the > key. + Leave insert mode, move the cursor onto the character + and press ga. This will display the decimal, hex and octal + representation of the character. In this case it is 174. > + if has('win32') + let g:yankring_replace_n_pkey = '' + let g:yankring_replace_n_nkey = '' + " Instead map these keys to moving through items in the quickfix window. + nnoremap :cp + nnoremap :cn + endif +< Other users have also stated that this will work: > + let g:yankring_replace_n_pkey = '' + let g:yankring_replace_n_nkey = '' + +3.3 Customizing Maps *yankring-custom-maps* + +The YankRing plugin uses the yankring_n_keys global variable to create +a number of defaults maps. The maps are of the form: > + nnoremap Y :YRYankCount 'Y' +< +When capital Y is pressed, the YankRing will execute 'Y' and capture the +output from Vim. But there are cases where you do not want the default +behaviour of Vim, since you have customized some of these maps. + +In this case, I usually map Y to be |y$|, which makes it consistent with +the |D| and |C| operators. The way yankring_n_keys works does not allow +me to customize this behaviour. Since many people may like to customize +the behaviour of these maps the YankRing will check to see if a +function called YRRunAfterMaps() exists. If it does, it will call +this function after it has created the maps. So in my case, I created +the following function in my |vimrc|: > + function! YRRunAfterMaps() + nnoremap Y :YRYankCount 'y$' + endfunction +< +You can do anything you need in this function. > + nnoremap Y :YRYankCount 'y$' +< +This line remaps Y (which the user presses) to the YRYankCount command. The +YRYankCount tells Vim to execute y$ instead. + + +============================================================================== +4. Using the YankRing Window: *yankring-window* + +This is a new feature as of the 2.0 release. The YankRing uses a +separate split buffer to display the yankring. There are many buffer +specific maps that allow you to operate over the various elements from +within the yankring. + +To display the YankRing buffer you can issue the :YRShow command. For +convience you can map a key, , to this command: > + :nnoremap :YRShow + +Status line~ +The first line in the YankRing window is the status line. > + AutoClose=1;ClipboardMonitor=1;Cmds:p,P,d,r,s,a,c,u,q,,;Help=? +< +Help=?, pressing [?] will toggle the display of available commands the +yankring window supports. Pressing [?] again will remove the additional +items. + +AutoClose=1 indicates the window will close when an action is performed +against elements within the yankring. If you wish to perform multiple +yankring operations press [a] to toggle the auto close feature off. Use the +commands below and when finished you can press [a] to toggle auto close on and +press [q] to close the window. The Cmds displayed are simply reminders of +the available keys. + +ClipboardMonitor=1 indicates the YankRing will monitor the clipboard (+) +during Focus change events. If the clipboard has changed since the YankRing +last checked, the contents are added to the YankRing. Pressing [c] allows +you to quickly toggle this setting since it may not be useful at times. + +YankRing window key list~ +The following table lists the description of the keys that can be used +in the YankRing window. + + Key Description~ + p Puts text after the cursor. In visual mode, all elements + selected will be pasted. + P Puts text before the cursor. In visual mode, all elements + selected will be pasted. + gp Just like "p", but leave the cursor just after the new text. + gP Just like "P", but leave the cursor just after the new text. + Just like "p". + Just like "p". + <2-LeftMouse> Just like "p". Normal mode only. + d Removes the element from the yankring. In visual mode all + elements selected will be removed. + r Just like "p", but in visual mode if many lines are selected + it will paste these in reverse order. + s Prompts you for a regex to search the YankRing and display + only matching items. + a Toggles the g:yankring_window_auto_close setting. + u Updates the YankRing window. + q Closes the YankRing window. + Toggles the width of the vertical window by the + g:yankring_window_increment setting. + ? Toggles the display of the help. + + + + +============================================================================== +5. Commands: *yankring-commands* + +The predefined mappings call some specific commands with special parameters. +If you are going to create additional maps, it is important you mirror +the same parameters. Most of these commands have been made obsolete by +the YankRing window, since it incorporates the functionality below, but +through maps against a buffer, instead of commands. This makes it much easier +to use. + + +5.1 YRToggle *YRToggle* + Allows you to enable and disable the YankRing quickly. This + command will remove the default maps and recreate them. + + Examples: > + :YRToggle " Toggles it + :YRToggle 1 " Enables it + :YRToggle 0 " Disables it +< + +5.2 YRClear *YRClear* + Clears all elements from the yankring. + See also |YRClear-example|. + + +5.3 YRShow *YRShow* + Similar to |:register|, will display all the entries in the yankring. + The element at the top will be the next element pasted from the + yankring. + + Examples: > + :YRShow " Shows all entries in the yankring + + --- YankRing --- + Elem Content + 1 five^@ + 2 four^@ + 3 three^@ + 4 two^@ + 5 one^@ +< + +5.5 YRGetElem *YRGetElem* + This command has two modes. If no parameters are provided, it + becomes interactive. It uses YRShow to display the list and + allows you to choose which element to paste. If a parameter + is supplied it will paste that element from the yankring. If the + number specified is outside of the YankRing an error is returned. + You may want to create a separate mapping for this call. > + nnoremap yr :YRGetElem +< See also |YRSearch|. + + Examples: + Assume there are 10 elements in the YankRing and element 6 is + at the top of the ring. > + :YRGetElem " Interactive mode, you choose from a list + :YRGetElem 4 " Will paste element 5. + :YRGetElem 12 " Will paste element 6. + :YRGetElem 99 " Error, invalid choice is reported + :YRGetElem 0 " Error, invalid choice is reported + + +5.6 YRGetMultiple *YRGetMultiple* + Will paste many elements from the YankRing in one command. + If the number specified is 1 or less, it is assumed you want + just the current element pasted. If the number specified is + greater than or equal to the number of elements in the yankring, + it is assumed you want all elements pasted. If a ! is included + as part of the command, paste the items in reverse order. + See the |yankring-tutorial| for more examples. + + Examples: + Assume there are 10 elements in the YankRing. > + :YRGetMultiple 4 " Will paste elements 1,2,3,4 + :YRGetMultiple! 4 " Will paste elements 4,3,2,1 + :YRGetMultiple " Will paste element 1 + :YRGetMultiple 12 " Will paste elements 1,2,...,10 + :YRGetMultiple 99 " Will paste elements 1,2,...,10 + :YRGetMultiple 0 " Will paste element 1 + + +5.7 YRPush *YRPush* + Allows the user to "push" additional entries into the yankring. + If you yanked text via a key mapping which does not use the + YankRing (or there is text on the clipboard) you can use this + command to add the text to the yankring. + + Examples: > + :YRPush " Push the " register's contents + :YRPush '*' " Push the "* register's contents (clipboard) + :YRPush '+' " Push the "+ register's contents (clipboard) + :YRPush 'a' " Push the "a register's contents +< See also |YRPush-example|. + + +5.8 YRPop *YRPop* + Allows you to pop any elements from the yankring. If no parameters + are provided, the 1st element is removed from the yankring. The + command optionally takes a second parameter to specify how many + elements to pop. The default value is 1. + + Examples: > + :YRPop " Removes the highest numbered element from the + yankring + :YRPop 3 " Removes the 3rd element from the yankring + :YRPop 3,5 " Removes 5 elements from the YankRing beginning + at element 3 +< See also |YRPop-example|. + + +5.9 YRYankCount *YRYankCount* + This command has the most mappings created for it. If you are + in normal mode and you are not specifying a range, this command + will add the text to the yankring. + + The goal of this command is to allow the YankRing to be integrated + as seamlessly as possible with Vim. So it supports counts and + registers. If you create a mapping to it, you must pass as a + parameter the action you want Vim to perform. You could do the + following: > + nnoremap \test :YRYankCount 'dd' +< This map is executed when you hit the '\test' keystrokes, but + it will actually delete the current line and add it to the + yankring. + + The following are the default mappings: > + nnoremap yy :YRYankCount 'yy' + nnoremap dd :YRYankCount 'dd' + nnoremap yw :YRYankCount 'yw' + nnoremap dw :YRYankCount 'dw' + nnoremap ye :YRYankCount 'ye' + nnoremap de :YRYankCount 'de' + nnoremap yiw :YRYankCount 'yiw' + nnoremap diw :YRYankCount 'diw' + nnoremap Y :YRYankCount 'Y' + nnoremap D :YRYankCount 'D' + nnoremap y$ :YRYankCount 'y$' + nnoremap d$ :YRYankCount 'd$' + nnoremap yG :YRYankCount 'yG' + nnoremap dG :YRYankCount 'dG' +< + Examples: + yy - Adds the current line to the yankring. + dd - Adds the current line to the YankRing and deletes it. + 5yw - Adds 5 words to the yankring. + "ade - Deletes the word, and puts it into both the yankring + and the "a register. + 10"zyy - Places 10 lines into both the YankRing and the "z + register. + See also |yankring-tutorial|. + + +5.10 YRYankRange *YRYankRange* + This command by default is only called in visual mode. All + visual modes (|characterwise-visual|, |linewise-visual|, + |blockwise-visual|) are supported. Any visually selected text + is added to the yankring. You can also call this command + directly using a range. + + Examples: + Visual mode + ----------- + Press v (to enter visual mode), highlight want you want, + press y (to yank the selected area). + Repeat using V and Control-V. + + Normal mode + ----------- > + :5,20YRYankRange " Will yank lines 5-20 into the yankring + :5,20YRDeleteRange " Will delete lines 5-20 and add them to + the yankring +< See also |YRYankRange-example|. + + +5.11 YRDeleteRange *YRDeleteRange* + This command is identical to YRYankRange, except the range is + also deleted. + + +5.12 YRPaste *YRPaste* + This command will paste elements from the yankring. By default it has + been mapped to p and P to match Vim's native key strokes. The text + pasted is exactly what was yanked, including newline characters and + blockwise-visual mode behaviours. It supports counts and registers. + + Examples: + p " Paste the current element from the YankRing after the cursor + P " Paste the current element from the YankRing before the cursor + 5p " Paste the current element from the YankRing after the cursor + 5 times + "ap " Ignore the YankRing and paste the contents of register "a + 5"ap " Ignore the YankRing and paste the contents of register "a + 5 times + See also |yankring-tutorial|. + + +5.13 YRReplace *YRReplace* + The purpose of the YankRing is to gain access to previously yanked + (or deleted) elements. This command will replace the previously + paste with a different entry from the yankring. + By default, I choose (P for previous) to replace the last paste + while moving backwards through the yankring. (N for next) + replaces the last paste while moving forward through the yankring. + + Examples: + See the |yankring-tutorial| for examples. + + +5.14 YRMapsCreate *YRMapsCreate* + This public function is responsible for creating the maps which + enable the yankring. This function is called by the YRToggle + command. + + +5.15 YRMapsDelete *YRMapsDelete* + This public function removes the YankRing maps and disables + the yankring. This function is called by the YRToggle command. + + +5.16 YRSearch *YRSearch* + This command is similar to |YRGetElem|. The command takes + one parameter which is a regular expression. Similar to + YRGetElem, it will display all items in the YankRing that match + the regular expression. It is also interactive, and will + prompt you to enter which match you wish pasted. + See also |YRSearch-example|. + + +============================================================================== +6. Tutorial *yankring-tutorial* + +To understand how to use the yankring, the following example will +demonstrate the various features. Assume you have created the following +mapping: > + nnoremap :YRShow +< + Assume we have this buffer: > + one + two + three + four + five +< *YRShow-example* + Now yank (yy) each line separately starting at line 1. + Display the contents of the YankRing by executing the command + YRShow, or pressing . The contents of the YankRing is + displayed in a new buffer. The size, location and type of buffer + is configurable via various options. See section 3 for more details. > + :YRShow or F11 + --- YankRing --- + Elem Content + 1 five^@ + 2 four^@ + 3 three^@ + 4 two^@ + 5 one^@ +< Since we yanked the text starting at line 1 and finishing at + line 5, the most current YankRing element is the last one, the + contents of line 5. "five^@" is displayed, the "^@" is a + newline character (since we issued a "yy"). + + *yankring-window-example* + At this point, you have two options. You can choose which element + from the YankRing you wish to paste and press or 'p' or 'P' + and a variety of other options, see |yankring-window|. After pressing + the key, the YankRing window will close (default behaviour). Pressing + '?' will display additional help for the commands that are active within + the YankRing window. Pressing '?' will toggle the help. + + You do not need to interact with the YankRing using the YankRing window. + Using the window makes many tasks must easier, but for speed using some + of the other maps can be preferrable if you know what you have yanked / + deleted recently. It was designed to work with Vim in the usual manner. + You can press, 'p', to paste the last item in yanked or deleted. + + Close the YankRing window by pressing 'q' or F11 (which toggles it). + + *YRReplace-example* + Now, go to the end of the file and press 'p'. The resulting + buffer appears as: > + one + two + three + four + five + five +< + Assume you did not want 'five", but a different entry from within the + yankring. moves backwards through the yankring, it will replace + the previous pasted text with a different item from the yankring. This + allows you to quickly iterate through different elements. is the + default mapping, this can be user defined. See the following options for + more details: > + yankring_replace_n_nkey, yankring_replace_n_pkey +< + After pressing the buffer results in: > + one + two + three + four + five + four +< Now press 2. This would be the same as pressing + two times in a row. This results in: > + one + two + three + four + five + two +< Now press to move forwards through the yankring, + this results in: > + one + two + three + four + five + three +< Display the contents of the yankring. > + :YRShow + --- YankRing --- + Elem Content + 1 five^@ + 2 four^@ + 3 three^@ + 4 two^@ + 5 one^@ +< + Now lets yank some text with a key stroke that has not been + mapped to the yankring. Place your cursor at the start of + line 4. Press 'ytr', yank-to-(to the character r), which yanks + the 'fou' letters (no newline character). Now press p. Here is + the result: > + one + two + three + ffouour + five + three +< This is good, even though the keys 'ytr' has not been mapped + to YRYankCount, the YankRing still pasted the most recently + yanked text. Since the text did not have a newline character + the 'fou' was inserted after the 'f'. + + Now replace that previous paste with the current element from + the YankRing by pressing . This is the result: > + one + two + three + four + one + five + three +< The #1 entry in the YankRing is still the line "five@". When + choosing the next entry, it wraps around to the last entry in + the yankring, element #5. The 'fou' was replaced with 'one^@'. + Since it had a newline character in it (when it was yanked) the + newline is included when it is pasted. + + *YRPush-example* + Assume you need to paste text from the system clipboard, and this + is text you will need routinely. We can simulate this by running + this command (see |quote+|): > + :let @+ = "From the clipboard\n" + :echo @+ + +< With the cursor at the start of the line with the word 'five', press 'p'. + We still have pasted the 'fou' which is in the default paste buffer. > + one + two + three + four + two + ffouive + three +< We have the option of getting the text from the clipboard directly + with the following. > + First undo the previous change - u + Next - "+p +< The line changes since we bypassed the yankring, and specified + which register to get the text from: > + four + five + From the clipboard + three +< replaces this with the #1 entry in the yankring: > + four + five + five + three +< Now add the contents of the clipboard to the yankring: > + :YRPush '+' +< Move the cursor to the last row 'three' and press 'p'. The result is: > + four + five + one + three + From the clipboard +< YRPush '+' adds the value of the register '+' to the yankring, but it + also adds its contents to the default Vim paste buffer. So pressing + 'p' pasted this text. Adding a new value to the YankRing we have + repositioned it which you can see with: > + :YRShow or F11 + --- YankRing --- + Elem Content + 1 From the clipboard^@ + 2 five^@ + 3 four^@ + 4 three^@ + 5 two^@ + 6 one^@ +< *YRClear-example* + Now we will clear the yankring, and begin over again. Delete all lines + from the buffer and replace them with the original rows: > + one + two + three + four + five +< Now run this command to clear the YankRing to start over: > + :YRClear +< + Issue a 'yy' on each of the 5 lines. If you run the YRShow command you + should see the following: > + :YRShow or F11 + --- YankRing --- + Elem Content + 1 five^@ + 2 four^@ + 3 three^@ + 4 two^@ + 5 one^@ +< *any-item-example* + If you need to quickly browse the YankRing to determine which element you + wish to paste you can simply press 'p' or or on any element + displayed in the YankRing window. Press '?' for more detailed description + of the commands available. + + Using the YankRing window can be much faster if you do not want to cycle + through the YankRing using and to find the element. + + *multiple-items-example* + There are times when you need to move through a buffer capturing many + different lines (or snippets of code) and eventually want to switch + buffers and paste these elements. With some advance planning you can do + this without the YankRing by issuing commands of the form: > + "ayy + "Ayy +< When specifying the register using UPPERCASE, Vim appends the yanked text + to register "a, instead of replacing it. Many times you forget the + advance planning (or didn't even know about this great feature) you can + use the YankRing window to do this easily. If this is the current + yankring: > + :YRShow or F11 + --- YankRing --- + Elem Content + 1 five^@ + 2 four^@ + 3 three^@ + 4 two^@ + 5 one^@ +< The YankRing works in |visual-mode|. To demonstrate move the cursor in + the buffer to the line with 'two'. Press 'F11' to display the yankring + window. Move the cursor to element 2, press 'V' to enable + |linewise-visual| mode and then press 'j' twice. This should have + visually highlighted elements 2,3,4. Press 'p' to paste all the + highlighted elements: > + one + two + four + three + two + three + four + five +< You can see here it has pasted four, three, two after the second line of + the buffer. Now press 'u' to undo our last change. Leave the cursor + on the second line 'two'. Press 'F11' to show the YankRing again. + Visually select the same lines, but this time press 'r' instead of 'p'. + 'r' is for reverse, so it will paste the following: > + one + two + two + three + four + three + four + five +< + *YRGetMultiple-example* + The same behaviour listed above (by visually selecting items in the + YankRing window) can be achieved using the YRGetMultiple command. + Assume there are 10 elements in the YankRing. > + :YRGetMultiple 4 " Will paste elements 1,2,3,4 + :YRGetMultiple! 4 " Will paste elements 4,3,2,1 + :YRGetMultiple " Will paste element 1 + :YRGetMultiple 12 " Will paste elements 1,2,...,10 + :YRGetMultiple 99 " Will paste elements 1,2,...,10 + :YRGetMultiple 0 " Will paste element 1 +< + *YRSearch-example* + The default size of the YankRing is 100 elements. It can be + tedious searching through the YankRing to find the element you + need. YRSearch is similar to YRShow except it will limit the + items displayed to only those items matching the regex provided. > + :YRShow + --- YankRing --- + Elem Content + 1 Three Mississippi + 2 Two Mississippi + 3 One Mississippi + 4 @", '\\/.*$^~[]' ) + :YRSearch Mississippi + --- YankRing --- + Elem Content + 1 Three Mississippi + 2 Two Mississippi + 3 One Mississippi +< Consider some items which require escaping the search string: > + :YRSearch @", '\\ + --- YankRing --- + Elem Content + 1 @", '\\/.*$^~[]' ) +< Forward slashes and various other symbols require escapes, in this + case the slash was not escaped enough: > + :YRSearch @", '\\/ + --- YankRing --- + Elem Content +< There are enough escapes this time: > + :YRSearch @", '\\\\/ + --- YankRing --- + Elem Content + 1 @", '\\/.*$^~[]' ) +< Period, star, dollar and so on require one slash: > + :YRSearch @", '\\\\/\.\*\$\^\~\[\] + --- YankRing --- + Elem Content + 1 @", '\\/.*$^~[]' ) + +< *YRPop-example* + You can remove any element from the YankRing by pressing pressing 'd' from + within the YankRing window. Visual mode is also supported to remove more + than one element at a time. > + :YRShow + --- YankRing --- + Elem Content + 1 four^@ + 2 three^@ + 3 two^@ + 4 one^@ +< Visually select elements 2,3. Press 'd', the result is: > + :YRShow + --- YankRing --- + Elem Content + 1 four^@ + 2 one^@ + +< *yankring-visual-example* + There are 3 visual modes and all are supported. Any visually selected + text is added to the yankring. You can try the various modes. Move + the cursor to inside the buffer (not the YankRing window). + + |characterwise-visual| + Go to line 1, press 'v' and move using the cursor keys until you have + highlighted some text. Then press y to yank the visually selected + area. Pressing p with paste the yanked region. + + |linewise-visual| + Go to line 2, press 'V' and move using the cursor keys until you have + highlighted some text. Notice the entire line is selected (including + the carriage returns). Then press y to yank the visually selected + area. Pressing p with paste the yanked region. + + |blockwise-visual| + Go to line 3 column 4, press CTRL-V and move to the right using the + cursor keys until you have highlighted some text. Then press y to + yank the visually selected area. Pressing p with paste the yanked + region. Notice the pasted text retained its blockwise visual + characteristics. + + *YRYankRange-example* + YRYankRange is called during visual modes, but it is also possible to + use this via the command line. > + :1,4YRYankRange + :3,$YRDeleteRange + :YRShow +< + *global-example* + Using Vim's |:global| command can be very useful at times. The example + adds all rows (in a buffer) to the YankRing if they have a certain + phrase: > + :g/addme/YRYankCount 'yy' +< This is the breakdown for the above command: > + :g - for each line in the buffer + /addme - check if the string "addme" is in the line + /YRYankCount 'yy' - Ask the YankRing to execute the 'yy' command + + +============================================================================== +7. History *yankring-history* + + 10.0: January 31, 2010 + NF: Change the buffer name to [YankRing] to resemble other + non-user buffers. + NF: Added g:yankring_min_element_length which can prevent + items from being added to the YankRing if they are too small. + For example, single character deletes (Vedran M). + BF: When shifting focus back to Vim, the YankRing may incorrectly + report: "YR:Failed to change to the yankring buffer, + please contact author". + BF: When entering Vim for the first time and hitting "p" + nothing was pasted (Mark Huiskes). + BF: When entering Vim for the first time and the + yankring_clipboard_monitor = 1, the clipboard entry + was not automatically added to the yankring. + BF: When overriding the default and setting + g:yankring_window_use_bottom = 0, the YankRing would + report the error (Sergey Khorev): + E21: Cannot make changes, 'modifiable' is off + + 9.0: August 29, 2009: + BF: You cannot execute a macro with ":normal @a". It is still + not possible, but you can execute it with ":normal! @a" + (A S Budden). + BF: When g:yankring_persist = 0 the YankRing could go into + an infinite loop (A S Budden). + BF: When replaying a macro which used any of the zap + keys (f,F,t,T,/,?) you were prompted again for the + string to match on (Ovidiu C). + BF: When checking the clipboard for changes + (g:yankring_clipboard_monitor == 1) only add the item + if it is not already in the ring. Previously, the item + was moved to the top of the YankRing each time you flipped + focus. + + 8.0: December 21, 2008: + NF: Changed the implementation of YRGetSearch() (David Liang). + BF: Under some unknown circumstances, the yankring can fail + to change to the correct buffer. Put in code to double + check and abort. + BF: Yanking and pasting a line which ends in a backslash + resulted in the backslash being replaced by "@@@". + BF: When repeating a command (".") which used any of the zap + keys (f,F,t,T,/,?) you were prompted again for the + string to match on (Vasilii Pascal). + + 7.0: November 14, 2008: + NF: Added support for the / and ? motions so that y/search is + supported (Vasilii Pascal). + NF: When the YankRing window is displayed (or updated) an additional + check is made against the default register. If it has changed + since the YankRing recorded it, the value will be added to the + history. + NF: Added support for more motions h, j, k, l, H, M, L, ^, 0, -, +, _. + And a pile of g motions g_, g^, gm, g$, gk, gj, gg, ge, gE. + NF: The YankRing window will display a message it is operating + in a limited mode if not using Vim 7.2 or the correct patch + level. + BF: Correction to some internal code which could lead to an + endless loop (John Beckett). + BF: Opening and closing the YankRing window with "set report=0" + reported "1 line less" messages (Bill McCarthy). + BF: Changed the default value of g:yankring_paste_check_default_buffer + to check if the default paste buffer has changed when pressing + 'p'. For example, if a plugin has changed the default registers + it will be pasted rather than the top item from the YankRing. + BF: YRMapsDelete did not remove all the maps created by the YankRing. + BF: Under particular circumstances, yanking text with embedded @ + characters were not properly stored and retrieved from the + YankRing (Andrew Long). + BF: Changed to use xmaps instead of vmaps so that the maps only work + in visual mode and not select mode (David Liang). + + 6.1: October 31, 2008: + BF: If the g:yankring_history_dir contains spaces (default on + Windows) an error was reported. A simple work around was to + let g:yankring_history_dir = 'c:\Vim' or no spaces (Matt). + + 6.0: October 25, 2008: + NF: The YankRing now maintains the history in a file. This means + if you are running multiple instances of Vim, they all see + the same yankring. + NF: The location and name of the file is configurable by the user. + NF: The g:yankring_separator is no longer used and has been removed. + NF: The g:yankring_max_element_length can be used to limit the size + of an element in the yankring. + NF: The g:yankring_share_between_instances can be used to indicate + whether each instance of Vim running on a machine should share + the history file or whether each should have their own + individual history file. + NF: The g:yankring_clipboard_monitor can be used to indicate + whether changes to the system clipboard should be added to the + YankRing (default is on). + NF: The YankRing window can toggle the clipboard monitor by pressing + 'c'. See the help in the window by pressing ?. + NF: Added some highlighting to the YankRing window (Marty Grenfell). + + 5.0: September 21, 2008: + NF: The YankRing can recognize certain Vim commands which do not + change the contents of a buffer and not attempt to capture it. + NF: The global variables which allow you to customize the behaviour + are now space separated instead of comma separated. This + provides greater flexibility but will require you to modify + your vimrc (if you have customized it). (Andy Wokula) + BF: If using from within insert mode, the YankRing inserted + characters into the buffer instead of capturing the changes, + this was fixed by Andy Wokula (Agathoklis Hatzimanikas). + BF: The YankRing did not properly account for all the different + forms of counts "5yy" worked but "y5y" did not (Edwin Shao). + + 4.1: August 9, 2008: + NF: The YankRing now allows you to override which operators should + be ignored (yankring_ignore_operator). By default this is + set for the standard Vim operators which do not modify any + registers (Examples: = and gu) (Andy Wokula). + NF: The YankRing did not map v_x (Matt Tolton). + BF: The expression register (quote=) was not accounted for correctly + (Agathoklis Hatzimanikas). + BF: Using the v:operator variable must be escaped when used in + a regular expression. + + 4.0: June 24, 2008: + NF: The YankRing by default now captures all |text-objects| and + all motions (|motion.txt|) which Vim supports. Version 3.0 only + supported a subset of the basic motion commands. + NF: Prior to this version only predefined maps triggered the + capture of data into the yankring. These maps only supported + yanks and deletes. The YankRing now also supports + operator-pending mode, which allows a greater range of operations + to be automatically captured and added to the yankring. + Operating pending mode functionality requires Vim 7.2 or Vim 7.1 + with patch #205. If using Vim 7.1 you can determine this with: + echo has("patch205") + NF: Prior to this version only yanks and deletes were registered + in the yankring. Changes are now also captured into the + yankring. + NF: The YankRing will also capture the system cliboard when focus is + returned to the vim window. This is useful if you copy text + between applications. + NF: The YankRing window always opened bottom horizontal. Now it + can be opened top or bottom and horizontal or vertically. + This can be controlled via variables in your .vimrc. + BF: The YankRing has an option to persist between instances + of Vim by storing the values in global variables within + the viminfo. This has led to some unusual ordering of + items in the ring from conflicts between instances. + This option has been turn off by default. + BF: Their was an issue with yanking using y$. + + 3.1: September 10, 2007: + NF: YRClear will now unlet all global variables it uses to store + the data if the persist storage is specified (the default). + Large values in the viminfo file could possibly affect other + applications. + + 3.0: September 7, 2007: + NF: Converted the YankRing to use the new Vim7's List object which + means it is no longer compatible with Vim6. + NF: By default the YankRing will now maintain the yankring's items + persistently by default. It does this via the |viminfo| file. + This means the contents of the YankRing rely on the internal + variables of only 1 Vim instance. + BF: YRToggle was not unmapping 'gp' and 'gP'. + BF: YRSearch prompted the user for a regex even if one was provided + on the command line. + BF: If g:yankring_manage_numbered_reg is enabled, the "." operator + did not correctly repeat the previous action (Pedro DeRose). + + 2.2: November 1, 2005: + NF: Added 'x' to the list of yankring_n_keys. This is very useful + in visual mode since it can delete a lot of characters. + + 2.2: October 19, 2005: + BF: If you pressed '?' to toggle the display of the help in the + YankRing window, the window would close. This also applied to + 'a', which allowed you to toggle the autoclose feature. + + 2.1: October 11, 2005: + NF: Added the ability for the YankRing to override Vim's numbered + registers. Instead of the numbered registers holding the last + yanked value, and the 9 previous deletes, they will now reflect + the top 10 items in the yankring. This allows you to reference + them directly with "5p. + + 2.0: August 20, 2005: + NF: Much improved usability, the YankRing now has a "GUI" to service + the yankring. If YRShow or YRSearch is used, a split buffer is + opened which displays all the elements in the yankring. There + are a number of maps that allow you to interact with the + contents. The window can be positioned vertically or + horizontally as well as being sized all through options + specified in your vimrc. + NF: YRPop can now delete any items from the yankring, rather + that just from the top. + NF: YRSetTop has been removed, it is no longer required as the + internal representation of the YankRing has changed. + BF: If g:yankring_ignore_duplicate is set (which is the default) + you could get some unpredicable results when moving + backwards and forwards ( and ) through the + previous values. + + 1.7: June 10, 2005: + BF: The expression register support added in version 1.6 used + getreg('='), which has the side effect of executing the + expression register. Depending on what was in the register + this could have unusual results. Changed to use histget(). + + 1.6: April 20, 2005: + NF: YRSearch is similar to YRGetElem. Given a regular expression + it will interactively display all the elements in the yankring + that match the regular expression. You can enter the number + of the element to paste it. If you have many elements within + the yankring, this can help you identify them more easily. + NF: Updated the default history size from 30 to 100, which is + partially the reason for the YRSearch command. + NF: By default it supports "gp" and "gP", in addition to "p" and "P". + NF: Added support for the expression register (:h quote=). Here + is an example of how it is used: + "="X"P + + 1.5: March 30, 2005: + NF: The YankRing now respects the cpoptions setting, if 'y' is + included and you press '.', the previous yank command is executed + and added to the yankring. You can also add this behaviour by + setting this in your |vimrc|: > + let g:yankring_dot_repeat_yank = 1 +< NF: Duplicates will not be added to the YankRing by default. If + a duplicate is found, the element will be moved to the top + of the yankring. This can be controlled by setting this in + your |vimrc|: > + let g:yankring_ignore_duplicate = 0 (1 is default) +< BF: Regression from version 1.4, the '.' operator may incorrectly + insert garbage. + + 1.4: March 28, 2005: + NF: YRToggle has been updated. If you toggle the YankRing off + (disable) the maps it creates are removed. Calling YRToggle + again will recreate the maps. This truly disables the yankring, + where the previous version attempted to do this via code. + BF: Using the '.' operator was not correctly replaying operations + that did not move text in some way (g~t_) changed the case + of the text but a '.' did not replay it. + BF: When replacing previously pasted text the YankRing did not + respect what key was used to paste the text originally. + All replaced items were pasted using 'p', even if you had + originally pasted the text with 'P'. + + 1.3: March 16, 2005: + BF: The '.' operator did not handle the <<, >> shift operator. + Pressing '.' would result in the previous YankRing operation + instead of repeating the shift. + + 1.2: March 14, 2005: + NF: Added support for '.' operator to repeat the last change. + NF: Changed YRGetElem to show the contents of the yankring + and allow you to choose which element you want pasted. + It is only interactive if you do not provide a parameter. + NF: Added 'ygg,dgg' default maps by extending the yankring_n_keys + variable. + + 1.1: March 09, 2005: + NF: Added support for the black hole register |quote_|. + NF: Custom Maps allows the user to more finely tune the yankring + maps to perform whatever action they require. This function, + YRRunAfterMaps(), is run automatically after the YankRing + creates it's default mappings. See |yankring-custom-maps|. + NF: Added some more default maps by extending the yankring_n_keys + variable. It now contains: + yy,dd,yw,dw,ye,de,yE,dE,yiw,diw,yaw,daw,y$,d$,Y,D,yG,dG + NOTE: You can easily extend these default mappings by + creating this global variable in your |vimrc|, you do not + have to wait for the plugin to be updated. + NF: Added support for Dr. Chips GetLatestVimScripts plugin. + BF: The check for g:yankring_n_keys was incorrect, so it was not + possible to override the default maps. + + 1.0: March 08, 2005: + NF: Initial release. + +vim: ts=4 ft=help tw=78 diff -r 1b7aa8ec75fb -r 309b770e8a8d vim/bundle/yankring/plugin/yankring.vim --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vim/bundle/yankring/plugin/yankring.vim Wed Jun 09 17:53:48 2010 -0400 @@ -0,0 +1,2461 @@ +" yankring.vim - Yank / Delete Ring for Vim +" --------------------------------------------------------------- +" Version: 10.0 +" Authors: David Fishburn +" Last Modified: 2010 Jan 24 +" Script: http://www.vim.org/scripts/script.php?script_id=1234 +" Based On: Mocked up version by Yegappan Lakshmanan +" http://groups.yahoo.com/group/vim/post?act=reply&messageNum=34406 +" License: GPL (Gnu Public License) +" GetLatestVimScripts: 1234 1 :AutoInstall: yankring.vim + +if exists('loaded_yankring') || &cp + finish +endif + +if v:version < 700 + echomsg 'yankring: You need at least Vim 7.0' + finish +endif + +let loaded_yankring = 100 + +let s:yr_has_voperator = 0 +if v:version > 701 || ( v:version == 701 && has("patch205") ) + let s:yr_has_voperator = 1 +endif + +if !exists('g:yankring_history_dir') + let g:yankring_history_dir = expand('$HOME') +else + let g:yankring_history_dir = expand(g:yankring_history_dir) +endif + +if !exists('g:yankring_history_file') + let g:yankring_history_file = 'yankring_history' +endif + +" Allow the user to override the # of yanks/deletes recorded +if !exists('g:yankring_max_history') + let g:yankring_max_history = 100 +elseif g:yankring_max_history < 0 + let g:yankring_max_history = 100 +endif + +" Specify the minimum length of 1 entry +if !exists('g:yankring_min_element_length') + let g:yankring_min_element_length = 1 +endif + +" Specify the maximum length of 1 entry (1MB default) +if !exists('g:yankring_max_element_length') + let g:yankring_max_element_length = 1048576 +endif + +" Allow the user to specify if the plugin is enabled or not +if !exists('g:yankring_enabled') + let g:yankring_enabled = 1 +endif + +" Specify max display length for each element for YRShow +if !exists('g:yankring_max_display') + let g:yankring_max_display = 0 +endif + +" Check if yankring should persist between Vim instances +if !exists('g:yankring_persist') + let g:yankring_persist = 1 +endif + +" Check if yankring share 1 file between all instances of Vim +if !exists('g:yankring_share_between_instances') + let g:yankring_share_between_instances = 1 +endif + +" Specify whether the results of the ring should be displayed +" in a separate buffer window instead of the use of echo +if !exists('g:yankring_window_use_separate') + let g:yankring_window_use_separate = 1 +endif + +" Specifies whether the window is closed after an action +" is performed +if !exists('g:yankring_window_auto_close') + let g:yankring_window_auto_close = 1 +endif + +" When displaying the buffer, how many lines should it be +if !exists('g:yankring_window_height') + let g:yankring_window_height = 8 +endif + +" When displaying the buffer, how many lines should it be +if !exists('g:yankring_window_width') + let g:yankring_window_width = 30 +endif + +" When displaying the buffer, where it should be placed +if !exists('g:yankring_window_use_horiz') + let g:yankring_window_use_horiz = 1 +endif + +" When displaying the buffer, where it should be placed +if !exists('g:yankring_window_use_bottom') + let g:yankring_window_use_bottom = 1 +endif + +" When displaying the buffer, where it should be placed +if !exists('g:yankring_window_use_right') + let g:yankring_window_use_right = 1 +endif + +" If the user presses , toggle the width of the window +if !exists('g:yankring_window_increment') + let g:yankring_window_increment = 50 +endif + +" Controls whether the . operator will repeat yank operations +" The default is based on cpoptions: |cpo-y| +" y A yank command can be redone with ".". +if !exists('g:yankring_dot_repeat_yank') + let g:yankring_dot_repeat_yank = (&cpoptions=~'y'?1:0) +endif + +" Only adds unique items to the yankring. +" If the item already exists, that element is set as the +" top of the yankring. +if !exists('g:yankring_ignore_duplicate') + let g:yankring_ignore_duplicate = 1 +endif + +" Vim automatically manages the numbered registers: +" 0 - last yanked text +" 1-9 - last deleted items +" If this option is turned on, the yankring will manage the +" values in them. +if !exists('g:yankring_manage_numbered_reg') + let g:yankring_manage_numbered_reg = 0 +endif + +" Allow the user to specify what characters to use for the mappings. +if !exists('g:yankring_n_keys') + " 7.1.patch205 introduces the v:operator function which was essential + " to gain the omap support. + if s:yr_has_voperator == 1 + " Use omaps for the rest of the functionality + let g:yankring_n_keys = 'Y D x X' + else + let g:yankring_n_keys = 'x yy dd yw dw ye de yE dE yiw diw yaw daw y$ d$ Y D yG dG ygg dgg' + endif +endif + +" Allow the user to specify what operator pending motions to map +if !exists('g:yankring_o_keys') + " o-motions and text objects, without zap-to-char motions + let g:yankring_o_keys = 'b B w W e E d h j k l H M L y G ^ 0 $ , ;' + let g:yankring_o_keys .= ' g_ g^ gm g$ gk gj gg ge gE - + _ ' + let g:yankring_o_keys .= ' iw iW aw aW as is ap ip a] a[ i] i[ a) a( ab i) i( ib a> a< i> i< at it a} a{ aB i} i{ iB a" a'' a` i" i'' i`' +endif + +if !exists('g:yankring_zap_keys') + let g:yankring_zap_keys = 'f F t T / ? @' +endif + +" Allow the user to specify what operator pending motions to map +if !exists('g:yankring_ignore_operator') + let g:yankring_ignore_operator = 'g~ gu gU ! = gq g? > < zf g@' +endif +let g:yankring_ignore_operator = ' '.g:yankring_ignore_operator.' ' + +" Whether we should map the . operator +if !exists('g:yankring_map_dot') + let g:yankring_map_dot = 1 +endif + +" Whether we sould map the "g" paste operators +if !exists('g:yankring_paste_using_g') + let g:yankring_paste_using_g = 1 +endif + +if !exists('g:yankring_v_key') + let g:yankring_v_key = 'y' +endif + +if !exists('g:yankring_del_v_key') + let g:yankring_del_v_key = 'd x' +endif + +if !exists('g:yankring_paste_n_bkey') + let g:yankring_paste_n_bkey = 'P' +endif + +if !exists('g:yankring_paste_n_akey') + let g:yankring_paste_n_akey = 'p' +endif + +if !exists('g:yankring_paste_v_bkey') + let g:yankring_paste_v_bkey = 'P' +endif + +if !exists('g:yankring_paste_v_akey') + let g:yankring_paste_v_akey = 'p' +endif + +if !exists('g:yankring_paste_check_default_buffer') + let g:yankring_paste_check_default_buffer = 1 +endif + +if !exists('g:yankring_replace_n_pkey') + let g:yankring_replace_n_pkey = '' +endif + +if !exists('g:yankring_replace_n_nkey') + let g:yankring_replace_n_nkey = '' +endif + +if !exists('g:yankring_clipboard_monitor') + let g:yankring_clipboard_monitor = (has('clipboard')?1:0) +endif + +if !exists('g:yankring_default_menu_mode') + let g:yankring_default_menu_mode = 3 +endif + +" Script variables for the yankring buffer +let s:yr_buffer_name = '[YankRing]' +let s:yr_buffer_last_winnr = -1 +let s:yr_buffer_last = -1 +let s:yr_buffer_id = -1 +let s:yr_search = '' +let s:yr_remove_omap_dot = 0 +let s:yr_history_version = 'v2' +let s:yr_history_v1_nl = '@@@' +let s:yr_history_v1_nl_pat = '\%(\\\)\@ 0 + let new_state = ((a:1 == 1) ? 1 : 0) + endif + + " YRToggle accepts an integer value to specify the state + if new_state == g:yankring_enabled + return + elseif new_state == 1 + call s:YRMapsCreate() + else + call s:YRMapsDelete() + endif +endfunction + + +" Enables or disables the yankring +function! s:YRDisplayElem(disp_nbr, script_var) + if g:yankring_max_display == 0 + if g:yankring_window_use_separate == 1 + let max_display = 500 + else + let max_display = g:yankring_window_width + + \ g:yankring_window_increment - + \ 12 + endif + else + let max_display = g:yankring_max_display + endif + + let elem = matchstr(a:script_var, '^.*\ze,.*$') + if s:yr_history_version == 'v1' + " v1 + " let elem = substitute(elem, '\%(\\\)\@max_display)? + \ (strpart(elem,0,max_display). + \ '...'): + \ elem + \ ) + \ ) + + return "" +endfunction + + +" Enables or disables the yankring +function! s:YRShow(...) + " If no parameter was provided assume the user wants to + " toggle the display. + let toggle = 1 + if a:0 > 0 + let toggle = matchstr(a:1, '\d\+') + endif + + if toggle == 1 + if bufwinnr(s:yr_buffer_id) > -1 + " If the YankRing window is already open close it + exec bufwinnr(s:yr_buffer_id) . "wincmd w" + hide + + " Switch back to the window which the YankRing + " window was opened from + if bufwinnr(s:yr_buffer_last) != -1 + " If the buffer is visible, switch to it + exec s:yr_buffer_last_winnr . "wincmd w" + endif + + return + endif + endif + + " Reset the search string, since this is automatically called + " if the yankring window is open. A previous search must be + " cleared since we do not want to show new items. The user can + " always run the search again. + let s:yr_search = "" + + " It is possible for registers to be changed outside of the + " maps of the YankRing. Perform this quick check when we + " show the contents (or when it is refreshed). + if g:yankring_paste_check_default_buffer == 1 + let save_reg = 0 + let register = ((&clipboard=='unnamed')?'+':'"') + + if &clipboard == 'unnamed' && getreg('+') != s:yr_prev_clipboard + let save_reg = 1 + endif + if register == '"' && getreg('"') != s:yr_prev_reg_unnamed + let save_reg = 1 + endif + + if save_reg == 1 + " The user has performed a yank / delete operation + " outside of the yankring maps. Add this + " value to the yankring. + call YRRecord(register) + endif + endif + + " List is shown in order of replacement + " assuming using previous yanks + let output = "--- YankRing ---\n" + let output = output . "Elem Content\n" + + call s:YRHistoryRead() + let disp_item_nr = 1 + for elem in s:yr_history_list + let output = output . s:YRDisplayElem(disp_item_nr, elem) . "\n" + let disp_item_nr += 1 + endfor + + if g:yankring_window_use_separate == 1 + call s:YRWindowOpen(output) + else + echo output + endif +endfunction + + +" Used in omaps if a following character is required +" like with motions (f,t) +function! s:YRGetChar() + let msg = "YR:Enter character:" + echomsg msg + let c = getchar() + if c =~ '^\d\+$' + let c = nr2char(c) + echomsg msg.c + endif + return c +endfunction + + +" Used in omaps if a following string is required +" like with motions (/,?) +" function! s:YRGetSearch() +" " let msg = "YR:Enter string:" +" " echomsg msg +" let str = input("YR:Enter string:") +" " let str = '' +" " while 1==1 +" " let c = getchar() +" " if c =~ '^\d\+$' +" " let c = nr2char(c) +" " if c == "\" +" " return c +" " endif +" " if c == "\" +" " break +" " endif +" " let str = str.c +" " echomsg msg.str +" " else +" " break +" " endif +" " endwhile +" return str +" endfunction + + +" Paste a certain item from the yankring +" If no parameter is provided, this function becomes interactive. It will +" display the list (using YRShow) and allow the user to choose an element. +function! s:YRGetElem(...) + if s:yr_count == 0 + call s:YRWarningMsg('YR: yankring is empty') + return -1 + endif + + let default_buffer = ((&clipboard=='unnamed')?'+':'"') + + let direction = 'p' + if a:0 > 1 + " If the user indicated to paste above or below + " let direction = ((a:2 ==# 'P') ? 'P' : 'p') + if a:2 =~ '\(p\|gp\|P\|gP\)' + let direction = a:2 + endif + endif + + " Check to see if a specific value has been provided + let elem = 0 + if a:0 > 0 + " Ensure we get only the numeric value (trim it) + let elem = matchstr(a:1, '\d\+') + let elem = elem - 1 + else + " If no parameter was supplied display the yankring + " and prompt the user to enter the value they want pasted. + call s:YRShow(0) + + if g:yankring_window_use_separate == 1 + " The window buffer is used instead of command line + return + endif + + let elem = input("Enter # to paste:") + + " Ensure we get only the numeric value (trim it) + let elem = matchstr(elem, '\d\+') + + if elem == '' + " They most likely pressed enter without entering a value + return + endif + + let elem = elem - 1 + endif + + if elem < 0 || elem >= s:yr_count + call s:YRWarningMsg("YR: Invalid choice:".elem) + return -1 + endif + + let default_buffer = ((&clipboard=='unnamed')?'+':'"') + call setreg(default_buffer + \ , s:YRGetValElemNbr((elem), 'v') + \ , s:YRGetValElemNbr((elem), 't') + \ ) + exec "normal! ".direction + + " Set the previous action as a paste in case the user + " press . to repeat + call s:YRSetPrevOP('p', '', default_buffer, 'n') + +endfunction + + +" Starting the top of the ring it will paste x items from it +function! s:YRGetMultiple(reverse_order, ...) + if s:yr_count == 0 + call s:YRWarningMsg('YR: yankring is empty') + return + endif + + " If the user provided a range, exit after that many + " have been displayed + let max = 1 + if a:0 == 1 + " If no yank command has been supplied, assume it is + " a full line yank + let max = matchstr(a:1, '\d\+') + endif + if max > s:yr_count + " Default to all items if they specified a very high value + let max = s:yr_count + endif + + " Base the increment on the sort order of the results + let increment = ((a:reverse_order==0)?(1):(-1)) + if a:reverse_order == 0 + let increment = 1 + let elem = 0 + else + let increment = -1 + let elem = (max - 1) + endif + + if a:0 > 1 + let iter = 1 + while iter <= a:0 + let elem = (a:{iter} - 1) + call s:YRGetElem(elem) + let iter = iter + 1 + endwhile + else + while max > 0 + " Paste the first item, and move on to the next. + " digits the element # is + call s:YRGetElem(elem) + let elem = elem + increment + let max = max - 1 + endwhile + endif +endfunction + + +" Given a regular expression, check each element within +" the yankring, display only the matching items and prompt +" the user for which item to paste +function! s:YRSearch(...) + if s:yr_count == 0 + call s:YRWarningMsg('YR: yankring is empty') + return + endif + + let s:yr_search = "" + " If the user provided a range, exit after that many + " have been displayed + if a:0 == 0 || (a:0 == 1 && a:1 == "") + let s:yr_search = input('Enter [optional] regex:') + else + let s:yr_search = a:1 + endif + + if s:yr_search == "" + " Show the entire yankring + call s:YRShow(0) + return + endif + + " List is shown in order of replacement + " assuming using previous yanks + let output = "--- YankRing ---\n" + let output = output . "Elem Content\n" + let valid_choices = [] + + let search_result = filter(copy(s:yr_history_list), "v:val =~ '".s:yr_search."'") + + let disp_item_nr = 1 + + for elem in s:yr_history_list + if elem =~ s:yr_search + let output = output . s:YRDisplayElem(disp_item_nr, elem) . "\n" + call add(valid_choices, disp_item_nr.'') + endif + let disp_item_nr += 1 + endfor + + if len(valid_choices) == 0 + let output = output . "Search for [".s:yr_search."] did not match any items " + endif + + if g:yankring_window_use_separate == 1 + call s:YRWindowOpen(output) + else + if len(valid_choices) > 0 + echo output + let elem = input("Enter # to paste:") + + " Ensure we get only the numeric value (trim it) + let elem = matchstr(elem, '\d\+') + + if elem == '' + " They most likely pressed enter without entering a value + return + endif + + if index(valid_choices, elem) != -1 + exec 'YRGetElem ' . elem + else + " User did not choose one of the elements that were found + " Remove leading , + call s:YRWarningMsg( "YR: Item[" . elem . "] not found, only valid choices are[" . + \ join(valid_choices, ',') . + \ "]" + \ ) + return -1 + endif + + else + call s:YRWarningMsg( "YR: The pattern [" . + \ s:yr_search . + \ "] does not match any items in the yankring" + \ ) + endif + endif + +endfunction + + +" Resets the common script variables for managing the ring. +function! s:YRReset() + let s:yr_history_list = [] + " Update the history file + call s:YRHistorySave() +endfunction + + +" Clears the yankring by simply setting the # of items in it to 0. +" There is no need physically unlet each variable. +function! s:YRInit(...) + let s:yr_next_idx = 0 + let s:yr_last_paste_idx = 0 + let s:yr_count = 0 + let s:yr_history_last_upd = 0 + let s:yr_history_list = [] + let s:yr_paste_dir = 'p' + + " For the . op support + let s:yr_prev_op_code = '' + let s:yr_prev_op_mode = 'n' + let s:yr_prev_count = '' + let s:yr_prev_reg = '' + let s:yr_prev_reg_unnamed = '' + let s:yr_prev_reg_small = '' + let s:yr_prev_reg_insert = '' + let s:yr_prev_reg_expres = '' + let s:yr_prev_clipboard = '' + let s:yr_prev_vis_lstart = 0 + let s:yr_prev_vis_lend = 0 + let s:yr_prev_vis_cstart = 0 + let s:yr_prev_vis_cend = 0 + let s:yr_prev_changenr = 0 + let s:yr_prev_repeating = 0 + + " This is used to determine if the visual selection should be + " reset prior to issuing the YRReplace + let s:yr_prev_vis_mode = 0 + + if a:0 == 0 && g:yankring_persist == 0 + " The user wants the yankring reset each time Vim is started + call s:YRClear() + endif + + call s:YRHistoryRead() +endfunction + + +" Clears the yankring by simply setting the # of items in it to 0. +" There is no need physically unlet each variable. +function! s:YRClear() + call s:YRReset() + call s:YRInit('DoNotClear') + + " If the yankring window is open, refresh it + call s:YRWindowUpdate() +endfunction + + +" Determine which register the user wants to use +" For example the 'a' register: "ayy +function! s:YRRegister() + " v:register can be blank in some (unknown) cases + " so test for this condition and return the + " default register + let user_register = ((v:register=='')?('"'):(v:register)) + if &clipboard == 'unnamed' && user_register == '"' + let user_register = '+' + endif + return user_register +endfunction + + +" Allows you to push a new item on the yankring. Useful if something +" is in the clipboard and you want to add it to the yankring. +" Or if you yank something that is not mapped. +function! s:YRPush(...) + let user_register = s:YRRegister() + + if a:0 > 0 + " If no yank command has been supplied, assume it is + " a full line yank + let user_register = ((a:1 == '') ? user_register : a:1) + endif + + " If we are pushing something on to the yankring, add it to + " the default buffer as well so the next item pasted will + " be the item pushed + let default_buffer = ((&clipboard=='unnamed')?'+':'"') + call setreg(default_buffer, getreg(user_register), + \ getregtype(user_register)) + + call s:YRSetPrevOP('', '', '', 'n') + call YRRecord(user_register) +endfunction + + +" Allows you to pop off any element from the yankring. +" If no parameters are provided the first element is removed. +" If a vcount is provided, that many elements are removed +" from the top. +function! s:YRPop(...) + if s:yr_count == 0 + call s:YRWarningMsg('YR: yankring is empty') + return + endif + + let v_count = 1 + if a:0 > 1 + let v_count = a:2 + endif + + " If the user provided a parameter, remove that element + " from the yankring. + " If no parameter was provided assume the first element. + let elem_index = 0 + if a:0 > 0 + " Get the element # from the parameter + let elem_index = matchstr(a:1, '\d\+') + let elem_index = elem_index - 1 + endif + + " If the user entered a count, then remove that many + " elements from the ring. + while v_count > 0 + call s:YRMRUDel('s:yr_history_list', elem_index) + let v_count = v_count - 1 + endwhile + + " If the yankring window is open, refresh it + call s:YRWindowUpdate() +endfunction + + +" Adds this value to the yankring. +function! YRRecord(...) + + let register = '"' + if a:0 > 0 + " If no yank command has been supplied, assume it is + " a full line yank + let register = ((a:1 == '') ? register : a:1) + endif + + " v:register can be blank in some (unknown) cases + " if v:register == '' || v:register == '_' + if v:register == '_' + " Black hole register, ignore recording the operation + return "" + endif + + let register = ((&clipboard=='unnamed')?'+':register) + + " let s:yr_prev_changenr = changenr() + if register == '"' + " If the change has occurred via an omap, we must delay + " the capture of the default register until this event + " since register updates are not reflected until the + " omap function completes + let s:yr_prev_reg_unnamed = getreg('"') + let s:yr_prev_reg_small = getreg('-') + endif + + " Add item to list + " This will also account for duplicates. + call s:YRMRUAdd( 's:yr_history_list' + \ , getreg(register) + \ , getregtype(register) + \ ) + + if register =~ '[+*]' + let s:yr_prev_clipboard = @+ + endif + + " If the yankring window is open, refresh it + call s:YRWindowUpdate() + + " Manage the numbered registers + if g:yankring_manage_numbered_reg == 1 + call s:YRSetNumberedReg() + endif + + return "" +endfunction + + +" Adds this value to the yankring. +function! YRRecord3() + let register = '"' + + " v:register can be blank in some (unknown) cases + " if v:register == '' || v:register == '_' + if v:register == '_' + " Black hole register, ignore recording the operation + return "" + endif + + let register = ((&clipboard=='unnamed')?'+':register) + + if register == '"' + " If the change has occurred via an omap, we must delay + " the capture of the default register until this event + " since register updates are not reflected until the + " omap function completes + let s:yr_prev_reg_unnamed = getreg('"') + let s:yr_prev_reg_small = getreg('-') + endif + + if s:yr_remove_omap_dot == 1 + call s:YRMapsCreate('add_only_zap_keys') + endif + + " Add item to list + " This will also account for duplicates. + call s:YRMRUAdd( 's:yr_history_list' + \ , getreg(register) + \ , getregtype(register) + \ ) + + if register =~ '[+*]' + let s:yr_prev_clipboard = @+ + endif + + " If the yankring window is open, refresh it + call s:YRWindowUpdate() + + " Manage the numbered registers + if g:yankring_manage_numbered_reg == 1 + call s:YRSetNumberedReg() + endif + + return "" +endfunction + + +" Record the operation for the dot operator +function! s:YRSetPrevOP(op_code, count, reg, mode) + let s:yr_prev_op_code = a:op_code + let s:yr_prev_op_mode = a:mode + let s:yr_prev_count = a:count + let s:yr_prev_changenr = changenr() + let s:yr_prev_reg = a:reg + let s:yr_prev_reg_unnamed = getreg('"') + let s:yr_prev_reg_small = getreg('-') + let s:yr_prev_reg_insert = getreg('.') + let s:yr_prev_vis_lstart = line("'<") + let s:yr_prev_vis_lend = line("'>") + let s:yr_prev_vis_cstart = col("'<") + let s:yr_prev_vis_cend = col("'>") + let s:yr_prev_reg_expres = histget('=', -1) + + if a:mode == 'n' + " In normal mode, the change has already + " occurred, therefore we can mark the + " actual position of the change. + let s:yr_prev_chg_lstart = line("'[") + let s:yr_prev_chg_lend = line("']") + let s:yr_prev_chg_cstart = col("'[") + let s:yr_prev_chg_cend = col("']") + else + " If in operator pending mode, the change + " has not yet occurred. Therefore we cannot + " use the '[ and ]' markers. But we can + " store the current line position. + let s:yr_prev_chg_lstart = line(".") + let s:yr_prev_chg_lend = line(".") + let s:yr_prev_chg_cstart = col(".") + let s:yr_prev_chg_cend = col(".") + endif + + " If storing the last change position (using '[, ']) + " is not good enough, then another option is to: + " Use :redir on the :changes command + " and grab the last item. Store this value + " and compare it is YRDoRepeat. +endfunction + + +" Adds this value to the yankring. +function! s:YRDoRepeat() + let dorepeat = 0 + + if s:yr_has_voperator == 1 + " Let Vim handle the repeat, just capture the updates + " as usual. + return 0 + endif + + if s:yr_prev_op_code =~ '^c' + " You cannot repeat change operations, let Vim's + " standard mechanism handle these, or the user will + " be prompted again, instead of repeating the + " previous change. + return 0 + endif + + if g:yankring_manage_numbered_reg == 1 + " When resetting the numbered register we are + " must ignore the comparision of the " register. + if s:yr_prev_reg_small == getreg('-') && + \ s:yr_prev_reg_insert == getreg('.') && + \ s:yr_prev_reg_expres == histget('=', -1) && + \ s:yr_prev_vis_lstart == line("'<") && + \ s:yr_prev_vis_lend == line("'>") && + \ s:yr_prev_vis_cstart == col("'<") && + \ s:yr_prev_vis_cend == col("'>") && + \ s:yr_prev_chg_lstart == line("'[") && + \ s:yr_prev_chg_lend == line("']") && + \ s:yr_prev_chg_cstart == col("'[") && + \ s:yr_prev_chg_cend == col("']") + let dorepeat = 1 + endif + else + " Check the previously recorded value of the registers + " if they are the same, we need to reissue the previous + " yankring command. + " If any are different, the user performed a command + " command that did not involve the yankring, therefore + " we should just issue the standard "normal! ." to repeat it. + if s:yr_prev_reg_unnamed == getreg('"') && + \ s:yr_prev_reg_small == getreg('-') && + \ s:yr_prev_reg_insert == getreg('.') && + \ s:yr_prev_reg_expres == histget('=', -1) && + \ s:yr_prev_vis_lstart == line("'<") && + \ s:yr_prev_vis_lend == line("'>") && + \ s:yr_prev_vis_cstart == col("'<") && + \ s:yr_prev_vis_cend == col("'>") + let dorepeat = 1 + endif + if dorepeat == 1 && s:yr_prev_op_mode == 'n' + " Hmm, not sure why I was doing this now + " so I will remove it + " let dorepeat = 0 + " if s:yr_prev_chg_lstart == line("'[") && + " \ s:yr_prev_chg_lend == line("']") && + " \ s:yr_prev_chg_cstart == col("'[") && + " \ s:yr_prev_chg_cend == col("']") + " let dorepeat = 1 + " endif + elseif dorepeat == 1 && s:yr_prev_op_mode == 'o' + " Hmm, not sure why I was doing this now + " so I will remove it + " let dorepeat = 0 + " if s:yr_prev_chg_lstart == line("'[") && + " \ s:yr_prev_chg_lend == line("']") && + " \ s:yr_prev_chg_cstart == col("'[") && + " \ s:yr_prev_chg_cend == col("']") + " let dorepeat = 1 + " endif + endif + endif + + " " If another change has happened that was not part of the + " " yankring we cannot replay it (from the yankring). Use + " " the standard ".". + " " If the previous op was a change, do not use the yankring + " " to repeat it. + " " changenr() is buffer specific, so anytime you move to + " " a different buffer you will definitely perform a + " " standard "." + " " Any previous op that was a change, must be replaced using "." + " " since we do not want the user prompted to enter text again. + " if s:yr_prev_changenr == changenr() && s:yr_prev_op_code !~ '^c' + " let dorepeat = 1 + " endif + + " If we are going to repeat check to see if the + " previous command was a yank operation. If so determine + " if yank operations are allowed to be repeated. + if dorepeat == 1 && s:yr_prev_op_code =~ '^y' + " This value be default is set based on cpoptions. + if g:yankring_dot_repeat_yank == 0 + let dorepeat = 0 + endif + endif + return dorepeat +endfunction + + +" Manages the Vim's numbered registers +function! s:YRSetNumberedReg() + + let i = 1 + + while i <= 10 + if i > s:yr_count + break + endif + + call setreg( (i-1) + \ , s:YRGetValElemNbr((i-1),'v') + \ , s:YRGetValElemNbr((i-1),'t') + \ ) + let i += 1 + endwhile +endfunction + + +" This internal function will add and subtract values from a starting +" point and return the correct element number. It takes into account +" the circular nature of the yankring. +function! s:YRGetNextElem(start, iter) + + let needed_elem = a:start + a:iter + + " The yankring is a ring, so if an element is + " requested beyond the number of elements, we + " must wrap around the ring. + if needed_elem > s:yr_count + let needed_elem = needed_elem % s:yr_count + endif + + if needed_elem == 0 + " Can happen at the end or beginning of the ring + if a:iter == -1 + " Wrap to the bottom of the ring + let needed_elem = s:yr_count + else + " Wrap to the top of the ring + let needed_elem = 1 + endif + elseif needed_elem < 1 + " As we step backwards through the ring we could ask for a negative + " value, this will wrap it around to the end + let needed_elem = s:yr_count + endif + + return needed_elem + +endfunction + + +" Lets Vim natively perform the operation and then stores what +" was yanked (or deleted) into the yankring. +" Supports this for example - 5"ayy +" +" This is a legacy function now since the release of Vim 7.2 +" and the use of omaps with YankRing 5.0 and above. +" If Vim 7.1 has patch205, then the new omaps and the v:operator +" variable is used instead. +function! s:YRYankCount(...) range + + let user_register = s:YRRegister() + let v_count = v:count + + " Default yank command to the entire line + let op_code = 'yy' + if a:0 > 0 + " If no yank command has been supplied, assume it is + " a full line yank + let op_code = ((a:1 == '') ? op_code : a:1) + endif + + if op_code == '.' + if s:YRDoRepeat() == 1 + if s:yr_prev_op_code != '' + let op_code = s:yr_prev_op_code + let v_count = s:yr_prev_count + let user_register = s:yr_prev_reg + endif + else + " Set this flag so that YRRecord will + " ignore repeats + let s:yr_prev_repeating = 1 + exec "normal! ." + return + endif + else + let s:yr_prev_repeating = 0 + endif + + " Supports this for example - 5"ayy + " A delete operation will still place the items in the + " default registers as well as the named register + exec "normal! ". + \ ((v_count > 0)?(v_count):''). + \ (user_register=='"'?'':'"'.user_register). + \ op_code + + if user_register == '_' + " Black hole register, ignore recording the operation + return + endif + + call s:YRSetPrevOP(op_code, v_count, user_register, 'n') + + call YRRecord(user_register) +endfunction + + +" Handles ranges. There are visual ranges and command line ranges. +" Visual ranges are easy, since we pass through and let Vim deal +" with those directly. +" Command line ranges means we must yank the entire line, and not +" just a portion of it. +function! s:YRYankRange(do_delete_selection, ...) range + + let user_register = s:YRRegister() + let default_buffer = ((&clipboard=='unnamed')?'+':'"') + + " Default command mode to normal mode 'n' + let cmd_mode = 'n' + if a:0 > 0 + " Change to visual mode, if command executed via + " a visual map + let cmd_mode = ((a:1 == 'v') ? 'v' : 'n') + endif + + if cmd_mode == 'v' + " We are yanking either an entire line, or a range + exec "normal! gv". + \ (user_register==default_buffer?'':'"'.user_register). + \ 'y' + if a:do_delete_selection == 1 + exec "normal! gv". + \ (user_register==default_buffer?'':'"'.user_register). + \ 'd' + endif + else + " In normal mode, always yank the complete line, since this + " command is for a range. YRYankCount is used for parts + " of a single line + if a:do_delete_selection == 1 + exec a:firstline . ',' . a:lastline . 'delete '.user_register + else + exec a:firstline . ',' . a:lastline . 'yank ' . user_register + endif + endif + + if user_register == '_' + " Black hole register, ignore + return + endif + + call s:YRSetPrevOP('', '', user_register, 'n') + call YRRecord(user_register) +endfunction + + +" Paste from either the yankring or from a specified register +" Optionally a count can be provided, so paste the same value 10 times +function! s:YRPaste(replace_last_paste_selection, nextvalue, direction, ...) + " Disabling the yankring removes the default maps. + " But there are some maps the user can create on their own, and + " these would most likely call this function. So place an extra + " check and display a message. + if g:yankring_enabled == 0 + call s:YRWarningMsg( + \ 'YR: The yankring is currently disabled, use YRToggle.' + \ ) + return + endif + + + let user_register = s:YRRegister() + let default_buffer = ((&clipboard == 'unnamed')?'+':'"') + let v_count = v:count + + " Default command mode to normal mode 'n' + let cmd_mode = 'n' + if a:0 > 0 + " Change to visual mode, if command executed via + " a visual map + let cmd_mode = ((a:1 == 'v') ? 'v' : 'n') + endif + + " User has decided to bypass the yankring and specify a specific + " register + if user_register != default_buffer + if a:replace_last_paste_selection == 1 + call s:YRWarningMsg( 'YR: A register cannot be specified in replace mode' ) + return + else + " Check for the expression register, in this special case + " we must copy it's evaluation into the default buffer and paste + if user_register == '=' + " Save the default register since Vim will only + " allow the expression register to be pasted once + " and will revert back to the default buffer + let save_default_reg = @" + call setreg(default_buffer, eval(histget('=', -1)) ) + else + let user_register = '"'.user_register + endif + exec "normal! ". + \ ((cmd_mode=='n') ? "" : "gv"). + \ ((v_count > 0)?(v_count):''). + \ ((user_register=='=')?'':user_register). + \ a:direction + if user_register == '=' + let @" = save_default_reg + endif + " In this case, we have bypassed the yankring + " If the user hits next or previous we want the + " next item pasted to be the top of the yankring. + let s:yr_last_paste_idx = 0 + endif + let s:yr_paste_dir = a:direction + let s:yr_prev_vis_mode = ((cmd_mode=='n') ? 0 : 1) + return + endif + + " Try to second guess the user to make these mappings less intrusive. + " If the user hits paste, compare the contents of the paste register + " to the current entry in the yankring. If they are different, lets + " assume the user wants the contents of the paste register. + " So if they pressed [yt ] (yank to space) and hit paste, the yankring + " would not have the word in it, so assume they want the word pasted. + if a:replace_last_paste_selection != 1 + if s:yr_count > 0 || (default_buffer == '+' && len(@+) == 0) + " Only check the default buffer is the user wants us to. + " This was necessary prior to version 4.0 since we did not + " capture as many items as 4.0 and above does. (A. Budden) + if g:yankring_paste_check_default_buffer == 1 && + \ getreg(default_buffer) != s:yr_prev_reg_unnamed + " The user has performed a yank / delete operation + " outside of the yankring maps. First, add this + " value to the yankring. + call YRRecord(default_buffer) + " Now, use the most recently yanked text, rather than the + " value from the yankring. + exec "normal! ". + \ ((cmd_mode=='n') ? "" : "gv"). + \ ((v_count > 0)?(v_count):''). + \ a:direction + let s:yr_paste_dir = a:direction + let s:yr_prev_vis_mode = ((cmd_mode=='n') ? 0 : 1) + + " In this case, we have bypassed the yankring + " If the user hits next or previous we want the + " next item pasted to be the top of the yankring. + let s:yr_last_paste_idx = 0 + return + endif + else + exec "normal! ". + \ ((cmd_mode=='n') ? "" : "gv"). + \ ((v_count > 0)?(v_count):''). + \ a:direction + let s:yr_paste_dir = a:direction + let s:yr_prev_vis_mode = ((cmd_mode=='n') ? 0 : 1) + return + endif + endif + + if s:yr_count == 0 || (default_buffer == '+' && len(@+) == 0) + " Nothing to paste + return + endif + + if a:replace_last_paste_selection == 1 + " Replacing the previous put + let start = line("'[") + let end = line("']") + + if start != line('.') + call s:YRWarningMsg( 'YR: You must paste text first, before you can replace' ) + return + endif + + if start == 0 || end == 0 + return + endif + + " If a count was provided (ie 5), multiply the + " nextvalue accordingly and position the next paste index + let which_elem = a:nextvalue * ((v_count > 0)?(v_count):1) * -1 + let s:yr_last_paste_idx = s:YRGetNextElem( + \ s:yr_last_paste_idx, which_elem + \ ) + + let save_reg = getreg(default_buffer) + let save_reg_type = getregtype(default_buffer) + call setreg( default_buffer + \ , s:YRGetValElemNbr((s:yr_last_paste_idx-1),'v') + \ , s:YRGetValElemNbr((s:yr_last_paste_idx-1),'t') + \ ) + + " First undo the previous paste + exec "normal! u" + " Check if the visual selection should be reselected + " Next paste the correct item from the ring + " This is done as separate statements since it appeared that if + " there was nothing to undo, the paste never happened. + exec "normal! ". + \ ((s:yr_prev_vis_mode==0) ? "" : "gv"). + \ s:yr_paste_dir + call setreg(default_buffer, save_reg, save_reg_type) + call s:YRSetPrevOP('', '', '', 'n') + else + " User hit p or P + " Supports this for example - 5"ayy + " And restores the current register + let save_reg = getreg(default_buffer) + let save_reg_type = getregtype(default_buffer) + let s:yr_last_paste_idx = 1 + call setreg(default_buffer + \ , s:YRGetValElemNbr(0,'v') + \ , s:YRGetValElemNbr(0,'t') + \ ) + exec "normal! ". + \ ((cmd_mode=='n') ? "" : "gv"). + \ ( + \ ((v_count > 0)?(v_count):''). + \ a:direction + \ ) + call setreg(default_buffer, save_reg, save_reg_type) + call s:YRSetPrevOP( + \ a:direction + \ , v_count + \ , default_buffer + \ , 'n' + \ ) + let s:yr_paste_dir = a:direction + let s:yr_prev_vis_mode = ((cmd_mode=='n') ? 0 : 1) + endif + +endfunction + + +" Handle any omaps +function! YRMapsExpression(sid, motion, ...) + let cmds = a:motion + " echomsg "YRMapsE:".localtime() + " echomsg "YRMapsE 1:".cmds.":".v:operator.":".s:yr_maps_created_zap + + if (a:motion =~ '\.' && s:yr_remove_omap_dot == 1) || a:motion =~ '@' + " If we are repeating a series of commands we must + " unmap the _zap_ keys so that the user is not + " prompted when a command is replayed. + " These maps must be re-instated in YRRecord3() + " after the action of the replay is completed. + call s:YRMapsDelete('remove_only_zap_keys') + endif + + " Check if we are in operator-pending mode + if a:motion =~ '\('.substitute(g:yankring_zap_keys, ' ', '\\|', 'g').'\)' + if a:motion =~ '\(/\|?\)' + let zapto = (a:0==0 ? "" : input("YR:Enter string:")) + if zapto != "" + let zapto = zapto . "\" + else + let zapto = "\" + endif + else + let zapto = (a:0==0 ? "" : s:YRGetChar()) + endif + + if zapto == "\" + " Abort if the user hits Control C + call s:YRWarningMsg( "YR:Aborting command:".v:operator.a:motion ) + return "\" + endif + + let cmds = cmds . zapto + endif + + " There are a variety of commands which do not change the + " registers, so these operators should be ignored when + " determining which operations to record + " Simple example is '=' which simply formats the + " the selected text. + if ' \('.escape(join(split(g:yankring_ignore_operator), '\|'), '/.*~$^[]' ).'\) ' !~ escape(v:operator, '/.*~$^[]') + " Check if we are performing an action that will + " take us into insert mode + if '[cCsS]' !~ escape(v:operator, '/.*~$^[]') && a:motion !~ '@' + " if '[cCsS]' !~ escape(v:operator, '/.*~$^[]') + " If we have not entered insert mode, feed the call + " to record the current change when the function ends. + " This is necessary since omaps do not update registers + " until the function completes. + " The InsertLeave event will handle the motions + " that place us in insert mode and record the + " changes when insert mode ends. + let cmds .= a:sid. "yrrecord" + endif + endif + + " echomsg "YRMapsE 5:".a:motion.":'".cmds."':".s:yr_maps_created_zap + return cmds + +endfunction + + +" Handle any the @ +function! s:YRMapsMacro(bang, ...) range + " If we are repeating a series of commands we must + " unmap the _zap_ keys so that the user is not + " prompted when a command is replayed. + " These maps must be re-instated in YRRecord3() + " after the action of the replay is completed. + call s:YRMapsDelete('remove_only_zap_keys') + + " let zapto = (a:0==0 ? "" : s:YRGetChar()) + let zapto = s:YRGetChar() + + if zapto == "\" + " Abort if the user hits Control C + call s:YRWarningMsg( "YR:Aborting command:".v:operator.a:motion ) + return "" + endif + + let v_count = v:count + " If no count was specified it will have a value of 0 + " so set it to at least 1 + let v_count = ((v_count > 0)?(v_count):'') + + let range = '' + if a:firstline != a:lastline + let rannge = a:firstline.','.a:lastline + endif + + let cmd = range."normal! ".v_count.'@'.zapto + " DEBUG + " echomsg cmd + exec cmd + + call s:YRMapsCreate('add_only_zap_keys') +endfunction + + +" Create the default maps +function! s:YRMapsCreate(...) + " 7.1.patch205 introduces the v:operator function which was + " essential to gain the omap support. + if s:yr_has_voperator == 1 + let s:yr_remove_omap_dot = 1 + for key in split(g:yankring_zap_keys) + try + if key != '@' + exec 'omap ' key 'YRMapsExpression("", "'. key. '", 1)' + endif + catch + endtry + endfor + endif + + silent! nmap @ YRMapsExpression("", "@", "1") + + let s:yr_maps_created_zap = 1 + + if a:0 > 0 + " We have only removed the _zap_ keys temporarily + " so abandon further changes. + return + endif + + " 7.1.patch205 introduces the v:operator function which was essential + " to gain the omap support. + if s:yr_has_voperator == 1 + let s:yr_remove_omap_dot = 1 + " Set option to add and remove _zap_ keys when + " repeating commands + let o_maps = split(g:yankring_o_keys) + " Loop through and prompt the user for all buffer connection parameters. + for key in o_maps + exec 'omap ' key 'YRMapsExpression("", "'. escape(key,'\"'). '")' + endfor + endif + + " Iterate through a space separated list of mappings and create + " calls to the YRYankCount function + let n_maps = split(g:yankring_n_keys) + " Loop through and prompt the user for all buffer connection parameters. + for key in n_maps + " exec 'nnoremap '.key." :YRYankCount '".key."'" + " exec 'nnoremap '.key." :YRYankCount '".key."'" + " Andy Wokula's suggestion + exec 'nmap' key key."yrrecord" + endfor + + if g:yankring_map_dot == 1 + if s:yr_has_voperator == 1 + nmap . YRMapsExpression("", ".") + else + nnoremap . :YRYankCount '.' + endif + endif + + if g:yankring_v_key != '' + exec 'xnoremap '.g:yankring_v_key." :YRYankRange 'v'" + endif + if g:yankring_del_v_key != '' + for v_map in split(g:yankring_del_v_key) + if strlen(v_map) > 0 + try + exec 'xnoremap '.v_map." :YRDeleteRange 'v'" + catch + endtry + endif + endfor + endif + if g:yankring_paste_n_bkey != '' + exec 'nnoremap '.g:yankring_paste_n_bkey." :YRPaste 'P'" + if g:yankring_paste_using_g == 1 + exec 'nnoremap g'.g:yankring_paste_n_bkey." :YRPaste 'gP'" + endif + endif + if g:yankring_paste_n_akey != '' + exec 'nnoremap '.g:yankring_paste_n_akey." :YRPaste 'p'" + if g:yankring_paste_using_g == 1 + exec 'nnoremap g'.g:yankring_paste_n_akey." :YRPaste 'gp'" + endif + endif + if g:yankring_paste_v_bkey != '' + exec 'xnoremap '.g:yankring_paste_v_bkey." :YRPaste 'P', 'v'" + endif + if g:yankring_paste_v_akey != '' + exec 'xnoremap '.g:yankring_paste_v_akey." :YRPaste 'p', 'v'" + endif + if g:yankring_replace_n_pkey != '' + exec 'nnoremap '.g:yankring_replace_n_pkey." :YRReplace '-1', 'P'" + endif + if g:yankring_replace_n_nkey != '' + exec 'nnoremap '.g:yankring_replace_n_nkey." :YRReplace '1', 'p'" + endif + + let g:yankring_enabled = 1 + let s:yr_maps_created = 1 +endfunction + + +" Create the default maps +function! s:YRMapsDelete(...) + + let o_maps = split(g:yankring_zap_keys) + for key in o_maps + try + if key != '@' + silent! exec 'ounmap' key + endif + catch + endtry + endfor + + let s:yr_maps_created_zap = 0 + + if a:0 > 0 + " We have only removed the _zap_ keys temporarily + " so abandon further changes. + return + endif + + " Iterate through a space separated list of mappings and create + " calls to an appropriate YankRing function + let n_maps = split(g:yankring_n_keys) + " Loop through and prompt the user for all buffer connection parameters. + for key in n_maps + try + silent! exec 'nunmap' key + catch + endtry + endfor + + let o_maps = split(g:yankring_o_keys) + for key in o_maps + try + silent! exec 'ounmap' key + catch + endtry + endfor + + if g:yankring_map_dot == 1 + exec "nunmap ." + endif + if g:yankring_v_key != '' + exec 'vunmap '.g:yankring_v_key + endif + if g:yankring_del_v_key != '' + for v_map in split(g:yankring_del_v_key) + if strlen(v_map) > 0 + try + exec 'vunmap '.v_map + catch + endtry + endif + endfor + endif + if g:yankring_paste_n_bkey != '' + exec 'nunmap '.g:yankring_paste_n_bkey + if g:yankring_paste_using_g == 1 + exec 'nunmap g'.g:yankring_paste_n_bkey + endif + endif + if g:yankring_paste_n_akey != '' + exec 'nunmap '.g:yankring_paste_n_akey + if g:yankring_paste_using_g == 1 + exec 'nunmap g'.g:yankring_paste_n_akey + endif + endif + if g:yankring_paste_v_bkey != '' + exec 'vunmap '.g:yankring_paste_v_bkey + endif + if g:yankring_paste_v_akey != '' + exec 'vunmap '.g:yankring_paste_v_akey + endif + if g:yankring_replace_n_pkey != '' + exec 'nunmap '.g:yankring_replace_n_pkey + endif + if g:yankring_replace_n_nkey != '' + exec 'nunmap '.g:yankring_replace_n_nkey + endif + + let g:yankring_enabled = 0 + let s:yr_maps_created = 0 +endfunction + +function! s:YRGetValElemNbr( position, type ) + + let needed_elem = a:position + + " The List which contains the items in the yankring + " history is also ordered, most recent at the top + let elem = s:YRMRUGet('s:yr_history_list', needed_elem) + + if elem >= 0 + if a:type == 't' + return matchstr(elem, '^.*,\zs.*$') + else + let elem = matchstr(elem, '^.*\ze,.*$') + if s:yr_history_version == 'v1' + " Match three @@@ in a row as long as it is not + " preceeded by a @@@ + " v1 + let elem = substitute(elem, s:yr_history_v1_nl_pat, "\n", 'g') + let elem = substitute(elem, '\\@', '@', 'g') + else + let elem = substitute(elem, s:yr_history_v2_nl_pat, "\n", 'g') + endif + return elem + endif + else + return -1 + endif + + return "" +endfunction + +function! s:YRMRUReset( mru_list ) + + let {a:mru_list} = [] + + return 1 +endfunction + +function! s:YRMRUSize( mru_list ) + return len({a:mru_list}) +endfunction + +function! s:YRMRUElemFormat( element, element_type ) + let elem = a:element + if g:yankring_max_element_length != 0 + let elem = strpart(a:element, 0, g:yankring_max_element_length) + endif + if s:yr_history_version == 'v1' + let elem = escape(elem, '@') + let elem = substitute(elem, "\n", s:yr_history_v1_nl, 'g') + else + let elem = substitute(elem, "\n", s:yr_history_v2_nl, 'g') + endif + " Append the regtype to the end so we have it available + let elem = elem.",".a:element_type + + return elem +endfunction + +function! s:YRMRUHas( mru_list, find_str ) + " This function will find a string and return the element # + let find_idx = index({a:mru_list}, a:find_str) + + return find_idx +endfunction + +function! s:YRMRUGet( mru_list, position ) + " This function will return the value of the item at a:position + " Find the value of one element + let value = get({a:mru_list}, a:position, -2) + + return value +endfunction + +function! s:YRMRUAdd( mru_list, element, element_type ) + " Only add new items if they do not already exist in the MRU. + " If the item is found, move it to the start of the MRU. + let found = -1 + " let elem = a:element + " if g:yankring_max_element_length != 0 + " let elem = strpart(a:element, 0, g:yankring_max_element_length) + " endif + " if s:yr_history_version == 'v1' + " let elem = escape(elem, '@') + " let elem = substitute(elem, "\n", s:yr_history_v1_nl, 'g') + " else + " let elem = substitute(elem, "\n", s:yr_history_v2_nl, 'g') + " endif + " " Append the regtype to the end so we have it available + " let elem = elem.",".a:element_type + + if strlen(a:element) < g:yankring_min_element_length + return 1 + endif + + let elem = s:YRMRUElemFormat(a:element, a:element_type) + + " Refresh the List + call s:YRHistoryRead() + + let found = s:YRMRUHas(a:mru_list, elem) + + " Special case for efficiency, if it is first item in the + " List, do nothing + if found != 0 + if found != -1 + " Remove found item since we will add it to the top + call remove({a:mru_list}, found) + endif + call insert({a:mru_list}, elem, 0) + call s:YRHistorySave() + endif + + return 1 +endfunction + +function! s:YRMRUDel( mru_list, elem_nbr ) + + if a:elem_nbr >= 0 && a:elem_nbr < s:yr_count + call remove({a:mru_list}, a:elem_nbr) + call s:YRHistorySave() + endif + + return 1 +endfunction + +function! s:YRHistoryRead() + let refresh_needed = 1 + let yr_history_list = [] + let yr_filename = s:yr_history_file_{s:yr_history_version} + + if filereadable(yr_filename) + let last_upd = getftime(yr_filename) + + if s:yr_history_last_upd != 0 && last_upd <= s:yr_history_last_upd + let refresh_needed = 0 + endif + + if refresh_needed == 1 + let s:yr_history_list = readfile(yr_filename) + let s:yr_history_last_upd = last_upd + let s:yr_count = len(s:yr_history_list) + return + else + return + endif + else + if s:yr_history_version == 'v2' + " Check to see if an upgrade is required + " else, let the empty yr_history_list be returned. + if filereadable(s:yr_history_file_v1) + " Perform upgrade to v2 of the history file + call s:YRHistoryUpgrade('v1') + return + endif + endif + endif + + let s:yr_history_list = yr_history_list + call s:YRHistorySave() + +endfunction + +function! s:YRHistorySave() + if len(s:yr_history_list) > g:yankring_max_history + " Remove items which exceed the max # specified + call remove(s:yr_history_list, g:yankring_max_history) + endif + + let rc = writefile(s:yr_history_list, s:yr_history_file_{s:yr_history_version}) + + if rc == 0 + let s:yr_history_last_upd = getftime(s:yr_history_file_{s:yr_history_version}) + let s:yr_count = len(s:yr_history_list) + else + call s:YRErrorMsg( + \ 'YRHistorySave: Unable to save yankring history file: '. + \ s:yr_history_file_{s:yr_history_version} + \ ) + endif +endfunction + +function! s:YRHistoryUpgrade(version) + if a:version == 'v1' + if filereadable(s:yr_history_file_v1) + let v1_list = readfile(s:yr_history_file_v1) + let v2_list = [] + for elem in v1_list + " Restore from version 1 + let elem = substitute(elem, s:yr_history_v1_nl_pat, "\n", 'g') + let elem = substitute(elem, '\\@', '@', 'g') + " Encode to version 2 + let elem = substitute(elem, "\n", s:yr_history_v2_nl, 'g') + call add(v2_list, elem) + endfor + let s:yr_history_list = v2_list + call s:YRHistorySave() + call s:YRWarningMsg( + \ "YR:History file:". + \ s:yr_history_file_v1. + \ ' has been upgraded.' + \ ) + endif + endif +endfunction + +" YRWindowUpdate +" Checks if the yankring window is already open. +" If it is, it will refresh it. +function! s:YRWindowUpdate() + let orig_win_bufnr = bufwinnr('%') + + " Switch to the yankring buffer + " only if it is already visible + if bufwinnr(s:yr_buffer_id) != -1 + call s:YRShow(0) + " Switch back to the original buffer + exec orig_win_bufnr . "wincmd w" + endif +endfunction + +" YRWindowStatus +" Displays a brief command list and option settings. +" It also will toggle the Help text. +function! s:YRWindowStatus(show_help) + let full_help = 0 + let orig_win_bufnr = bufwinnr('%') + let yr_win_bufnr = bufwinnr(s:yr_buffer_id) + + if yr_win_bufnr == -1 + " Do not update the window status since the + " yankring is not currently displayed. + return "" + endif + " Switch to the yankring buffer + if orig_win_bufnr != yr_win_bufnr + " If the buffer is visible, switch to it + exec yr_win_bufnr . "wincmd w" + endif + + let msg = 'AutoClose='.g:yankring_window_auto_close. + \ ';ClipboardMonitor='.g:yankring_clipboard_monitor. + \ ';Cmds:,[g]p,[g]P,d,r,s,a,c,u,q,;Help=?'. + \ (s:yr_search==""?"":';SearchRegEx='.s:yr_search) + + if s:yr_has_voperator == 0 + let msg = msg . "\nYankRing has limited functionality without Vim 7.2 or higher" + endif + + " Toggle help by checking the first line of the buffer + if a:show_help == 1 && getline(1) !~ 'selection' + let full_help = 1 + let msg = + \ '" : [p]aste selection'."\n". + \ '" double-click : [p]aste selection'."\n". + \ '" [g]p : [g][p]aste selection'."\n". + \ '" [g]P : [g][P]aste selection'."\n". + \ '" r : [p]aste selection in reverse order'."\n". + \ '" s : [s]earch the yankring for text'."\n". + \ '" u : [u]pdate display'."\n". + \ '" a : toggle [a]utoclose setting'."\n". + \ '" c : toggle [c]lipboard monitor setting'."\n". + \ '" q : [q]uit / close the yankring window'."\n". + \ '" ? : Remove help text'."\n". + \ '" : toggles the width of the window'."\n". + \ '" Visual mode is supported for above commands'."\n". + \ msg + endif + + let saveMod = &modifiable + + " Go to the top of the buffer and remove any previous status + " Use the blackhole register so it does not affect the yankring + setlocal modifiable + exec 0 + silent! exec 'norm! "_d/^---'."\n" + call histdel("search", -1) + + silent! 0put =msg + + " Erase it's contents to the blackhole + silent! exec '%g/^\s*$/delete _' + call histdel("search", -1) + + call cursor(1,1) + if full_help == 0 + call search('^\d', 'W') + endif + + let &modifiable = saveMod + + if orig_win_bufnr != s:yr_buffer_id + exec orig_win_bufnr . "wincmd w" + endif +endfunction + +" YRWindowOpen +" Display the Most Recently Used file list in a temporary window. +function! s:YRWindowOpen(results) + + " Setup the cpoptions properly for the maps to work + let old_cpoptions = &cpoptions + set cpoptions&vim + setlocal cpoptions-=a,A + + " Save the current buffer number. The yankring will switch back to + " this buffer when an action is taken. + let s:yr_buffer_last = bufnr('%') + let s:yr_buffer_last_winnr = winnr() + + if bufwinnr(s:yr_buffer_id) == -1 + if g:yankring_window_use_horiz == 1 + if g:yankring_window_use_bottom == 1 + let location = 'botright' + else + let location = 'topleft' + " Creating the new window will offset all other + " window numbers. Account for that so we switch + " back to the correct window. + let s:yr_buffer_last_winnr = s:yr_buffer_last_winnr + 1 + endif + let win_size = g:yankring_window_height + else + " Open a horizontally split window. Increase the window size, if + " needed, to accomodate the new window + if g:yankring_window_width && + \ &columns < (80 + g:yankring_window_width) + " one extra column is needed to include the vertical split + let &columns = &columns + g:yankring_window_width + 1 + let s:yr_winsize_chgd = 1 + else + let s:yr_winsize_chgd = 0 + endif + + if g:yankring_window_use_right == 1 + " Open the window at the rightmost place + let location = 'botright vertical' + else + " Open the window at the leftmost place + let location = 'topleft vertical' + " Creating the new window will offset all other + " window numbers. Account for that so we switch + " back to the correct window. + let s:yr_buffer_last_winnr = s:yr_buffer_last_winnr + 1 + endif + let win_size = g:yankring_window_width + endif + + " Special consideration was involved with these sequence + " of commands. + " First, split the current buffer. + " Second, edit a new file. + " Third record the buffer number. + " If a different sequence is followed when the yankring + " buffer is closed, Vim's alternate buffer is the yanking + " instead of the original buffer before the yankring + " was shown. + let cmd_mod = '' + if v:version >= 700 + let cmd_mod = 'keepalt ' + endif + exec 'silent! ' . cmd_mod . location . ' ' . win_size . 'split ' + + " Using :e and hide prevents the alternate buffer + " from being changed. + exec ":e " . escape(s:yr_buffer_name, ' ') + " Save buffer id + let s:yr_buffer_id = bufnr('%') + 0 + else + " If the buffer is visible, switch to it + exec bufwinnr(s:yr_buffer_id) . "wincmd w" + endif + + " Perform a double check to ensure we have entered the correct + " buffer since we don't want to do the %d_ in the wrong buffer! + if (bufnr('%') + 0) != s:yr_buffer_id + call s:YRWarningMsg( + \ "YR:Failed to change to the yankring buffer, please contact author id:". + \ s:yr_buffer_id. + \ ' last:'.s:yr_buffer_last + \ ) + return -1 + endif + + " Mark the buffer as scratch + setlocal buftype=nofile + setlocal bufhidden=hide + setlocal noswapfile + setlocal nowrap + setlocal nonumber + setlocal nobuflisted + setlocal noreadonly + setlocal modifiable + + " set up syntax highlighting + syn match yankringTitle #^--- YankRing ---$#hs=s+4,he=e-4 + syn match yankringHeaders #^Elem Content$# + syn match yankringItemNumber #^\d\+# + + syn match yankringKey #^AutoClose.*#hs=e-6 + syn match yankringKey #^AutoClose.*\[g\]p#hs=e-3 contains=yankringKey + syn match yankringKey #^AutoClose.*\[p\]P#hs=e-3 contains=yankringKey + syn match yankringKey #^AutoClose.*,d,#hs=e-1,he=e-1 contains=yankringKey + syn match yankringKey #^AutoClose.*,r,#hs=e-1,he=e-1 contains=yankringKey + syn match yankringKey #^AutoClose.*,s,#hs=e-1,he=e-1 contains=yankringKey + syn match yankringKey #^AutoClose.*,a,#hs=e-1,he=e-1 contains=yankringKey + syn match yankringKey #^AutoClose.*,c,#hs=e-1,he=e-1 contains=yankringKey + syn match yankringKey #^AutoClose.*,u,#hs=e-1,he=e-1 contains=yankringKey + syn match yankringKey #^AutoClose.*,q,#hs=e-1,he=e-1 contains=yankringKey + syn match yankringKey #^AutoClose.*#hs=e-6 contains=yankringKey + syn match yankringKey #^AutoClose.*?$#hs=e contains=yankringKey + + syn match yankringKey #^".*:#hs=s+1,he=e-1 + syn match yankringHelp #^".*$# contains=yankringKey + + hi link yankringTitle directory + hi link yankringHeaders keyword + hi link yankringItemNumber constant + hi link yankringKey identifier + hi link yankringHelp string + + " Clear all existing maps for this buffer + " We should do this for all maps, but I am not sure how to do + " this for this buffer/window only without affecting all the + " other buffers. + mapclear + " Create a mapping to act upon the yankring + nnoremap <2-LeftMouse> :call YRWindowActionN('p' ,'n') + nnoremap :call YRWindowActionN('p' ,'n') + xnoremap :call YRWindowAction ('p' ,'v') + nnoremap p :call YRWindowActionN('p' ,'n') + xnoremap p :call YRWindowAction ('p' ,'v') + nnoremap P :call YRWindowActionN('P' ,'n') + xnoremap P :call YRWindowAction ('P' ,'v') + nnoremap gp :call YRWindowActionN('gp','n') + xnoremap gp :call YRWindowAction ('gp','v') + nnoremap gP :call YRWindowActionN('gP','n') + xnoremap gP :call YRWindowAction ('gP','v') + nnoremap d :call YRWindowActionN('d' ,'n') + xnoremap d :call YRWindowAction ('d' ,'v') + xnoremap r :call YRWindowAction ('r' ,'v') + nnoremap s :call YRWindowAction ('s' ,'n') + nnoremap a :call YRWindowAction ('a' ,'n') + nnoremap c :call YRWindowAction ('c' ,'n') + nnoremap ? :call YRWindowAction ('?' ,'n') + nnoremap u :call YRWindowAction ('u' ,'n') + nnoremap q :call YRWindowAction ('q' ,'n') + nnoremap \|:silent exec 'vertical resize '. + \ ( + \ g:yankring_window_use_horiz!=1 && winwidth('.') > g:yankring_window_width + \ ?(g:yankring_window_width) + \ :(winwidth('.') + g:yankring_window_increment) + \ ) + + " Erase it's contents to the blackhole + silent! exec '%delete _' + + " Display the status line / help + call s:YRWindowStatus(0) + exec 'normal! G' + + " Display the contents of the yankring + silent! put =a:results + + if getline('$') == '' + " Erase last blank line + silent! exec '$delete _' + endif + + " Move the cursor to the first line with an element + exec 0 + call search('^\d','W') + + setlocal nomodifiable + " + " Restore the previous cpoptions settings + let &cpoptions = old_cpoptions + +endfunction + +function! s:YRWindowActionN(op, cmd_mode) + let v_count = v:count + " If no count was specified it will have a value of 0 + " so set it to at least 1 + let v_count = ((v_count > 0)?(v_count):1) + + if v_count > 1 + if !exists("b:yankring_show_range_error") + let b:yankring_show_range_error = v_count + else + let b:yankring_show_range_error = b:yankring_show_range_error - 1 + endif + + if b:yankring_show_range_error == 1 + call s:YRWarningMsg("YR:Use visual mode if you need to specify a count") + unlet b:yankring_show_range_error + endif + return + endif + + call s:YRWindowAction(a:op, a:cmd_mode) + let v_count = v_count - 1 + + if g:yankring_window_auto_close == 1 && v_count == 0 && a:op != 'd' + " If autoclose is set close the window unless + " you are removing items from the YankRing + exec 'bdelete '.s:yr_buffer_id + return "" + endif + + return "" +endfunction + +function! s:YRWindowAction(op, cmd_mode) range + let default_buffer = ((&clipboard=='unnamed')?'+':'"') + let opcode = a:op + let lines = [] + let v_count = v:count + let cmd_mode = a:cmd_mode + let firstline = a:firstline + let lastline = a:lastline + + if a:lastline < a:firstline + let firstline = a:lastline + let lastline = a:firstline + endif + + if cmd_mode == 'n' + let v_count = 1 + " If a count was provided (5p), we want to repeat the paste + " 5 times, but this also alters the a:firstline and a:lastline + " ranges, which while in normal mode we do not want + let lastline = firstline + endif + " If no count was specified it will have a value of 0 + " so set it to at least 1 + let v_count = ((v_count > 0)?(v_count):1) + + if '[dr]' =~ opcode + " Reverse the order of the lines to act on + let begin = lastline + while begin >= firstline + call add(lines, getline(begin)) + let begin = begin - 1 + endwhile + else + " Process the selected items in order + let begin = firstline + while begin <= lastline + call add(lines, getline(begin)) + let begin = begin + 1 + endwhile + endif + + if opcode ==# 'q' + " Close the yankring window + if s:yr_winsize_chgd == 1 + " Adjust the Vim window width back to the width + " it was before we showed the yankring window + let &columns= &columns - (g:yankring_window_width) + endif + + " Hide the YankRing window + hide + + if bufwinnr(s:yr_buffer_last) != -1 + " If the buffer is visible, switch to it + exec s:yr_buffer_last_winnr . "wincmd w" + endif + + return + elseif opcode ==# 's' + " Switch back to the original buffer + exec s:yr_buffer_last_winnr . "wincmd w" + + call s:YRSearch() + return + elseif opcode ==# 'u' + " Switch back to the original buffer + exec s:yr_buffer_last_winnr . "wincmd w" + + call s:YRShow(0) + return + elseif opcode ==# 'a' + let l:curr_line = line(".") + " Toggle the auto close setting + let g:yankring_window_auto_close = + \ (g:yankring_window_auto_close == 1?0:1) + " Display the status line / help + call s:YRWindowStatus(0) + call cursor(l:curr_line,0) + return + elseif opcode ==# 'c' + let l:curr_line = line(".") + " Toggle the clipboard monitor setting + let g:yankring_clipboard_monitor = + \ (g:yankring_clipboard_monitor == 1?0:1) + " Display the status line / help + call s:YRWindowStatus(0) + call cursor(l:curr_line,0) + return + elseif opcode ==# '?' + " Display the status line / help + call s:YRWindowStatus(1) + return + endif + + " Switch back to the original buffer + exec s:yr_buffer_last_winnr . "wincmd w" + + " Intentional case insensitive comparision + if opcode =~? 'p' + let cmd = 'YRGetElem ' + let parms = ", '".opcode."' " + elseif opcode ==? 'r' + let opcode = 'p' + let cmd = 'YRGetElem ' + let parms = ", 'p' " + elseif opcode ==# 'd' + let cmd = 'YRPop ' + let parms = "" + endif + + " Only execute this code if we are operating on elements + " within the yankring + if '[auq?]' !~# opcode + while v_count > 0 + " let iter = 0 + " let index = 0 + for line in lines + let elem = matchstr(line, '^\d\+') + if elem > 0 + if elem > 0 && elem <= s:yr_count + " if iter > 0 && opcode =~# 'p' + if opcode =~# 'p' + " Move to the end of the last pasted item + " only if pasting after (not above) + " '] + endif + exec cmd . elem . parms + " let iter += 1 + endif + endif + endfor + let v_count = v_count - 1 + endwhile + + if opcode ==# 'd' + call s:YRShow(0) + return "" + endif + + if g:yankring_window_auto_close == 1 && cmd_mode == 'v' + exec 'bdelete '.s:yr_buffer_id + return "" + endif + + endif + + return "" + +endfunction + +function! s:YRWarningMsg(msg) + echohl WarningMsg + echomsg a:msg + echohl None +endfunction + +function! s:YRErrorMsg(msg) + echohl ErrorMsg + echomsg a:msg + echohl None +endfunction + +function! s:YRWinLeave() + " Track which window we are last in. We will use this information + " to determine where we need to paste any contents, or which + " buffer to return to. + + if s:yr_buffer_id < 0 + " The yankring window has never been activated + return + endif + + if winbufnr(winnr()) == s:yr_buffer_id + " Ignore leaving the yankring window + return + endif + + if bufwinnr(s:yr_buffer_id) != -1 + " YankRing window is visible, so save off the previous buffer ids + let s:yr_buffer_last_winnr = winnr() + let s:yr_buffer_last = winbufnr(s:yr_buffer_last_winnr) + " else + " let s:yr_buffer_last_winnr = -1 + " let s:yr_buffer_last = -1 + endif +endfunction + +function! s:YRFocusGained() + if g:yankring_clipboard_monitor == 1 + " If the clipboard has changed record it inside the yankring + " echomsg "YRFocusGained[".len(@+)."][".@+.']['.s:yr_prev_clipboard.']' + if len(@+) > 0 && @+ != s:yr_prev_clipboard + let elem = s:YRMRUElemFormat( + \ getreg('+') + \ , getregtype('+') + \ ) + let found = s:YRMRUHas('s:yr_history_list', elem) + + " Only add the item to the "top" of the ring if it is + " not in the ring already. + if found == -1 + call YRRecord("+") + " silent! call YRRecord("+") + endif + endif + + " If the yankring window is open, refresh it + call s:YRWindowUpdate() + endif +endfunction + +function! s:YRInsertLeave() + " The YankRing uses omaps to execute the prescribed motion + " and then appends to the motion a call to a YankRing + " function to record the contents of the changed register. + " + " We cannot append a function call to the end of a motion + " that results in Insert mode. For example, any command + " like 'cw' enters insert mode. Appending a function call + " after the w, simply writes out the call as if the user + " typed it. + " + " Using the InsertLeave event, allows us to capture the + " contents of any changed register after it completes. + + call YRRecord(s:YRRegister()) + + " When performing a change (not a yank or delete) + " it is not possible to call yrrecord at the end + " of the command (or it's contents will be inserted + " into the buffer instead of executed). + " So, when using ".", we have to remove the _zap_ + " keys and then re-add them back again after we + " record the updates. + if s:yr_remove_omap_dot == 1 + call s:YRMapsCreate('add_only_zap_keys') + endif + +endfunction + +" Deleting autocommands first is a good idea especially if we want to reload +" the script without restarting vim. +" Call YRFocusGained to check if the clipboard has been updated +augroup YankRing + autocmd! + autocmd VimEnter * :if has('clipboard') | call YRFocusGained() | endif + autocmd WinLeave * :call YRWinLeave() + autocmd FocusGained * :if has('clipboard') | call YRFocusGained() | endif + autocmd InsertLeave * :call YRInsertLeave() +augroup END + + +" copy register +inoremap