5

I'm writing cucumber example tables, which are formatted like this:

| title   | title  |
| data    | data   |
| data    | data   |

How can I replace one of the data or title cells with a word of differing length without disturbing the layout?

9
  • 4
    Use R to overwrite text? (and possibly add some spaces)
    – VanLaser
    Commented Mar 17, 2016 at 15:41
  • That's the closest I've found, seems clumsy though. Maybe I have too much faith in vim :-)
    – Tim Abell
    Commented Mar 17, 2016 at 15:42
  • 1
    There are text alignment plugins, you could use one of those to re-align columns after an edit (e.g. "EasyAlign").
    – VanLaser
    Commented Mar 17, 2016 at 15:43
  • I'm actually in VsVim which doesn't have plugins, I can switch out to full vim but it's a faff. SpecFlow does actually re-align it all but I have to replace the last | to get it to fire.
    – Tim Abell
    Commented Mar 17, 2016 at 15:47
  • 3
    If you are using VsVim you should put that in your question as there are significant differences between VsVim and Vim. If you where using Vim then I would suggest an alignment plugin like EasyAlign or Tabular. This can be improved upon this by using cucumbertables or Vim Table Mode. As VanLaser suggested you can use R. Since SpeckFlow realigns on | can you do cf| and add your text? Commented Mar 17, 2016 at 16:38

3 Answers 3

3

If you were using Vim them you could use some alignment plugin, as mentioned on the question comments. Or you could use the vimwiki plugin, which provides several helpers to use tables.

As you are using VsVim, you could try to isolate the table formatting function in vimwiki/autoload/vimwiki/tbl.vim and adapt it in a VsVim plugin:

function! vimwiki#tbl#format(lnum, ...) "{{{
  if !(&filetype ==? 'vimwiki')
    return
  endif
  let line = getline(a:lnum)
  if !s:is_table(line)
    return
  endif

  if a:0 == 2
    let col1 = a:1
    let col2 = a:2
  else
    let col1 = 0
    let col2 = 0
  endif

  let indent = s:get_indent(a:lnum)
  if &expandtab
    let indentstring = repeat(' ', indent)
  else
    let indentstring = repeat(' ', indent / &tabstop) . repeat(' ', indent % &tabstop)
  endif

  for [lnum, row] in s:get_aligned_rows(a:lnum, col1, col2)
    let row = indentstring.row
    call setline(lnum, row)
  endfor

  let &tw = s:textwidth
endfunction "}}}
3
  • Thanks, I'm not aware of any plugin support for VsVim but I'll look into the full vim plugins.
    – Tim Abell
    Commented Mar 18, 2016 at 14:14
  • 1
    @TimAbell I don't know much about VsVim, but based on this VsVim issue I would guess that it is possible. If you don't find clear information on the documentation you should consider opening a issue asking about it.
    – mMontu
    Commented Mar 18, 2016 at 18:03
  • Cheers, that's definitely worth looking into.
    – Tim Abell
    Commented Mar 19, 2016 at 20:37
2

