Kann ich benachrichtigt werden, wenn ich Änderungen von der Undatei rückgängig mache?

15

Ich benutze die Undofile-Funktion in Vim jetzt schon eine Weile. Es ist eine sehr schöne Funktion.

Ein Ärger ist jedoch, dass es sehr leicht ist, versehentlich Änderungen rückgängig zu machen, die ich beim letzten Öffnen der Datei vorgenommen habe. Das kann vor 2 Minuten, vor einer Stunde, vor einer Woche oder vor einem Monat sein.

Nehmen wir zum Beispiel an, ich öffne eine Datei, nehme einige Änderungen vor, ändere einige andere Dateien und stelle fest, dass meine Änderungen nicht erforderlich waren oder es sich nur um einige temporäre Debug-Anweisungen handelte.

Zuvor konnte ich nur die uTaste gedrückt halten, bis Vim sagte "Bereits bei der ältesten Änderung" :wq, und fertig. Aber jetzt muss ich sehr vorsichtig sein, um die Änderungen, die ich beim letzten Öffnen der Datei vorgenommen habe, nicht rückgängig zu machen. Es gibt keine offensichtliche Möglichkeit zu sehen, wann Sie dies tun.

Gibt es eine Möglichkeit, dies expliziter zu machen? Zum Beispiel, indem man es irgendwo anzeigt, eine Warnung ausgibt oder sogar um eine Bestätigung bittet.

Martin Tournoij
quelle
Dies ist eine Frage, die ich mich seit der Enthüllung der undofile-Funktion gefragt habe, und eine, die ich seit der Enthüllung dieser Website stellen wollte! Ich hoffe, es gibt eine gute Antwort. Ich glaube, ich würde auch eine Antwort akzeptieren, die einen Befehl gab, mit dem die Datei auf den Zustand zurückgesetzt wurde, in dem sie sich seit dem letzten Öffnen befand . (Oder besser, sprang zurück zu diesem Rückgängig-Zustand.)
Rich

Antworten:

8

Ich hatte genau dieses Problem. Folgendes habe ich zu meinem vimrc hinzugefügt, um es für mich zu reparieren:

" Always write undo history, but only read it on demand
" use <leader>u to load old undo info
" modified from example in :help undo-persistence

if has('persistent_undo')
  set undodir=~/.vim/undo " location to store undofiles
  nnoremap <leader>u :call ReadUndo()<CR>
  au BufWritePost * call WriteUndo()
endif

func! ReadUndo()
  let undofile = undofile(expand('%'))
  if filereadable(undofile)
    let undofile = escape(undofile,'% ')
    exec "rundo " . undofile
  endif
endfunc

func! WriteUndo()
  let undofile = escape(undofile(expand('%')),'% ')
  exec "wundo " . undofile
endfunc

Beachten Sie, dass Sie die Option nicht festlegen undofile. Sie müssen von Ihrem vimrc entfernen, wenn Sie es haben.

Rückgängig machen funktioniert jetzt wie "normal". Wir schreiben aber manuell in die Undo-Datei mit dem wundoBefehl in BufWritePost.

Die ReadUndo()Funktion liest die Undo-Datei manuell mit rundound setzt die undofileOption. Jetzt können wir uweiter in die Geschichte eingehen. Ich habe das hier abgebildet <Leader>u.

Das könnte wahrscheinlich besser sein. Die vorherigen Rückgängig-Informationen werden überschrieben, sodass Sie nicht zum vorherigen Zeitpunkt zurückkehren können, zu dem Sie die Datei bearbeitet haben. Aber das habe ich selbst nicht gebraucht.

superjer
quelle
Hinweis: Hierbei ist ein Problem aufgetreten: Sie speichern nur den Inhalt der aktuellen Sitzung in der Undo-Datei. Dies unterscheidet sich vom Standardverhalten, bei dem der Inhalt der vorherigen Sitzung und alle bereits in der Rückgängig-Datei enthaltenen Informationen gespeichert werden. Sie können dies beheben, indem Sie die Rückgängig-Datei lesen, die Änderungen rückgängig machen und dann schreiben die Undo-Datei ... Aber das scheint nicht möglich zu sein ...: - /
Martin Tournoij
@Carpetsmoker Ich stimme zu. Das wäre schön, wenn es möglich wäre. Zufälligerweise hat das für mich lange genug gut funktioniert. YMMV.
Superjer
5

Oooh, ooh, endlich eine Chance, diesen raffinierten Befehl vorzuführen!

Vim kann "in der Zeit zurückgehen". Es hat einen :earlierBefehl ...

                                                        :ea :earlier            
:earlier {count}        Go to older text state {count} times.                   
:earlier {N}s           Go to older text state about {N} seconds before.        
:earlier {N}m           Go to older text state about {N} minutes before.        
:earlier {N}h           Go to older text state about {N} hours before.          
:earlier {N}d           Go to older text state about {N} days before.           

