Posso essere avvisato quando annullo le modifiche dal file undofile?


15

Sto usando la funzione undofile in Vim da un po 'di tempo. È una caratteristica molto bella.

Tuttavia, un fastidio è che è molto facile annullare accidentalmente le modifiche che ho fatto l'ultima volta che ho aperto il file; che potrebbe essere 2 minuti fa, un'ora fa, la settimana scorsa o un mese fa.

Ad esempio, diciamo che apro un file, apporto alcune modifiche, spengo e cambio altri file e scopro che le mie modifiche non erano necessarie, o forse erano solo alcune dichiarazioni di debug temporanee.

In precedenza, potevo semplicemente tenere premuto il utasto fino a quando Vim ha detto "Già al più vecchio cambiamento" :wq, ed essere fatto. Ma ora devo stare molto attento a non annullare le modifiche apportate l'ultima volta che ho aperto il file. Non c'è modo ovvio per vedere quando lo fai.

C'è un modo per renderlo più esplicito? Ad esempio mostrandolo da qualche parte, emettendo un avviso o persino chiedendo una conferma.


Questa è una domanda che mi sono posto dalla presentazione della funzione di non-file, e che avevo intenzione di fare in giro a porre dopo la presentazione di questo sito! Spero che ci sia una buona risposta. Penso che accetterei anche una risposta che ha dato un comando che ha riportato il file allo stato in cui si trovava dall'ultima volta che l'hai aperto . (O meglio, è tornato a quello stato di annullamento.)
Rich

Risposte:


8

Ho avuto questo esatto problema. Ecco cosa ho aggiunto al mio vimrc per risolverlo:

" 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

Si noti che non si imposta l' undofileopzione; devi rimuoverlo dal tuo vimrc se ce l'hai.

Annulla ora funziona come "normale". Ma noi facciamo scriviamo manualmente al file di annullamento con il wundocomando BufWritePost.

La ReadUndo()funzione legge manualmente il file di annullamento con rundoe imposta l' undofileopzione. Ora possiamo usare uper tornare indietro nella storia. Ho mappato questo a <Leader>u.

Questo potrebbe probabilmente essere migliore. Sovrascrive le informazioni di annullamento precedenti, quindi non è possibile tornare alla precedente precedente modifica del file. Ma non ne ho avuto bisogno.


Nota Ho notato un problema con questo: si salva solo il contenuto della sessione corrente nel file di annullamento. Questo è diverso dal comportamento predefinito, in cui vengono salvati i contenuti della sessione precedente + tutte le informazioni già presenti nel file di annullamento ... Il modo per risolvere questo problema sarebbe leggere il file di annullamento, unire le modifiche di annullamento e quindi scrivere il file di annullamento ... Ma questo non sembra possibile ...: - /
Martin Tournoij

@Carpetsmoker Sono d'accordo. Sarebbe bello farlo se fosse possibile. A quanto pare, questo ha funzionato abbastanza bene per me per molto tempo. YMMV.
superjer

5

Oooh, ooh, finalmente un'occasione per sfoggiare questo ingegnoso comando!

Vim può "tornare indietro nel tempo". Ha un :earliercomando ...

                                                        :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.                                

... che può ripristinare il file a uno stato precedente. Questo può essere usato in vari modi.

  • Se riesci a fare una stima del ballpark del tempo impiegato per apportare queste modifiche, puoi inserire un tempo in questo comando. Oppure, ad esempio, se sai di non aver modificato il file (ad eccezione delle modifiche che desideri annullare) il giorno passato, puoi utilizzare

    :earlier 1d
    
  • Se hai scritto (salvato) il file una sola volta dopo le modifiche che desideri annullare o non hai salvato il file, puoi utilizzare

    :earlier 1f
    

    Come descritto nel :helptesto, questo tornerà alla versione precedentemente scritta se hai appena scritto il file o tornerà all'ultima volta che è stato salvato se non hai apportato modifiche.

Questo non risponde esattamente alla tua domanda, ma sembra un po 'un problema XY.


