Bisakah saya diberitahu ketika saya membatalkan perubahan dari undofile?

15

Saya telah menggunakan fitur undofile di Vim untuk sementara waktu sekarang. Ini fitur yang sangat bagus.

Namun, satu gangguan adalah sangat mudah untuk secara tidak sengaja membatalkan perubahan yang saya lakukan terakhir kali saya membuka file; yang mungkin 2 menit yang lalu, satu jam yang lalu, minggu lalu, atau sebulan yang lalu.

Sebagai contoh, katakanlah saya membuka file, membuat beberapa perubahan, mematikan dan mengubah beberapa file lainnya, dan menemukan bahwa perubahan saya tidak diperlukan, atau mungkin itu hanya beberapa pernyataan debug sementara.

Sebelumnya, saya hanya bisa memegang ukunci sampai Vim berkata "Sudah pada perubahan tertua" :wq,, dan selesai. Tapi sekarang saya harus sangat berhati-hati untuk tidak membatalkan perubahan yang saya lakukan terakhir kali saya membuka file. Tidak ada cara yang jelas untuk melihat ketika Anda melakukan ini.

Apakah ada cara untuk membuatnya lebih eksplisit? Misalnya dengan menunjukkannya di suatu tempat, mengeluarkan peringatan, atau bahkan meminta konfirmasi.

Martin Tournoij
sumber
Ini adalah pertanyaan yang saya telah bertanya-tanya sejak pembukaan fitur undofile, dan satu saya sudah bermaksud untuk bertanya sejak pembukaan situs ini! Berharap ada jawaban yang bagus. Saya pikir saya juga akan menerima jawaban yang memberi perintah yang mengembalikan file ke keadaan semula sejak terakhir kali Anda membukanya . (Atau lebih baik, melompat kembali ke kondisi kehancuran itu.)
Rich

Jawaban:

8

Saya punya masalah persis ini. Inilah yang saya tambahkan ke vimrc saya untuk memperbaikinya untuk saya:

" 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

Perhatikan bahwa Anda tidak mengatur undofileopsi; Anda perlu menghapus dari vimrc Anda jika Anda memilikinya.

Undo sekarang berfungsi sebagai "normal". Tapi kita jangan secara manual menulis ke file undo dengan wundoperintah di BufWritePost.

The ReadUndo()fungsi manual membaca file undo dengan rundodan menetapkan undofilepilihan. Sekarang kita bisa menggunakan uuntuk kembali ke sejarah. Saya memetakan ini ke <Leader>u.

Ini mungkin bisa lebih baik. Ini menimpa info undo sebelumnya, jadi Anda tidak bisa kembali ke waktu sebelumnya sebelumnya Anda mengedit file. Tetapi saya sendiri tidak membutuhkannya.

superjer
sumber
Catatan Saya perhatikan ada masalah dengan ini: Anda hanya menyimpan konten sesi saat ini ke file undo. Ini berbeda dari perilaku default, di mana isi dari sesi sebelumnya + semua info yang sudah ada dalam file undo disimpan ... Cara untuk memperbaikinya, adalah dengan membaca file undo, menggabungkan perubahan undo, dan kemudian menulis undo file ... Tapi ini sepertinya tidak mungkin ...: - /
Martin Tournoij
@Carpetsmoker saya setuju. Akan menyenangkan untuk melakukan itu jika memungkinkan. Kebetulan ini sudah cukup baik bagi saya untuk waktu yang lama. YMMV.
superjer
5

Oooh, ooh, akhirnya kesempatan untuk memamerkan perintah bagus ini !

Vim dapat "kembali ke masa lalu." Ini memiliki :earlierperintah ...

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

... yang dapat mengembalikan file kembali ke keadaan sebelumnya. Ini dapat digunakan dalam beberapa cara.

  • Jika Anda dapat membuat perkiraan rata-rata tentang berapa lama Anda melakukan perubahan ini, Anda dapat memberi waktu pada perintah ini. Atau, misalnya, jika Anda tahu Anda belum mengubah file (kecuali untuk perubahan yang ingin Anda batalkan) pada hari terakhir, Anda dapat menggunakan

    :earlier 1d
    
  • Jika Anda hanya menulis (menyimpan) file satu kali sejak perubahan yang ingin Anda batalkan, atau Anda belum menyimpan file sama sekali, Anda dapat menggunakan

    :earlier 1f
    

    Seperti dijelaskan dalam :helpteks, ini akan kembali ke versi yang ditulis sebelumnya jika Anda baru saja menulis file, atau kembali ke waktu terakhir disimpan jika Anda tidak memiliki perubahan yang disimpan.

Ini tidak menjawab pertanyaan Anda dengan tepat, tetapi kedengarannya seperti masalah XY.

