Làm cách nào để lưu và khôi phục kết quả của lệnh 'set'?


7

Từ :h :setchúng tôi biết rằng

:se[t] Hiển thị tất cả các tùy chọn khác với giá trị mặc định của chúng.

Trong một vimscript tôi muốn lưu tất cả các tùy chọn đã được người dùng sửa đổi, sửa đổi chúng và sau đó khôi phục chúng về giá trị do người dùng đặt.

Làm thế nào tôi có thể làm điều đó một cách dễ dàng?

Tôi có cách đơn giản là lưu trữ kết quả của :set(hoặc một lệnh tương tự) vào một biến mà tôi có thể nguồn hoặc chuyển sang lệnh khác sau đó để khôi phục những gì nó chứa không?

Hoặc tôi phải phân tích thủ công đầu ra của lệnh (điều mà tôi có thể làm nhưng rất nhiều công việc không có gì nếu một sự thay thế đơn giản hơn tồn tại)?


Chúng tôi đã biết rằng các tùy chọn bắt đầu bằng & biểu tượng. Chúng ta có thể đưa ra một lệnh thiết lập dài dòng và sau đó ghi lại đầu ra trong một biến hoặc đăng ký và sau đó tách từng trong số chúng và lưu trữ các giá trị của chúng trong một từ điển và sau đó khôi phục từ đó.
SibiCoder

@SibiCoder: Có, tôi có thể phân tích kết quả theo cách thủ công nhưng điều đó không thực sự tiện lợi, câu hỏi của tôi là tìm cách tự động hơn để làm điều đó.
statox

1
Theo như tôi biết thì không có cách tiêu chuẩn nào để làm điều đó, và làm nó mà không có sự hỗ trợ từ Vim sẽ là vấn đề tốt nhất. Tuy nhiên, có một :optionlệnh và một OptionSetsự kiện tự động, vì vậy có thể có đủ sự quan tâm cho tính năng "lưu / khôi phục trạng thái". Hỏi về vim_dev.
Sato Katsura

2
@SatoKatsura: Chết tiệt, tôi không biết :optionlệnh này khá hay! Bạn nói đúng có lẽ tôi sẽ nhấn vim_dev. @ Christian: Vâng, tôi hiểu rằng đây không phải là một quy trình công việc, thực ra tôi chỉ đang cố gắng thực hiện một ý tưởng của tôi nhưng có lẽ đó không phải là một ý tưởng hay :-)
statox

1
@ChristianBrabandt Có thể tìm thấy các tùy chọn (toàn cầu và cục bộ) khác với mặc định sẽ hữu ích. Hiện tại (1) không có cách nào để làm cho Vim tràn ra một danh sách đầy đủ các tùy chọn (nghĩa là có tính đến các mô hình biên dịch tài khoản, v.v.) và (2) thậm chí còn không ghi lại các tùy chọn nào có phiên bản cục bộ (Fi không selectioncó cục bộ phiên bản? Người ta phải đọc các nguồn để tìm hiểu.)
Sato Katsura

Câu trả lời:


5

1. :mkexrc

Cách dễ nhất là sử dụng :mkexrclệnh. Với lệnh này, chúng ta có thể lưu tất cả các tùy chọn đã thay đổi vào một tệp. Khi bạn cần khôi phục lại tất cả các tùy chọn đã lưu từ tệp, chỉ cần :sourcenó.

mkexrc snapshot
source snapshot

2. :set:redir

Các :mkexrclệnh thiết sử dụng một tập tin để giữ tất cả các tùy chọn. (Và nó cũng tiết kiệm ánh xạ chính hiện hành.) Khi chúng ta không muốn sử dụng bất kỳ tập tin, chúng ta có thể chuyển hướng đầu ra của :setlệnh vào biến vim với :redir.

redir => snapshot
silent set
redir END

Sau đó, chúng tôi có thể khôi phục lại tất cả các tùy chọn từ ảnh chụp nhanh như sau.

