Czy mogę otrzymywać powiadomienia, gdy cofam zmiany z nieoficjalnego pliku?

15

Od jakiegoś czasu korzystam z funkcji nieofile w Vimie. To bardzo miła funkcja.

Jednak jedną irytacją jest to, że bardzo łatwo jest przypadkowo cofnąć zmiany, które zrobiłem przy ostatnim otwarciu pliku; co może być 2 minuty temu, godzinę temu, w zeszłym tygodniu lub miesiąc temu.

Załóżmy na przykład, że otwieram plik, wprowadzam kilka zmian, idę i zmieniam inne pliki, i odkrywam, że moje zmiany nie były wymagane, a może były to tylko tymczasowe instrukcje debugowania.

Wcześniej mogłem przytrzymać uklawisz, dopóki Vim nie powie „Już przy najstarszej zmianie” :wqi gotowe. Ale teraz muszę bardzo uważać, aby nie cofnąć zmian, które zrobiłem przy ostatnim otwarciu pliku. Nie ma oczywistego sposobu, aby zobaczyć, kiedy to robisz.

Czy jest jakiś sposób, aby uczynić to bardziej wyraźnym? Na przykład pokazując go gdzieś, wydając ostrzeżenie lub nawet prosząc o potwierdzenie.

Martin Tournoij
źródło
To jest pytanie, które zastanawiałem się od czasu odsłonięcia funkcji nieoficjalnej, i które chciałem zadać od czasu odsłonięcia tej strony! Mam nadzieję, że jest dobra odpowiedź. Myślę, że zaakceptowałbym również odpowiedź, która wydała polecenie, które przywróciło plik do stanu, w jakim był od ostatniego otwarcia . (Lub lepiej, wróciłem do tego stanu cofania.)
Rich

Odpowiedzi:

8

Miałem dokładnie ten problem. Oto, co dodałem do mojego vimrc, aby to naprawić:

" 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

Pamiętaj, że nie ustawiasz undofileopcji; musisz go usunąć z vimrc, jeśli go posiadasz.

Cofnij działa teraz jako „normalne”. Ale nie ręcznie zapisu pliku cofania z wundopolecenia w BufWritePost.

ReadUndo()Funkcja ręcznego odczytuje plik cofania się rundoi ustawia undofileopcję. Teraz możemy użyć, uaby wrócić do historii. Odwzorowałem to na <Leader>u.

To może być prawdopodobnie lepsze. Zastępuje poprzednie informacje o cofnięciu, więc nie możesz wrócić do poprzedniej poprzedniej edycji pliku. Ale sam tego nie potrzebowałem.

superjer
źródło
Uwaga Zauważyłem problem: zapisujesz tylko zawartość bieżącej sesji w pliku cofania. Różni się to od domyślnego zachowania, w którym zapisywana jest zawartość poprzedniej sesji + wszystkie informacje znajdujące się już w pliku cofania ... Aby to naprawić, należy odczytać plik cofnięcia, scalić zmiany cofnięcia, a następnie napisać plik cofania ... Ale to nie wydaje się możliwe ...: - /
Martin Tournoij
@Carpetsmoker Zgadzam się. Byłoby miło to zrobić, gdyby było to możliwe. Tak się składa, że ​​działało to dla mnie wystarczająco długo. YMMV.
superjer
5

Oooch, ooh, wreszcie szansa, aby pokazać to fajne polecenie!

Vim może „cofnąć się w czasie”. Ma :earlierpolecenie ...

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

... co może przywrócić plik do poprzedniego stanu. Można to wykorzystać na wiele sposobów.

  • Jeśli potrafisz oszacować, ile czasu zajęło ci wprowadzenie tych zmian, możesz podać czas na to polecenie. Lub na przykład, jeśli wiesz, że nie zmieniłeś pliku (z wyjątkiem zmian, które chcesz cofnąć) w poprzednim dniu, możesz użyć

    :earlier 1d
    
  • Jeśli plik został zapisany (zapisany) tylko raz od czasu zmian, które chcesz cofnąć lub w ogóle nie zapisałeś pliku, możesz użyć

    :earlier 1f
    

    Jak opisano w :helptekście, plik zostanie przywrócony do poprzednio zapisanej wersji, jeśli właśnie napisałeś plik, lub przywróci ostatni raz, gdy został zapisany, jeśli nie masz żadnych zapisanych zmian.

To nie odpowiada dokładnie na twoje pytanie, ale brzmi jak problem XY.

