1

I regularly find myself editing some code, encountering an error in a function and then changing the function call and the function itself to debug the problem. Once the problem in the function is solved, I then want to revert the call to what it was. Of course I always forget to save the call to a register beforehand, and I figured the most intuitive solution would be to undo the last change on the current line. I have not been able to find such functionality in Vim however. Is there something I overlooked? In my mind I see a mapping that branches the undo tree and replays all edits, except the last one on the current line.

  • 1
    see if U (uppercase) helps... – Sundeep Feb 27 '17 at 10:13
  • 1
    From :h U: "Undo all latest changes on one line, the line where the latest change was made." I'm specifically not looking to undo the latest change in the file, but the latest change on the line. – Octaviour Feb 27 '17 at 10:31
  • undotree() doesn't contain line numbers. Probably the easiest way is to 1) undo all changes back to original state, 2) yank link, 3) put changes back, 4) put. That being said, I personally like to use a version control system (git, hg, svn, whatever) for pretty much everything I do to solve this – Martin Tournoij Feb 27 '17 at 16:38
  • your first option sounds interesting. I do use version control as well, but I find myself not committing for these changes as they seem very small at first. – Octaviour Feb 27 '17 at 17:02
2

This will probably do what you want:

function! Undoline()
  let pos = getpos(".")
  let current = getline(pos[1])
  let chg = changenr()
  while changenr() > 0 && current ==# getline(pos[1])
    silent exec 'u'
  endwhile
  let old = getline(pos[1])

  while changenr() < chg
    silent exec 'redo'
  endwhile
  " undo if we jumped over a gap
  if changenr() > chg
    silent exec 'u'
  endif
  call setpos('.', pos)
  if old ==# current
    echo 'no change found'
  else
    call setline(pos[1], old)
  endif
endfunction

It will undo until the line you are on changes, take the old value, redo to your current state and then replace the line with the old value.

It will obviously not work if a line above was added or removed, in that case take a look at one of the undo browsing plugins.

  • Thanks, this goes a long way. The only problem I have currently is that I can't undo two edits (it just goes back and forth as the undo is considered a change). I'll wait a bit and see if anyone comes up with another solution. By the way, is there a reason you do not use let current = getline('.')? – Octaviour Feb 28 '17 at 11:40
  • @Octaviour if you know a bit of vimscript you can modify the function to accept a count argument and then wait until you find count undo changes on that line. I used getline that way for consistency (see old). – laktak Feb 28 '17 at 11:56
  • Count might be an option. I was thinking more in the direction of keeping track of changnumbers that are introduced because of this function. I think this is a better solution because it does not require you to keep track of the number of changes. I've accepted your solution as I think it solves the initial problem and polishing it is trivial. – Octaviour Feb 28 '17 at 12:41
  • @Octaviour do you have a more polished version you could share? – alec Mar 7 at 2:00

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.