I'm using vim all the time for text editing these days, and have a particular use case: editing a dataset using successive commands. The main problem, as often, is fixing things, ideally rather quickly.

The picture is this:

  • a folder with a number of files, e.g. 1000, that need to be cleaned-up / formatted.
  • the cleaning process is quite empirical: checking files, seeing things that need to be changed/erased, thinking of the regex to do it, applying it to the dataset.

I am aware, and use, batch commands as described in these lovely answers here and here, and am in awe at the variety of approaches one can have using bufdo, argdo, windo, tabdo, cfdo or lfdo.

The slight issue, however, is that if you try to undo what you just did, by doing bufdo undo, that command has no memory of the previous batch command, and so if one file was changed by the next-to-last (good) regex, but not by the last (misguided) one you applied because their was no match, then the next-to-last (good) one will be undone, alongside all the undo-s you want in all the files that have been changed by the last misguided regex.

However, there's one thing that I haven't yet come across, which is a way to streamline the use of batch commands like bufdo so that:

  1. it is easy and clean to undo (and would lead to a navigable undo tree for the dataset, as it were, not just for individual files).
  2. maybe it would be possible to see which files have been changed and where are the modifications (seems like a good use for the quickfix/location lists), so that it is possible to apply the regex, go have a quick look to see if everything is fine, before editing further.

I saw somewhere, sadly I can't find it any more now, that I could use git and commit at each step, but that's very slow compared to a do/undo workflow.

Any ideas? Thanks in advance for any feedback! Not sure if I'm confident yet to actually write such a plugin, but leads on how to start doing that could also be welcome.

  • See :h :earlier, :h 'undofile', :h :diffthis – Matt May 14 '20 at 15:43
  • 1
    "use git and commit at each step, but that's very slow compared to a do/undo workflow" This shouldn't be that slow really... Why is it that slow in your case? Using git commits is probably one of the best ways to handle this, since it helps you document your steps and retrace them if needed. Should be very handy if you later realize you made a mistake 4 or 5 steps earlier. – filbranden May 14 '20 at 16:32
  • 1
    @filbranden I agree, it certainly is the safest and most thorough option. However, there are cases where one wants to do some quick prototyping, no? A bit like the difference between editing one large file with an editor, or doing sed commands and then committing... I now fantasize about something more advanced, perhaps a bit like @tpope's Fugitive, where you can easily explore the changes, etc. I hope to do it some time in the future. – Jeremie May 15 '20 at 20:16

I use argdo in this answer, same rule applies to bufdo, windo , ... .

Don't use argdo undo

argdo undo failes in these conditions:

  • Some buffers remain unchanged after last argdo, might caused by 0 pattern match, execute undo on them is a mistake, it will undo changes made by older argdo.
  • An error occurs during your last argdo, when an error occurs, argdo abort, the buffer cause error becomes your curren buffer, further buffers remain unchanged, it's actually similar to previous case.

You can use argdo undo only when your last argdo ... make changes to all the buffer or it's your first argdo and you don't enable :h 'undofile'. That's too hard to work with, my advice is stay away from it.

argdo w --> argdo whatevery --> argdo e!

The right way to undo last argdo is argdo e!, it doesn't care if some of the files failed to make change, it works even when an error happens during last argdo. The prerequest of this approach is you must always save all the files before you run argdo change_cmd.

This is the method I've been using for a long time, it serves me well.

You can replace argdo w with argdo update if you want to avoid write unchanged files.

Find out which buffer remain unchanged

When a buffer is changed, it's :h 'modified' is on, this command should show you which buffer remain unchanged

argdo if ! &modified | echom expand('%') 'remain unchanged' | endif

Again, you must save all your buffers before argdo change_cmd

batch earlier

argdo earlier 10m

undo all changes in last 10m. See :h :earlier, :h :later

The special cdo, ldo

You didn't mention them, but I think I should mention them, as cdo is the most used batch command(at least for me). Unlike other *do, cdo works on quicikfix list, it might contain multiple entries for a buffer, when an error occurs during cdo, it aborts if and only if the error is caused by the last entry in a buffer, otherwise it reports error and continue it's job, which means when you see an error during cdo, you have no idea if current buffer is the buffer that cause error, you must see :message to find out what went wrong. See this question for detail.

Batch undo n steps.

AFAIK there is no reliable builtin way to do this. But it's possible to achieve this by argdo earlier {N}s, you must find a way to record the time point when you execute argdo, AFAIK there is no autocmd events for this, you must build custom command to wrap it, not sure if it's worth the trouble.

  • 1
    Brilliant, great idea! Thanks so much. Ok, I think for this answer to be complete I'd add just one thing: a mention of the option hidden, which is required (I was always updating/saving mine since I did not have it set). – Jeremie May 15 '20 at 20:24

I think building on your logic @dedowsdi one could then do

argdo if &modified | earlier | endif

I can see how one could have several wrapper functions written around this, that's great!

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.