Vimscript: Trợ giúp với Tự động tải, Phạm vi & <SID>


9

Tôi đã làm việc về việc mô đun hóa và chuyển đổi một mã trong vimrcmột số plugin / plugin độc lập và có thể tái sử dụng. Tôi đã gặp phải một vấn đề với tự động tải & phạm vi mà tôi gặp khó khăn trong việc hiểu. Tôi đã đọc qua :h autoload, :h <sid>, :h script-local, nhưng tôi vẫn không hoàn toàn rõ ràng về cách thức hoạt động này.

Tôi đã xem xét một số plugin được phát triển tốt để tìm ra một số mẫu thường được sử dụng và đã cấu trúc các plugin của mình như sau:

" ~/.vim/autoload/myplugin.vim

if exists('g:loaded_myplugin')
  finish
endif

let g:loaded_myplugin = 1
let g:myplugin_version = 0.0.1

" Save cpoptions.
let s:cpo_save = &cpo
set cpo&vim

function! myplugin#init() " {{{
  " Default 'init' function. This will run the others with default values,
  " but the intent is that they can be called individually if not all are
  " desired.
  call myplugin#init_thing_one()
  call myplugin#init_thing_two()
endfunction" }}}

function! myplugin#init_thing_one() " {{{
  " init thing one
  call s:set_default('g:myplugin_thing_one_flag', 1)
  " do some things ...
endfunction " }}}

function! myplugin#init_thing_two() " {{{
  " init thing two
  call s:set_default('g:myplugin_thing_two_flag', 1)
  " do some things ...
endfunction " }}}

function! s:set_default(name, default) " {{{
" Helper function for setting default values.
  if !exists(a:name)
    let {a:name} = a:default
  endif
endfunction " }}}

" Restore cpotions.
let &cpo = s:cpo_save
unlet s:cpo_save

Khi bắt đầu vimrc của tôi, tôi chạy plugin với:

if has('vim_starting')
  if &compatible | set nocompatible | endif
  let g:myplugin_thing_one_flag = 0
  let g:myplugin_thing_two_flag = 2
  call myplugin#init()
endif

Tất cả điều này dường như hoạt động chính xác và như mong đợi - nhưng mỗi khi một chức năng được gọi, s:set_default(...)chức năng được gọi cho mỗi cờ, không hiệu quả - vì vậy tôi đã cố gắng di chuyển chúng ra khỏi các chức năng:

" ~/.vim/autoload/myplugin.vim
" ...
set cpo&vim

" Set all defaults once, the first time this plugin is referenced:
call s:set_default('g:myplugin_thing_one_flag', 1)
call s:set_default('g:myplugin_thing_two_flag', 1)

function! myplugin#init() " {{{
" ...

Nhưng điều này gây ra lỗi mà tôi không chắc nên giải quyết như thế nào:

Error detected while processing /Users/nfarrar/.vim/myplugin.vim
line   40:
E117: Unknown function: <SNR>3_set_default

Tôi vẫn không hiểu rõ về phạm vi của vim, nhưng từ những gì tôi đã đọc - có vẻ như vim thực hiện một hình thức xáo trộn tên với các tập lệnh để cung cấp 'phạm vi'. Nó gán (không chắc chính xác quá trình này hoạt động như thế nào) một SID duy nhất cho mỗi tệp được tải trong thời gian chạy - và khi bạn gọi một hàm có tiền tố định danh phạm vi tập lệnh ( s:), nó sẽ thay thế một cách rõ ràng định danh đó bằng SID được ánh xạ .

Trong một số trường hợp, tôi đã thấy các tập lệnh gọi các hàm như thế này (nhưng nó không hoạt động trong trường hợp của tôi, tôi không hiểu tại sao và hy vọng ai đó có thể giải thích điều này):

call <SID>set_default('g:myplugin_thing_one_flag', 1)
call <SNR>set_default('g:myplugin_thing_one_flag', 1)

Những điều sau đây không hoạt động, nhưng tôi không chắc đó có phải là một mô hình hay không:

" ~/.vim/autoload/myplugin.vim
" ...
set cpo&vim

" Set all defaults once, the first time this plugin is referenced:
call myplugin#set_default('g:myplugin_thing_one_flag', 1)
call myplugin#set_default('g:myplugin_thing_two_flag', 1)

function! myplugin#init() " {{{
" ...

function! myplugin#set_default(name, default) " {{{
    " ...
endfunction " }}}

Trong tập lệnh cục bộ, nó ghi:

When executing an autocommand or a user command, it will run in the context of
the script it was defined in.  This makes it possible that the command calls a
local function or uses a local mapping.

Otherwise, using "<SID>" outside of a script context is an error.

If you need to get the script number to use in a complicated script, you can
use this function:

    function s:SID()
      return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze_SID$')
    endfun

Có vẻ như đây có thể là cách tiếp cận tôi cần thực hiện, nhưng tôi không hoàn toàn chắc chắn tại sao, hoặc chính xác làm thế nào để sử dụng nó. Bất cứ ai có thể cung cấp một số cái nhìn sâu sắc?

Câu trả lời:


4

Trong trường hợp đầu tiên, lỗi bạn gặp phải là bạn đang cố gắng gọi một hàm trước khi nó tồn tại. Đó là, Vim đang tiến triển thông qua kịch bản của bạn. Khi nhìn thấy calldòng, nó chưa xử lý functiondòng tạo ra những gì bạn muốn gọi, dẫn đến lỗi unknown function.

