Чи можу я отримати сповіщення, коли я скасовую зміни від скасування файлу?


15

Я вже деякий час використовую функцію скасування файлу у Vim. Це дуже приємна особливість.

Однак одне роздратування полягає в тому, що випадково скасувати зміни, які я робив останній раз, коли я відкрив файл, дуже легко; це може бути 2 хвилини тому, годину тому, минулого тижня чи місяць тому.

Наприклад, скажімо, я відкриваю файл, вношу кілька змін, виходжу та змінюю деякі інші файли та виявляю, що мої зміни не потрібні були, або, можливо, це були лише кілька тимчасових заяв про налагодження.

Раніше я міг просто потримати uклавішу, поки Вім не сказав "Вже найдавніша зміна" :wq, і буде зроблено. Але тепер я повинен бути дуже обережним, щоб не скасувати зміни, які я робив востаннє, коли я відкрив файл. Немає очевидного способу побачити, коли ви це робите.

Чи є якийсь спосіб зробити це більш явним? Наприклад, показавши його десь, видавши попередження або навіть попросивши підтвердження.


Це питання, яке я замислювався з моменту оприлюднення функції скасування файлу, і таке, про яке я мав намір звернутися до запитання з часу відкриття цього сайту! Сподіваючись, що відповідь хороша. Я думаю, я також прийняв би відповідь, яка дала команду, яка повернула файл у стан, у якому він знаходився з моменту останнього відкриття . (А ще краще, відскочив до того стану скасування.)
Багатий

Відповіді:


8

У мене була така точна проблема. Ось що я додав у свій vimrc, щоб виправити його:

" 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

Зауважте, що ви не встановлюєте undofileпараметр; вам потрібно видалити його з vimrc, якщо він у вас є.

Скасувати зараз працює як "нормальний". Але ми ж вручну записувати в файл відмін з wundoкомандою в BufWritePost.

ReadUndo()Функція вручну зчитує файл відкоту з rundoі встановлює undofileопцію. Тепер ми можемо скористатися, uщоб повернутися в історію. Я відобразив це на <Leader>u.

Це, мабуть, може бути і краще. Він перезаписує попередню інформацію про скасування, тому ви не можете повернутися до попереднього попереднього редагування файлу. Але я сам цього не потребував.


Зверніть увагу , що я помітив проблему з цим: ви тільки зберегти вміст поточної сесії в файл відкоту. Це відрізняється від поведінки за замовчуванням, де зберігається вміст попереднього сеансу + вся інформація, яка вже є у файлі скасування ... Способом виправити це було б прочитати файл скасування, об'єднати зміни скасування, а потім записати файл скасування ... Але це не здається можливим ...: - /
Мартін Турноїй

@Carpetsmoker Я згоден. Було б непогано зробити це, якби це було можливо. Як це буває, це давно працювало для мене досить добре. YMMV.
superjer

5

Ооо, о, нарешті, шанс продемонструвати цю витончену команду!

Vim може "повернутися у часі". У ньому є :earlierкоманда ...

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

... що може повернути файл до попереднього стану. Це можна використати різними способами.

  • Якщо ви можете зробити бальну оцінку того, скільки часу знадобилося вам зробити ці зміни, ви можете ввести час у цю команду. Або, наприклад, якщо ви знаєте, що не змінили файл (крім змін, які ви хочете скасувати) за минулу добу, ви можете використовувати

    :earlier 1d
    
  • Якщо ви написали (зберегли) файл лише один раз після змін, які ви хочете скасувати, або ви взагалі не зберегли файл, ви можете використовувати

    :earlier 1f
    

    Як описано в :helpтексті, це повернеться до раніше написаної версії, якщо ви тільки що написали файл, або повернетеся до останнього часу, коли він був збережений, якщо у вас немає збережених змін.

Це не відповідає точно на ваше запитання, але це здається трохи проблемою XY.


