I often need to delineate "sections" of source code files using a comment character to create horizontal lines. For example if I wanted to draw such a line in a python file I could do

:Hline # 79

where the first argument is the character and the second character is the number of times it is to be repeated to create a line. The thing I could come up with is this:

command! -nargs=1 Hline let b:horiz_line = repeat('<args>', 79) <bar>
    \ exe "norm i" . b:horiz_line

Which allows me to say :Hline #.

  1. I can't thing of a way to specify both the arguments
  2. Would it be possible to specify default values ? Like for example, the first argument could be set to the commentstring and the second argument could be set to the textwidth? Or if not set defaults to - and 79 characters.
  • 1
    Gonna be a lot easier with a function (called by your command). That okay? – B Layer Jul 20 at 13:46
  • yes of course. I would still like to call it using an easy command name. I just cant figure out to handle multiple values with defaults. – First User Jul 20 at 13:48
  • Yeah, can keep the same command syntax...but handling optional arguments will be a whole lot easier if it's backed by a function. – B Layer Jul 20 at 13:49

Here's the general idea...

func! MakeHLine(...) abort
    " Default delim char and width
    let l:ch = '#'
    let l:tw = 78

    " Override defaults per given parameters
    if a:0
        let l:ch = a:1
        if a:0 == 2
            let l:tw = a:2

    " Do the actual work
    eval repeat(l:ch, l:tw)->append(line("."))

" -nargs=* means as many args as you want. I'm not checking
" for invalid numbers of params. A good thing for you to add
" to the function.
command! -nargs=* Hline call MakeHLine(<f-args>)

I'm simply adding the text on a line following the line the cursor is on.

One issue, though, is that due to the way optional arguments work you can't specify only the width. You can do no args or just the character or both character and width (or that can be flipped if you want to be able to specify just the width).

Certainly, it's possible to do but it'll require code that does a deeper parse of the parameter list.


Here's one way you could allow full flexibility with the parameters, i.e. if you pass a single param it can either be a number/width or a non-number/delimiter-char. Also, with two params the position doesn't matter. Put width before char or vice-versa.

func! MakeHLine(...) range
    let l:ch = '#'
    let l:tw = 28

    if a:0
        " Use regexes to determine what's in each param
        for l:arg in a:000
            if l:arg =~ '^\d\+$'
                let l:tw = l:arg + 0
            elseif l:arg =~ '^[^0-9]\+$'
                let l:ch = l:arg
                throw "Invalid parameter " . l:arg

    eval repeat(l:ch, l:tw)->append(line("."))

command! -nargs=* Hline call MakeHLine(<f-args>)
  • would it be possible to do something like let l:tw = &tw to get the textwidth for that file? – First User Jul 20 at 14:18
  • Yeah. Doesn't that work exactly like you typed it? (That setting is always local to buffer.) – B Layer Jul 20 at 14:19
  • 1
    yes it does. thanks. – First User Jul 20 at 14:20
  • why eval and not call? – D. Ben Knoble Jul 20 at 15:00
  • or even put =repeat(…)? – D. Ben Knoble Jul 20 at 15:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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