0

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.
3
  • 1
    Gonna be a lot easier with a function (called by your command). That okay?
    – B Layer
    Jul 20, 2021 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, 2021 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, 2021 at 13:49

1 Answer 1

3

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
        endif
    endif

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

" -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.

Bonus

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
            else
                throw "Invalid parameter " . l:arg
            endif
        endfor
    endif

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

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

Your Answer

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

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