for opt in split(snapshot,'\n')[1:]
    exe "silent set " . opt
endfor

Cách tiếp cận trên là nhanh chóng và dễ sử dụng. Nhưng cách tiếp cận có hai vấn đề. Đầu tiên, chúng ta phải thoát một số ký tự bao gồm khoảng trắng từ ảnh chụp nhanh ( :help option-backslash) . Nếu không, chúng ta có thể gặp lỗi khi các ký tự này không được thoát. Ví dụ,

set breakat=@ !+=

làm tăng lỗi 'E518' nhưng,

set breakat=@\ \!+=

hoạt động tốt

Và vấn đề thứ hai là tất cả các phím đặc biệt được thay đổi thành các ký tự bình thường khi chúng ta chuyển hướng đầu ra thành ảnh chụp nhanh. Ví dụ: ^I(tab) được thay đổi theo nghĩa đen thành ^I. Cách giải quyết có thể phức tạp một chút.

let g:optionDict = {}

function SaveOpts()
    redir => snapshot
    " We make a snapshot of options which differ from their default value.
    " If we want to make a snapshot of all options, do set all.
    silent set
    redir END

    for opt in split(snapshot, '\W\+')
        if strlen(opt) > 3
            if exists('&' . opt)
                exe 'let g:optionDict.'. opt . '=&' . opt
            elseif opt[0:1] == 'no' && exists('&' . opt[2:])
                exe 'let g:optionDict.'. opt[2:]. '=&' . opt[2:]
            endif
        endif
    endfor
endfunc

function RestoreOpts()
    for [opt, val] in items(g:optionDict)
        try
            exe 'silent set ' . opt . '=' . escape(val, " \t|\\\"")
        catch /:E474/ " Invalid argument, do set {option} or no{option}
            if val == '1'
                exe 'silent set ' . opt
            elseif val == '0'
                exe 'silent set no' . opt
            endif
        endtry
    endfor
endfunc

3. :setlocal

Chúng tôi cũng có thể lưu một tùy chọn khôi phục bằng tay với :setlocal.

:setlocalchỉ ảnh hưởng đến bộ đệm hoặc cửa sổ hiện tại. Nếu chúng tôi thay đổi tùy chọn với :setlocal, :setlocal {option}<hoặc :set {option}<có thể khôi phục lại các tùy chọn đã thay đổi. Cái trước đặt giá trị cục bộ của {tùy chọn} từ giá trị toàn cục của nó và cái sau loại bỏ tất cả các tùy chọn cục bộ để tất cả các giá trị tùy chọn trở về giá trị toàn cầu của chúng. Ví dụ,

setlocal ts=16
set ts<

4

Bạn có thể truy cập giá trị của cài đặt bằng let option_val = &optiontập lệnh. Những gì tôi đã thấy là sử dụng một lệnh để lưu trữ các tùy chọn mà bạn muốn thay đổi. Bạn lặp lại qua lệnh để lưu giá trị cũ và đặt giá trị mới.

Thí dụ

Đây là một cách tiếp cận trực tiếp hơn. Tôi sẽ sử dụng điều này nếu các cài đặt được biết trước.

function! s:do_something() abort
  " Options needed by the plugin
  let plugin_options = {
        \ 'list': 0,
        \ 'winfixwidth': 1,
        \ 'autoindent': 0,
        \ 'filetype': 'ini',
        \ }

  echo 'desired:' plugin_options

  for option in keys(plugin_options)
    execute 'let old_val = &'.option
    execute 'let &'.option.' = plugin_options[option]'
    let plugin_options[option] = old_val
  endfor

  echo 'changed:' plugin_options

  for option in keys(plugin_options)
    execute 'let &'.option.' = plugin_options[option]'
  endfor
endfunction


call s:do_something()

Các plugin_optionsbiến được scoped đến chức năng, vì vậy nó có thể được tái sử dụng để lưu trữ các giá trị tùy chọn cũ.

