" Vim plugin to align C function arguments, GNOME style. " " This software is available under the MIT license. " " Copyright © 2009 Damien Lespiau " " Permission is hereby granted, free of charge, to any person " obtaining a copy of this software and associated documentation " files (the "Software"), to deal in the Software without " restriction, including without limitation the rights to use, " copy, modify, merge, publish, distribute, sublicense, and/or sell " copies of the Software, and to permit persons to whom the " Software is furnished to do so, subject to the following " conditions: " " The above copyright notice and this permission notice shall be " included in all copies or substantial portions of the Software. " " THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, " EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES " OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND " NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT " HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, " WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING " FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR " OTHER DEALINGS IN THE SOFTWARE. " " Author: Damien Lespiau " Last Modified: 20091207 " Save value of cpoptions, and then set it to vim default let s:save_cpo = &cpo set cpo&vim " Ensure that the plugin is loaded only once if exists("is_c_align_args_plugin_loaded") finish endif let is_c_align_args_plugin_loaded = 1 function s:sanitize(string) let l:sane = substitute(a:string, '[\n,]', '', 'g') " did not find a strip function let l:sane = substitute(l:sane, '^\s\+', '', 'g') let l:sane = substitute(l:sane, '\s\+$', '', '') return l:sane endfunction function s:getpos() let l:pos = getpos('.') return [l:pos[1], l:pos[2]] endfunction function s:setpos(newpos) call setpos('.', [0, a:newpos[0], a:newpos[1], 0]) endfunction function s:find_param_end() return searchpos(',\|)', '') endfunction function s:position_is_before(a, b) return (a:a[0] < a:b[0] || (a:a[0] == a:b[0] && a:a[1] <= (a:b[1] ))) endfunction function s:save_intial_state() " save position/marks/registers we are going to use in the script to " restore them later let s:old_position = getpos('.') let s:old_mark_a = getpos("'a") let s:old_paste_reg = getreg('"', 1) let s:old_paste_reg_mode = getregtype('"') " we use the (0 cinoption to align the args to the first '(' let s:old_cinoptions = &cinoptions set cinoptions=(0 endfunction function s:restore_initial_state() " restore cinoptions let &cinoptions=s:old_cinoptions " restore position/marks/registers call setreg('"', s:old_paste_reg, s:old_paste_reg_mode) if s:old_mark_a[2] != 0 call setpos("'a", s:old_mark_a) else delmark a endif call setpos('.', s:old_position) endfunction " find the start and end of the parameters list function s:find_arg_list_start_end() let s:stmt_end = searchpos('{\|;', 'cW') " WIP function/declaration: fallback to finding the next ')' if s:stmt_end[0] == 0 call search(')', 'cW') endif let s:param_list_end = searchpos(')', 'cb') normal % let s:param_list_start = s:getpos() endfunction function s:align_args() call s:save_intial_state() call s:find_arg_list_start_end() " canonicalize the arguments list: " * put all the arguments on a single line (where param_list_start is) " * cleanup tabs and extra white space let l:i = s:param_list_end[0] - s:param_list_start[0] while l:i > 0 normal J let l:i -= 1 endwhile s/\s\+/ /ge " find the start and end of the parameters list, again call s:find_arg_list_start_end() " initalize a few variables for the scan of the parameters. let l:param_start = deepcopy(s:param_list_start) let l:param_end = s:find_param_end() let l:params = [] let l:max_type_len = 0 let l:max_stars_nr = 0 while s:position_is_before(l:param_end, s:param_list_end) && \s:position_is_before(l:param_start, l:param_end) " Invariants: " * the cursor is at the end of the current arg ie either on " the comma, or on the closing bracket. param_end holds " that position. " * param_start is the previous param_end (or the opening '(' " for the first iteration) " [ argument (eg 'gint i'), " position (in the buffer) of the argument last space, " length of the qualifiers + type + ' ' string, " nb of stars before the arg name ] let l:new_param = [] " get the argument normal ma call s:setpos([l:param_start[0], l:param_start[1] + 1]) normal y`a let l:param_string = s:sanitize(@") call add(l:new_param, l:param_string) normal `a " get the position (in the buffer) of the space before the " argument name, normal F call add(l:new_param, s:getpos()) normal `a " type_len is the length of the qualifiers + type string, " including the final space separator let l:type_len = strridx (l:new_param[0], ' ') if l:type_len == -1 call s:restore_initial_state() return endif call add(l:new_param, l:type_len) " stars_nr is the number of stars before the argument name normal b let l:stars_nr = s:getpos()[1] - l:new_param[1][1] - 1 normal `a call add(l:new_param, l:stars_nr) " save the biggest type_len and stars_nr so far if l:type_len > l:max_type_len let l:max_type_len = l:type_len endif if l:stars_nr > l:max_stars_nr let l:max_stars_nr = l:stars_nr endif call add(l:params, l:new_param) let l:param_start = deepcopy(l:param_end) let l:param_end = s:find_param_end() endwhile " uncomment this line to paste l:params to "a and debug that thing " call setreg('a', string(l:params)) " add spaces where needed let l:offset = 0 for l:param in l:params let l:spaces_nr = l:max_type_len - l:param[2] + \l:max_stars_nr - l:param[3] call s:setpos([l:param[1][0], l:param[1][1] + l:offset]) let l:offset += l:spaces_nr " hum think of something smarter? while l:spaces_nr > 0 normal a let l:spaces_nr -= 1 endwhile endfor " insert after each ',' call s:setpos(s:param_list_start) while search(',', '', line('.')) exe "normal a\" endwhile call s:restore_initial_state() endfunction noremap