Userfunc

Nvim 的 :help 頁面,從 原始碼 使用 tree-sitter-vimdoc 解析器 產生


定義與使用函式。
這在使用者手冊的 41.7 節中介紹。
1. 定義函式
可以定義新的函式。這些函式可以像內建函式一樣被呼叫。函式會執行一系列 Ex 命令。普通模式命令可以使用 :normal 命令執行。
為了避免與內建函式混淆,函式名稱必須以大寫字母開頭。為了防止在不同的腳本中使用相同的名稱,請將它們設定為腳本本地的。如果您確實使用了全域函式,請避免使用明顯的簡短名稱。一個好習慣是讓函式名稱以腳本的名稱開頭,例如 "HTMLcolor()"。
也可以使用大括號,請參閱 curly-braces-names
autoload 功能很有用,可以用於僅在呼叫函式時才定義函式。
local-function
腳本的本地函式必須以 "s:" 開頭。本地腳本函式只能從腳本內部以及在腳本中定義的函式、使用者命令和自動命令中呼叫。也可以從腳本中定義的映射中呼叫該函式,但當映射在腳本外部展開時,必須使用 <SID> 而不是 "s:"。只有腳本本地函式,沒有緩衝區本地或視窗本地函式。
:fu :function E128 E129 E123 :fu[nction] 列出所有函式及其參數。
:fu[nction][!] {name} 列出函式 {name},並使用行號註解,除非給定了 "!"。{name} 可以是一個 字典 函式引用 條目。
:function dict.init
請注意,{name} 不是一個表達式,您不能使用作為函式參考的變數。您可以使用這個骯髒的技巧來列出以變數 "Funcref" 參考的函式。
let g:MyFuncref = Funcref
func g:MyFuncref
unlet g:MyFuncref
:fu[nction] /{pattern} 列出名稱與 {pattern} 匹配的函式。範例:列出所有以 "File" 結尾的函式。
:function /File$
:function-verbose
'verbose' 非零時,列出函式也會顯示其上次定義的位置。範例:
:verbose function SetFileTypeSH
    function SetFileTypeSH(name)
        Last set from /usr/share/vim/vim-7.0/filetype.vim
