Могу ли я получать уведомления об отмене изменений из файла?


15

Я уже давно использую функцию отмены файлов в Vim. Это очень хорошая особенность.

Однако одно раздражение заключается в том, что очень легко случайно отменить изменения, которые я сделал в последний раз, когда открывал файл; это может быть 2 минуты назад, час назад, на прошлой неделе или месяц назад.

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

Раньше я мог просто держать uключ, пока Вим не сказал «Уже при самых старых изменениях» :wq, и все готово. Но теперь я должен быть очень осторожным, чтобы не отменить изменения, которые я сделал в прошлый раз, когда я открыл файл. Нет очевидного способа увидеть, когда вы это делаете.

Есть ли способ сделать это более явным? Например, показывая его где-нибудь, выдавая предупреждение или даже запрашивая подтверждение.


Это вопрос, который меня интересовал с момента раскрытия функции отмены файлов, и я собирался задать этот вопрос с момента открытия этого сайта! Надеюсь, что есть хороший ответ. Я думаю, что я бы также принял ответ, который дал команду, которая вернула файл в состояние, в котором он находился с момента последнего открытия . (Или лучше, отскочил назад к тому состоянию отмены.)
Rich

Ответы:


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.
Супер-

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и случайно прохожу мимо изменений, которые я сделал прямо сейчас ... Я не уверен, что здесь может быть первоначальная «проблема Х»?
Мартин Турной

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

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

«X» и «Y» просто перефразируют одну и ту же проблему для меня? Я упомянул «недели» в своем вопросе, но это также могут быть часы или минуты (изменив это) ... Кстати, это не плохой ответ, как таковой , я (и я) просто надеялся, что есть что-то лучшее ...
Мартин Турной

Я с @Carpetsmoker на этот раз. Я знал о: раньше в течение некоторого времени, но я все еще отключил отключение по той же причине, что описана в вопросе.
Богатый

4

Обновление 2015-06-28 : я исправил небольшую ошибку и выпустил ее как плагин . Код плагина немного лучше, так как он снова предупреждает после перемещения курсора; Я рекомендую вам использовать плагин.



Ответ от superjer прекрасно работает, но имеет неприятный побочный эффект, что вы можете отменить изменения только из последнего сеанса 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>

Это интегрируется с плагином repeat.vim; это требует моего плагина ingo-библиотеки .


Вы используете "постоянный", чтобы означать "состояние, в котором был файл, когда буфер загружался"? Обычно я ожидаю, что «постоянный» будет означать состояние файла, сохраненного в данный момент на диске.
Рич

@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 , необходимы, потому что undotree()['seq_cur']иногда их недостаточно, чтобы точно определить текущую позицию в дереве отмены. Вы можете прочитать больше об этом здесь .


это выглядит как хорошее решение. Но у вас есть ошибка в вашем au. vimrc там является виновником. Просто оставьте это
Науман
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.