Đối với autocmd trong ftplugin, tôi nên sử dụng khớp mẫu hoặc <đệm>?


14

Tôi có một autocmd cho các tệp TeX và Markdown để lưu tệp tự động. Không có gì bất thường:

autocmd CursorHold *.tex,*.md w

Tuy nhiên, khi cài đặt tùy chỉnh cho các tệp này tăng lên, tôi chia chúng ra ftplugin/tex.vimftplugin/markdown.vim:

" ftplugin/tex.vim
autocmd CursorHold *.tex w
" ftplugin/markdown.vim
autocmd CursorHold *.md w

Bây giờ, các tệp này chỉ có nguồn gốc cho các tệp thích hợp, do đó, việc khớp mẫu là không cần thiết. Rõ ràng, autocmds có thể là bộ đệm cục bộ. Từ :h autocmd-buffer-local:

Buffer-local autocommands are attached to a specific buffer.  They are useful
if the buffer does not have a name and when the name does not match a specific
pattern.  But it also means they must be explicitly added to each buffer.

Instead of a pattern buffer-local autocommands use one of these forms:
        <buffer>        current buffer
        <buffer=99>     buffer number 99
        <buffer=abuf>   using <abuf> (only when executing autocommands)
                        <abuf>

Điều đó dường như có nghĩa là cho việc sử dụng như vậy. Bây giờ, cả hai ftplugin/tex.vimftplugin/markdown.vimcó thể có:

autocmd CursorHold <buffer> w

Tôi không thực sự lo ngại về việc gia hạn thực tế miễn là filetype là đúng, vì vậy đây tiết kiệm cho tôi khỏi phải lo lắng về *.md*.markdownvà bất cứ điều gì khác phần mở rộng có giá trị cho Markdown.

Đây có phải là cách sử dụng <buffer>đúng? Có bất kỳ cạm bẫy nào tôi nên nhận thức? Mọi thứ sẽ trở nên lộn xộn nếu tôi xóa một bộ đệm và mở một bộ đệm khác (hy vọng các con số sẽ không va chạm với nhau, nhưng là)?


1
Tôi khá chắc chắn nếu bạn xóa bộ đệm, bất kỳ autocmds cục bộ nào cũng bị xóa.
Tumbler41

@ Tumbler41 thực sự. Nó nói rằng trong sự giúp đỡ, một vài đoạn xuống.
muru

1
Không chính xác những gì bạn yêu cầu, nhưng up(viết tắt :update) sẽ tốt hơn wtrong autocmd của bạn (tránh viết không cần thiết).
mMontu

@mMontu Đẹp. Nó thậm chí còn giải quyết một vấn đề tôi gặp phải khi autocmd kích hoạt cho một tệp mà tôi đang kiểm tra từ lịch sử git. Bộ đệm đã chỉ đọc và wthất bại. Nhiều lần. :upkhông làm gì trong trường hợp đó. :)
muru

Vui mừng bạn thích :) Ngẫu nhiên, bạn cũng có thể thấy tùy chọn 'autowrite' hữu ích (tùy thuộc vào động lực của bạn, bạn có thể bỏ autocmds).
mMontu

Câu trả lời:


11

Cách sử dụng <đệm> này có đúng không?

Tôi nghĩ đó là chính xác, nhưng bạn chỉ cần bọc nó trong một nhóm, và xóa phần sau, để đảm bảo rằng autocmd sẽ không bị trùng lặp mỗi khi bạn thực hiện lệnh tải lại cùng bộ đệm.

Như bạn đã giải thích, mẫu đặc biệt <buffer>cho phép bạn dựa vào cơ chế phát hiện filetype tích hợp, được triển khai bên trong tệp $VIMRUNTIME/filetype.vim.

Trong tệp này, bạn có thể tìm thấy các autocmds tích hợp của Vim chịu trách nhiệm thiết lập kiểu tệp chính xác cho bất kỳ bộ đệm nào. Ví dụ: để đánh dấu:

" Markdown
au BufNewFile,BufRead *.markdown,*.mdown,*.mkd,*.mkdn,*.mdwn,*.md  setf markdown

Trong plugin filetype của bạn, bạn có thể sao chép các mẫu tương tự cho mỗi autocmd bạn cài đặt. Ví dụ: để tự động lưu bộ đệm khi con trỏ của bạn không di chuyển trong vài giây:

au CursorHold *.markdown,*.mdown,*.mkd,*.mkdn,*.mdwn,*.md  update

Nhưng <buffer>là cách ít dài dòng hơn:

au CursorHold <buffer> update

Ngoài ra, nếu một ngày nào đó một tiện ích mở rộng khác hợp lệ và $VIMRUNTIME/filetype.vimđược cập nhật để bao gồm nó, thì autocmds của bạn sẽ không được thông báo. Và bạn sẽ phải cập nhật tất cả các mẫu của chúng bên trong các plugin filetype của bạn.


Mọi thứ sẽ trở nên lộn xộn nếu tôi xóa một bộ đệm và mở một bộ đệm khác (hy vọng các con số sẽ không va chạm với nhau, nhưng là)?

Tôi không chắc nhưng tôi không nghĩ rằng Vim có thể sử dụng lại số bộ đệm của bộ đệm bị xóa. Tôi không thể tìm thấy phần có liên quan từ trợ giúp, nhưng tôi đã tìm thấy đoạn này từ vim.wikia.com :

Không. Vim sẽ không sử dụng lại số bộ đệm của bộ đệm đã xóa cho bộ đệm mới. Vim sẽ luôn gán số thứ tự tiếp theo cho bộ đệm mới.

Ngoài ra, như @ Tumbler41 đã giải thích, khi bạn xóa bộ đệm, autocmds của nó sẽ bị xóa. Từ :h autocmd-buflocal:

Dĩ nhiên, khi bộ đệm bị xóa, bộ đệm tự động cục bộ cũng không còn nữa.

Nếu bạn muốn tự kiểm tra, bạn có thể làm như vậy bằng cách tăng mức độ chi tiết của Vim lên 6. Bạn có thể làm điều đó tạm thời, chỉ với một lệnh, bằng cách sử dụng công cụ :verbosesửa đổi. Vì vậy, bên trong bộ đệm markdown của bạn, bạn có thể thực thi:

:6verbose bwipe

Sau đó, nếu bạn kiểm tra tin nhắn của Vim:

:messages

Bạn sẽ thấy một dòng trông như thế này:

auto-removing autocommand: CursorHold <buffer=42>

Trong trường hợp 42là số lượng đệm markdown của bạn.


Có bất kỳ cạm bẫy nào tôi nên nhận thức?

Có 3 tình huống mà tôi coi là cạm bẫy và liên quan đến mô hình đặc biệt <buffer>. Trong hai trong số đó, <buffer>có thể là một vấn đề, mặt khác đó là một giải pháp.

Cạm bẫy 1

Trước tiên, bạn nên cẩn thận với cách bạn xóa các nhóm của bộ đệm tự động cục bộ. Bạn phải làm quen với đoạn trích này:

augroup your_group_name
    autocmd!
    autocmd Event pattern command
augroup END

Vì vậy, bạn có thể bị cám dỗ sử dụng nó cho bộ đệm - bộ đệm tự động cục bộ, không được sửa đổi, như thế này:

augroup my_markdown
    autocmd!
    autocmd CursorHold <buffer> update
augroup END

Nhưng điều này sẽ có tác dụng không mong muốn. Lần đầu tiên bạn tải bộ đệm đánh dấu, hãy gọi nó A, autocmd của nó sẽ được cài đặt chính xác. Sau đó, khi bạn sẽ tải lạiA , autocmd sẽ bị xóa (vì autocmd!) và được cài đặt lại. Vì vậy, các nhóm sẽ ngăn chặn chính xác sự trùng lặp của autocmd.