請參閱 :verbose-cmd 以取得更多資訊。
E124 E125 E853 E884 :fu[nction][!] {name}([arguments]) [range] [abort] [dict] [closure] 定義一個名為 {name} 的新函式。函式的主體會在後續的行中出現,直到匹配的 :endfunction 為止。
名稱必須由字母數字字元和 '_' 組成,並且必須以大寫字母或 "s:" 開頭(請參閱上方)。請注意,不允許使用 "b:" 或 "g:"。(自 patch 7.4.260 起,如果函式名稱中包含冒號,則會給出 E884,例如 "foo:bar()"。在該 patch 之前,不會給出任何錯誤)。
{name} 可以是 字典 函式引用 條目。
:function dict.init(arg)
"dict" 必須是現有的字典。如果 "init" 條目還不存在,則會添加它。否則,需要 [!] 來覆寫現有的函式。結果是 函式引用 到編號的函式。該函式只能與 函式引用 一起使用,如果沒有對它的更多引用,則會被刪除。E127 E122 當這個名稱的函式已經存在且未使用 [!] 時,會給出錯誤訊息。有一種例外情況:當再次來源腳本時,先前在該腳本中定義的函式將會被靜默取代。當使用 [!] 時,現有的函式會被靜默取代。除非它正在執行中,否則就會發生錯誤。注意:請謹慎使用 !。如果不小心使用,可能會導致現有的函式被意外取代,這很難除錯。
有關 {arguments} 的資訊,請參閱 function-argument
:func-range a:firstline a:lastline 當添加 [range] 參數時,函式本身應負責處理範圍。範圍會以 "a:firstline" 和 "a:lastline" 傳遞。如果排除 [range],":{range}call" 將會為範圍中的每一行呼叫該函式,並且游標會位於每一行的開頭。請參閱 function-range-example。游標仍然會移動到範圍的第一行,這與所有 Ex 命令的情況相同。:func-abort
當添加 [abort] 參數時,函式會在偵測到錯誤時立即中止。:func-dict
當添加 [dict] 參數時,函式必須透過 字典 中的條目來呼叫。本地變數 "self" 會被設定為該字典。請參閱 Dictionary-function:func-closure E932 當添加 [closure] 參數時,函式可以存取外部範圍的變數和參數。這通常稱為閉包。在此範例中,Bar() 使用了 Foo() 範圍內的 "x"。即使在 Foo() 返回後,它仍然被引用。
:function! Foo()
:  let x = 0
:  function! Bar() closure
:    let x += 1
:    return x
:  endfunction
:  return funcref('Bar')
:endfunction
:let F = Foo()
:echo F()
1
:echo F()
2
:echo F()
3
function-search-undo
最後使用的搜尋模式和重做命令 "." 不會被函式變更。這也表示當函式返回時,:nohlsearch 的效果會被還原。
:endf :endfunction E126 E193 W22 :endf[unction] [argument] 函式定義的結尾。最好將它放在單獨的一行上,不要帶有 [argument]。
[argument] 可以是: | command 下一個要執行的命令 \n command 下一個要執行的命令 " comment 總是忽略任何其他被忽略的內容,當 'verbose' 非零時會給出警告。對以下命令的支援是在 Vim 8.0.0654 中添加的,在此之前,任何參數都會被靜默忽略。
為了能夠在 :execute 命令中定義函式,請使用換行符而不是 :bar
:exe "func Foo()\necho 'foo'\nendfunc"
:delf :delfunction E131 E933 :delf[unction][!] {name} 刪除函式 {name}{name} 也可以是 字典 條目,它是 函式引用
:delfunc dict.init
這會從 "dict" 中移除 "init" 條目。如果沒有對該函式的更多引用,則會刪除該函式。使用 !,如果該函式不存在,則不會發生錯誤。:retu :return E133 :retu[rn] [expr] 從函式返回。當給定 "[expr]" 時,會評估它並將其作為函式的結果傳回。如果未給定 "[expr]",則會傳回數字 0。當函式在沒有明確的 ":return" 的情況下結束時,會傳回數字 0。請注意,不會檢查無法訪問的行,因此如果命令在 ":return" 之後,則不會發出警告。此外,也不會檢查下一行是否包含有效的命令。忘記行繼續反斜線可能會被忽略。
return 'some text'
       .. ' some more text'
將會很樂意地傳回 "some text" 而不會發生錯誤。它應該是
return 'some text'
       \ .. ' some more text'
如果在 :try 之後但在匹配的 :finally(如果存在)之前使用 ":return",則會先執行 ":finally" 之後直到匹配的 :endtry 的命令。此過程適用於函式內所有巢狀的 ":try"。函式會在最外層的 ":endtry" 處返回。
function-argument a:var 可以透過給定其名稱來定義參數。在函式中,然後可以使用 "a:name" 來使用它("a:" 表示參數)。a:0 a:1 a:000 E740 ... 最多可以給定 20 個參數,以逗號分隔。在具名參數之後,可以指定參數 "...",這表示後面可能會選擇性地跟隨更多參數。在函式中,額外的參數可以使用 "a:1"、"a:2" 等。"a:0" 會設定為額外參數的數量(可以是 0)。"a:000" 會設定為包含這些參數的 列表。請注意,"a:1" 與 "a:000[0]" 相同。E742
a: 範圍及其中的變數無法變更,它們是固定的。但是,如果使用複合類型,例如 列表字典,您可以變更其內容。因此,您可以將 列表 傳遞給函式,並讓函式向其中新增項目。如果您想確保函式無法變更 列表字典,請使用 :lockvar
也可以定義不帶任何參數的函式。您仍然必須提供 ()。
允許在函式主體內定義另一個函式。
optional-function-argument
您可以為位置具名參數提供預設值。這使得它們對於函式呼叫而言是選擇性的。當在呼叫時未指定位置參數時,預設表達式會被用來初始化它。這僅適用於使用 :function 宣告的函式,不適用於 lambda 表達式 expr-lambda
範例:
function Something(key, value = 10)
   echo a:key .. ": " .. a:value
