Tôi có thể được thông báo khi tôi hoàn tác các thay đổi từ phần không hợp lệ không?


15

Tôi đã sử dụng tính năng không tốt trong Vim được một thời gian rồi. Đó là một tính năng rất hay.

Tuy nhiên, một điều khó chịu là rất dễ vô tình hoàn tác các thay đổi mà tôi đã làm lần trước khi tôi mở tệp; có thể là 2 phút trước, một giờ trước, tuần trước hoặc một tháng trước.

Ví dụ: giả sử tôi mở một tệp, thực hiện một vài thay đổi, tắt và thay đổi một số tệp khác và phát hiện ra rằng những thay đổi của tôi không bắt buộc hoặc có lẽ chúng chỉ là một vài câu lệnh gỡ lỗi tạm thời.

Trước đây, tôi chỉ có thể giữ uchìa khóa cho đến khi Vim nói "Đã thay đổi lâu đời nhất" :wq, và được thực hiện. Nhưng bây giờ tôi phải rất cẩn thận để không hoàn tác những thay đổi tôi đã làm lần trước khi tôi mở tệp. Không có cách rõ ràng để xem khi bạn đang làm điều này.

Có cách nào để làm cho điều này rõ ràng hơn? Ví dụ: bằng cách hiển thị nó ở đâu đó, đưa ra cảnh báo hoặc thậm chí yêu cầu xác nhận.


Đây là một câu hỏi mà tôi đã tự hỏi kể từ khi công bố tính năng không phù hợp, và tôi đã có ý định đi xung quanh để hỏi kể từ khi công bố trang web này! Hy vọng có một câu trả lời tốt. Tôi nghĩ rằng tôi cũng sẽ chấp nhận một câu trả lời đã đưa ra một lệnh hoàn nguyên tệp về trạng thái kể từ lần cuối bạn mở nó. (Hoặc tốt hơn, nhảy trở lại trạng thái hoàn tác đó.)
Rich

Câu trả lời:


8

Tôi đã có vấn đề chính xác này. Đây là những gì tôi đã thêm vào vimrc của mình để sửa nó cho tôi:

" 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

Lưu ý rằng bạn không đặt undofiletùy chọn; bạn cần xóa khỏi vimrc nếu bạn có nó.

Hoàn tác bây giờ hoạt động như "bình thường". Nhưng chúng ta làm bằng tay viết thư cho các file undo với wundolệnh trong BufWritePost.

Các ReadUndo()chức năng tự đọc file undo với rundovà đặt undofiletùy chọn. Bây giờ chúng ta có thể sử dụng uđể đi sâu hơn vào lịch sử. Tôi đã ánh xạ này đến <Leader>u.

Điều này có lẽ có thể tốt hơn. Nó ghi đè thông tin hoàn tác trước đó, vì vậy bạn không thể quay lại lần trước bạn đã chỉnh sửa tệp. Nhưng bản thân tôi không cần điều đó.


Lưu ý Tôi đã nhận thấy một vấn đề với điều này: bạn chỉ lưu nội dung của phiên hiện tại vào tệp hoàn tác. Điều này khác với hành vi mặc định, trong đó nội dung của phiên trước + tất cả thông tin đã có trong tệp hoàn tác được lưu ... Cách khắc phục điều này, sẽ là đọc tệp hoàn tác, hợp nhất các thay đổi hoàn tác, sau đó viết tập tin hoàn tác ... Nhưng điều này dường như không thể ...: - /
Martin Tournoij

@Carpetsmoker Tôi đồng ý. Sẽ thật tốt nếu làm điều đó nếu có thể. Khi nó xảy ra, điều này đã làm việc đủ tốt cho tôi trong một thời gian dài. YMMV.
superjer

5

Oooh, ooh, cuối cùng là một cơ hội để thể hiện lệnh này tiện lợi!

Vim có thể "quay ngược thời gian". Nó có một :earlierlệnh ...

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

... có thể hoàn nguyên tệp trở lại trạng thái trước đó. Điều này có thể được sử dụng theo một số cách.

  • Nếu bạn có thể ước tính sân bóng cho bạn mất bao lâu để thực hiện những thay đổi này, bạn có thể dành thời gian cho lệnh này. Hoặc, ví dụ, nếu bạn biết bạn đã thay đổi tệp (ngoại trừ những thay đổi bạn muốn hoàn tác) trong ngày qua, bạn có thể sử dụng

    :earlier 1d
    
  • Nếu bạn chỉ viết (lưu) tệp một lần kể từ khi thay đổi bạn muốn hoàn tác hoặc bạn chưa lưu tệp, bạn có thể sử dụng

    :earlier 1f
    

    Như được mô tả trong :helpvăn bản, điều này sẽ trở lại phiên bản đã viết trước đó nếu bạn vừa viết tệp hoặc trở lại lần cuối cùng nó được lưu nếu bạn không có bất kỳ thay đổi nào được lưu.

Điều này không trả lời chính xác câu hỏi của bạn, nhưng nó có vẻ như là một vấn đề XY.


