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

只愿长相守 提交于 2019-12-22 05:33:31

问题


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.


回答1:


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



回答2:


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.

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



来源:https://stackoverflow.com/questions/42954285/how-to-make-vims-global-command-confirm-able-before-executing-the-ex-command

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!