32

How can I fill the remainder of a line with the specified character up to a certain column using Vim? For example, imagine that the cursor is on column four and I want to fill the remainder of the current line with dashes up to column 80. How would I do that?

John Topley
  • 113,588
  • 46
  • 195
  • 237
  • `76A-` maybe? Seems like doing the maths in your head is gonna be pretty quick, relative to other solutions. – naught101 Dec 16 '14 at 07:05

9 Answers9

76

You can do 80Ax<Esc>d80| for a simpler solution.

Conner
  • 30,144
  • 8
  • 52
  • 73
  • 5
    This should be the top answer! `80A-d80|`. What does the pipe do though? – d11wtq Apr 13 '13 at 12:05
  • 3
    `:help |` - it's a motion to a specified column -- in this case, column 80. So this uses 100Ax to do an append to the end of the line 100 times and the character appended is x. Then, it deletes everything to column 80. – Conner Apr 13 '13 at 22:04
  • 1
    @macmadness86, are you trying to use it in ex mode? Don't type `:` before the command, just use it in normal mode. – Conner Mar 19 '15 at 17:31
  • 1
    Definitely most simple yet efficient solution. – helvete Sep 04 '15 at 09:38
  • Great solution, I was briefly confused when it didn't work as expected for long lines -- wrapping fouled me up (e.g. a trailing `}` can be displayed as byte 64, column 132 if your window has the right width and `set wrap` is on). The solution was `:set nowrap`, `:42,56 norm 80A d80|A\` ` and finally `:set wrap` to fill up a whole range, and add markdown end of code/end of line. – Marcel Marré Jul 05 '18 at 15:00
  • I just realise my explanation above is a little cryptic, and I can't seem to edit it any longer. The wrapping of trailing words or characters into the next displayed line under `set wrap` can lead to `d80|` deleting that. – Marcel Marré Jul 06 '18 at 06:47
27

Here's a function to implement what you ask, and slightly more.

  • It fills the line from its current end of line, rather than the cursor position
  • It forces a single space between what's currently on the line and the repeated chars
  • It allows you to specify any string to fill the rest of the line with
  • It uses vim's textwidth setting to decide how long the line should be (rather than just assuming 80 chars)

The function is defined as follows:

" fill rest of line with characters
function! FillLine( str )
    " set tw to the desired total length
    let tw = &textwidth
    if tw==0 | let tw = 80 | endif
    " strip trailing spaces first
    .s/[[:space:]]*$//
    " calculate total number of 'str's to insert
    let reps = (tw - col("$")) / len(a:str)
    " insert them, if there's room, removing trailing spaces (though forcing
    " there to be one)
    if reps > 0
        .s/$/\=(' '.repeat(a:str, reps))/
    endif
endfunction

Insert that into your .vimrc, and make a mapping to it, e.g.

map <F12> :call FillLine( '-' )

Then you can press F12 to apply the hyphens to the current line

Note: this could probably be easily extended to act on a selection in VISUAL mode, but currently works for single lines only.*

drfrogsplat
  • 2,577
  • 3
  • 19
  • 28
  • 1
    If this function does what it claims to, it does about as much as you could ever ask for. This is the kind of post that should be strongly rewarded on So. Why is this showing `0` upvotes? +1! – Keith Pinson Aug 30 '12 at 23:35
  • 1
    A way to increase conciseness for those who don't mind the ternary operator is to combine the two lines of the assignment of `tw`, thus: `let tw = &textwidth ? &textwidth : 80`. – Keith Pinson Aug 31 '12 at 19:07
  • Further tips: unless you are concerned about adding a space on when `reps` is zero, you can eliminate that `if` statement. You can also use the ternary operator to eliminate the need to define `tw` as a distinct variable (see my previous comment). In addition, I defined a command in by `.vimrc` thus: `command -nargs=1 Fill call FillLine()` which I can use thus: `:Fill '-'` etc. This keeps me from having to bind one key for each possible string I would ever want to call the function on, but gives me a less verbose way of invoking the function than the typical `call` syntax. – Keith Pinson Aug 31 '12 at 19:58
5