1
це звучить як трохи проблема XY : Чому? Я скасовую u, і я випадково проходжу повз зміни, які я вніс зараз ... Я не впевнений, яка тут може бути оригінальна "проблема X"?
Мартін Турной

1
Я зараз :earlierдо речі, але мені ще потрібно здогадатися ; так само, як мені потрібно здогадуватися при використанні u... У випадках, мабуть, трохи краще, але я вважаю за краще щось більш чітке (якщо можливо).
Мартін Турноїй

1
@Carpetsmoker "X" - це "я хочу скасувати лише ці зміни, які я нещодавно вніс". "Y" - "Як я можу скасувати зміни та проігнорувати скасування файлу?" Ви як і раніше повинні здогадуватися, але ви сказали у своєму запитанні, що останні зміни, які ви внесли, були минулого тижня чи раніше, тож ви могли просто зробити щось подібне :ea 5d. Ви також можете використовувати :ea 1fпідхід. У будь-якому випадку, це набагато менш зернисте.
Дверноруб

"X" і "Y" просто здаються перефразированием тієї самої проблеми? Я дійсно згадував «тижня» в моєму питанні, але це також може бути годин або хвилин ( з змінами , що) ... Це не поганий відповідь як такої , до речі, я був (і ранку) просто сподіваючись , що що - то краще ...
Мартін Турноїй

Я разом з @Carpetsmoker. Я знав про: раніше протягом певного часу, але я все ще вимкнув скасування файлу саме з тієї причини, що описана в питанні.
Багатий

4

Оновлення 2015-06-28 : я виправив невелику помилку та випустив це як плагін . Код плагіна трохи кращий, оскільки він знову попереджає після переміщення курсору; Я рекомендую вам використовувати плагін.



Відповідь супержера чудово працює, але має сумний побічний ефект, що ви можете скасувати зміни лише з останнього сеансу Vim, а не з усіх попередніх сеансів Vim.

Це тому, що wundoперезаписує файл скасування; це не злито. Наскільки мені відомо, немає способу це виправити.

Тож ось моє альтернативне рішення, воно покаже велике червоне попереджувальне повідомлення, коли ви скасовуєте зміни з файлу скасування.

Це схоже на відповідь Інго Каркат , але він не потребує зовнішнього плагіна і має деякі тонкі відмінності (відображається попередження замість звукового сигналу, не потрібно uдвічі натискати ).

Зверніть увагу , це тільки модифікуємо uі <C-r>прив'язується до нього і НЕU , :undoі :redoкоманді.

" 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

У мене є наступне, яке зупиняється і подає звукові сигнали, коли скасування досягає збереженого вмісту буфера.

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>

Це інтегрується з плагіном repe.vim; для цього потрібен мій плагін Ingo-library .


Чи використовуєте ви "персистую", щоб означати "стан, у якому знаходився файл під час завантаження буфера"? Зазвичай я очікую, що "зберігається" означає стан файлу, який зараз збережено на диску.
Багатий

@Rich: Ні, ми маємо те саме визначення. Мої відображення не роблять саме те, що запитує, але я все-таки вважаю це дуже корисним.
Інго Каркат

2

Я думаю, я також прийняв би відповідь, яка дала команду, яка повернула файл у стан, у якому він знаходився з моменту останнього відкриття. (А ще краще, відскочив до цього стану скасування.)

> Багатий

Мені дуже сподобалась цитована ідея, тому я зробив :Revertкоманду. Сподіваюся, ви знайдете його відповідним до вашого питання.

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)

Допоміжні функції Get_seqта Real_seq, засновані на скасуванні , потрібні, оскільки undotree()['seq_cur']іноді недостатньо для точного визначення поточного положення в дереві скасування. Більше про це можна прочитати тут .


це виглядає як хороше рішення. Але у вас є помилка у вашому au. vimrc там є винуватцем. Просто залиште це
Науман
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.