Bây giờ, giả sử bạn tải bộ đệm đánh dấu thứ 2, hãy gọi nó B, trong cửa sổ thứ 2. TẤT CẢ các autocmds của auggroup sẽ bị xóa: đó là autocmd của Avà một trong số đó B. Sau đó, một autocmd SINGLE sẽ được cài đặt choB .

Vì vậy, khi bạn thực hiện một số thay đổi Bvà đợi vài giây CursorHoldđể được kích hoạt, nó sẽ tự động được lưu. Nhưng nếu bạn quay trở lạiA và làm điều tương tự, bộ đệm sẽ không được lưu. Điều này là do lần cuối cùng bạn tải bộ đệm đánh dấu, có sự mất cân bằng giữa những gì bạn đã xóa và những gì bạn đã thêm. Bạn đã loại bỏ nhiều hơn những gì bạn đã thêm.

Giải pháp là không xóa TẤT CẢ các autocmds, mà chỉ xóa các bộ đệm hiện tại, bằng cách chuyển mẫu đặc biệt <buffer>tới :autocmd!:

augroup my_markdown
    autocmd! CursorHold <buffer>
    autocmd CursorHold <buffer> update
augroup END

Lưu ý rằng bạn có thể thay thế CursorHoldbằng một ngôi sao để khớp với bất kỳ sự kiện nào trong dòng loại bỏ autocmds:

augroup my_markdown
    autocmd! * <buffer>
    autocmd CursorHold <buffer> update
augroup END

Bằng cách này, bạn không phải chỉ định tất cả các sự kiện mà autocmds của bạn đang lắng nghe khi bạn muốn xóa nhóm.


Cạm bẫy 2

Có một cạm bẫy khác, nhưng lần này <buffer> này không phải là vấn đề, đó là giải pháp.

Khi bạn bao gồm một tùy chọn cục bộ trong một plugin filetype, bạn có thể làm như thế này:

setlocal option1=value
setlocal option2

Điều này sẽ hoạt động như mong đợi cho các tùy chọn bộ đệm cục bộ, nhưng không phải lúc nào cũng cho các tùy chọn cục bộ cửa sổ. Để minh họa vấn đề, bạn có thể thử thí nghiệm sau. Tạo tập tin ~/.vim/after/ftdetect/potion.vimvà bên trong nó viết:

autocmd BufNewFile,BufRead *.pn setfiletype potion

Tệp này sẽ tự động đặt loại tệp potioncho bất kỳ tệp nào có phần mở rộng .pn. Bạn không cần phải bọc nó trong một nhóm, bởi vì, đối với loại tệp cụ thể này, Vim sẽ tự động thực hiện (xem :h ftdetect).

Nếu các thư mục trung gian không tồn tại trên hệ thống của bạn, bạn có thể tạo chúng.

Tiếp theo, tạo plugin filetype ~/.vim/after/ftplugin/potion.vimvà bên trong nó viết:

setlocal list

Theo mặc định, trong một potiontệp, cài đặt này sẽ khiến các ký tự tab được hiển thị dưới dạng ^Ivà cuối dòng là$ .

Bây giờ, tạo tối thiểu vimrc; bên trong /tmp/vimrcviết:

filetype plugin on

... Để kích hoạt các plugin filetype.

Ngoài ra, tạo một tệp thuốc /tmp/pn.pnvà một tệp ngẫu nhiên /tmp/file. Trong tệp thuốc, viết một cái gì đó:

foo
bar
baz

Trong tệp ngẫu nhiên, viết đường dẫn đến tệp thuốc /tmp/pn.pn:

/tmp/pn.pn

Bây giờ, hãy bắt đầu Vim với tối thiểu các lần khởi tạo, chỉ cần tìm nguồn vimrcvà mở cả hai tệp trong chế độ xem dọc:

$ vim -Nu /tmp/vimrc -O /tmp/pn.pn /tmp/file

Bạn sẽ thấy 2 khung nhìn dọc. Tệp thuốc ở bên trái hiển thị cuối dòng có ký hiệu đô la, tệp ngẫu nhiên ở bên phải hoàn toàn không hiển thị chúng.

Đặt tiêu điểm cho tệp ngẫu nhiên và nhấn gfđể hiển thị tệp thuốc có đường dẫn nằm dưới con trỏ. Bây giờ bạn nhìn thấy bộ đệm thuốc tương tự trong chế độ xem bên phải, nhưng lần này, cuối dòng không được hiển thị với ký hiệu đô la. Và nếu bạn gõ :setlocal list?, Vim nên trả lời nolist:

nhập mô tả hình ảnh ở đây

Toàn bộ chuỗi sự kiện:

BufRead event → set 'filetype' option → load filetype plugins

... đã không xảy ra, bởi vì lần đầu tiên BufRead, đã không xảy ra khi bạn nhấn gf. Bộ đệm đã được tải.

Điều này có vẻ bất ngờ, bởi vì khi bạn thêm vào setlocal listbên trong plugin filetype của bạn, bạn có thể đã nghĩ rằng nó sẽ cho phép 'list'tùy chọn trong bất kỳ cửa sổ nào hiển thị bộ đệm thuốc.

Vấn đề không cụ thể đối với potionloại tệp mới này . Bạn có thể trải nghiệm nó với một markdowntập tin quá.

Nó cũng không cụ thể cho 'list'tùy chọn. Bạn có thể trải nghiệm nó với các thiết lập cửa sổ địa phương khác, như 'conceallevel', 'foldmethod', 'foldexpr','foldtitle' , ...

Nó cũng không cụ thể cho gflệnh. Bạn có thể trải nghiệm nó với các lệnh khác có thể thay đổi bộ đệm hiển thị trong cửa sổ hiện hành: dấu ấn toàn cầu, C-o(di chuyển lạc hậu trong jumplist cửa sổ địa phương), :b {buffer_number}...

Để tóm tắt, các tùy chọn cục bộ cửa sổ sẽ được đặt chính xác, nếu và chỉ khi:

  • tập tin chưa được đọc trong phiên Vim hiện tại (vì BufRead sẽ phải bị loại bỏ)
  • tập tin đang được hiển thị trong một cửa sổ nơi các tùy chọn cục bộ cửa sổ đã được đặt chính xác
  • cửa sổ mới được tạo bằng một lệnh như :split(trong trường hợp này, nó sẽ kế thừa các tùy chọn cục bộ cửa sổ từ cửa sổ nơi lệnh được thực thi)

Nếu không, các tùy chọn cục bộ cửa sổ có thể không được đặt chính xác.

Một giải pháp khả thi sẽ là đặt chúng không trực tiếp từ một plugin filetype, mà từ một autocmd được cài đặt ở phần sau, nó sẽ lắng nghe BufWinEnter. Sự kiện này sẽ được kích hoạt mỗi khi bộ đệm được hiển thị trong cửa sổ.

Vì vậy, ví dụ, thay vì viết này:

setlocal list

Bạn sẽ viết điều này:

augroup my_potion
    au! * <buffer>
    au BufWinEnter <buffer> setlocal list
augroup END

Và ở đây, bạn tìm thấy một mẫu đặc biệt <buffer>.

nhập mô tả hình ảnh ở đây


Cạm bẫy 3

Nếu bạn thay đổi kiểu tệp của bộ đệm, autocmds sẽ vẫn còn. Nếu bạn muốn loại bỏ chúng, bạn cần cấu hình b:undo_ftplugin(xem :h undo_ftplugin) và bao gồm lệnh này trong đó:

exe 'au! my_markdown * <buffer>'

Tuy nhiên, đừng cố gắng loại bỏ chính nhóm, bởi vì vẫn có thể có một số bộ đệm đánh dấu có autocmds bên trong nó.

FWIW, đây là đoạn mã UltiSnips mà tôi đang sử dụng để đặt b:undo_ftplugin:

snippet undo "undo ftplugin settings" bm
" teardown {{{1

let b:undo_ftplugin =         get(b:, 'undo_ftplugin', '')
\                     .(empty(get(b:, 'undo_ftplugin', '')) ? '' : '|')
\                     ."${1:
\                          setl ${2:option}<}${3:
\                        | exe '${4:n}unmap <buffer> ${5:lhs}'}${6:
\                        | exe 'au! ${7:group_name} * <buffer>'}${8:
\                        | unlet! b:${9:variable}}${10:
\                        | delcommand ${11:Cmd}}
\                      "
$0
endsnippet

Và đây là một ví dụ về giá trị mà tôi có ~/.vim/after/ftplugin/awk.vim:

let b:undo_ftplugin =         get(b:, 'undo_ftplugin', '')
                    \ .(empty(get(b:, 'undo_ftplugin', '')) ? '' : '|')
                    \ ."
                    \   setl cms< cocu< cole< fdm< fdt< tw<
                    \|  exe 'nunmap <buffer> K'
                    \|  exe 'au! my_awk * <buffer>'
                    \|  exe 'au! my_awk_format * <buffer>'
                    \  "

Là một lưu ý phụ, tôi hiểu lý do tại sao bạn đặt câu hỏi, bởi vì khi tôi tìm tất cả các dòng có mẫu đặc biệt <buffer>được sử dụng trong các tệp mặc định của Vim:

:vim /au\%[tocmd!].\{-}<buffer>/ $VIMRUNTIME/**/*

Tôi chỉ tìm thấy 9 trận đấu (bạn có thể tìm thấy nhiều hơn hoặc ít hơn, tôi đang sử dụng Vim phiên bản 8.0, với các bản vá lên đến 134). Và trong số 9 trận đấu, 7 trận trong tài liệu, chỉ có 2 trận thực sự có nguồn gốc. Bạn nên tìm thấy chúng trong $ VIMRUNTIME / cú pháp / dircolors.vim :

autocmd CursorMoved,CursorMovedI <buffer> call s:preview_color('.')
autocmd CursorHold,CursorHoldI   <buffer> call s:reset_colors()

Tôi không biết nếu nó có thể gây ra một vấn đề, nhưng họ không phải là bên trong một augroup, có nghĩa là mỗi khi bạn tải lại một bộ đệm mà filetype là dircolors(nó sẽ xảy ra nếu bạn chỉnh sửa một file có tên .dircolors, .dir_colorshoặc những người có đầu con đường với/etc/DIR_COLORS ), plugin cú pháp sẽ thêm một autocmd bộ đệm cục bộ mới.

Bạn có thể kiểm tra nó như thế này:

$ vim ~/.dir_colors
:au * <buffer>

Lệnh cuối cùng sẽ hiển thị điều này:

CursorHold
    <buffer=1>
              call s:reset_colors()
CursorHoldI
    <buffer=1>
              call s:reset_colors()
CursorMoved
    <buffer=1>
              call s:preview_color('.')
CursorMovedI
    <buffer=1>
              call s:preview_color('.')

Bây giờ, tải lại bộ đệm và hỏi lại các autocmds cục bộ cho bộ đệm hiện tại là gì:

:e
:au * <buffer>

Lần này, bạn sẽ thấy:

CursorHold
    <buffer=1>
              call s:reset_colors()
              call s:reset_colors()
CursorHoldI
    <buffer=1>
              call s:reset_colors()
              call s:reset_colors()
CursorMoved
    <buffer=1>
              call s:preview_color('.')
              call s:preview_color('.')