Just copy paste the below functions (https://github.com/imbichie/vim-vimrc-/blob/master/MCCB_MCCE.vim) in your .vimrc or .gvimrc file and call the MCCB or MCCE function.

" MCCB - Move the Character to the Column from the Begin of line
" This is a function for Moving the specified Character 
" in a given range of lines to a the specified Column from the Begin of the line
" NOTE 1 :- If the specified character and the first character of the line are same
"           then the number of Occurrence (num_occr) will be one less than the actual
" NOTE 2 :- Maximum space between the specified character with in the range 
"           of lines should be less than or equal to 80, if we need more than 80
"           then we need to insert more spaces by increasing the value 80 in the 
"           "nmap s 80i <ESC>" line inside the function
" Usage :-  in command mode do it like below
" Eg 1:-    :5,11call MCCB(1, '=', 8)
"           The above command will move the 1st Occurrence from the begin of Character =
"           to the 8th Column of the lines from 5 to 11
" Eg 2 :-   :7,10call MCCB(2, '+', 12)
"           The above command will move the 2nd Occurrence of Character + to the 12th
"           Column of the lines from 7 to 10
    function! MCCB (num_occr, mv_char, col_num) range
        if (a:firstline <= a:lastline)
            nmap s 80i <ESC>
            let line_num = a:firstline
            while line_num <= a:lastline
                execute "normal " . line_num . "G0" . a:num_occr . "f" . a:mv_char . "s" . a:col_num . "|dw"
                let line_num = line_num + 1
            endwhile
            nunmap s
        else
            execute printf('ERROR : Start line %d is higher thatn End line %d, a:firstline, a:lastline)
        endif
    endfunction

" MCCE - Move the Character to the Column from the End of line
" This is a function for Moving the specified Character 
" in a given range of lines to a the specified Column from the End of the line
" NOTE 1 :- If the specified character and the last character of the line are same
"           then the number of Occurrence (num_occr) will be one less than the actual
" NOTE 2 :- Maximum space between the specified character with in the range 
"           of lines should be less than or equal to 80, if we need more than 80
"           then we need to insert more spaces by increasing the value 80 in the 
"           "nmap s 80i <ESC>" line inside the function
" Usage :-  in command mode do it like below
" Eg 1:-    :5,11call MCCE(1, ';', 20)
"           The above command will move the 1st Occurrence from the End of Character ;
"           to the 20th Column of the lines from 5 to 11
" Eg 2 :-   :7,10call MCCE(5, ' ', 26)
"           The above command will move the 5th Occurrence from the End of Character " " (white Space)
"           to the 26th Column of the lines from 7 to 10
    function! MCCE (num_occr, mv_char, col_num) range
        if (a:firstline <= a:lastline)
            nmap s 80i <ESC>
            let line_num = a:firstline
            while line_num <= a:lastline
                execute "normal " . line_num . "G$" . a:num_occr . "F" . a:mv_char . "s" . a:col_num . "|dw"
                let line_num = line_num + 1
            endwhile
            nunmap s
        else
            execute printf('ERROR : Start line %d is higher thatn End line %d, a:firstline, a:lastline)
        endif
    endfunction
3
  • I didn't test the function but I see nmap s and nunmap s it means that if I have s mapped I'll loose my mapping after executing the function, am I right? If I understood it correctly I think it would be interesting to save the mapping of use 80i directly in the execute to avoid messing with the user configuration :-)
    – statox
    Commented Mar 18, 2016 at 9:52
  • Thanks, this might be just me but I didn't understand much of that so I'm not sure how it'd work.
    – Tim Abell
    Commented Mar 18, 2016 at 14:15
  • Hi statox, Yes you can use the 80i directly at the place of s, so no need of nmap s. Hi Tim Abell, this will work surely..
    – imbichie
    Commented Mar 20, 2016 at 7:52
2

For what you've asked, there may be a simpler solution than employing alignment plugins. What you need is a text object for table cells:

For reference: http://learnvimscriptthehardway.stevelosh.com/chapters/15.html

onoremap ic :<c-u>normal! T\|vt\|<cr>                                                                                                                                                                             
onoremap ac :<c-u>normal! F\|vf\|<cr>

I noticed the ic, ac (mnemonic: inside cell, around cell) objects are free, but you can of course change them to i| a|.

Then for changing the content of a cell, just press cic or combine with whatever operator you need. (dac : delete a cell)

For completeness, i found a mapping useful which creates a separator row:

inoremap \|\| <Esc>kyyp:s/\v[^\|]/-/g<CR>:nohlsearch<CR>                                                                                                                                                           

So that you can press || while starting a new row to copy the line above and essentially replace anything but pipes with a dash.

3
  • Thanks, that's a neat trick, but when I run cic (change inside cell), it collapses the cell to zero chars wide and I have to manually re-pad it with spaces, which is what I was hoping to avoid.
    – Tim Abell
    Commented Feb 10, 2017 at 9:54
  • Yea, for that re-aligning I would suggest to go with some existing alignment plugins. Eg github.com/godlygeek/tabular for an ex command :Tabularize /regex/l1<CR> which you can of course invoke by a mapping or github.com/junegunn/vim-easy-align which provides a motion aware alignment operator (such as in gaip*| go aligned inside paragraph, split on every occurrence of |). However, assuming you really have the layout already, and just do not want to crash it, really just use Replace Mode (as invoked by R) when in appropriate position. Commented Feb 25, 2017 at 13:56
  • Why vic and vac don't work? Edit: it requires vnoremap instead.
    – tejasvi
    Commented Feb 7, 2021 at 15:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.