" lion.vim - A Vim plugin for text alignment operators " Maintainer: Tom McDonald " Version: 1.0 let s:count = 1 let s:lion_prompt = get(g:, 'lion_prompt', 'Pattern [/]: ') function! s:command(func, ...) let s:count = v:count if a:0 return ":" . "\call " . a:func . "(visualmode(), 1)\" else return ":" . "\set opfunc=" . a:func . "\g@" endif endfunction function! s:alignRight(type, ...) return s:align('right', a:type, a:0, '') endfunction function! s:alignLeft(type, ...) return s:align('left', a:type, a:0, '') endfunction " Align a range to a particular character function! s:align(mode, type, vis, align_char) let sel_save = &selection let &selection = "inclusive" try " Do we have a character from argument, or should we get one from input? let align_pattern = a:align_char let skip = 0 if align_pattern == '' let align_pattern = nr2char(getchar()) if align_pattern =~ '[1-9]' while 1 let char = nr2char(getchar()) if char !~ '[0-9]' break endif let align_pattern .= char endwhile if char != "\" let skip = str2nr(align_pattern) - 1 let align_pattern = char endif endif endif if align_pattern == '/' let align_pattern .= input(s:lion_prompt) elseif align_pattern == ' ' let align_pattern = '/\S\zs\s' endif " Determine range boundaries if a:vis let pos = s:getpos("'<", "'>", visualmode()) else let pos = s:getpos("'[", "']", a:type) endif let [start_line, end_line, start_col, end_col, middle_start_col, middle_end_col] = pos " Check for 'lion_squeeze_spaces' options if exists('b:lion_squeeze_spaces') let s:lion_squeeze_spaces = get(b:, 'lion_squeeze_spaces') elseif exists('g:lion_squeeze_spaces') let s:lion_squeeze_spaces = get(g:, 'lion_squeeze_spaces') else let s:lion_squeeze_spaces = 0 endif " Squeeze extra spaces before aligning if s:lion_squeeze_spaces for lnum in range(start_line, end_line) call setline(lnum, substitute(getline(lnum), '\(^\s*\)\@LionRepeat".align_pattern."\") else silent! call repeat#set("\LionRepeat".align_pattern) endif finally let &selection = sel_save endtry endfunction function! s:getpos(start, end, mode) let [_, start_line, start_col, _] = getpos(a:start) let [_, end_line, end_col, _] = getpos(a:end) let [middle_start_col, middle_end_col] = [0, -1] if a:mode == 'V' || a:mode == 'line' let [start_col, end_col] = [0, -1] elseif a:mode == "\" let [middle_start_col, middle_end_col] = [start_col, end_col] endif return [start_line, end_line, start_col, end_col, middle_start_col, middle_end_col] endfunction " Match the position of a character in a line after accounting for artificial width set by tabs function! s:match_pos(mode, line, char, count, line_number, start, end) if strlen(a:char) == 1 let pattern = escape(a:char, '~^$.') else let pattern = a:char[1:] " Add start-of-match anchor at the end if there isn't already one in the pattern if a:mode == 'left' && match(pattern, '\\zs') == -1 let pattern .= '\zs' endif endif if a:end == -1 let line = a:line else let line = a:line[:(a:end - 1)] endif if a:mode == 'right' let real_pos = match(line, pattern, a:start - 1, a:count) elseif a:mode == 'left' let real_pos = s:first_non_ws_after(line, pattern, a:start - 1, a:count) endif if real_pos == -1 let virtual_pos = -1 else let virtual_pos = virtcol([a:line_number, real_pos]) endif return [real_pos, virtual_pos] endfunction " Get the first non-whitespace after [count] instances of [char] function! s:first_non_ws_after(line, pattern, start, count) let pattern = len(a:pattern) == 1 ? a:pattern . '\zs' : a:pattern let char_pos = match(a:line, pattern, a:start, a:count) if char_pos == -1 return -1 else return match(a:line, '\S', char_pos) endif endfunction " Echo a string and wait for input (used when I'm debugging) function! s:debug_str(str) echo a:str let x = getchar() endfunction function! s:assign_map(map, func) if a:map == '' return endif execute 'nmap ' . a:map . ' Lion' . a:func execute 'vmap ' . a:map . ' VLion' . a:func endfunction nnoremap LionRepeat . nnoremap LionRight command("alignRight") vnoremap VLionRight command("alignRight", 1) nnoremap LionLeft command("alignLeft") vnoremap VLionLeft command("alignLeft", 1) if get(g:, 'lion_create_maps', 1) call s:assign_map(get(g:, 'lion_map_right', 'gl'), 'Right') call s:assign_map(get(g:, 'lion_map_left', 'gL'), 'Left') endif