copilot-cmp
前回はcopilot.lua
のおかげで、無事にGitHub Copilot
から飛び立ったわけなんですが、
目的地であるnvim-cmp
は、まだ受け入れ態勢が整っていません。
...急いで滑走路と管制塔を建設しましょう❗まじで👷♀️
This repository transforms https://github.com/zbirenbaum/copilot.lua into a cmp source.
このリポジトリは、https://github.com/zbirenbaum/copilot.lua を cmp のソースに変換します。
Copilot suggestions will automatically be loaded into your cmp menu as snippets and display their full contents when a copilot suggestion is hovered.
Copilot のサジェストは自動的にスニペットとして cmp メニューに読み込まれ、 copilot のサジェストにカーソルを合わせるとその全内容が表示されます。
copilot-cmp
を導入することで、Copilot
の提案をnvim-cmp
で扱うことができるようになります😆
Boy, you’re gonna carry that weight 1
Carry that weight a long time
あーあ、 きみはその重荷を背負うんだ
その重みを ずっと背負うことになるんだよ
Setup
If you already have copilot.lua installed, you can install this plugin with packer as you would any other with the following code:
すでにcopilot.luaがインストールされている場合、以下のコードで他のプラグインと同様にpackerでインストールすることができます:
use {
"zbirenbaum/copilot-cmp",
after = { "copilot.lua" },
config = function ()
require("copilot_cmp").setup()
end
}
copilot-cmp
単体では特にコンフィグも無いのでpacker
からこのままsetup()
しちゃっていいと思います。
Install
で、これをどこに入れようかちょっと考えちゃいますが、わたしはcmp
一味に迎え入れることにしました😆
use {
'hrsh7th/nvim-cmp',
config = function() require 'extensions.nvim-cmp' end,
requires = {
'hrsh7th/cmp-nvim-lsp',
{
'L3MON4D3/LuaSnip',
tag = "v1.*",
run = 'make install_jsregexp',
config = function() require 'extensions.luasnip' end,
requires = { 'saadparwaiz1/cmp_luasnip', 'rafamadriz/friendly-snippets' },
},
+ {
+ 'zbirenbaum/copilot-cmp',
+ after = { 'copilot.lua' },
+ config = function() require('copilot_cmp').setup() end,
+ }
},
}
packer.nvim
ここでまたpacker
を少しだけ補足します。
after
after
は以下のような説明がされています。
after = string or list, -- Specifies plugins to load before this plugin.
-- このプラグインの前にロードするプラグインを指定します。
この場合はcopilot.lua
がロード後、続いてcopilot-cmp
がロードされます。
PackerStatus
PackerStatus
を覗いてみるとわかるんですが、
copilot.lua
がロードされていない場合はcopilot-cmp
もロードされません。
PackerStatus
については、
かろうじてQuickstartに記載があります。
-- Show list of installed plugins
-- インストールされているプラグインのリストを表示する
:PackerStatus
少し見えにくいかもしれませんが、(not loaded)
と表示されていますね😉
逆に、copilot.lua
がロードされると、copilot-cmp
もロードされます。
ってしてからもう一回確認してみましょう。
(not loaded)
の表示が消えました😆
Config
それではコンフィグを組み込んでいきましょう。
suggestion / panel
It is recommended to disable copilot.lua's suggestion and panel modules, as they can interfere with completions properly appearing in copilot-cmp.
copilot.luaのsuggestionモジュールとpanelモジュールは、 copilot-cmpで適切に表示される補完を妨害することがあるので、無効にすることをお勧めします。
To do so, simply place the following in your copilot.lua config:
そのためには、copilot.luaの設定に以下を記述してください:
はい、入れましょう😉
require('copilot').setup {
+ suggestion = { enabled = false },
+ panel = { enabled = false },
copilot_node_command = 'node',
}
nvim-cmp
そしたらnvim-cmp
側の調整も行いましょう😆
Tab Completion Configuration (Highly Recommended)
一個ほんとに自信のない項目があって、それがこれなんすわぁ😫
Unlike other completion sources, copilot can use other lines above or below an empty line to provide a completion. This can cause problematic for individuals that select menu entries with TAB.
他の補完ソースとは異なり、copilot は空行の上または下にある他の行を使用して補完を提供することができます。 このため、TAB を使用してメニュー項目を選択する場合に問題が発生することがあります。
This behavior is configurable via cmp's config and the following code will make it so that the menu still appears normally, but tab will fallback to indenting unless a non-whitespace character has actually been typed.
この動作は、cmp の config で設定できます。以下のコードでは、メニューは通常どおり表示されますが、 空白以外の文字が実際に入力されない限り、タブはインデントにフォールバックされます。
えっ...、ええっ...、なんですのん...❓
とりあえずこれ、Super-Tab
の処理が既にいることもあって複雑なので、ファイルを分けて書いてみます。
local cmp = require 'cmp'
local luasnip = require 'luasnip'
local function has_words_before()
local line, col = unpack(vim.api.nvim_win_get_cursor(0))
return col ~= 0 and vim.api.nvim_buf_get_lines(0, line - 1, line, true)[1]:sub(col, col):match '%s' == nil
end
local function has_copilot()
if vim.api.nvim_buf_get_option(0, 'buftype') == 'prompt' then
return false
end
local line, col = unpack(vim.api.nvim_win_get_cursor(0))
return col ~= 0 and vim.api.nvim_buf_get_text(0, line - 1, 0, line - 1, col, {})[1]:match '^%s*$' == nil
end
local M = {}
M.tab = function(fallback)
if cmp.visible() then
cmp.select_next_item(has_copilot() and { behavior = cmp.SelectBehavior.Select } or {})
return
end
if luasnip.expand_or_jumpable() then
luasnip.expand_or_jump()
return
end
if has_words_before() then
cmp.complete()
return
end
fallback()
end
M.shift_tab = function(fallback)
if cmp.visible() then
cmp.select_prev_item()
return
end
if luasnip.jumpable(-1) then
luasnip.jump(-1)
return
end
fallback()
end
return M
local cmp = require 'cmp'
local act = require 'extensions.nvim-cmp-actions'
local map = cmp.mapping
cmp.setup {
mapping = map.preset.insert {
['<C-d>'] = map.scroll_docs(-4),
['<C-f>'] = map.scroll_docs(4),
['<C-Space>'] = map.complete(),
['<C-e>'] = map.abort(),
['<CR>'] = map.confirm { select = false },
['<Tab>'] = map(act.tab, { 'i', 's' }),
['<S-Tab>'] = map(act.shift_tab, { 'i', 's' }),
},
-- (以下略)
}
無理やりねじ込んではみたものの、 そもそも何が問題なのかをわたしがよくわかっていないので、まじで自信がありません。
"入力"がどうとかの話をしているのでInsert
モードの話だとは思うんだけど...。
ま、まあおかしかったら直しておいてください😅
sources
nvim-cmp
のsources
に新人を紹介してあげましょう。はい、なかよし😆
sources = cmp.config.sources {
{ name = 'nvim_lsp' },
{ name = 'luasnip' },
+ { name = 'copilot' },
},
これでNeovim
の空にGitHub Copilot
🛫 copilot.lua
🗺️ copilot-cmp
🛬 nvim-cmp
という航路が完成しました❗
Cleared to land.
まもなく着陸体制...😦
中身が的確かどうかは別として、ちゃんとCopilot
からの提案が挙げられています😉
Nice Landing❗😭
And in the middle of the celebrations
I break down
そして祝いの真っ只中に
僕は泣き崩れる
Uses cmp
前項を見て、「ノットエレガント💢」と思う人もいるでしょう。
Copilot
の威力を真に感じられるのは、例えば以下のような方法です👩✈️
まずは適当にlua
ファイルを作って、:Takeoff
したあと、以下のようにコメントを入力してみましょう。
-- 受け取った文字列に、数字があったら全てを合計した値を返す.
ってやっただけで...、
エレガント❗
ベリーエレガント❗❗
エルルルルェ ガンンンンンンンヌャスッ❗❗❗💨
なんかもうエレガントっていうか、怖いこの人たち...。
“It's kind of scary but exciting, because it's the future”
"一種の怖さはあるけどわくわくするね、なぜってこれが未来だから"
ところでねえ、コパイロットくん❗
これ「数字がなかったらnil
返す」って言っときながら0
返ってるよね😑
...だからね、こうしてみたらどうかな😆
合計が0
っていうケースも忘れないで😉
えっ❗マイナス値⁉️ ...それは忘れよう。
あくまでもわたしの環境でやったらこうなりました〜というもので、同じ手順で同じコードが提案されるかどうかはわかりません🦭
「全体の流れとしてこんな感じだよー」ぐらいで、よろしくどうぞ❗
Spot in. Good night!
Copilot
の実力は、なんだか測りきれない感もありますが、堪能はできましたね🤗
間違った提案はしてくるけど、それはこっちが気づいてあげればいいだけなので、温かく見守ってあげましょう😽
(逆に言うと、細かい誤りに自分で気付けないと...🙀)
Boy, you’re gonna carry that weight
Carry that weight a long time 3
あーあ、 きみはその重荷を背負うんだ
その重みを ずっと背負うことになるんだよ