1
Nghe có vẻ như một chút vấn đề XY : Tại sao? Tôi đang hoàn tác uvà tôi vô tình đi qua những thay đổi tôi đã thực hiện ngay bây giờ ... Tôi không chắc "vấn đề X" ban đầu có thể ở đây là gì?
Martin Tournoij 23/2/2015

1
Bây giờ tôi về :earlierbằng cách này, nhưng tôi vẫn cần phải đoán ; giống như tôi cần đoán khi sử dụng u... Trong trường hợp có lẽ tốt hơn một chút, nhưng tôi thích cái gì đó rõ ràng hơn (nếu có thể).
Martin Tournoij 23/2/2015

1
@Carpetsmoker "X" là "Tôi muốn hoàn tác chỉ những thay đổi mà tôi thực hiện gần đây nhất." "Y" là "Làm thế nào tôi có thể hoàn tác các thay đổi và bỏ qua phần không phù hợp?" Bạn vẫn phải đoán, nhưng bạn đã nói trong câu hỏi của mình rằng những thay đổi cuối cùng bạn thực hiện là vào tuần trước hoặc sớm hơn, vì vậy bạn có thể làm điều gì đó như thế :ea 5d. Bạn cũng có thể sử dụng :ea 1fphương pháp này. Trong mọi trường hợp, nó ít chi tiết hơn nhiều.
Doorknob 23/2/2015

"X" và "Y" có vẻ như là một vấn đề tương tự với tôi? Tôi đã đề cập đến "tuần" trong câu hỏi của mình, nhưng cũng có thể là vài giờ hoặc vài phút (đã sửa đổi) ... Đây không phải là một câu trả lời tồi như vậy , tôi đã (và) chỉ hy vọng có một cái gì đó tốt hơn ...
Martin Tournoij 23/2/2015

Tôi với @Carpetsmoker về điều này. Tôi đã biết về: sớm hơn một thời gian, nhưng tôi vẫn tắt nguồn vì lý do chính xác được mô tả trong câu hỏi.
Giàu

4

Cập nhật 2015-06-28 : Tôi đã sửa một lỗi nhỏ và phát hành dưới dạng plugin . Mã plugin tốt hơn một chút, trong đó nó cảnh báo lại sau khi di chuyển con trỏ; Tôi khuyên bạn nên sử dụng plugin.



Câu trả lời từ superjer hoạt động rất tốt, nhưng có tác dụng phụ đáng tiếc là bạn chỉ có thể hoàn tác các thay đổi từ phiên Vim cuối cùng chứ không phải tất cả các phiên Vim trước đó.

Điều này là do wundoghi đè tập tin hoàn tác; nó không được hợp nhất. Theo tôi biết, không có cách nào để khắc phục điều này.

Vì vậy, đây là giải pháp thay thế của tôi, nó sẽ hiển thị một thông báo cảnh báo lớn màu đỏ khi bạn hoàn tác các thay đổi từ tệp hoàn tác.

Điều này tương tự như câu trả lời của Ingo Karkat , nhưng nó không yêu cầu plugin bên ngoài và có một số khác biệt tinh tế (hiển thị cảnh báo thay vì tiếng bíp, không yêu cầu bạn nhấn uhai lần).

Lưu ý đây chỉ Sửa đổi u<C-r>với phím tắt, và không những U, :undo:redolệnh.

" 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

Tôi có những điều sau đây, nó dừng lại và phát ra tiếng bíp khi hoàn tác nội dung bộ đệm.

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>

Điều này tích hợp với plugin repeat.vim; nó đòi hỏi plugin thư viện ingo của tôi .


Bạn có đang sử dụng "kiên trì" để có nghĩa là "trạng thái của tệp khi bộ đệm được tải" không? Tôi thường mong đợi "tồn tại" có nghĩa là trạng thái của tệp hiện được lưu vào đĩa.
Giàu

@Rich: Không, chúng tôi có cùng định nghĩa. Ánh xạ của tôi không thực hiện chính xác những gì câu hỏi yêu cầu, nhưng tôi vẫn thấy nó rất hữu ích.
Ingo Karkat

2

Tôi nghĩ rằng tôi cũng sẽ chấp nhận một câu trả lời đã đưa ra một lệnh hoàn nguyên tệp về trạng thái kể từ lần cuối bạn mở nó. (Hoặc tốt hơn, nhảy trở lại trạng thái hoàn tác đó.)

> Giàu

Tôi thực sự thích ý tưởng được trích dẫn vì vậy tôi đã thực hiện :Revertlệnh. Tôi hy vọng bạn sẽ thấy nó liên quan đến câu hỏi của bạn.

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)

Các chức năng của trình trợ giúp Get_seqReal_seq, dựa trên mức không đồng ý , là cần thiết vì undotree()['seq_cur']đôi khi không đủ để xác định vị trí hiện tại trong cây hoàn tác. Bạn có thể đọc thêm về nó ở đây .


Đây có vẻ là một giải pháp tốt. Nhưng bạn có một lỗi trong của bạn au. các vimrc trong đó là thủ phạm. Đơn giản chỉ cần bỏ nó đi
Naumann
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.