Snippet Engine to Boost Your Performance
What are Snippets
スニペットとはなんでしょうか。これは、ある程度決まった構文を入力するときに非常に便利です。 予めそれを登録しておくことで、簡単にコードを書くことができます。
例えば、for 文などはよくスニペットとして登録されます。
for i in range(10):
print(i)
# Create a snippet
for ${1:i} in range(${2:n}):
$0LuaSnip
現在、このスニペットを挿入して、カーソル移動をしてくれるプラグインで一番良く使われているのは LuaSnip だと思います。
このスニペットエンジンでは、上のような構文の登録だけじゃなく、スニペットの途中で自分が書いた内容で動的に生成することができます。
下の動画では、関数を定義するスニペットを使っていますが、変数を入力するとその変数名が関数ドキュメントの方に動的に反映されているのがわかると思います。
Setup
local luasnip = require("luasnip")
require("luasnip.loaders.from_snipmate").lazy_load()
require("luasnip.loaders.from_vscode").lazy_load()
-- manually load snippets from `molleweide/LuaSnip-snippets.nvim`
require("luasnip.loaders.from_lua").lazy_load({
paths = vim.api.nvim_get_runtime_file("lua/luasnip_snippets/snippets", false),
})
require("luasnip.loaders.from_lua").lazy_load({
paths = vim.fn.stdpath("config") .. "/lua/snippets",
})
-- LuaSnip startup config
local util = require("luasnip.util.util")
luasnip.config.setup({
region_check_events = "InsertEnter,CursorMoved", -- "CursorMoved", "CursorHold", "InsertEnter"
delete_check_events = "TextChanged,CursorMoved",
-- extend ft snippets to load
load_ft_func = require("luasnip.extras.filetype_functions").extend_load_ft({
c = { "cpp" },
markdown = { "lua", "json", "html" },
html = { "css", "javascript" },
typescript = { "javascript" },
all = { "_" },
}),
ext_opts = {
[require("luasnip.util.types").choiceNode] = {
active = {
virt_text = { { "o", "GruvboxOrange" } },
},
},
},
-- allow nested snippet placeholders
parser_nested_assembler = function(_, snippet)
local select = function(snip, no_move)
snip.parent:enter_node(snip.indx)
for _, node in ipairs(snip.nodes) do
node:set_mark_rgrav(true, true)
end
if not no_move then
vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes("<Esc>", true, false, true), "n", true)
local pos_begin, pos_end = snip.mark:pos_begin_end()
util.normal_move_on(pos_begin)
vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes("v", true, false, true), "n", true)
util.normal_move_before(pos_end)
vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes("o<C-G>", true, false, true), "n", true)
end
end
function snippet:jump_into(dir, no_move)
if self.active then
if dir == 1 then
self:input_leave()
return self.next:jump_into(dir, no_move)
else
select(self, no_move)
return self
end
else
self:input_enter()
if dir == 1 then
select(self, no_move)
return self
else
return self.inner_last:jump_into(dir, no_move)
end
end
end
function snippet:jump_from(dir, no_move)
if dir == 1 then
return self.inner_first:jump_into(dir, no_move)
else
self:input_leave()
return self.prev:jump_into(dir, no_move)
end
end
return snippet
end,
})Add Your Snippets
では実際に自分でスニペットを定義してみましょう。 LuaSnip ではたくさんの方法でスニペットを登録できますが、今回は簡単なやり方を紹介します。 (もっと複雑ことができる方法を知りたい方はこれらの動画を見てください。機能紹介 (最初は茶番 w), がち解説 : 英語ですが、スニペットの凄さがわかるので一見の価値あり。)
スニペットの登録は ~/.config/nvim/snippets/<language name>.snippets というファイルに書き込んで登録します。
例えば今回 python 用のスニペットを作成してみましょう。
snippet fori For loop in range
for ${1:i} in range(${2:n}):
$0スニペットの定義は、snippet と書いて始まります。次の単語 fori がスニペット名でコーディングにこの文字列を打ち込むとスニペットが発火します。スニペット名の後ろには説明文が続き、改行以外任意の文字が使用できます。省略してもいいです。
次の行からスニペット本体を一段インデントして記述します。ここで、${1} は 1 番にカーソルが来る位置です。${1:default value} と:の後ろに文字を入れることで、デフォルト値を設定することもできます。${0} は最後にカーソルが来る位置です。
ちなみにこの記述の仕方を snipmate 方式といいます。他にも例えば…
snippet tdev Snippet to add torch device
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
snippet tryef Try Except Finally
try:
${0:pass}
except ${2} as ${3:e}:
${4:pass}
finally:
${5:pass}最後に、LuaSnip の設定ファイルに以下の行を追加します。(上の Setup にはすでに入ってる)
require("luasnip.loaders.from_snipmate").lazy_load()Load Your VSCode Snippets
ところで、VSCode でスニペットを使っていた人は、json で記述する方式になれている人もいるかもしれません。
上で記述したスニペットの書き方は私がおすすめする方法なだけで、あの json での書き方も理解することができます。 その場合はその json たちがある フォルダ名 を以下のように登録してください。
require("luasnip.loaders.from_vscode").lazy_load({
paths = {
"/path/to/your/snippets/folder",
-- "/maybe/another/folder/as/well",
},
})Load Friendly Snippets
スニペット便利すぎて草、なんか色々な人が作ってる例を見れるところないの?
あります。friendly-snippets (VSCode 方式) とか vim-snippets (snipmate 方式) とか。
これらを使用するには、プラグインとして入れて、下の行を設定に追加します。(上の設定にはすでに追加済み)
require("luasnip.loaders.from_snipmate").lazy_load() -- load snipmate type snippets
require("luasnip.loaders.from_vscode").lazy_load() -- load vscode like json type snippetsAdd Snippet Of Current File Type
ところで、コードを書いていて、これスニペットに登録したいなって思ったときに ~/.config/nvim/snippets/<language name>.snippets をわざわざ開きに行くのはちょっと面倒くさかったりします。
ということで、現在編集中のファイルの言語のスニペットファイルを編集しに行くコマンドを作成しました。
:LsEditSnip というコマンドを実行すると自動でファイルを開いてくれます。そして、そのファイルを保存して閉じると自動でその変更が適用されます。
(ここでは説明しなかった LuaSnip 方式で書く場合のファイルを開くコマンド :LsEditLua も追加しています)
-- Command to edit snippets of the current file
local snippet_file_infos = {
snipmate = {
prefix = vim.fn.stdpath("config") .. "/snippets",
ext = "snippets",
},
lua = {
prefix = vim.fn.stdpath("config") .. "/lua/snippets",
ext = "lua",
},
}
local function edit_snippet_files(sniptype, ft)
local file_found = nil
require("luasnip.loaders").edit_snippet_files({
format = function(file, source_name)
if source_name ~= sniptype or string.find(file, "site") then
return nil
end
file_found = file
return file_found
end,
})
if file_found == nil then
file_found = snippet_file_infos[sniptype].prefix .. "/" .. ft .. "." .. snippet_file_infos[sniptype].ext
vim.notify("Create new snippet file for " .. ft .. " at " .. file_found)
vim.cmd("edit " .. file_found)
end
return file_found
end
vim.api.nvim_create_user_command("LsEditSnip", function()
edit_snippet_files("snipmate", vim.bo.filetype)
end, {})
vim.api.nvim_create_user_command("LsEditLua", function()
edit_snippet_files("lua", vim.bo.filetype)
end, {})