endfunction
call Something('empty')        "empty: 10"
call Something('key', 20)        "key: 20"
參數預設表達式會在函式呼叫時進行評估,而不是在定義函式時進行評估。因此,可以使用在定義函式時無效的表達式。這些表達式也僅會在呼叫期間未指定參數時才會被評估。
E989
帶有預設表達式的選擇性參數必須在任何強制參數之後出現。您可以在所有選擇性的具名參數之後使用 "..."。
後面的參數預設值可以參考先前的參數,但反之則不行。它們必須像所有參數一樣,以 "a:" 作為前綴。
可行的範例:
:function Okay(mandatory, optional = a:mandatory)
:endfunction
不起作用的範例:
:function NoGood(first = a:second, second = 10)
:endfunction
當不使用 "..." 時,函式呼叫中的參數數量必須至少等於強制具名參數的數量。當使用 "..." 時,參數的數量可能大於強制和選擇性參數的總和。
local-variables
在函式內部可以使用本地變數。這些變數會在函式返回時消失。全域變數需要使用 "g:" 來存取。在函式內部,本地變數會在不帶任何前綴的情況下存取。但是,如果您願意,也可以加上 "l:" 作為前綴。某些保留名稱(例如 "version")需要這樣做。
範例:
:function Table(title, ...)
:  echohl Title
:  echo a:title
:  echohl None
:  echo a:0 .. " items:"
:  for s in a:000
:    echon ' ' .. s
:  endfor
:endfunction
然後可以使用以下方式呼叫此函式:
call Table("Table", "line1", "line2")
call Table("Empty Table")
若要傳回多個值,請傳回 列表
:function Compute(n1, n2)
:  if a:n2 == 0
:    return ["fail", 0]
:  endif
:  return ["ok", a:n1 / a:n2]
:endfunction
然後可以使用以下方式呼叫此函式:
:let [success, div] = Compute(102, 6)
:if success == "ok"
:  echo div
:endif
2. 呼叫函式
:cal :call E107 E117 :[範圍]cal[l] {name}([引數]) 呼叫函式。函式的名稱和其引數由 :function 指定。最多可以使用 20 個引數。傳回值會被捨棄。如果沒有範圍且函式接受範圍,則函式會被呼叫一次。當給定範圍時,游標會在執行函式之前定位在第一行的開頭。當給定範圍且函式本身不處理它時,函式會針對範圍中的每一行執行,游標位於該行的第一欄。游標會留在最後一行 (可能被最後一次函式呼叫移動)。引數會針對每一行重新評估。因此,這會起作用:function-range-example
:function Mynumber(arg)
:  echo line(".") .. " " .. a:arg
:endfunction
:1,5call Mynumber(getline("."))
無論如何都會定義 "a:firstline" 和 "a:lastline",它們可以用來在範圍的開始或結束時執行不同的操作。
一個函式本身處理範圍的範例
:function Cont() range
:  execute (a:firstline + 1) .. "," .. a:lastline .. 's/^/\t\\ '
:endfunction
:4,8call Cont()
此函式會在範圍內的所有行前面插入接續字元 "\",但第一行除外。
當函式傳回複合值時,可以進一步取消參照,但此時不會使用範圍。範例
:4,8call GetDict().method()
在這裡,GetDict() 取得範圍,但 method() 沒有。
E132
使用者函式的遞迴性受限於 'maxfuncdepth' 選項。
也可以使用 :eval。它不支援範圍,但允許方法鏈接,例如:
eval GetList()->Filter()->append('$')
也可以在評估表達式時或當它被用作方法時呼叫函式
let x = GetList()
let y = GetList()->Filter()
3. 在函式中清理
:defer
:defer {func}({args}) 當目前的函式完成時,呼叫 {func}{args} 在此處評估。
通常,函式中的命令會產生全域影響,必須在函式結束時還原。在各種情況下處理此問題可能會很麻煩。尤其是在遇到意外錯誤時。這可以使用 try / finally 區塊來完成,但當有多個時,這會變得複雜。
一個更簡單的解決方案是使用 defer。它會在函式傳回時排程函式呼叫,無論是否有錯誤。範例
func Filter(text) abort
  call writefile(a:text, 'Tempfile')
  call system('filter < Tempfile > Outfile')
  call Handle('Outfile')
  call delete('Tempfile')
  call delete('Outfile')
