ฉันจะได้รับการแจ้งเตือนเมื่อฉันเลิกทำการเปลี่ยนแปลงจากการคลายตัวหรือไม่?


15

ฉันใช้คุณสมบัติ undofileใน Vim มาระยะหนึ่งแล้ว มันเป็นคุณสมบัติที่ดีมาก

อย่างไรก็ตามสิ่งหนึ่งที่น่ารำคาญก็คือมันง่ายมากที่จะเลิกทำการเปลี่ยนแปลงโดยไม่ตั้งใจซึ่งเป็นครั้งสุดท้ายที่ฉันเปิดไฟล์ ซึ่งอาจเป็น 2 นาทีที่ผ่านมาชั่วโมงที่ผ่านมาสัปดาห์ที่ผ่านมาหรือเดือนที่ผ่านมา

ตัวอย่างเช่นสมมติว่าฉันเปิดไฟล์ทำการเปลี่ยนแปลงเล็กน้อยออกไปและเปลี่ยนแปลงไฟล์อื่น ๆ และพบว่าการเปลี่ยนแปลงของฉันไม่จำเป็นต้องใช้

ก่อนหน้านี้ฉันสามารถกดuปุ่มค้างไว้จนกระทั่ง Vim พูดว่า "การเปลี่ยนแปลงที่เก่าแก่ที่สุด" :wqและจะเสร็จสิ้น แต่ตอนนี้ฉันต้องระวังอย่างมากที่จะไม่ยกเลิกการเปลี่ยนแปลงที่ฉันทำครั้งล่าสุดที่ฉันเปิดไฟล์ ไม่มีวิธีที่ชัดเจนที่จะเห็นเมื่อคุณทำเช่นนี้

มีวิธีใดที่จะทำให้ชัดเจนยิ่งขึ้น? ตัวอย่างเช่นการแสดงที่ใดที่หนึ่งออกคำเตือนหรือแม้กระทั่งขอการยืนยัน


นี่เป็นคำถามที่ฉันสงสัยตั้งแต่มีการเปิดตัวคุณสมบัติ undofile และสิ่งหนึ่งที่ฉันตั้งใจจะถามตั้งแต่เปิดตัวเว็บไซต์นี้! หวังว่าจะมีคำตอบที่ดี ฉันคิดว่าฉันยังยอมรับคำตอบที่ให้คำสั่งที่ย้อนกลับไฟล์เป็นสถานะที่เป็นมาตั้งแต่ครั้งสุดท้ายที่คุณเปิดมัน (หรือดีกว่ากลับไปที่สถานะเลิกทำเช่นนั้น)
รวย

คำตอบ:


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 ของคุณหากคุณมี

เลิกทำตอนนี้ทำงานเป็น "ปกติ" แต่เราทำด้วยตนเองเขียนไปยังแฟ้มเลิกกับคำสั่งในwundoBufWritePost

ReadUndo()ฟังก์ชั่นด้วยตนเองอ่านแฟ้มยกเลิกด้วยrundoและตั้งค่าundofileตัวเลือก ตอนนี้เราสามารถใช้uเพื่อย้อนกลับไปสู่ประวัติศาสตร์ต่อไป <Leader>uฉันแมปนี้

นี่อาจจะดีกว่า มันเขียนทับข้อมูลยกเลิกก่อนหน้านี้ดังนั้นคุณจึงไม่สามารถย้อนกลับไปก่อนหน้านี้เมื่อคุณแก้ไขไฟล์ แต่ฉันไม่ต้องการตัวเอง


หมายเหตุ: ฉันพบปัญหาเกี่ยวกับการนี้คุณเพียงบันทึกเนื้อหาของเซสชันปัจจุบันไปยังแฟ้มยกเลิก สิ่งนี้แตกต่างจากพฤติกรรมเริ่มต้นที่เนื้อหาของเซสชั่นก่อนหน้า + ข้อมูลทั้งหมดที่อยู่ในไฟล์เลิกทำถูกบันทึก ... วิธีการแก้ไขปัญหานี้คือการอ่านไฟล์เลิกทำรวมการเปลี่ยนแปลงเลิกทำแล้วเขียน ไฟล์ยกเลิก ... แต่ดูเหมือนจะเป็นไปไม่ได้ ... : - /
Martin Tournoij

@Carpetsmoker ฉันเห็นด้วย มันจะเป็นการดีถ้าหากเป็นไปได้ เมื่อมันเกิดขึ้นสิ่งนี้ได้ผลดีพอสำหรับฉันเป็นเวลานาน YMMV
superjer

5

Oooh, ooh ในที่สุดโอกาสที่จะแสดงคำสั่งที่ดีนี้ !

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.                                

... ซึ่งสามารถคืนค่าไฟล์กลับสู่สถานะก่อนหน้า สามารถใช้งานได้หลายวิธี

  • หากคุณสามารถประเมิน ballpark ว่าคุณใช้เวลานานแค่ไหนในการเปลี่ยนแปลงเหล่านี้คุณสามารถป้อนเวลาในคำสั่งนี้ หรือตัวอย่างเช่นหากคุณรู้ว่าคุณไม่ได้เปลี่ยนไฟล์ (ยกเว้นการเปลี่ยนแปลงที่คุณต้องการยกเลิก) ในวันที่ผ่านมาคุณสามารถใช้

    :earlier 1d
    
  • หากคุณเขียน (บันทึก) ไฟล์เพียงครั้งเดียวนับตั้งแต่การเปลี่ยนแปลงที่คุณต้องการยกเลิกหรือคุณไม่ได้บันทึกไฟล์เลยคุณสามารถใช้

    :earlier 1f
    

    ตามที่อธิบายไว้ใน:helpข้อความสิ่งนี้จะเปลี่ยนกลับเป็นเวอร์ชันที่เขียนก่อนหน้านี้หากคุณเพิ่งเขียนไฟล์หรือย้อนกลับเป็นครั้งสุดท้ายที่ถูกบันทึกหากคุณไม่มีการเปลี่ยนแปลงที่บันทึกไว้

