├── README └── plugin └── autoclose.vim /README: -------------------------------------------------------------------------------- 1 | This is a mirror of http://www.vim.org/scripts/script.php?script_id=1849 2 | 3 | There are very simple ways to insert a closing brace by setting up a few mappings. The problem with all these approaches is that they break undo, history or both. This script is my sixth attempt at correcting this problem and the first one that has worked in all scenarios. 4 | 5 | Using this script, typing ``(`` will result in (|), where | is the cursor position and the double backticks are just marking input. Typing a ``)`` will move the cursor outside the parens. This moving outside works even in nested scenarios. Typing ``if(my_array['key`` results in if(my_array['key|']) and ``)`` gets you if(my_array['key'])|. 6 | 7 | The paired characters are: [, (, {, ", ' 8 | 9 | If you like this script, you should also check out surround.vim 10 | -------------------------------------------------------------------------------- /plugin/autoclose.vim: -------------------------------------------------------------------------------- 1 | " File: autoclose.vim 2 | " Author: Karl Guertin 3 | " Version: 1.2 4 | " Last Modified: June 18, 2009 5 | " Description: AutoClose, closes what's opened. 6 | " 7 | " This plugin closes opened parenthesis, braces, brackets, quotes as you 8 | " type them. As of 1.1, if you type the open brace twice ({{), the closing 9 | " brace will be pushed down to a new line. 10 | " 11 | " You can enable or disable this plugin by typing \a (or a if you 12 | " changed your Leader char). You can define your own mapping and will need 13 | " to do so if you have something else mapped to \a since this plugin won't 14 | " clobber your mapping. Here's how to map \x: 15 | " 16 | " nmap x ToggleAutoCloseMappings 17 | " 18 | " You'll also probably want to know you can type ( if mswin is 19 | " set) and the next character you type doesn't have mappings applied. This 20 | " is useful when you want to insert only an opening paren or something. 21 | " 22 | " NOTE: If you're using this on a terminal and your arrow keys are broken, 23 | " be sure to :set ttimeout and :set ttimeoutlen=100 24 | " 25 | " Version Changes: --------------------------------------------------{{{2 26 | " 1.2 -- Fixed some edge cases where double the closing characters are 27 | " entered when exiting insert mode. 28 | " Finally (!) reproduced the arrow keys problem other people were 29 | " running into and fixed. 30 | " Typing a closing character will now behave consistently (jump 31 | " out) regardless of the plugin's internal state. 32 | " 33 | " As a part of the close fix, I've opted to not try tracking the 34 | " position of the closing characters through all the things that 35 | " could be done with them, so arrowing/jumping around and not 36 | " winding up back where you started will cause the input to not be 37 | " repeatable. 38 | " June 18, 2009 39 | " 1.1.2 -- Fixed a mapping typo and caught a double brace problem, 40 | " September 20, 2007 41 | " 1.1.1 -- Missed a bug in 1.1, September 19, 2007 42 | " 1.1 -- When not inserting at the end, previous version would eat chars 43 | " at end of line, added double open->newline, September 19, 2007 44 | " 1.0.1 -- Cruft from other parts of the mapping, knew I shouldn't have 45 | " released the first as 1.0, April 3, 2007 46 | 47 | " Setup -----------------------------------------------------{{{2 48 | if exists('g:autoclose_loaded') || &cp 49 | finish 50 | endif 51 | 52 | 53 | let g:autoclose_loaded = 1 54 | let s:cotstate = &completeopt 55 | 56 | if !exists('g:autoclose_on') 57 | let g:autoclose_on = 1 58 | endif 59 | 60 | " (Toggle) Mappings -----------------------------{{{1 61 | " 62 | nmap ToggleAutoCloseMappings :call ToggleAutoCloseMappings() 63 | if (!hasmapto( 'ToggleAutoCloseMappings', 'n' )) 64 | nmap a ToggleAutoCloseMappings 65 | endif 66 | fun ToggleAutoCloseMappings() " --- {{{2 67 | if g:autoclose_on 68 | iunmap " 69 | iunmap ' 70 | iunmap ( 71 | iunmap ) 72 | iunmap [ 73 | iunmap ] 74 | iunmap { 75 | iunmap } 76 | iunmap 77 | iunmap 78 | iunmap 79 | let g:autoclose_on = 0 80 | echo "AutoClose Off" 81 | else 82 | inoremap " =QuoteDelim('"') 83 | inoremap ' =match(getline('.')[col('.') - 2],'\w') == 0 && getline('.')[col('.')-1] != "'" ? "'" : QuoteDelim("'") 84 | inoremap ( (=CloseStackPush(')') 85 | inoremap ) =CloseStackPop(')') 86 | inoremap [ [=CloseStackPush(']') 87 | inoremap ] =CloseStackPop(']') 88 | "inoremap { {=CloseStackPush('}') 89 | inoremap { =OpenSpecial('{','}') 90 | inoremap } =CloseStackPop('}') 91 | inoremap =OpenCloseBackspace() 92 | inoremap =OpenCloseBackspace() 93 | inoremap =CloseStackPop('') 94 | inoremap =CloseStackPop('') 95 | "the following simply creates an ambiguous mapping so vim fully 96 | "processes the escape sequence for terminal keys, see 'ttimeout' for a 97 | "rough explanation, this just forces it to work 98 | if &term[:4] == "xterm" 99 | inoremap OC 100 | endif 101 | let g:autoclose_on = 1 102 | if a:0 == 0 103 | "this if is so this message doesn't show up at load 104 | echo "AutoClose On" 105 | endif 106 | endif 107 | endf 108 | let s:closeStack = [] 109 | 110 | " AutoClose Utilities -----------------------------------------{{{1 111 | function OpenSpecial(ochar,cchar) " ---{{{2 112 | let line = getline('.') 113 | let col = col('.') - 2 114 | "echom string(col).':'.line[:(col)].'|'.line[(col+1):] 115 | if a:ochar == line[(col)] && a:cchar == line[(col+1)] "&& strlen(line) - (col) == 2 116 | "echom string(s:closeStack) 117 | while len(s:closeStack) > 0 118 | call remove(s:closeStack, 0) 119 | endwhile 120 | return "\a\;\".a:cchar."\\"_xk$\"_xa" 121 | endif 122 | return a:ochar.CloseStackPush(a:cchar) 123 | endfunction 124 | 125 | function CloseStackPush(char) " ---{{{2 126 | "echom "push" 127 | let line = getline('.') 128 | let col = col('.')-2 129 | if (col) < 0 130 | call setline('.',a:char.line) 131 | else 132 | "echom string(col).':'.line[:(col)].'|'.line[(col+1):] 133 | call setline('.',line[:(col)].a:char.line[(col+1):]) 134 | endif 135 | call insert(s:closeStack, a:char) 136 | "echom join(s:closeStack,'').' -- '.a:char 137 | return '' 138 | endf 139 | 140 | function JumpOut(char) " ----------{{{2 141 | let column = col('.') - 1 142 | let line = getline('.') 143 | let mcol = match(line[column :], a:char) 144 | if a:char != '' && mcol >= 0 145 | "Yeah, this is ugly but vim actually requires each to be special 146 | "cased to avoid screen flashes/not doing the right thing. 147 | echom len(line).' '.(column+mcol) 148 | if line[column] == a:char 149 | return "\" 150 | elseif column+mcol == len(line)-1 151 | return "\A" 152 | else 153 | return "\f".a:char."\" 154 | endif 155 | else 156 | return a:char 157 | endif 158 | endf 159 | function CloseStackPop(char) " ---{{{2 160 | "echom "pop" 161 | if(a:char == '') 162 | pclose 163 | endif 164 | if len(s:closeStack) == 0 165 | return JumpOut(a:char) 166 | endif 167 | let column = col('.') - 1 168 | let line = getline('.') 169 | let popped = '' 170 | let lastpop = '' 171 | "echom join(s:closeStack,'').' || '.lastpop 172 | while len(s:closeStack) > 0 && ((lastpop == '' && popped == '') || lastpop != a:char) 173 | let lastpop = remove(s:closeStack,0) 174 | let popped .= lastpop 175 | "echom join(s:closeStack,'').' || '.lastpop.' || '.popped 176 | endwhile 177 | "echom ' --> '.popped 178 | if line[column : column+strlen(popped)-1] != popped 179 | return JumpOut('') 180 | endif 181 | if column > 0 182 | call setline('.',line[:column-1].line[(column+strlen(popped)):]) 183 | else 184 | call setline('.','') 185 | endif 186 | return popped 187 | endf 188 | 189 | function QuoteDelim(char) " ---{{{2 190 | let line = getline('.') 191 | let col = col('.') 192 | if line[col - 2] == "\\" 193 | "Inserting a quoted quotation mark into the string 194 | return a:char 195 | elseif line[col - 1] == a:char 196 | "Escaping out of the string 197 | return "\=".s:SID()."CloseStackPop(\"\\".a:char."\")\" 198 | else 199 | "Starting a string 200 | return a:char."\=".s:SID()."CloseStackPush(\"\\".a:char."\")\" 201 | endif 202 | endf 203 | 204 | " The strings returned from QuoteDelim aren't in scope for , so I 205 | " have to fake it using this function (from the Vim help, but tweaked) 206 | function s:SID() 207 | return matchstr(expand(''), '\d\+_\zeSID$') 208 | endfun 209 | 210 | function OpenCloseBackspace() " ---{{{2 211 | "if pumvisible() 212 | " pclose 213 | " call StopOmni() 214 | " return "\" 215 | "else 216 | let curline = getline('.') 217 | let curpos = col('.') 218 | let curletter = curline[curpos-1] 219 | let prevletter = curline[curpos-2] 220 | if (prevletter == '"' && curletter == '"') || 221 | \ (prevletter == "'" && curletter == "'") || 222 | \ (prevletter == "(" && curletter == ")") || 223 | \ (prevletter == "{" && curletter == "}") || 224 | \ (prevletter == "[" && curletter == "]") 225 | if len(s:closeStack) > 0 226 | call remove(s:closeStack,0) 227 | endif 228 | return "\\" 229 | else 230 | return "\" 231 | endif 232 | "endif 233 | endf 234 | 235 | " Initialization ----------------------------------------{{{1 236 | if g:autoclose_on 237 | let g:autoclose_on = 0 238 | silent call ToggleAutoCloseMappings() 239 | endif 240 | " vim: set ft=vim ff=unix et sw=4 ts=4 : 241 | " vim600: set foldmethod=marker foldmarker={{{,}}} foldlevel=1 : 242 | --------------------------------------------------------------------------------