1
sembra un po 'un problema XY : perché? Sto annullando u, e per sbaglio vado oltre le modifiche che ho fatto in questo momento ... Non sono sicuro di quale potrebbe essere il "problema X" originale?
Martin Tournoij,

1
A proposito, adesso :earlier, ma devo ancora indovinare ; proprio come devo indovinare quando uso il u... In casi probabilmente è un po 'meglio, ma preferirei qualcosa di più esplicito (se possibile).
Martin Tournoij,

1
@Carpetsmoker "X" è "Voglio annullare solo queste modifiche che ho apportato di recente." "Y" è "Come posso annullare le modifiche e ignorare il file non compilato?" In un certo senso devi ancora indovinare, ma hai detto nella tua domanda che le ultime modifiche che hai apportato sono state la scorsa settimana o precedenti, quindi potresti semplicemente fare qualcosa del genere :ea 5d. Puoi anche usare l' :ea 1fapproccio. In ogni caso, è molto meno granulare.
Doorknob,

"X" e "Y" mi sembrano solo una riformulazione dello stesso problema? Ho menzionato "settimane" nella mia domanda, ma potrebbe anche essere ore o minuti (modificato quello) ... Questa non è una cattiva risposta in quanto tale , stavo (e sto) sperando che ci sia qualcosa di meglio ...
Martin Tournoij,

Sono con @Carpetsmoker su questo. Ho saputo di: prima da un po 'di tempo, ma ho ancora disattivato il profilo per esattamente il motivo descritto nella domanda.
Ricco

4

Aggiornamento 28/06/2015 : ho corretto un piccolo bug e l'ho rilasciato come plugin . Il codice del plugin è leggermente migliore, in quanto avvisa di nuovo dopo aver spostato il cursore; Ti consiglio di usare il plugin.



La risposta di superjer funziona alla grande, ma ha lo sfortunato effetto collaterale di poter annullare solo le modifiche dell'ultima sessione di Vim e non tutte le precedenti sessioni di Vim.

Questo perché wundosovrascrive il file di annullamento; non è unito. Per quanto ne so, non c'è modo di risolvere questo problema.

Quindi ecco la mia soluzione alternativa, mostrerà un grande messaggio di avviso rosso quando annulli le modifiche dal file di annullamento.

Questo è simile alla risposta di Ingo Karkat , ma non richiede un plug-in esterno e presenta alcune sottili differenze (visualizza l'avviso invece del segnale acustico, non richiede di premere udue volte).

Nota Questo solo modifica la ue <C-r>lega, e non il U, :undoe :redocomandi.

" 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

3

Ho il seguente, che si interrompe e emette un segnale acustico quando l'annullamento raggiunge il contenuto del buffer persistente.

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>

Questo si integra con il plugin repeat.vim; richiede il mio plugin ingo-library .


Stai usando "persistente" per indicare "lo stato in cui si trovava il file al caricamento del buffer"? Di solito mi aspetto che "persistente" significhi lo stato del file attualmente salvato su disco.
Ricco

@Rich: No, abbiamo la stessa definizione. I miei mapping non fanno esattamente ciò che la domanda richiede, ma l'ho ancora trovato molto utile.
Ingo Karkat,

2

Penso che accetterei anche una risposta che ha dato un comando che ha riportato il file allo stato in cui si trovava dall'ultima volta che l'hai aperto. (O meglio, è tornato a quello stato di annullamento.)

> Ricco

Mi è piaciuta molto l'idea citata, quindi ho fatto il :Revertcomando. Spero che lo troverai pertinente alla tua domanda.

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)

Le funzioni di supportoGet_seq e Real_seq, basate su undotree , sono necessarie perché a undotree()['seq_cur']volte non è sufficiente individuare la posizione corrente nella struttura di annullamento. Puoi leggere di più qui .


questa sembra una buona soluzione. Ma hai un errore nel tuo au. il vimrc lì dentro è il colpevole. Basta lasciarlo fuori
Naumann,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.