Gagang pintu
sumber
1
sepertinya sedikit masalah XY : Kenapa? Saya membatalkan u, dan saya secara tidak sengaja melewati perubahan yang saya buat sekarang ... Saya tidak yakin apa yang sebenarnya "masalah-X" bisa berada di sini?
Martin Tournoij
1
Ngomong :earlier-ngomong, sekarang aku akan melakukannya, tetapi aku masih harus menebak ; sama seperti saya perlu menebak ketika menggunakan u... Dalam kasus ini mungkin sedikit lebih baik, tapi saya lebih suka sesuatu yang lebih eksplisit (jika mungkin).
Martin Tournoij
1
@Carpetsmoker "X" adalah "Saya ingin membatalkan saja perubahan ini yang saya buat baru-baru ini." "Y" adalah "Bagaimana saya bisa membatalkan perubahan dan mengabaikan undofile?" Anda masih harus menebak, tetapi Anda mengatakan dalam pertanyaan Anda bahwa perubahan terakhir yang Anda lakukan adalah minggu lalu atau lebih awal, sehingga Anda bisa melakukan sesuatu seperti :ea 5d. Anda juga bisa menggunakan :ea 1fpendekatan tersebut. Bagaimanapun, itu jauh kurang granular.
Gagang pintu
"X" dan "Y" sepertinya mengulangi masalah yang sama bagiku? Saya tidak menyebutkan "minggu" dalam pertanyaan saya, tetapi juga bisa menjadi jam, atau menit (dimodifikasi itu) ... Ini bukan jawaban yang buruk seperti itu dengan cara, saya (dan am) hanya berharap ada sesuatu yang lebih baik ...
Martin Tournoij
Saya dengan @Carpetsmoker yang satu ini. Saya sudah tahu tentang: sebelumnya untuk beberapa waktu, tetapi saya masih belum menonaktifkan secara persis alasan yang dijelaskan dalam pertanyaan.
Kaya
4

Pembaruan 2015-06-28 : Saya memperbaiki bug kecil, dan merilis ini sebagai plugin . Kode plugin sedikit lebih baik, karena memperingatkan lagi setelah memindahkan kursor; Saya sarankan Anda menggunakan plugin.



Jawaban dari superjer bekerja dengan sangat baik, tetapi memiliki efek samping yang disayangkan bahwa Anda hanya dapat membatalkan perubahan dari sesi Vim terakhir, dan tidak semua sesi Vim sebelumnya.

Ini karena wundomenimpa file undo; itu tidak digabung. Sejauh yang saya tahu, tidak ada cara untuk memperbaikinya.

Jadi inilah solusi alternatif saya, itu akan menampilkan pesan peringatan merah besar ketika Anda membatalkan perubahan dari file undo.

Ini mirip dengan jawaban Ingo Karkat , tetapi tidak memerlukan plugin eksternal dan memiliki beberapa perbedaan halus (menampilkan peringatan alih-alih bunyi bip, tidak mengharuskan Anda menekan udua kali).

Catatan ini hanya memodifikasi satu udan <C-r>mengikat, dan tidak dengan U, :undo, dan :redoperintah.

" 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
sumber
3

Saya memiliki yang berikut, yang berhenti dan berbunyi bip saat membatalkan mencapai konten buffer yang bertahan.

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>

Ini terintegrasi dengan plugin repeat.vim; itu membutuhkan plugin ingo-library saya .

Ingo Karkat
sumber
Apakah Anda menggunakan "persisten" yang berarti "status file saat buffer dimuat"? Saya biasanya berharap "bertahan" berarti keadaan file saat ini disimpan ke disk.
Rich
@ Rich: Tidak, kami memiliki definisi yang sama. Pemetaan saya tidak melakukan persis apa yang diminta pertanyaan, tetapi saya masih merasa sangat berguna.
Ingo Karkat
2

Saya pikir saya juga akan menerima jawaban yang memberi perintah yang mengembalikan file ke keadaan semula sejak terakhir kali Anda membukanya. (Atau lebih baik, melompat kembali ke keadaan membatalkan itu.)

> Kaya

Saya sangat menyukai ide yang dikutip sehingga saya membuat :Revertperintah. Saya harap Anda akan menemukan itu relevan dengan pertanyaan Anda.

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)

Fungsi pembantu Get_seqdan Real_seq, berdasarkan undotree , diperlukan karena undotree()['seq_cur']kadang-kadang tidak cukup untuk menentukan posisi saat ini di pohon dibatalkan . Anda dapat membaca lebih lanjut di sini .

dsfdsfd
sumber
ini sepertinya solusi yang bagus. Tetapi Anda memiliki kesalahan dalam au. yang vimrc di sana adalah pelakunya. Biarkan saja
Naumann