สิ่งนี้ไม่ตอบคำถามของคุณอย่างแม่นยำ แต่ดูเหมือนว่าจะเป็นปัญหา XY เล็กน้อย


1
ดูเหมือนว่าจะเป็นปัญหา XY เล็กน้อย : ทำไม ฉันเลิกทำuแล้วฉันก็ผ่านการเปลี่ยนแปลงที่ฉันทำไปโดยไม่ได้ตั้งใจ ... ฉันไม่แน่ใจว่า "X-problem" ต้นฉบับอาจเป็นอะไรที่นี่
Martin Tournoij

1
ตอนนี้ฉันกำลัง:earlierไป แต่ฉันยังต้องคาดเดา ; เช่นเดียวกับที่ฉันต้องเดาเมื่อใช้u... ในกรณีที่มันอาจจะดีขึ้นเล็กน้อย แต่ฉันต้องการบางสิ่งที่ชัดเจนมากขึ้น (ถ้าเป็นไปได้)
Martin Tournoij

1
@Carpetsmoker "X" คือ "ฉันต้องการที่จะเลิกทำเพียงการเปลี่ยนแปลงเหล่านี้ที่ผมทำส่วนใหญ่เมื่อเร็ว ๆ นี้." "Y" คือ "ฉันจะเลิกทำการเปลี่ยนแปลงและเพิกเฉยต่อการเลิกทำได้อย่างไร" การจัดเรียงของคุณยังคงต้องเดา :ea 5dแต่คุณกล่าวว่าในคำถามของคุณว่าการเปลี่ยนแปลงล่าสุดที่คุณทำอยู่สัปดาห์ที่ผ่านมาหรือก่อนหน้านี้ดังนั้นคุณก็สามารถทำสิ่งที่ชอบ คุณสามารถใช้:ea 1fวิธีนี้ได้เช่นกัน ไม่ว่าในกรณีใดมันมีขนาดเล็กกว่ามาก
Doorknob

"X" และ "Y" ดูเหมือนจะใช้ถ้อยคำใหม่ของปัญหาเดียวกันกับฉันใช่ไหม ฉันได้พูดถึง "สัปดาห์" ในคำถามของฉัน แต่อาจเป็นชั่วโมงหรือนาที (แก้ไขนั้น) ... นี่ไม่ใช่คำตอบที่ไม่ดีเช่นนี้โดยวิธีฉันเป็น (และฉัน) เพียงแค่หวังว่าจะมีสิ่งที่ดีกว่า ...
Martin Tournoij

ฉันอยู่กับ @Carpetsmoker ในอันนี้ ฉันเคยรู้จักมาแล้ว: ก่อนหน้านี้สักพักหนึ่ง แต่ฉันยังคงปิดการปลดล็อคด้วยเหตุผลที่อธิบายไว้ในคำถาม
Rich

4

ปรับปรุง 2015/06/28 : ผมแก้ไขข้อผิดพลาดเล็ก ๆ และปล่อยออกมานี้เป็นปลั๊กอิน รหัสปลั๊กอินจะดีกว่าเล็กน้อยซึ่งจะเตือนอีกครั้งหลังจากเลื่อนเคอร์เซอร์ ฉันขอแนะนำให้คุณใช้ปลั๊กอิน



คำตอบจากsuperjer ใช้งานได้ดี แต่มีผลข้างเคียงที่โชคร้ายที่คุณสามารถยกเลิกการเปลี่ยนแปลงจากเซสชัน Vim ล่าสุดเท่านั้นและไม่ใช่เซสชัน Vim ก่อนหน้าทั้งหมด

นี่เป็นเพราะwundoเขียนทับไฟล์เลิกทำ มันไม่ได้ผสาน เท่าที่ฉันรู้ไม่มีวิธีแก้ไขปัญหานี้

ดังนั้นนี่คือโซลูชันทางเลือกของฉันมันจะแสดงข้อความเตือนสีแดงขนาดใหญ่เมื่อคุณเลิกทำการเปลี่ยนแปลงจากไฟล์เลิกทำ

สิ่งนี้คล้ายกับคำตอบของ Ingo Karkatแต่ไม่ต้องการปลั๊กอินภายนอกและมีความแตกต่างเล็กน้อย (แสดงคำเตือนแทนการส่งเสียงบี๊บคุณไม่ต้องกด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

@ ริช: ไม่เรามีคำจำกัดความเดียวกัน การแมปของฉันไม่ได้ทำตามที่คำถามถาม แต่ฉันก็ยังพบว่ามันมีประโยชน์มาก
Ingo Karkat

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ในนั้นเป็นผู้ร้าย เพียงแค่ปล่อยมันออกมา
Naumann
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.