Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make vim's `:global` command confirm-able before executing the ex Command?

Tags:

vim

How can I make vim's :global command ask the user if they want to execute the ex command? Similar to what happens with the :substite command with the 'c' option, for example %s:Foo:Fighters:gc

I tried:

:g/mypattern/.s:.*\n::gc

and

:g/mypattern/s:.*\n::gc

but if there is a match on below line it is jumped. For example:

MATCH
NONMATCH
MATCH

MATCH
MATCH

The result is:

NONMATCH

MATCH  <<-- this should be erased.

A promptable g/FOO/d would be perfect.

like image 795
Rodrigo Gurgel Avatar asked Mar 22 '17 14:03

Rodrigo Gurgel


2 Answers

There is no native way to do this. The typical method would be to record a macro and repeat a macro. Making sure you n or / at the end of the macro to advance to the next match. Skipping is now simply n and @@ to execute the macro.

Custom :Global command

However if you truly want to have :global command with a confirm you can sort of mimic this by using confirm() inside the your command. The general idea is to do something like this:

:g/pat/if confirm("&yes\n&no", 2) == 1 | cmd | endif

This doesn't quite work for the following reasons:

  • You have no idea where your cursor is. Need something like :match and :redraw
  • Does not abort well. Need a way to throw an exception to abort
  • Very unwieldily to type this all out

I have come up with the following confirming :Global/:G command

command! -nargs=+ -range=% -complete=command Global <line1>,<line2>call <SID>global_confirm(<q-args>)
command! -nargs=+ -range=% -complete=command G <line1>,<line2>call <SID>global_confirm(<q-args>)
function! s:global_confirm(args) range
  let args = a:args
  let sep = args[0]
  let [pat, cmd; _] = split(args[1:], '\v([^\\](\\\\)*\\)@<!%d' . char2nr(sep), 1) + ['', '']
  match none
  let options = ['throw "Global: Abort"', cmd, '', 'throw "Global: Abort"']
  let cmd = 'exe ''match IncSearch /\c\%''.line(''.'').''l''.@/.''/'''
  let cmd .= '| redraw'
  let cmd .= '| exe get(options, confirm("Execute?", "&yes\n&no\n&abort", 2))'
  try
    execute a:firstline . ',' . a:lastline . 'g'.sep.pat.sep.cmd
  catch /Global: Abort/
  finally
    match none
  endtry
endfunction

Note: Use as-is. Uses IncSearch for highlight and forces \c.

Now you can run :G/foo/d.

Custom :Confirm command

If you rather use a similar technique to the one @Randy Morris provided and use the following :Confirm {cmd} command to confirm {cmd} before execution.

command! -nargs=+ -complete=command Confirm execute <SID>confirm(<q-args>) | match none
function! s:confirm(cmd)
  let abort = 'match none | throw "Confirm: Abort"'
  let options = [abort, a:cmd, '', abort]
  match none
  execute 'match IncSearch /\c\%' . line('.') . 'l' . @/ . '/'
  redraw
  return get(options, confirm('Execute?', "&yes\n&no\n&abort", 2), abort)
endfunction

This will allow you to use :g/foo/Confirm d

For more help see:

:h @
:h q
:h confirm()
:h :exe
:h get()
:h :match
:h :redraw
like image 172
Peter Rincker Avatar answered Oct 21 '22 13:10

Peter Rincker


As far as I know there is no way to do this natively. I think I've hacked together a way to do this but it's probably buggy as I haven't written vimscript in a long time. In this I've defined a command C which accepts an ex command as its arguments. Each line returned via :global is then passed to this ex command if you press y or Y. Any other key causes this line to be skipped.

let s:GlobalConfirmSignNumber = 42
sign define GlobalConfirmMarker text=>> texthl=Search

function GlobalConfirm(cmd)
    let line = getpos(".")[1]
    execute "sign place " . s:GlobalConfirmSignNumber . " line=" . line . " name=GlobalConfirmMarker file=" . expand("%:p")
    redraw!
    echomsg "Execute? (y/N) "
    try
        let char = nr2char(getchar())
        if (char == "y" || char == "Y")
             execute a:cmd
        endif
    finally
        " Ensure signs are cleaned up if execution is aborted.
        execute "sign unplace " . s:GlobalConfirmSignNumber
    endtry
    redraw!
endfunction

command -nargs=* C call GlobalConfirm(<q-args>)

Here's a gif of it in action. In this gif I'm running the command norm! gUU for every line which contains ba. In this case I confirmed every match by pressing y three times.

gif in action

If anyone can make improvements to this (especially the signs bit) please edit at will.

like image 43
Randy Morris Avatar answered Oct 21 '22 13:10

Randy Morris