endfunc
如果某些原因導致函式中止,這裡的 'Tempfile' 和 'Outfile' 將不會被刪除。可以使用 :defer 來避免這種情況
func Filter(text) abort
  call writefile(a:text, 'Tempfile')
  defer delete('Tempfile')
  defer delete('Outfile')
  call system('filter < Tempfile > Outfile')
  call Handle('Outfile')
endfunc
請注意,刪除 "Outfile" 的排程是在呼叫 system() 之前,因為即使 system() 失敗,它也可以被建立。
延遲執行的函式會以相反的順序呼叫,最後一個加入的會先執行。一個沒用的範例
func Useless() abort
  for s in range(3)
    defer execute('echomsg "number ' .. s .. '"')
  endfor
endfunc
現在 :messages 顯示:number 2 number 1 number 0
延遲執行函式的任何傳回值都會被捨棄。函式後面不能接任何東西,例如 "->func" 或 ".member"。目前:defer GetArg()->TheFunc()` 無效,它可能會在以後的版本中起作用。
會報告錯誤,但不會導致中止延遲執行函式的執行或改變延遲執行函式外的執行。
不接受範圍。函式可以是帶有額外引數的部分函式,但不能是字典。 E1300
4. 自動載入函式
autoload-functions
當使用許多或大型函式時,可以在它們被使用時才自動定義它們。有兩種方法:使用自動命令和使用 'runtimepath' 中的 "autoload" 目錄。
使用自動命令
這在使用者手冊第 41.14 節中介紹。
如果你有一個很長的 Vim 腳本檔案的插件,自動命令很有用。你可以定義自動命令並使用 :finish 快速退出腳本。這會使 Vim 啟動更快。然後自動命令應該再次載入同一個檔案,設定一個變數以跳過 :finish 命令。
使用 FuncUndefined 自動命令事件,其模式符合要定義的函式。範例
:au FuncUndefined BufNet* source ~/vim/bufnetfuncs.vim
檔案 "~/vim/bufnetfuncs.vim" 應該定義以 "BufNet" 開頭的函式。另請參閱 FuncUndefined
使用自動載入腳本
autoload E746 這在使用者手冊第 41.15 節中介紹。
在 "autoload" 目錄中使用腳本比較簡單,但需要使用完全正確的檔案名稱。可以自動載入的函式具有這樣的名稱
:call filename#funcname()
當呼叫這樣的函式,且尚未定義時,Vim 會在 'runtimepath' 中的 "autoload" 目錄中搜尋名為 "filename.vim" 的腳本檔案。例如 "~/.config/nvim/autoload/filename.vim"。該檔案應該像這樣定義函式
function filename#funcname()
   echo "Done!"
endfunction
如果檔案不存在,Vim 也會在 'packpath'(在 "start" 下)中搜尋,以允許在套件尚未新增到 'runtimepath' 時,從你的 vimrc 呼叫套件的函式(請參閱 套件)。
檔案名稱和函式中 # 之前的名稱必須完全匹配,且定義的函式必須具有完全如同呼叫時的名稱。
可以使用子目錄。函式名稱中的每個 # 都像是路徑分隔符。因此,當呼叫函式時
:call foo#bar#func()
Vim 會在 'runtimepath' 中搜尋檔案 "autoload/foo/bar.vim"。
這在讀取尚未設定的變數時也適用
:let l = foo#bar#lvar
但是,當自動載入腳本已經載入時,它不會再次為未知的變數載入。
當將值指派給這樣的變數時,不會發生任何特殊的事情。這可以用於將設定傳遞給自動載入腳本,然後再載入
:let foo#bar#toggle = 1
:call foo#bar#func()
請注意,當你犯錯並呼叫應該在自動載入腳本中定義的函式,但該腳本實際上沒有定義該函式時,你會收到遺失函式的錯誤訊息。如果你修復了自動載入腳本,它不會自動再次載入。重新啟動 Vim 或手動載入腳本。
另請注意,如果你有兩個腳本檔案,一個呼叫另一個的函式,反之亦然,在使用的函式被定義之前,它不會起作用。避免在頂層使用自動載入功能。
提示:如果你要散佈一堆腳本,請閱讀 distribute-script
主要
指令索引
快速參考