Ví dụ nâng cao

Tập lệnh bên dưới sẽ cho phép bạn thay đổi cài đặt ở bất kỳ đâu trong tập lệnh của mình trong khi duy trì cài đặt gốc cho đến khi bạn gọi s:restore_settings()

function! s:set(option, value) abort
  if !exists('b:_saved_settings')
    let b:_saved_settings = {}
  endif

  " Only save the original values
  if !has_key(b:_saved_settings, a:option)
    execute 'let b:_saved_settings[a:option] = &'.a:option
  endif

  execute 'let &'.a:option.' = a:value'
endfunction

function! s:restore_settings(...) abort
  if !exists('b:_saved_settings')
    return
  endif

  if a:0
    for option in a:000
      if has_key(b:_saved_settings, option)
        execute 'let &'.option.' = b:_saved_settings[option]'
        call remove(b:_saved_settings, option)
      endif
    endfor
  else
    for option in keys(b:_saved_settings)
      execute 'let &'.option.' = b:_saved_settings[option]'
    endfor

    unlet! b:_saved_settings
  endif
endfunction


" Options needed by the plugin
call s:set('list', 0)
call s:set('winfixwidth', 1)
call s:set('autoindent', 0)
call s:set('filetype', 'ini')

echo 'original:' b:_saved_settings
call s:restore_settings('list', 'filetype')
echo 'partial restore:' b:_saved_settings

call s:restore_settings()

s:set()có phần giống với set. Nó sẽ lưu giá trị của cài đặt ban đầu nếu nó chưa được lưu, sau đó sử dụng giá trị cài đặt của bạn.

Gọi s:restore_settings()mà không có đối số sẽ thực hiện khôi phục đầy đủ các thay đổi của bạn. Với các đối số, nó sẽ khôi phục một phần chúng. Nếu bạn quyết định sử dụng điều này, sẽ đáng để gọi s:restore_settings()trước khi gọi ban đầu s:set()trong trường hợp tập lệnh của bạn trước đó gặp lỗi trước khi khôi phục.


1
Điều đó không tự động như tôi mong đợi nhưng đó có vẻ là một cách giải quyết tốt. Cảm ơn!
statox

1
@statox Vâng, nhưng nó có chủ ý mà tôi nghĩ có thể tốt hơn nếu bạn không làm bất cứ điều gì công phu. Tôi sẽ cập nhật câu trả lời với một kịch bản trừu tượng hơn một chút và không yêu cầu bạn phải cài đặt chính.
Tommy A

@TommA, Chúng tôi có thể muốn thoát một số ký tự như khoảng trắng, đường ống, v.v. khỏi các giá trị tùy chọn. Những người có thể có vấn đề.
MS.Kim

@ MS.Kim Điều đó chỉ cần thiết nếu nó đang được thực thi 'set statusline='.a:value. Vì nó sử dụng phép gán biến, nên nó không thành vấn đề. Bạn có thể kiểm tra nó với s:set('statusline', 'pipe | test')hoặcs:set('errorformat', '%A testing %m%Z')
Tommy A

@TommyA Bạn nói đúng. Tôi nghĩ rằng let &{option}=a.valuecó thể gây ra vấn đề như 'set {option}'=a.valuecó thể. Nhưng đề cập của bạn vẫn có giá trị cho những người (bao gồm cả tôi), những người có khả năng chỉ sử dụng 'set {option}='a.value.
MS.Kim

1

Trong lh-vim-lib , tôi cung cấp một lh#on#exit()hàm cho phép khôi phục những thứ khác nhau về giá trị trước đó của chúng.

Về các lựa chọn, về bản chất, nó chỉ đăng ký (để sau) a exe 'let &opt='.&opt. Tôi sử dụng nó theo cách này:

let cleanup = lh#on#exit()
      \.restore('&efm')
try
   ... change &efm here ...
   do stuff that may fail