If I understand the question correctly, this can be accomplished like this: in normal mode subtract the cursor's current column position from the desired ending column, then type the result followed by 'i' to enter insert mode, then the character you want to fill the space with. End by returning to normal mode. For example, with the cursor at column four in normal mode, if you wanted to fill the rest of the line up to column 80 with dashes, type 76i- then Esc or Ctrl-[ to return to normal mode. This should result in 76 dashes starting in column 4 and ending in column 79.

foven
  • 696
  • 1
  • 5
  • 15
4

If you have the virtualedit option set to block or all, you can create a visual selection (even over empty space) up to the desired column:
v80| (if virtualedit=all) or
<c-v>80| (if virtualedit=block)

Then replace the selected area with dashes: r-

It's probably helpful to start visual mode after the last character in the line by hitting l to avoid overwriting the last character in the line. If you are not using virtualedit=all, then you need to set virtualedit+=onemore so you can move one character beyond the end of line in normal mode.

Endre Both
  • 5,540
  • 1
  • 26
  • 31
4

One of the other answers here is: 80Ax<Esc>d80|. I initially started using it as a key mapping like this:

nnoremap <leader>- 80A-<Esc>d80<bar>

...but I didn't like how it leaves the cursor at the end of the line. Also, for narrow windows (e.g. just a little wider than 80 cells), it causes the entire window to scroll horizontally because the cursor briefly jumps to the end of the long line before it's trimmed back to 80. This is partially resolved by returning to the beginning of the line:

nnoremap <leader>- 80A-<Esc>d80<bar>0

...but then the screen will "flash" briefly while the cursor jumps offscreen and back (thus scrolling the window briefly to the right and back). To prevent this, we can temporarily use reverse insert mode (:h revins or :h ri) to keep the cursor onscreen while appending. Here's the full command as a key mapping:

nnoremap <leader>- :set ri<cr>80A-<esc>81<bar>d$0:set nori<cr>
Tyler Streeter
  • 1,214
  • 12
  • 14
  • very useful as many people looking for an answer to this question will probably move in the direction of making it a reuseable command of some sort. – Adam Tolley Apr 22 '22 at 12:19
2

This answer answers your question. Just replace len computation with your desired column number (+/- 1 may be, I never remember), and remove the enclosing double-quotes added by the substitution.

Community
  • 1
  • 1
Luc Hermitte
  • 31,979
  • 7
  • 69
  • 83
1

Using the textwidth value is also possible. It allows for different maximum line widths depending on filetype (check :h 'tw'). Here is what I now use, it appends a space after existing line content if present and will prompt for the string to use for the pattern:

function! FillLine() abort
    if &textwidth
        let l:str = input('FillLine>')
        .s/\m\(\S\+\)$/\1 /e  " Add space after content (if present).

        " Calculate how many repetitions will fit.
        let l:lastcol = col('$')-1  " See :h col().
        if l:lastcol > 1
            let l:numstr = float2nr(floor((&textwidth-l:lastcol)/len(l:str)))
        else
            let l:numstr = float2nr(floor(&textwidth/len(l:str)))
        endif

        if l:numstr > 0
            .s/\m$/\=(repeat(l:str, l:numstr))/  " Append repeated pattern.
        endif
    else
        echohl WarningMsg
        echom "FillLine requires nonzero textwidth setting"
        echohl None
    endif
endfunction

You can map it for quick access of course. I like:

nnoremap <Leader>' :call FillLine()<Cr>

Note that the calculation assumes simple ASCII characters are being inserted. For more complicated strings, len(l:str) might not work. From :h strlen():

If you want to count the number of multi-byte characters use strchars().
Also see len(), strdisplaywidth() and strwidth().
adigitoleo
  • 92
  • 1
  • 8
0

You can insert first your dashes and then go to the first character and enter your text in replace mode: 80i-Esc0R

if you don't want to type the text, first delete the line with 0D, use 80i-Esc to insert the dashes and 0RCTRL+r " to paste the contents of the unamed register in replace mode.

César Alforde
  • 2,028
  • 2
  • 15
  • 17
0

I actually stumbled across this looking to align columns. Just in case anyone else does the same, this thread might be useful: How to insert spaces up to column X to line up things in columns?

stu0292
  • 471
  • 4
  • 7