3

I would like to define a custom Vim command that takes arguments grouped by single or double quotes. For example, if my arguments are a "b c" then I want a to be considered the first argument and "b c" considered the second argument.

I've unsuccessfully tried commands using <q-args> and <f-args> like this:

function! MyFunc(...)
    echo a:000
endfunction

command! -nargs=* MyCommandQ call MyFunc(<q-args>)
command! -nargs=* MyCommandF call MyFunc(<f-args>)
:MyCommandQ a "b c"     --> ['a "b c"']   (one argument passed)
:MyCommandF a "b c"     --> ['a', '"b', 'c"']   (three arguments passed)

If I use <q-args>, then I can process a:000 in this function to split on single or double quotes and discard whitespace:

function! MyFunc(...)
    echo filter(split(a:000[0],'[''"]'), 'v:val !~ "^\\s*$"')
endfunction

...but it feels like there should be a better way.

3

The problem is that the syntax of quoting strings in order to include whitespace in them is somewhat foreign to Vim, particularly in a context such as user-defined commands, which typically take space-separated arguments.

Vim commands such as :args will take a list of filenames, but if you need to add whitespace to one of the filenames, you'll do so by escaping the whitespace character with a backslash. This is how <f-args> works, so if you want to pass it a string with whitespace, you can do so by escaping it with a backslash:

:MyCommandF a b\ c
['a', 'b c']

As an alternative, you can have a command that takes Vim expressions separated by commas, in which case you can use quotes around your strings, but then you need to always quote your strings, since unquoted words will be interpreted as variables. You can do so by using <args> directly in your command definition. For example:

:command! -nargs=* MyCommandA call MyFunc(<args>)
:MyCommandA 'a', 'b c'
['a', 'b c']

This is similar to how Vim native commands such as :echo work.

An advantage of the second approach (using <args>) is that you can pass the command arguments that are not strings (such as numbers, lists, dictionaries, etc.) It's also easier to use Vim variables and functions to determine the value of arguments (though when using an <f-args> command you can always use the :execute command to assemble a command string using variables and functions too.)

If you want a syntax that ressembles the shell syntax of using quoted strings to include whitespace while interpreting unquoted words as strings, then I'm afraid that using <q-args> and parsing the resulting argument string yourself is the only option...

3
  • 1
    Thanks, I think I'll stick <q-args> and parse the resulting argument string. I'm working on a custom command which is a wrapper for :grep optimized for searching specific paths for my project. As a result, I want my command to have shell like syntax. The command was working fine with <q-args> and then appending all of it unprocessed to :grep but then I started thinking about situations where I might want to alter a quoted regex arg. I should probably look at some similar plugins for reference now. Dec 1 '21 at 12:14
  • 1
    @ChrisHeithoff I'd also consider whether you really want to wrap :grep or if you need some mappings or similar that setup something like :grep <cursor> my_special_args; I would be inclined to let :grep do the heavy lifting of argument parsing where possible. Heck, at that point, I might just do :command -nargs=+ Foo grep <args> my_special_args
    – D. Ben Knoble
    Dec 1 '21 at 14:04
  • 2
    @D.BenKnoble That's a good thought. Yes, I'm adding my_special_args which is a space-separated list of paths. I wanted to be a little fancier and I temporarily set the quickfixwindow to modifiable so I can substitute really long absolute file names in the quickfixwindow with shorter abbreviated names. I also was thinking about a variation where a visual selection was the argument to grep so I would want to escape special characters from the selection that have special regex meaning. I'm looking through github.com/mhinz/vim-grepper now for ideas. Dec 1 '21 at 14:47

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.