Klamka
źródło
1
to brzmi jak problem XY : dlaczego? Cofam się ui przypadkowo mijam zmiany, które wprowadziłem teraz ... Nie jestem pewien, jaki mógł być oryginalny „problem X”?
Martin Tournoij
1
A :earliertak przy okazji, ale wciąż muszę zgadywać ; tak jak muszę zgadywać podczas korzystania z u... W niektórych przypadkach jest to prawdopodobnie trochę lepsze, ale wolałbym coś bardziej wyraźnego (jeśli to możliwe).
Martin Tournoij
1
@Carpetsmoker „X” brzmi „Chcę cofnąć tylko te zmiany, które wprowadziłem ostatnio”. „Y” to „Jak mogę cofnąć zmiany i zignorować plik undofile?” Wciąż musisz zgadywać, ale powiedziałeś w swoim pytaniu, że ostatnie zmiany, które wprowadziłeś, były w zeszłym tygodniu lub wcześniej, więc możesz po prostu zrobić coś takiego :ea 5d. Możesz również użyć tego :ea 1fpodejścia. W każdym razie jest znacznie mniej szczegółowy.
Klamka
„X” i „Y” wydają mi się po prostu powtórzeniem tego samego problemu? W swoim pytaniu wspomniałem o „tygodniach”, ale mogą to być również godziny lub minuty (zmodyfikowałem to) ... Nawiasem mówiąc, nie jest to zła odpowiedź, tak na marginesie, miałem (i jestem) tylko nadzieję, że jest coś lepszego ...
Martin Tournoij
W tej sprawie jestem z @Carpetsmoker. Wiedziałem o: wcześniej przez pewien czas, ale nadal mam wyłączony plik z dokładnego powodu opisanego w pytaniu.
Bogaty
4

Aktualizacja 28.06.2015 : Naprawiłem mały błąd i wydałem go jako wtyczkę . Kod wtyczki jest nieco lepszy, ponieważ ostrzega ponownie po przesunięciu kursora; Polecam użyć wtyczki.



Odpowiedź od superjera działa świetnie, ale ma niefortunny efekt uboczny, że można cofnąć zmiany tylko z ostatniej sesji Vima, a nie ze wszystkich poprzednich sesji Vima.

Jest tak, ponieważ wundozastępuje plik cofania; nie jest scalony. O ile mi wiadomo, nie ma sposobu, aby to naprawić.

Oto moje alternatywne rozwiązanie, wyświetli duży czerwony komunikat ostrzegawczy podczas cofania zmian z pliku cofania.

Jest to podobne do odpowiedzi Ingo Karkat , ale nie wymaga zewnętrznej wtyczki i ma pewne subtelne różnice (wyświetla ostrzeżenie zamiast sygnału dźwiękowego, nie wymaga udwukrotnego naciśnięcia ).

Uwaga To tylko modyfikuje ui <C-r>wiąże i nieU , :undoi :redopoleceń.

" 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
źródło
3

Mam następujące informacje, które przerywają i emitują sygnał dźwiękowy, gdy polecenie Cofnij osiągnie zawartość bufora utrwalonego.

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>

To integruje się z wtyczką repeat.vim; wymaga mojej wtyczki Ingo-Library .

Ingo Karkat
źródło
Czy używasz słowa „utrwalony”, aby oznaczać „stan, w którym znajdował się plik podczas ładowania bufora”? Zwykle oczekiwałbym, że „utrwalony” będzie oznaczać stan pliku aktualnie zapisanego na dysku.
Bogaty
@Rich: Nie, mamy tę samą definicję. Moje mapowania nie robią dokładnie tego, o co pyta pytanie, ale nadal uważam je za bardzo przydatne.
Ingo Karkat
2

Myślę, że zaakceptowałbym również odpowiedź, która wydała polecenie, które przywróciło plik do stanu, w jakim był od ostatniego otwarcia. (Lub lepiej, wróciłem do tego stanu cofania).

> Bogaty

Naprawdę podobał mi się cytowany pomysł, więc wydałem :Revertpolecenie. Mam nadzieję, że uznasz to za istotne dla twojego pytania.

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)

Funkcje pomocnicze Get_seqi Real_seq, w oparciu o undotree , są niezbędne, ponieważ undotree()['seq_cur']czasami nie wystarcza do wskazania aktualnej pozycji w drzewie cofania. Możesz przeczytać więcej na ten temat tutaj .

dsfdsfd
źródło
to wygląda na dobre rozwiązanie. Ale masz błąd w swoim au. vimrc tam jest winowajcą. Po prostu to pomiń
Naumann,