0

A g command can find the lines globally and then apply actions, like the following would find lines that contain foo and delete those lines: :g/foo/d

An s command is more well-known for its substitution power: :s/foo/bar/ would substitute the first foo of the current line with bar.

I wonder if it is possible to create my own excmd which takes this form :<excmd>/<1st param>/<2nd param>/<options>, which, in turn calls a function like MyFunc( param1, param2, options ).

Having this syntactic sugar would greatly help since the function should deal with quotes, whitespaces and other special characters (like !, %) a lot, and using / as the delimiter would greatly avoid much escaping problems.

Many thanks in advance!

1

Yes of course. You'll just need to parse the parameter you receive.

Use the first char as delimiter, and split() the string with this delimiter. Of course, if you wish to support stuff like :Whatever/st\/uff/other, it'll be a little bit more complex.

I remember to have given a few answers on SO/vi.SE that implement such command: Here, here, and here.

For instance, in lh-style I have the following:

command! -nargs=1 -range -complete=custom,s:CompleteConvertNames
      \ ConvertNames <line1>,<line2>call s:ConvertNames(<f-args>)

" Function: s:ConvertNames(repl_arg) {{{3
" Syntax: ConvertNames/{regex}/{convertion_type}
function! s:ConvertNames(repl_arg) range abort
  let sep = a:repl_arg[0]
  let fields = split(a:repl_arg, sep)
  if len(fields) != 2 && len(fields) != 3
    throw ":NameConvert/{regex}/{convertion_type}/[{opt}] expects exactly two or three parameters"
  endif
  let convertion_type = fields[1]
  let i = lh#list#find_if(s:k_convertions, 'v:1_[0]=='.string(convertion_type))
  if i == -1
    throw "convertion (".convertion_type.") not found"
  endif
  " build the action to execute
  let ConvertFunc = function(s:k_convertions[i][1])
  let action = '\=(ConvertFunc(submatch(0)))'
  let cmd = a:firstline . ',' . a:lastline . 's'
        \. sep . fields[0]
        \. sep . action
        \. sep.(len(fields)>=3 ? fields[2] : '')
  " echomsg cmd
  exe cmd
endfunction

EDIT: Here is another (untested) example:

command! -nargs=1 
      \ Hello call s:Hello(<f-args>)

function! s:ConvertNames(repl_arg) range abort
  let sep = a:repl_arg[0]
  let fields = split(a:repl_arg, sep)
  let cmd = 'echo "Hello".'.string(join(fields, ',')))
  exe cmd
endfunction
  • Thank you for your answer! Sorry I don't understand how it works.. May I have an example of using :Hello/world/name which does :echo 'Hello world, name!'? – Sunny Pun Dec 27 '17 at 4:24
  • That && in the if seems like a bug – D. Ben Knoble Dec 27 '17 at 15:04
  • @DavidBenKnoble. I had to read it several times, but it doesn't seems so. It's equivalent to !( (len == 3) || (len == 2) ) which seems to be exactly what I wanted to test. Anyway, thank you for checking the code :) – Luc Hermitte Dec 27 '17 at 21:59
  • I misread; it is correct, if still difficult to parse. No matter. – D. Ben Knoble Dec 27 '17 at 22:05

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.