CursorMovedI
    <buffer=1>
              call s:preview_color('.')
              call s:preview_color('.')

Sau mỗi tải lại của tập tin, s:reset_colors()s:preview_color('.')sẽ được gọi là một lần bổ sung, mỗi một thời gian của sự kiện CursorHold, CursorHoldI, CursorMoved, CursorMovedIbị sa thải.

Đây có lẽ không phải là một vấn đề lớn, bởi vì ngay cả sau khi tải lại dircolors vài lần, tôi vẫn không thấy sự chậm chạp đáng chú ý hoặc hành vi bất ngờ từ Vim.

Nếu đó là vấn đề với bạn, bạn có thể liên hệ với người bảo trì plugin cú pháp, nhưng trong lúc này, nếu bạn muốn ngăn chặn việc sao chép autocmds, bạn có thể tạo plugin cú pháp của riêng mình cho dircolorscác tệp, sử dụng tệp ~/.vim/syntax/dircolors.vim. Trong đó, bạn sẽ nhập nội dung của plugin cú pháp gốc:

$ vim ~/.vim/syntax/dircolors.vim
:r $VIMRUNTIME/syntax/dircolors.vim

Sau đó, trong phần sau, bạn sẽ chỉ bọc các autocmds bên trong một nhóm mà bạn sẽ xóa. Vì vậy, bạn sẽ thay thế các dòng này:

autocmd CursorMoved,CursorMovedI <buffer> call s:preview_color('.')
autocmd CursorHold,CursorHoldI   <buffer> call s:reset_colors()

... với những cái này:

augroup my_dircolors_syntax
    autocmd! * <buffer>
    autocmd CursorMoved,CursorMovedI <buffer> call s:preview_color('.')
    autocmd CursorHold,CursorHoldI   <buffer> call s:reset_colors()
augroup END

Lưu ý rằng nếu bạn đã tạo dircolorsplugin cú pháp của mình với tệp ~/.vim/after/syntax/dircolors.vim, nó sẽ không hoạt động, bởi vì plugin cú pháp mặc định sẽ có nguồn gốc trước đó. Bằng cách sử dụng ~/.vim/syntax/dircolors.vim, plugin cú pháp của bạn sẽ có nguồn gốc trước cái mặc định và nó sẽ đặt biến bộ đệm cục bộ b:current_syntax, điều này sẽ ngăn plugin cú pháp mặc định không có nguồn gốc vì nó có chứa bảo vệ này:

if exists("b:current_syntax")
    finish
endif

Quy tắc chung dường như là: sử dụng các thư mục ~/.vim/ftplugin~/.vim/syntaxthư mục để tạo một plugin kiểu tệp / cú pháp tùy chỉnh và ngăn chặn plugin tiếp theo (cho cùng một kiểu tệp) trong đường dẫn thời gian chạy có nguồn gốc (bao gồm cả các mặc định). Và sử dụng ~/.vim/after/ftplugin, ~/.vim/after/syntaxkhông phải để ngăn các plugin khác có nguồn gốc, mà chỉ để có từ cuối cùng về giá trị của một số cài đặt.


1
Tôi ước tôi có thể nâng cao điều này hơn.
Giàu

3
@Rich tôi nâng cấp cái này khó hơn cho bạn. Khiếu nại duy nhất của tôi là thiếu một bản tóm tắt "tl; dr". Các trang tiểu tiết văn bản, tuy nhiên rất quan trọng để hiểu, làm tổn thương tâm hồn già nua của tôi để lội qua. Việc thay thế autocmd!bằng autocmd! CursorHold <buffer>trong augroupcác khối là một gotcha đặc biệt quan trọng - và nên được tô sáng phía trước. Tuy nhiên ... đây là một sự đầu tư đáng kinh ngạc về thời gian, công sức và nước mắt đẫm máu.
Cecil Curry
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.