:earlier {N}f           Go to older text state {N} file writes before.          
                        When changes were made since the last write             
                        ":earlier 1f" will revert the text to the state when    
                        it was written.  Otherwise it will go to the write      
                        before that.                                            
                        When at the state of the first file write, or when      
                        the file was not written, ":earlier 1f" will go to      
                        before the first change.                                

... wodurch die Datei auf einen früheren Zustand zurückgesetzt werden kann. Dies kann auf verschiedene Arten verwendet werden.

  • Wenn Sie einen Schätzwert für die Dauer dieser Änderungen erstellen können, können Sie eine Zeit in diesen Befehl eingeben. Wenn Sie beispielsweise wissen, dass Sie die Datei (mit Ausnahme der Änderungen, die Sie rückgängig machen möchten) am vergangenen Tag nicht geändert haben, können Sie verwenden

    :earlier 1d
    
  • Wenn Sie die Datei seit den Änderungen, die Sie rückgängig machen möchten, nur einmal geschrieben (gespeichert) haben oder die Datei überhaupt nicht gespeichert haben, können Sie sie verwenden

    :earlier 1f
    

    Wie im :helpText beschrieben, wird die zuvor geschriebene Version wiederhergestellt, wenn Sie die Datei gerade geschrieben haben, oder die zuletzt gespeicherte Version, wenn Sie keine Änderungen gespeichert haben.

Dies beantwortet Ihre Frage nicht genau, aber es klingt wie ein XY-Problem.

Türknauf
quelle
1
es klingt wie ein XY-Problem : Warum? Ich mache das rückgängig uund gehe versehentlich an den Änderungen vorbei, die ich gerade vorgenommen habe ... Ich bin nicht sicher, was das ursprüngliche "X-Problem" hier sein könnte?
Martin Tournoij
1
Ich jetzt :earlierübrigens ungefähr , aber ich muss noch raten ; so wie ich muß erraten , wenn die Verwendung von u... In den Fällen , ist es wahrscheinlich ein bisschen besser, aber ich würde etwas deutlicher (wenn möglich) bevorzugen.
Martin Tournoij
1
@Carpetsmoker "X" ist "Ich möchte nur die Änderungen rückgängig machen , die ich zuletzt vorgenommen habe." "Y" ist "Wie kann ich Änderungen rückgängig machen und die Undatei ignorieren?" Sie müssen immer noch raten, aber Sie sagten in Ihrer Frage, dass die letzten Änderungen, die Sie vorgenommen haben, letzte Woche oder früher waren, sodass Sie einfach so etwas tun können :ea 5d. Sie könnten auch den :ea 1fAnsatz verwenden. In jedem Fall ist es viel weniger körnig.
Türknauf
"X" und "Y" scheinen mir nur eine Umformulierung des gleichen Problems zu sein? Ich habe mention „Wochen“ in meiner Frage, aber es könnte auch Stunden oder Minuten (modifiziert , dass) sein ... Das ist keine schlechte Antwort als solche von der Art und Weise, ich war (und ist) nur die Hoffnung , es ist etwas besser ...
Martin Tournoij
Ich bin mit @Carpetsmoker in diesem Fall. Ich habe gewusst: vor einiger Zeit, aber ich habe immer noch undofile aus genau dem Grund in der Frage beschrieben ausgeschaltet.
Rich
4

Update 28.06.2015 : Ich habe einen kleinen Fehler behoben und als Plugin veröffentlicht . Der Plugin-Code ist insofern etwas besser, als er nach dem Bewegen des Cursors erneut warnt. Ich empfehle dir das Plugin zu benutzen.



Die Antwort von superjer funktioniert prima, hat aber den unglücklichen Nebeneffekt, dass Sie nur Änderungen von der letzten Vim-Sitzung rückgängig machen können und nicht von allen vorherigen Vim-Sitzungen.

Dies liegt daran, dass wundodie Undo-Datei überschrieben wird. Es wird nicht zusammengeführt. Soweit ich weiß, gibt es keine Möglichkeit, dies zu beheben.

Hier ist meine alternative Lösung. Sie zeigt eine große rote Warnmeldung an, wenn Sie Änderungen an der Rückgängig-Datei rückgängig machen.

Dies ähnelt der Antwort von Ingo Karkat , erfordert jedoch kein externes Plugin und weist einige geringfügige Unterschiede auf (es wird eine Warnung anstelle eines Pieptons angezeigt , Sie müssen nicht uzweimal drücken ).

Notieren Sie sich diese nur ändert das uund <C-r>bindet, und nicht die U, :undound :redoBefehle.

" Use the undo file
set undofile

" When loading a file, store the curent undo sequence
augroup undo
    autocmd!
    autocmd BufReadPost,BufCreate,BufNewFile * let b:undo_saved = undotree()['seq_cur'] | let b:undo_warned = 0
augroup end 