finally
   call cleanup.finalize()
endtry

Chúng tôi có thể đăng ký nhiều phục hồi như chúng tôi muốn. Ví dụ, chúng ta có thể nhắm mục tiêu một tùy chọn bộ đệm cục bộ : l:&efm. Tuy nhiên tôi không biết cách nào để nói: "quay trở lại giá trị tùy chọn toàn cầu".

Lưu ý rằng finallymệnh đề này rất quan trọng. Nếu bất kỳ thao tác nào có thể thất bại, nếu không có nó, tùy chọn sẽ không được khôi phục về cài đặt của người dùng cuối.

Tôi chưa cung cấp cách khôi phục bất kỳ tùy chọn nào có thể - xem câu trả lời của @ MS.Kim để xem làm thế nào chúng ta có thể có được danh sách của họ. Hoặc chúng ta có thể nghe tùy chọn thay đổi sự kiện hoặc chơi với c_CTRL-A_HOMEthủ thuật cũ (1). Nhưng, thành thật mà nói, tôi thực sự không nhìn thấy điểm. Khi, cục bộ (trong một hàm), tôi thay đổi giá trị tùy chọn, tôi biết chính xác tùy chọn nào tôi sẽ điều chỉnh.

Lưu ý rằng tôi cũng có thể:

  • khôi phục các tùy chọn plugin (biến) có thể là toàn cục hoặc bộ đệm cục bộ -> .restore_option(varname)
  • đăng ký hành động -> .register(':q')
  • khôi phục ánh xạ cục bộ bộ đệm (hoặc xác định chúng) -> .restore_buffer_mapping('<c-y>', 'i')

(1) Tôi đã tính đến lh-vim-lib (như thường lệ) -> echo lh#command#matching_askvim('option', '')

" Function: lh#command#matching_askvim(what, lead) {{{3
function! lh#command#matching_askvim(what, lead) abort
  let cleanup = lh#on#exit()
        \.register('delcom LHAskVimMatchingCompletion')
  try
    exe 'command! -complete='.a:what.' -nargs=* LHAskVimMatchingCompletion :echo "<args>"'
    silent! exe "norm! :LHAskVimMatchingCompletion ".a:lead."\<c-a>\"\<home>let\ cmds=\"\<cr>"
    return split(cmds, ' ')[1:]
  finally
    call cleanup.finalize()
  endtry
endfunction

Trông giống như một plugin rất thú vị. Tôi nghĩ rằng bạn đã đưa ra một quan điểm khá tốt ở đây: I haven't provided a way to restore any possible option [...]. I don't see the point. When, locally (in a function), I change option values, I exactly know which options I'll tweak.công bằng mà nói tôi không thể nhớ tại sao tôi cần điều đó nhưng tôi đồng ý rằng tốt hơn là chỉ chuyển đổi các tùy chọn bạn cần thay đổi.
statox

Ah. Từ nhận xét của bạn, tôi đã thêm một cách để liệt kê tất cả các tùy chọn hiện có nhờ hoàn thành dòng lệnh - lừa bẩn bên trong.
Luc Hermitte

(PS: Nếu tôi từng tham dự một tuppervim khác, chỉ cần hỏi)
Luc Hermitte

Ok đó là cách bạn nhận ra một nhà hoạt động kỳ cựu kỳ cựu ;-) Tôi không biết c_CTRL-A_HOMEthủ thuật cũ này và tôi sẽ cần một chút thời gian để tìm kiếm mã của bạn nhưng cảm ơn bạn rất nhiều vì đã cung cấp giải pháp! (PS tôi rất vui được gặp bạn trong một tuppervim tiếp theo!)
statox

1
Đó là cách chúng tôi đã tiến hành trước đây get(g:, - điều này khiến tôi mất một thời gian để khám phá. Nó vẫn hữu ích cho các công cụ không được hiển thị chính thức như các tùy chọn, biến môi trường, v.v.
Luc Hermitte
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.