" File: autoclose.vim " Author: Karl Guertin " Version: 1.2 " Last Modified: June 18, 2009 " Description: AutoClose, closes what's opened. " " This plugin closes opened parenthesis, braces, brackets, quotes as you " type them. As of 1.1, if you type the open brace twice ({{), the closing " brace will be pushed down to a new line. " " You can enable or disable this plugin by typing \a (or a if you " changed your Leader char). You can define your own mapping and will need " to do so if you have something else mapped to \a since this plugin won't " clobber your mapping. Here's how to map \x: " " nmap x ToggleAutoCloseMappings " " You'll also probably want to know you can type ( if mswin is " set) and the next character you type doesn't have mappings applied. This " is useful when you want to insert only an opening paren or something. " " NOTE: If you're using this on a terminal and your arrow keys are broken, " be sure to :set ttimeout and :set ttimeoutlen=100 " " Version Changes: --------------------------------------------------{{{2 " 1.2 -- Fixed some edge cases where double the closing characters are " entered when exiting insert mode. " Finally (!) reproduced the arrow keys problem other people were " running into and fixed. " Typing a closing character will now behave consistently (jump " out) regardless of the plugin's internal state. " " As a part of the close fix, I've opted to not try tracking the " position of the closing characters through all the things that " could be done with them, so arrowing/jumping around and not " winding up back where you started will cause the input to not be " repeatable. " June 18, 2009 " 1.1.2 -- Fixed a mapping typo and caught a double brace problem, " September 20, 2007 " 1.1.1 -- Missed a bug in 1.1, September 19, 2007 " 1.1 -- When not inserting at the end, previous version would eat chars " at end of line, added double open->newline, September 19, 2007 " 1.0.1 -- Cruft from other parts of the mapping, knew I shouldn't have " released the first as 1.0, April 3, 2007 " Setup -----------------------------------------------------{{{2 if exists('g:autoclose_loaded') || &cp finish endif let g:autoclose_loaded = 1 let s:cotstate = &completeopt if !exists('g:autoclose_on') let g:autoclose_on = 1 endif " (Toggle) Mappings -----------------------------{{{1 " nmap ToggleAutoCloseMappings :call ToggleAutoCloseMappings() if (!hasmapto( 'ToggleAutoCloseMappings', 'n' )) nmap a ToggleAutoCloseMappings endif fun ToggleAutoCloseMappings() " --- {{{2 if g:autoclose_on iunmap " iunmap ' iunmap ( iunmap ) iunmap [ iunmap ] iunmap { iunmap } iunmap iunmap iunmap let g:autoclose_on = 0 echo "AutoClose Off" else inoremap " =QuoteDelim('"') inoremap ' =match(getline('.')[col('.') - 2],'\w') == 0 && getline('.')[col('.')-1] != "'" ? "'" : QuoteDelim("'") inoremap ( (=CloseStackPush(')') inoremap ) =CloseStackPop(')') inoremap [ [=CloseStackPush(']') inoremap ] =CloseStackPop(']') "inoremap { {=CloseStackPush('}') inoremap { =OpenSpecial('{','}') inoremap } =CloseStackPop('}') inoremap =OpenCloseBackspace() inoremap =OpenCloseBackspace() inoremap =CloseStackPop('') inoremap =CloseStackPop('') "the following simply creates an ambiguous mapping so vim fully "processes the escape sequence for terminal keys, see 'ttimeout' for a "rough explanation, this just forces it to work if &term[:4] == "xterm" inoremap OC endif let g:autoclose_on = 1 if a:0 == 0 "this if is so this message doesn't show up at load echo "AutoClose On" endif endif endf let s:closeStack = [] " AutoClose Utilities -----------------------------------------{{{1 function OpenSpecial(ochar,cchar) " ---{{{2 let line = getline('.') let col = col('.') - 2 "echom string(col).':'.line[:(col)].'|'.line[(col+1):] if a:ochar == line[(col)] && a:cchar == line[(col+1)] "&& strlen(line) - (col) == 2 "echom string(s:closeStack) while len(s:closeStack) > 0 call remove(s:closeStack, 0) endwhile return "\a\;\".a:cchar."\\"_xk$\"_xa" endif return a:ochar.CloseStackPush(a:cchar) endfunction function CloseStackPush(char) " ---{{{2 "echom "push" let line = getline('.') let col = col('.')-2 if (col) < 0 call setline('.',a:char.line) else "echom string(col).':'.line[:(col)].'|'.line[(col+1):] call setline('.',line[:(col)].a:char.line[(col+1):]) endif call insert(s:closeStack, a:char) "echom join(s:closeStack,'').' -- '.a:char return '' endf function JumpOut(char) " ----------{{{2 let column = col('.') - 1 let line = getline('.') let mcol = match(line[column :], a:char) if a:char != '' && mcol >= 0 "Yeah, this is ugly but vim actually requires each to be special "cased to avoid screen flashes/not doing the right thing. echom len(line).' '.(column+mcol) if line[column] == a:char return "\" elseif column+mcol == len(line)-1 return "\A" else return "\f".a:char."\" endif else return a:char endif endf function CloseStackPop(char) " ---{{{2 "echom "pop" if(a:char == '') pclose endif if len(s:closeStack) == 0 return JumpOut(a:char) endif let column = col('.') - 1 let line = getline('.') let popped = '' let lastpop = '' "echom join(s:closeStack,'').' || '.lastpop while len(s:closeStack) > 0 && ((lastpop == '' && popped == '') || lastpop != a:char) let lastpop = remove(s:closeStack,0) let popped .= lastpop "echom join(s:closeStack,'').' || '.lastpop.' || '.popped endwhile "echom ' --> '.popped if line[column : column+strlen(popped)-1] != popped return JumpOut('') endif if column > 0 call setline('.',line[:column-1].line[(column+strlen(popped)):]) else call setline('.','') endif return popped endf function QuoteDelim(char) " ---{{{2 let line = getline('.') let col = col('.') if line[col - 2] == "\\" "Inserting a quoted quotation mark into the string return a:char elseif line[col - 1] == a:char "Escaping out of the string return "\=".s:SID()."CloseStackPop(\"\\".a:char."\")\" else "Starting a string return a:char."\=".s:SID()."CloseStackPush(\"\\".a:char."\")\" endif endf " The strings returned from QuoteDelim aren't in scope for , so I " have to fake it using this function (from the Vim help, but tweaked) function s:SID() return matchstr(expand(''), '\d\+_\zeSID$') endfun function OpenCloseBackspace() " ---{{{2 "if pumvisible() " pclose " call StopOmni() " return "\" "else let curline = getline('.') let curpos = col('.') let curletter = curline[curpos-1] let prevletter = curline[curpos-2] if (prevletter == '"' && curletter == '"') || \ (prevletter == "'" && curletter == "'") || \ (prevletter == "(" && curletter == ")") || \ (prevletter == "{" && curletter == "}") || \ (prevletter == "[" && curletter == "]") if len(s:closeStack) > 0 call remove(s:closeStack,0) endif return "\\" else return "\" endif "endif endf " Initialization ----------------------------------------{{{1 if g:autoclose_on let g:autoclose_on = 0 silent call ToggleAutoCloseMappings() endif " vim: set ft=vim ff=unix et sw=4 ts=4 : " vim600: set foldmethod=marker foldmarker={{{,}}} foldlevel=1 :