" Remap the keys
nnoremap u :call Undo()<Cr>u
nnoremap <C-r> <C-r>:call Redo()<Cr>


fun! Undo()
    " Don't do anything if we can't modify the buffer or there's no filename
    if !&l:modifiable || expand('%') == '' | return | endif

    " Warn if the current undo sequence is lower (older) than whatever it was
    " when opening the file
    if !b:undo_warned && undotree()['seq_cur'] <= b:undo_saved
        let b:undo_warned = 1
        echohl ErrorMsg | echo 'WARNING! Using undofile!' | echohl None
        sleep 1
    endif
endfun

fun! Redo()
    " Don't do anything if we can't modify the buffer or there's no filename
    if !&l:modifiable || expand('%') == '' | return | endif

    " Reset the warning flag
    if &l:modifiable && b:undo_warned && undotree()['seq_cur'] >= b:undo_saved
        let b:undo_warned = 0
    endif
endfun
Martin Tournoij
quelle
3

Ich habe die folgenden, die stoppt und piept, wenn Rückgängig den dauerhaften Pufferinhalt erreicht.

runtime autoload/repeat.vim " Must load the plugin now so that the plugin's mappings can be overridden.
let s:undoPosition = []
function! s:StopAtSavedPosition( action )
    " Buffers of type "nofile" and "nowrite" never are 'modified', so only do
    " the check for normal buffers representing files. (Otherwise, the warning
    " annoyingly happens on every undo.)
    if ingo#buffer#IsPersisted() && ! &l:modified && s:undoPosition != ingo#record#PositionAndLocation(1)
        " We've reached the undo position where the buffer contents correspond
        " to the persisted file. Stop and beep, and only continue when undo is
        " pressed again at the same position.
        call ingo#msg#WarningMsg(a:action . ' reached saved buffer state')
        execute "normal! \<C-\>\<C-n>\<Esc>" | " Beep.

        let s:undoPosition = ingo#record#PositionAndLocation(1)
        return 1
    else
        let s:undoPosition = []
        return 0
    endif
endfunction
nnoremap <silent> u     :<C-U>if ! &l:modifiable<Bar>execute 'normal! u'<Bar>elseif ! <SID>StopAtSavedPosition('Undo')<Bar>call repeat#wrap('u',v:count)<Bar>endif<CR>
nnoremap <silent> <C-R> :<C-U>if ! &l:modifiable<Bar>execute "normal! \<lt>C-R>"<Bar>elseif ! <SID>StopAtSavedPosition('Redo')<Bar>call repeat#wrap("\<Lt>C-R>",v:count)<Bar>endif<CR>

Dies ist in das repeat.vim-Plugin integriert. es benötigt mein ingo-library plugin .

Ingo Karkat
quelle
Verwenden Sie "persistent", um "den Zustand der Datei beim Laden des Puffers" zu bezeichnen? Normalerweise würde ich erwarten, dass "persistent" den Status der aktuell auf der Festplatte gespeicherten Datei bezeichnet.
Rich
@Rich: Nein, wir haben die gleiche Definition. Meine Zuordnungen machen nicht genau das, was die Frage verlangt, aber ich fand es trotzdem sehr nützlich.
Ingo Karkat
2

Ich glaube, ich würde auch eine Antwort akzeptieren, die einen Befehl gab, der die Datei auf den Zustand zurücksetzte, in dem sie sich seit dem letzten Öffnen befand. (Oder besser, sprang zurück zu diesem Rückgängig-Zustand.)

> Reich

Ich mochte die zitierte Idee wirklich, also gab ich :RevertBefehl. Ich hoffe, Sie werden es für Ihre Frage relevant finden.

function! s:Real_seq(inner, outer) abort
  let node = a:outer
  for i in a:inner
    if has_key(i, 'alt')
      call s:Real_seq(i.alt, deepcopy(node))
    endif
    if has_key(i, 'curhead')
      return {'seq': node.seq}
    endif
    let node.seq  = i.seq
  endfor
endfunction

function! s:Get_seq(tree) abort
  let query = s:Real_seq(a:tree.entries, {'seq': 0})
  if (type(query) == 4)
    return query.seq
  else
    return undotree()['seq_cur']
  endif
endfunction

au BufReadPost,BufNewFile * if !exists('b:undofile_start') 
      \ | let b:undofile_start = s:Get_seq(undotree()) | endif

command! -bar Revert execute "undo " . get(b:, 'undofile_start', 0)

Hilfsfunktionen Get_seqund Real_seq, basierend auf undotree , sind notwendig , weil undotree()['seq_cur']manchmal nicht genügend aktuelle Position im Rückgängig - Baum zu lokalisieren. Sie können mehr darüber lesen Sie hier .

dsfdsfd
quelle
das sieht nach einer guten lösung aus. Aber Sie haben einen Fehler in Ihrem au. der vimrc da drin ist der täter . Lass es einfach weg
Naumann