├── LICENSE ├── README.md ├── autoload ├── argwrap.vim └── argwrap │ ├── hooks.vim │ └── hooks │ ├── 000_curpos.vim │ └── filetype │ └── php │ └── 200_smart_brace.vim ├── doc └── argwrap.txt ├── img └── demo.gif └── plugin └── argwrap.vim /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2014-2019 Alex Yatskov 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Vim-Argwrap 2 | 3 | This repository has permanently moved to [git.foosoft.net](https://git.foosoft.net/alex/vim-argwrap). 4 | -------------------------------------------------------------------------------- /autoload/argwrap.vim: -------------------------------------------------------------------------------- 1 | " Copyright (c) 2014 Alex Yatskov 2 | " 3 | " Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | " this software and associated documentation files (the "Software"), to deal in 5 | " the Software without restriction, including without limitation the rights to 6 | " use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | " the Software, and to permit persons to whom the Software is furnished to do so, 8 | " subject to the following conditions: 9 | " 10 | " The above copyright notice and this permission notice shall be included in all 11 | " copies or substantial portions of the Software. 12 | " 13 | " THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | " IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | " FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | " COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | " IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | " CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | 20 | 21 | function! argwrap#validateRange(range) 22 | return len(a:range) > 0 && !(a:range.lineStart == 0 && a:range.colStart == 0 || a:range.lineEnd == 0 && a:range.colEnd == 0) 23 | endfunction 24 | 25 | function! argwrap#compareRanges(range1, range2) 26 | let [l:buffer, l:line, l:col, l:offset] = getpos('.') 27 | 28 | let l:lineDiff1 = a:range1.lineStart - l:line 29 | let l:colDiff1 = a:range1.colStart - l:col 30 | let l:lineDiff2 = a:range2.lineStart - l:line 31 | let l:colDiff2 = a:range2.colStart - l:col 32 | 33 | if l:lineDiff1 < l:lineDiff2 34 | return 1 35 | elseif l:lineDiff1 > l:lineDiff2 36 | return -1 37 | elseif l:colDiff1 < l:colDiff2 38 | return 1 39 | elseif l:colDiff1 > l:colDiff2 40 | return -1 41 | else 42 | return 0 43 | endif 44 | endfunction 45 | 46 | function! argwrap#findRange(braces) 47 | let l:filter = 'synIDattr(synID(line("."), col("."), 0), "name") =~? "string"' 48 | let [l:lineStart, l:colStart] = searchpairpos(a:braces[0], '', a:braces[1], 'Wcnb', filter) 49 | let [l:lineEnd, l:colEnd] = searchpairpos(a:braces[0], '', a:braces[1], 'Wcn', filter) 50 | return {'lineStart': l:lineStart, 'colStart': l:colStart, 'lineEnd': l:lineEnd, 'colEnd': l:colEnd} 51 | endfunction 52 | 53 | function! argwrap#findClosestRange() 54 | let l:ranges = [] 55 | for l:braces in [['(', ')'], ['\[', '\]'], ['{', '}']] 56 | let l:range = argwrap#findRange(braces) 57 | if argwrap#validateRange(l:range) 58 | call add(l:ranges, l:range) 59 | endif 60 | endfor 61 | 62 | if len(l:ranges) == 0 63 | return {} 64 | else 65 | return sort(l:ranges, 'argwrap#compareRanges')[0] 66 | endif 67 | endfunction 68 | 69 | function! argwrap#extractContainerArgText(range, linePrefix) 70 | let l:text = '' 71 | let l:trimPattern = printf('\m^\s*\(.\{-}\%%(%s\)\?\)\s*$', escape(a:linePrefix, '\$.*^[')) 72 | 73 | for l:lineIndex in range(a:range.lineStart, a:range.lineEnd) 74 | let l:lineText = getline(l:lineIndex) 75 | 76 | let l:extractStart = 0 77 | if l:lineIndex == a:range.lineStart 78 | let l:extractStart = a:range.colStart 79 | endif 80 | 81 | let l:extractEnd = strlen(l:lineText) 82 | if l:lineIndex == a:range.lineEnd 83 | let l:extractEnd = a:range.colEnd - 1 84 | endif 85 | 86 | if l:extractStart < l:extractEnd 87 | let l:extract = l:lineText[l:extractStart : l:extractEnd - 1] 88 | let l:extract = substitute(l:extract, l:trimPattern, '\1', '') 89 | if stridx(l:extract, a:linePrefix) == 0 90 | let l:extract = l:extract[len(a:linePrefix):] 91 | endif 92 | let l:extract = substitute(l:extract, ',$', ', ', '') 93 | let l:text .= l:extract 94 | endif 95 | endfor 96 | 97 | return l:text 98 | endfunction 99 | 100 | function! argwrap#updateScope(stack, char) 101 | let l:pairs = {'"': '"', '''': '''', ')': '(', ']': '[', '}': '{'} 102 | let l:length = len(a:stack) 103 | 104 | if l:length > 0 && get(l:pairs, a:char, '') == a:stack[l:length - 1] 105 | call remove(a:stack, l:length - 1) 106 | elseif index(values(l:pairs), a:char) >= 0 107 | call add(a:stack, a:char) 108 | endif 109 | endfunction 110 | 111 | function! argwrap#trimArgument(text) 112 | let l:trim = substitute(a:text, '^\s*\(.\{-}\)\s*$', '\1', '') 113 | let l:trim = substitute(l:trim, '\([:=]\)\s\{2,}', '\1 ', '') 114 | return substitute(l:trim, '\s\{2,}\([:=]\)', ' \1', '') 115 | endfunction 116 | 117 | function! argwrap#extractContainerArgs(text) 118 | let l:text = substitute(a:text, '^\s*\(.\{-}\)\s*$', '\1', '') 119 | 120 | let l:stack = [] 121 | let l:arguments = [] 122 | let l:argument = '' 123 | 124 | if len(l:text) > 0 125 | for l:index in range(strlen(l:text)) 126 | let l:char = l:text[l:index] 127 | call argwrap#updateScope(l:stack, l:char) 128 | 129 | if len(l:stack) == 0 && l:char == ',' 130 | let l:argument = argwrap#trimArgument(l:argument) 131 | if len(l:argument) > 0 132 | call add(l:arguments, l:argument) 133 | endif 134 | let l:argument = '' 135 | else 136 | let l:argument .= l:char 137 | endif 138 | endfor 139 | 140 | let l:argument = argwrap#trimArgument(l:argument) 141 | if len(l:argument) > 0 142 | call add(l:arguments, l:argument) 143 | endif 144 | endif 145 | 146 | return l:arguments 147 | endfunction 148 | 149 | function! argwrap#extractContainer(range) 150 | let l:textStart = getline(a:range.lineStart) 151 | let l:textEnd = getline(a:range.lineEnd) 152 | 153 | let l:indent = matchstr(l:textStart, '\s*') 154 | let l:prefix = l:textStart[strlen(l:indent) : a:range.colStart - 1] 155 | let l:suffix = l:textEnd[a:range.colEnd - 1:] 156 | 157 | return {'indent': l:indent, 'prefix': l:prefix, 'suffix': l:suffix} 158 | endfunction 159 | 160 | function! argwrap#wrapContainer(range, container, arguments, wrapBrace, tailComma, tailCommaBraces, tailIndentBraces, linePrefix, commaFirst, commaFirstIndent) 161 | let l:argCount = len(a:arguments) 162 | let l:line = a:range.lineStart 163 | let l:prefix = a:container.prefix[len(a:container.prefix) - 1] 164 | 165 | call setline(l:line, a:container.indent . a:container.prefix) 166 | 167 | for l:index in range(l:argCount) 168 | let l:last = l:index == l:argCount - 1 169 | let l:first = l:index == 0 170 | let l:text = '' 171 | 172 | if a:commaFirst 173 | let l:text .= a:container.indent . a:linePrefix 174 | if !l:first 175 | let l:text .= ', ' 176 | end 177 | let l:text .= a:arguments[l:index] 178 | else 179 | let l:text .= a:container.indent . a:linePrefix . a:arguments[l:index] 180 | if !l:last || a:tailComma || a:tailCommaBraces =~ l:prefix 181 | let l:text .= ',' 182 | end 183 | end 184 | 185 | if l:last && !a:wrapBrace 186 | let l:text .= a:container.suffix 187 | end 188 | 189 | call append(l:line, l:text) 190 | let l:line += 1 191 | silent! exec printf('%s>', l:line) 192 | 193 | if l:first && a:commaFirstIndent 194 | let width = &l:shiftwidth 195 | let &l:shiftwidth = 2 196 | silent! exec printf('%s>', l:line) 197 | let &l:shiftwidth = l:width 198 | end 199 | endfor 200 | 201 | if a:wrapBrace 202 | call append(l:line, a:container.indent . a:linePrefix . a:container.suffix) 203 | if a:tailIndentBraces =~ l:prefix 204 | silent! exec printf('%s>', l:line + 1) 205 | end 206 | endif 207 | endfunction 208 | 209 | function! argwrap#unwrapContainer(range, container, arguments, padded) 210 | let l:brace = a:container.prefix[strlen(a:container.prefix) - 1] 211 | if stridx(a:padded, l:brace) == -1 212 | let l:padding = '' 213 | else 214 | let l:padding = ' ' 215 | endif 216 | 217 | let l:text = a:container.indent . a:container.prefix . l:padding . join(a:arguments, ', ') . l:padding . a:container.suffix 218 | call setline(a:range.lineStart, l:text) 219 | exec printf('silent %d,%dd_', a:range.lineStart + 1, a:range.lineEnd) 220 | endfunction 221 | 222 | function! argwrap#getSetting(name) 223 | let l:bName = 'b:argwrap_' . a:name 224 | let l:gName = 'g:argwrap_' . a:name 225 | 226 | return exists(l:bName) ? {l:bName} : {l:gName} 227 | endfunction 228 | 229 | function! argwrap#initSetting(name, value) abort 230 | let l:setting = 'g:argwrap_'.a:name 231 | 232 | if !exists(l:setting) 233 | let {l:setting} = a:value 234 | endif 235 | endfunction 236 | 237 | function! argwrap#toggle() 238 | let l:linePrefix = argwrap#getSetting('line_prefix') 239 | let l:padded = argwrap#getSetting('padded_braces') 240 | let l:tailComma = argwrap#getSetting('tail_comma') 241 | let l:tailCommaBraces = argwrap#getSetting('tail_comma_braces') 242 | let l:tailIndentBraces = argwrap#getSetting('tail_indent_braces') 243 | let l:wrapBrace = argwrap#getSetting('wrap_closing_brace') 244 | let l:commaFirst = argwrap#getSetting('comma_first') 245 | let l:commaFirstIndent = argwrap#getSetting('comma_first_indent') 246 | 247 | let l:range = argwrap#findClosestRange() 248 | if !argwrap#validateRange(l:range) 249 | return 250 | endif 251 | 252 | let l:argText = argwrap#extractContainerArgText(l:range, l:linePrefix) 253 | let l:arguments = argwrap#extractContainerArgs(l:argText) 254 | if len(l:arguments) == 0 255 | return 256 | endif 257 | 258 | let l:container = argwrap#extractContainer(l:range) 259 | if l:range.lineStart == l:range.lineEnd 260 | call argwrap#hooks#execute('pre_wrap', l:range, l:container, l:arguments) 261 | call argwrap#wrapContainer(l:range, l:container, l:arguments, l:wrapBrace, l:tailComma, l:tailCommaBraces, l:tailIndentBraces, l:linePrefix, l:commaFirst, l:commaFirstIndent) 262 | call argwrap#hooks#execute('post_wrap', l:range, l:container, l:arguments) 263 | else 264 | call argwrap#hooks#execute('pre_unwrap', l:range, l:container, l:arguments) 265 | call argwrap#unwrapContainer(l:range, l:container, l:arguments, l:padded) 266 | call argwrap#hooks#execute('post_unwrap', l:range, l:container, l:arguments) 267 | endif 268 | endfunction 269 | -------------------------------------------------------------------------------- /autoload/argwrap/hooks.vim: -------------------------------------------------------------------------------- 1 | function! s:loadGlobalHooks() abort " {{{ 2 | if !exists('g:argwrap_global_hooks') 3 | let g:argwrap_global_hooks = [] 4 | 5 | for hook in globpath(&runtimepath, 'autoload/argwrap/hooks/*.vim', 0, 1) 6 | let l:filename = matchstr(hook, '\vhooks/\zs.+\ze\.vim$') 7 | 8 | call add(g:argwrap_global_hooks, printf('argwrap#hooks#%s', l:filename)) 9 | endfor 10 | endif 11 | 12 | return g:argwrap_global_hooks 13 | endfunction " }}} 14 | 15 | function! s:loadFiletypeHooks(filetype) abort " {{{ 16 | if !exists('g:argwrap_filetype_hooks.'.a:filetype) 17 | let g:argwrap_filetype_hooks[a:filetype] = [] 18 | let l:hooks = g:argwrap_filetype_hooks[a:filetype] 19 | 20 | for filetypeHook in globpath(&runtimepath, 'autoload/argwrap/hooks/filetype/*/*.vim', 0, 1) 21 | let l:filetype = matchstr(filetypeHook, '\vhooks/filetype/\zs.+\ze/.+\.vim$') 22 | let l:filename = matchstr(filetypeHook, '\vhooks/filetype/.+/\zs.+\ze\.vim$') 23 | 24 | call add(l:hooks, printf('argwrap#hooks#filetype#%s#%s', l:filetype, l:filename)) 25 | endfor 26 | endif 27 | 28 | return g:argwrap_filetype_hooks[a:filetype] 29 | endfunction " }}} 30 | 31 | function! s:load() abort " {{{ 32 | if !exists('b:argwrap_hooks') 33 | let b:argwrap_hooks = s:loadGlobalHooks() + s:loadFiletypeHooks(&filetype) 34 | endif 35 | 36 | return b:argwrap_hooks 37 | endfunction " }}} 38 | 39 | function! argwrap#hooks#execute(name, ...) abort " {{{ 40 | " Reverse the order of the hooks for post hooks so that a pre hook with a 41 | " low priority is executed before and a post hook is executed after 42 | " For instance for a hook responsible to preserve the cursor position it 43 | " must be the first to be executed to save the position of the cursor but 44 | " the last to be executed to restore it after all other hooks have been 45 | " executed 46 | let l:hooks = a:name =~? '\v^post' ? reverse(copy(s:load())) : s:load() 47 | 48 | for hook in l:hooks 49 | silent! call call(printf('%s#%s', hook, a:name), a:000) 50 | endfor 51 | endfunction " }}} 52 | 53 | " vim: ts=2 sw=2 et fdm=marker 54 | -------------------------------------------------------------------------------- /autoload/argwrap/hooks/000_curpos.vim: -------------------------------------------------------------------------------- 1 | function! s:extractCursorPositionForUnwrappedArguments(range, arguments) abort " {{{ 2 | let l:cursorColumn = col('.') 3 | let l:lineText = getline(a:range.lineStart) 4 | let l:position = {} 5 | 6 | let l:argumentNumber = 0 7 | for argument in a:arguments 8 | let l:argumentNumber += 1 9 | let l:argumentStart = stridx(l:lineText, argument) 10 | let l:argumentEnd = l:argumentStart + len(argument) 11 | 12 | if l:cursorColumn <= l:argumentStart 13 | let l:cursorColumn = l:argumentStart + 1 14 | endif 15 | 16 | if l:argumentEnd < l:cursorColumn 17 | if l:lineText[l:cursorColumn - 1:] =~ '\v^,' " Cursor on the separator 18 | if !argwrap#getSetting('comma_first') 19 | let l:cursorColumn = l:argumentEnd + 1 20 | else 21 | let l:position.argumentNumber = l:argumentNumber + 1 22 | let l:position.column = -1 23 | 24 | break 25 | endif 26 | elseif l:lineText[l:cursorColumn - 1:] =~ '\v^\s+,' " Cursor before the separator 27 | let l:cursorColumn = l:argumentEnd 28 | endif 29 | endif 30 | 31 | if l:cursorColumn <= l:argumentEnd + 1 32 | let l:position.argumentNumber = l:argumentNumber 33 | let l:position.column = l:cursorColumn - l:argumentStart 34 | 35 | break 36 | end 37 | endfor 38 | 39 | " If the position was not found it's because the cursor is after the last 40 | " argument 41 | if empty(l:position) 42 | let l:position.argumentNumber = l:argumentNumber 43 | let l:position.column = l:argumentEnd - l:argumentStart 44 | endif 45 | 46 | return l:position 47 | endfunction " }}} 48 | 49 | function! s:extractCursorPositionForWrappedArguments(range, arguments) abort " {{{ 50 | let l:position = {} 51 | let l:isCommaFirst = argwrap#getSetting('comma_first') 52 | let l:cursorColumn = col('.') 53 | let l:cursorArgumentNumber = line('.') - a:range.lineStart 54 | " In case the cursor is on the start line 55 | let l:cursorArgumentNumber = min([len(a:arguments), l:cursorArgumentNumber]) 56 | " In case the cursor is on the end line 57 | let l:cursorArgumentNumber = max([1, l:cursorArgumentNumber]) 58 | let l:argumentLine = getline('.') 59 | let l:argumentText = a:arguments[l:cursorArgumentNumber - 1] 60 | let l:argumentStart = stridx(l:argumentLine, l:argumentText) 61 | let l:argumentEnd = l:argumentStart + len(l:argumentText) 62 | let l:position.argumentNumber = l:cursorArgumentNumber 63 | let l:position.column = l:cursorColumn - l:argumentStart 64 | 65 | if l:cursorColumn <= l:argumentStart 66 | let l:position.column = 1 67 | 68 | if l:isCommaFirst 69 | if l:argumentLine[l:cursorColumn - 1:] =~ '\v^,' " Cursor on the separator 70 | " The cursor should be placed on the separtor 71 | let l:position.argumentNumber -= 1 72 | let l:position.column = len(a:arguments[l:position.argumentNumber - 1]) + 1 73 | elseif l:argumentLine[l:cursorColumn - 1:] =~ '\v^\s+,' " Cursor before the separator 74 | " The cursor should be placed on the end of the previous argument 75 | let l:position.argumentNumber -= 1 76 | let l:position.column = len(a:arguments[l:position.argumentNumber - 1]) 77 | endif 78 | endif 79 | endif 80 | 81 | if l:argumentEnd < l:cursorColumn 82 | let l:position.column = len(l:argumentText) 83 | 84 | if !l:isCommaFirst 85 | if l:argumentLine[l:cursorColumn - 1:] =~ '\v^\s+,' " Cursor before the separator 86 | " The cursor should be placed on the end of the current argument 87 | elseif l:argumentLine[l:cursorColumn - 1:] =~ '\v^,' " Cursor on the separator 88 | " The cursor should be placed on the separator 89 | let l:position.column += 1 90 | endif 91 | endif 92 | endif 93 | 94 | return l:position 95 | endfunction " }}} 96 | 97 | function! s:getCursorPositionForWrappedArguments(range, container, arguments) abort " {{{ 98 | let l:line = a:range.lineStart + a:container.cursor.argumentNumber 99 | let l:argumentStart = stridx(getline(l:line), a:arguments[a:container.cursor.argumentNumber - 1]) 100 | let l:column = l:argumentStart + a:container.cursor.column 101 | 102 | return {'line': l:line, 'column': l:column} 103 | endfunction " }}} 104 | 105 | function! s:getCursorPositionForUnwrappedArguments(range, container, arguments) abort " {{{ 106 | let l:line = a:range.lineStart 107 | let l:column = a:range.colStart 108 | 109 | " For each arguments before the one where the cursor must be positioned 110 | for index in range(a:container.cursor.argumentNumber - 1) 111 | " Add the length of the argument + 2 for the separator ', ' 112 | let l:column += len(a:arguments[index]) + 2 113 | endfor 114 | 115 | let l:column += a:container.cursor.column 116 | 117 | return {'line': l:line, 'column': l:column} 118 | endfunction " }}} 119 | 120 | function! s:setCursorPosition(position) abort " {{{ 121 | let l:curpos = getcurpos() 122 | let l:curpos[1] = a:position.line 123 | let l:curpos[2] = a:position.column 124 | 125 | call setpos('.', l:curpos) 126 | endfunction " }}} 127 | 128 | function! argwrap#hooks#000_curpos#pre_wrap(range, container, arguments) abort " {{{ 129 | let a:container.cursor = s:extractCursorPositionForUnwrappedArguments(a:range, a:arguments) 130 | endfunction " }}} 131 | 132 | function! argwrap#hooks#000_curpos#pre_unwrap(range, container, arguments) abort " {{{ 133 | let a:container.cursor = s:extractCursorPositionForWrappedArguments(a:range, a:arguments) 134 | endfunction " }}} 135 | 136 | function! argwrap#hooks#000_curpos#post_wrap(range, container, arguments) abort " {{{ 137 | let l:position = s:getCursorPositionForWrappedArguments(a:range, a:container, a:arguments) 138 | 139 | call s:setCursorPosition(l:position) 140 | endfunction " }}} 141 | 142 | function! argwrap#hooks#000_curpos#post_unwrap(range, container, arguments) abort " {{{ 143 | let l:position = s:getCursorPositionForUnwrappedArguments(a:range, a:container, a:arguments) 144 | 145 | call s:setCursorPosition(l:position) 146 | endfunction " }}} 147 | 148 | " vim: ts=2 sw=2 et fdm=marker 149 | -------------------------------------------------------------------------------- /autoload/argwrap/hooks/filetype/php/200_smart_brace.vim: -------------------------------------------------------------------------------- 1 | function! s:dealWithMethodArguments(container) abort " {{{ 2 | if a:container.suffix !~ '\v^\)' 3 | return 0 4 | endif 5 | 6 | if a:container.prefix !~? '\v^%(public|protected|private)(\s+static)?\s+function\s+\S+\s*\($' 7 | return 0 8 | endif 9 | 10 | return 1 11 | endfunction " }}} 12 | 13 | function! s:fixMethodOpeningBraceAfterWrap(range, container, arguments) abort " {{{ 14 | if !s:dealWithMethodArguments(a:container) 15 | return 16 | endif 17 | 18 | let l:lineEnd = a:range.lineEnd + len(a:arguments) 19 | 20 | " Add 1 more line if the brace is also wrapped 21 | if 0 != argwrap#getSetting('wrap_closing_brace') 22 | let l:lineEnd += 1 23 | endif 24 | 25 | if getline(l:lineEnd + 1) =~ '\v^\s*\{' 26 | execute printf('undojoin | normal! %dGJ', l:lineEnd) 27 | endif 28 | endfunction " }}} 29 | 30 | function! s:fixMethodOpeningBraceAfterUnwrap(range, container, arguments) abort " {{{ 31 | if !s:dealWithMethodArguments(a:container) 32 | return 33 | endif 34 | 35 | if a:container.suffix !~ '\v^\).*\{\s*$' 36 | return 37 | endif 38 | 39 | execute printf("undojoin | normal! %dG$F{gelct{\", a:range.lineStart) 40 | endfunction " }}} 41 | 42 | function! argwrap#hooks#filetype#php#200_smart_brace#pre_wrap(range, container, arguments) abort " {{{ 43 | " Do nothing but prevent the file to be loaded more than once 44 | " When calling an autoload function that is not define, the script that 45 | " should contain it is sourced every time the function is called 46 | endfunction " }}} 47 | 48 | function! argwrap#hooks#filetype#php#200_smart_brace#pre_unwrap(range, container, arguments) abort " {{{ 49 | " Do nothing but prevent the file to be loaded more than once 50 | " When calling an autoload function that is not define, the script that 51 | " should contain it is sourced every time the function is called 52 | endfunction " }}} 53 | 54 | function! argwrap#hooks#filetype#php#200_smart_brace#post_wrap(range, container, arguments) abort " {{{ 55 | if argwrap#getSetting('php_smart_brace') 56 | call s:fixMethodOpeningBraceAfterWrap(a:range, a:container, a:arguments) 57 | endif 58 | endfunction " }}} 59 | 60 | function! argwrap#hooks#filetype#php#200_smart_brace#post_unwrap(range, container, arguments) abort " {{{ 61 | if argwrap#getSetting('php_smart_brace') 62 | call s:fixMethodOpeningBraceAfterUnwrap(a:range, a:container, a:arguments) 63 | endif 64 | endfunction " }}} 65 | 66 | " vim: ts=2 sw=2 et fdm=marker 67 | -------------------------------------------------------------------------------- /doc/argwrap.txt: -------------------------------------------------------------------------------- 1 | argwrap.txt Wrap and unwrap function arguments, lists, and dictionaries in Vim 2 | 3 | ======================================================================================================================== 4 | CONTENTS *argwrap-contents* 5 | 6 | 1. ArgWrap...............................................................................................|argwrap-argwrap| 7 | 1.1. Installation...............................................................................|argwrap-installation| 8 | 1.2. Configuration.............................................................................|argwrap-configuration| 9 | 1.3. Hooks.............................................................................................|argwrap-hooks| 10 | 1.4. Usage.............................................................................................|argwrap-usage| 11 | 1.5. License.........................................................................................|argwrap-license| 12 | 13 | ======================================================================================================================== 14 | ARGWRAP *argwrap-argwrap* 15 | 16 | ArgWrap is an industrial strength argument wrapping and unwrapping extension for the Vim text editor. It can be used for 17 | collapsing and expanding everything from function calls to array and dictionary definitions. All operations are easily 18 | reversible and correctly preserve the indentation of the surrounding code. 19 | 20 | 21 | 22 | ------------------------------------------------------------------------------------------------------------------------ 23 | INSTALLATION *argwrap-installation* 24 | 25 | 1. Clone or otherwise download ArgWrap extension. Users of pathogen.vim (https://github.com/tpope/vim-pathogen) can 26 | clone the repository directly to their bundle directory: 27 | > 28 | $ git clone https://github.com/FooSoft/vim-argwrap ~/.vim/bundle/vim-argwrap 29 | < 30 | 2. Create a keyboard binding for the `ArgWrap` command inside your `~/.vimrc` file. 31 | For example, to declare a normal mode mapping, add the following command: 32 | > 33 | nmap a (ArgWrapToggle) 34 | < 35 | 36 | ------------------------------------------------------------------------------------------------------------------------ 37 | CONFIGURATION *argwrap-configuration* 38 | 39 | You can customize the behavior of this extension by setting values for any of the following optional buffer and 40 | global configuration variables in your `.vimrc` file. Buffer variables (prefixed with `b:`) take precedence over 41 | global variables (prefixed with `g:`), making them ideal for configuring the behavior of this extension on a file by 42 | file basis using `ftplugin` or `autocmd`. For example, the `argwrap_tail_comma` variable has two variants declared as 43 | `b:argwrap_tail_comma` and `g:argwrap_tail_comma`, for buffer and global scopes respectively. 44 | 45 | * argwrap_line_prefix 46 | Specifies a line prefix to be added and removed when working with languages that require newlines to be escaped. 47 | Line prefix disabled (default) 48 | > 49 | Foo( 50 | wibble, 51 | wobble, 52 | wubble 53 | ) 54 | < 55 | Line prefix enabled for Vimscript () 56 | > 57 | Foo( 58 | \wibble, 59 | \wobble, 60 | \wubble 61 | \) 62 | < 63 | * argwrap_padded_braces 64 | Specifies which brace types should be padded on the inside with spaces. 65 | Brace padding disabled (default) 66 | > 67 | [1, 2, 3] 68 | {1, 2, 3} 69 | < 70 | Brace padding enabled for square brackets only () 71 | > 72 | [ 1, 2, 3 ] 73 | {1, 2, 3} 74 | < 75 | Padding can be specified for multiple brace types () 76 | * argwrap_tail_comma 77 | Specifies if any closing brace should be preceded with a comma when wrapping lines. 78 | Tail comma disabled (default) 79 | > 80 | Foo( 81 | wibble, 82 | wobble, 83 | wubble 84 | ) 85 | < 86 | Tail comma enabled () 87 | > 88 | Foo( 89 | wibble, 90 | wobble, 91 | wubble, 92 | ) 93 | < 94 | * argwrap_tail_comma_braces 95 | Specifies which closing brace should be preceded with a comma when wrapping lines. 96 | Tail comma disabled (default) 97 | > 98 | Foo( 99 | wibble, 100 | wobble, 101 | wubble 102 | ) 103 | < 104 | Tail comma enabled for square brackets only () 105 | > 106 | [ 107 | 1, 108 | 2, 109 | 3, 110 | ] 111 | < 112 | * argwrap_tail_indent_braces 113 | Specifies if the closing brace should be indented to argument depth. 114 | Tail indent disabled 115 | > 116 | Foo( 117 | wibble, 118 | wobble, 119 | wubble 120 | ) 121 | < 122 | Tail indent enabled for parenthesis () 123 | > 124 | Foo( 125 | wibble, 126 | wobble, 127 | wubble 128 | ) 129 | < 130 | * argwrap_wrap_closing_brace 131 | Specifies if the closing brace should be wrapped to a new line. 132 | Brace wrapping enabled (default) 133 | > 134 | Foo( 135 | wibble, 136 | wobble, 137 | wubble 138 | ) 139 | < 140 | Brace wrapping disabled () 141 | > 142 | Foo( 143 | wibble, 144 | wobble, 145 | wubble) 146 | < 147 | * argwrap_comma_first 148 | Specifies if the argument comma delimiter should be placed before arguments. 149 | Comma first disabled (default) 150 | > 151 | Foo( 152 | wibble, 153 | wobble, 154 | wubble 155 | ) 156 | < 157 | Comma first enabled () 158 | > 159 | Foo( 160 | wibble 161 | , wobble 162 | , wubble 163 | ) 164 | < 165 | * argwrap_comma_first_indent 166 | Specifies if the first argument should be indented when used in conjunction with `argwrap_comma_first`. 167 | Comma first indent disabled (default) 168 | > 169 | Foo( 170 | wibble 171 | , wobble 172 | , wubble 173 | ) 174 | < 175 | Comma first indent enabled () 176 | > 177 | Foo( 178 | wibble 179 | , wobble 180 | , wubble 181 | ) 182 | < 183 | * argwrap_php_smart_brace 184 | Specifies if the opening brace of PHP methods should be wrap/unwrap as well. 185 | PHP smart brace disabled (default) 186 | > 187 | public function foo( 188 | int $x, 189 | int $y 190 | ) 191 | { 192 | < 193 | PHP smart brace enabled () 194 | > 195 | public function foo( 196 | int $x, 197 | int $y 198 | ) { 199 | < 200 | 201 | ------------------------------------------------------------------------------------------------------------------------ 202 | HOOKS *argwrap-hooks* 203 | 204 | It is possible to hook before or after a wrap/unwrap operation using 205 | autoloaded functions, the hooks are named: 206 | - `pre_wrap` 207 | - `pre_unwrap` 208 | - `post_wrap` 209 | - `post_unwrap` 210 | 211 | For example to do something after any wrap create a function: > 212 | argwrap#hooks#my_hook#post_wrap(range, container, arguments) 213 | < 214 | It is also possible to create a hook for a specific filetype: > 215 | argwrap#hooks#filetype#vim#my_hook#post_wrap(range, container, arguments) 216 | < 217 | 218 | Global hooks are loaded on the first time a wrap/unwrap operation is done. 219 | Filetype hooks however are only loaded for the current filetype. 220 | You can see the list of loaded hooks with: > 221 | echo g:argwrap_global_hooks 222 | echo g:argwrap_filetype_hooks 223 | echo b:argwrap_hooks 224 | < 225 | The hooks are loaded from any directory specified in the |runtimepath|. 226 | Global hooks will be executed before filetype ones. 227 | Global and filetype hooks are sorted by the |globpath()| function. Meaning you 228 | can control the execution order of the hooks by prefixing them with a 229 | priority. 230 | Post hooks order is reversed in order to keep the execution order logical. 231 | 232 | For example if there two hooks named `000_cursor` and `200_anything` , the 233 | cursor hook being responsible to preserve the cursor position it must be 234 | executed first to ensure no modification of the cursor position has been done 235 | yet so it receive the lowest priority. 236 | The execution stack for a wrap operation would then be: 237 | - `000_cursor#pre_wrap` 238 | - `000_anything#pre_wrap` 239 | - `wrap operation` 240 | - `000_anything#post_wrap` 241 | - `000_cursor#post_wrap` 242 | 243 | 244 | An important things to know when writing a new hook is that calling an 245 | |autoload| function which does not exist will source the file that should 246 | contain the function every time. 247 | So even if you do not need one of the hook, always define them all. This is a 248 | template you can use to get started: > 249 | function! argwrap#hooks#my_hook#pre_wrap(range, container, arguments) abort " {{{ 250 | " Do nothing but prevent the file to be loaded more than once 251 | " When calling an autoload function that is not define the script that 252 | " should contain it is sourced every time the function is called 253 | endfunction " }}} 254 | 255 | function! argwrap#hooks#my_hook#pre_unwrap(range, container, arguments) abort " {{{ 256 | " Do nothing but prevent the file to be loaded more than once 257 | " When calling an autoload function that is not define the script that 258 | " should contain it is sourced every time the function is called 259 | endfunction " }}} 260 | 261 | function! argwrap#hooks#my_hook#post_wrap(range, container, arguments) abort " {{{ 262 | " Do nothing but prevent the file to be loaded more than once 263 | " When calling an autoload function that is not define the script that 264 | " should contain it is sourced every time the function is called 265 | endfunction " }}} 266 | 267 | function! argwrap#hooks#my_hook#post_unwrap(range, container, arguments) abort " {{{ 268 | " Do nothing but prevent the file to be loaded more than once 269 | " When calling an autoload function that is not define the script that 270 | " should contain it is sourced every time the function is called 271 | endfunction " }}} 272 | < 273 | ------------------------------------------------------------------------------------------------------------------------ 274 | USAGE *argwrap-usage* 275 | 276 | 1. Position the cursor inside of the scope of the parenthesis, brackets or curly braces you wish to wrap/unwrap (not 277 | on top, before or after them). 278 | 2. Execute the keyboard binding you defined above to toggle the wrapping and unwrapping of arguments. 279 | 280 | ------------------------------------------------------------------------------------------------------------------------ 281 | LICENSE *argwrap-license* 282 | 283 | Permission is hereby granted, free of charge, to any person obtaining a copy of 284 | this software and associated documentation files (the "Software"), to deal in 285 | the Software without restriction, including without limitation the rights to 286 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 287 | the Software, and to permit persons to whom the Software is furnished to do so, 288 | subject to the following conditions: 289 | 290 | The above copyright notice and this permission notice shall be included in all 291 | copies or substantial portions of the Software. 292 | 293 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 294 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 295 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 296 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 297 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 298 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 299 | 300 | vim:tw=78:sw=4:ft=help:norl: 301 | -------------------------------------------------------------------------------- /img/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FooSoft/vim-argwrap/f3e26a5ad249d09467804b92e760d08b1cc457a1/img/demo.gif -------------------------------------------------------------------------------- /plugin/argwrap.vim: -------------------------------------------------------------------------------- 1 | " Copyright (c) 2014 Alex Yatskov 2 | " 3 | " Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | " this software and associated documentation files (the "Software"), to deal in 5 | " the Software without restriction, including without limitation the rights to 6 | " use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | " the Software, and to permit persons to whom the Software is furnished to do so, 8 | " subject to the following conditions: 9 | " 10 | " The above copyright notice and this permission notice shall be included in all 11 | " copies or substantial portions of the Software. 12 | " 13 | " THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | " IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | " FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | " COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | " IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | " CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | 20 | call argwrap#initSetting('line_prefix', '') 21 | call argwrap#initSetting('padded_braces', '') 22 | call argwrap#initSetting('tail_comma', 0) 23 | call argwrap#initSetting('tail_comma_braces', '') 24 | call argwrap#initSetting('tail_indent_braces', '') 25 | call argwrap#initSetting('wrap_closing_brace', 1) 26 | call argwrap#initSetting('comma_first', 0) 27 | call argwrap#initSetting('comma_first_indent', 0) 28 | call argwrap#initSetting('filetype_hooks', {}) 29 | call argwrap#initSetting('php_smart_brace', 0) 30 | 31 | command! ArgWrap call argwrap#toggle() 32 | 33 | nnoremap (ArgWrapToggle) :call argwrap#toggle() 34 | \ silent! call repeat#set("\(ArgWrapToggle)") 35 | --------------------------------------------------------------------------------