Nếu bạn di chuyển cuộc gọi của mình để thiết lập mặc định đến cuối tập lệnh của mình (trước khi khôi phục cponhưng sau tất cả các lệnh của bạn function, sẽ không có lỗi, vì Vim sẽ xử lý tập lệnh để tạo các hàm trước, vì vậy một khi nó được đến các calldòng, các hàm tồn tại. vd

"....

function! s:set_default(name, default) " {{{
  " Helper function for setting default values.
  if !exists(a:name)
    let {a:name} = a:default
  endif
endfunction " }}}

" Set all defaults once, the first time this plugin is referenced:
call s:set_default('g:myplugin_thing_one_flag', 1)
call s:set_default('g:myplugin_thing_two_flag', 1)

" Restore cpotions.
let &cpo = s:cpo_save
unlet s:cpo_save

Tôi không biết tại sao cú pháp thay thế gọi hàm của bạn set_defaultlà hàm tự động tải từ trong tập lệnh tự động tải hoạt động khi chức năng chưa được xác định. Tôi đoán đây là tác dụng phụ của việc triển khai (trong đó tập lệnh đã đọc không được đọc lại hoặc bạn sẽ có đệ quy vô hạn). Tôi sẽ không tin vào nó luôn hoạt động theo cách đó.


Nếu tôi hiểu chính xác, bạn đang nói rằng toàn bộ tệp không có nguồn gốc & được thực thi trước khi thực hiện lệnh gọi hàm được xác định trong vimrc của tôi? Có thể tôi đang hiểu nhầm ... nhưng đối với tôi, toàn bộ tập lệnh tự động tải được lấy nguồn và thực thi trước. Nếu tôi thêm một câu lệnh echom 'this is the function call'trong hàm được gọi từ vimrc và một câu lệnh khác ở echom 'file was sourced'bất kỳ nơi nào khác trong tệp (không phải trong hàm), tôi sẽ thấy cái sau trước, sau đó là cái trước.
nfarrar

Xin lỗi, tôi vừa nhận ra điều bạn đang nói - và bạn đã đúng. Vì lệnh gọi hàm đang diễn ra trong tập lệnh vì nó có nguồn gốc, nên nó cần xảy ra sau khi hàm được định nghĩa. Cảm ơn bạn!
nfarrar

12

Tôi đề nghị cấu trúc này:

.
└── myplugin
    ├── LICENSE.txt
    ├── README.md
    ├── autoload
    │   └── myplugin.vim
    ├── doc
    │   └── myplugin.txt
    └── plugin
        └── myplugin.vim

Điều này tương thích với tất cả các trình quản lý plugin hiện đại và giữ mọi thứ sạch sẽ. Trong số này:

  • myplugin/doc/myplugin.txt nên là tập tin trợ giúp
  • myplugin/plugin/myplugin.vim nên bao gồm:
    • kiểm tra mọi điều kiện tiên quyết về nhu cầu plugin của bạn, chẳng hạn như phiên bản Vim tối thiểu và các tính năng thời gian biên dịch Vim
    • ánh xạ chính
    • khởi tạo một lần, chẳng hạn như cài đặt mặc định
  • myplugin/autoload/myplugin.vim nên chứa mã chính của plugin của bạn.

Phạm vi thực sự khá đơn giản:

  • các hàm có tên bắt đầu bằng s:có thể xuất hiện ở bất cứ đâu, nhưng là cục bộ của tệp mà chúng được xác định; bạn chỉ có thể gọi chúng từ tệp nơi chúng được xác định;
  • chức năng trong myplugin/autoload/myplugin.vimphải có tên myplugin#function()và là toàn cầu; bạn có thể gọi chúng từ khắp mọi nơi (nhưng hãy cẩn thận khi gọi chúng khiến tệp myplugin/autoload/myplugin.vimđược tải);
  • tất cả các chức năng khác phải có tên bắt đầu bằng chữ in hoa, chẳng hạn như Function(), và cũng là toàn cục; bạn có thể gọi họ từ khắp mọi nơi.

<SID><Plug>được sử dụng cho ánh xạ, và chúng là một chủ đề mà bạn có lẽ nên tránh cho đến khi bạn hiểu đầy đủ về cách chúng hoạt động và vấn đề chúng phải giải quyết.

<SNR> là thứ mà bạn không bao giờ nên sử dụng trực tiếp.


Tại sao lại tách biệt giữa autoload/plugin/thư mục? Tôi đã luôn đặt mọi thứ vào plugin/và điều đó dường như hoạt động tốt?
Martin Tournoij

1
Thư mục tự động tải chỉ tải nội dung của nó khi cần thiết. Điều này có thể tăng tốc thời gian bắt đầu của vim phần nào. Nói cách khác, nó hoạt động tương tự như plugin/ngoại trừ nó chỉ được tải một khi cần thiết thay vì được tải khi khởi động.
EvergreenTree

2
@Carpetsmoker Khá nhiều như @EvergreenTree đã nói. Tất nhiên, nó không thực sự quan trọng đối với các plugin "nhỏ", nhưng nó vẫn thực hành tốt. Với Vim, bạn có rất ít quyền kiểm soát đối với trình thu gom rác và chỉ tải mọi thứ khi và nếu cần có thể tạo ra sự khác biệt. Mặt khác, có những nhược điểm tinh tế khi di chuyển mọi thứ sang autoload, vì bạn không thể kiểm tra sự tồn tại của một chức năng nếu tệp mà nó sống không được tải.
lcd047
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.