VSCodeとNeovimでCodeGemmaを使用できるようにする

VSCodeとNeovimでCodeGemmaを使用できるようにする

先日、CodeGemmaが更新されました。

ai.google.dev

ローカルで会話しながら開発ができるものを探していたので早速VSCodeやNeovimで試してみました。

Linux環境の話でWindowsMacは動作未確認です

今回は導入編です。

ollamaをインストールする

まずは公式のGithubにあるようにインストールします。

github.com

Linuxでは下記コマンドを実行します

curl -fsSL https://ollama.com/install.sh | sh

GPUを積んでいない場合は完了時にWARNINGが表示されますが、CPUでも動作自体はします。

ただし、動作が遅いので使い物になりません。

Core Ultraに期待。

CodeGemmaをpullする

インストールが完了するとollamaコマンドを使えるようになります。

下記コマンドを実行してCodeGemmaのモデルを落としてきます

ollama pull codegemma:latest

これで使えるようになるのはCodeGemmaの7Bモデルです。

CodeGemmaに期待するのは Github CopilotGemini Code Assist のような会話をしながらの開発なので7Bモデルが必要です。

※ 2Bモデルはコード補完のみの組み込み用です

CodeGemmaの他にも色々使えるので試してみると楽しいかもしれません

ollama.com

VSCode拡張機能を使用する

VSCodeではContinueを使用します。

continue.dev

インストールができたらアイコンをクリックしてウィンドウを表示し、右下の歯車マークからconfig.jsonを編集します。

modelsとtabAutocompleteModelの項目をcodegemmaのものに書き換えてください

実体は ~/.continue/ にあります

  "models": [
    {
      "title": "codegemma",
      "provider": "ollama",
      "model": "codegemma:latest"
    }
  ],
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

  "tabAutocompleteModel": {
    "title": "codegemma",
    "provider": "ollama",
    "model": "codegemma:latest"
  },

設定を保存すればそのままチャット等ができるようになります。

Neovimのプラグインを使用する

Neovimではgen.nvimを使用します。

github.com

setup(lazy)は以下のようにします

{
  "David-Kunz/gen.nvim",
  opts = {
    model = "codegemma:latest", -- The default model to use.
    show_model = true, -- Displays which model you are using at the beginning of your chat session.
  }
},

インストールが出来たら :Gen からChatを起動して動作を確認することができます。

nvimで複数ファイルをbuffersに読み込む

最近、CopilotChatをnvimで使用することのできるCopilotC-Nvim/CopilotChat.nvimluaで動くようになりましたね。

github.com

前回書いたコードブロックを選択したりdiffを表示したりする処理も標準搭載されるようになり進化を実感しています。

今回は次に来るであろう、VSCodeでいう/workspaceを対象にした機能に備えてbuffersに対象のファイルを読み込む処理を書いたので共有したいと思います。

buffersを利用して実現するみたいなやり取りがあったのは覚えているのですが、実際にどうなるかは不明です。

もし来なかったとしてもThePrimeagen/harpoon等でも使用できるので良しとしています。

github.com

早速ですが以下がその処理です。

---------------------------------------------------------
-- create filepath list from current directory
---------------------------------------------------------
local create_file_path_list_from_current_dir = function()
  cmd = "find * -type f"
  -- write it down to the current buffer
  vim.cmd("normal! i" .. cmd)
  vim.cmd(".!sh")

  print("execute command: " .. cmd)
end
vim.keymap.set("n", "<leader>lb", create_file_path_list_from_current_dir, { desc = 'create file path list from current directory' })


---------------------------------------------------------
-- load buffers from filepath list
---------------------------------------------------------
-- example
-- write "find * -type f"
-- and execute this command ":.!sh"
-- then, execute this command ":lua load_buffers_from_file_list()"
local load_buffers_from_file_list = function()

  local buf = vim.api.nvim_get_current_buf()
  local lines = vim.api.nvim_buf_get_lines(buf, 0, -1, false)

  for _, line in ipairs(lines) do
    -- if a buffer is already open with the same name, don't open it again
    local tmp_bufnr = vim.fn.bufnr(line)
    local opened_buffer_list = vim.api.nvim_list_bufs()
    local is_opened = false

    for _, opened_bufnr in ipairs(opened_buffer_list) do
      if opened_bufnr == tmp_bufnr then
      is_opened = true
      break
      end
    end

    if is_opened then
      goto continue
    end

    local bufnr = vim.api.nvim_create_buf(true, false)
    vim.api.nvim_buf_set_name(bufnr, line)
    vim.api.nvim_buf_call(bufnr, vim.cmd.edit)

    ::continue::
  end
  print("load buffers - Processing completed successfully.")
end

vim.keymap.set("n", "<leader>bl", load_buffers_from_file_list, { desc = 'load buffers from file list' })

使用イメージは以下です

  1. harpoonを開く

  2. <leader> lb

  3. harpoon上にカレント以降に存在するファイル一覧が書き込まれる(findコマンド)

    -> 必要に応じて検索 & :v//dとかで絞ったりする

  4. <leader> bl

  5. buffersにリストのファイルを追加

  6. Telescopeでbuffersの検索をしたり、copilot chatの追加機能で使用する(予定)

nvimでcopilot chatの最後のコードブロックを選択する

CopilotChatをNeovimで使用するにあたり、コードブロックの選択が面倒だったので選択してくれるものを作成しました。

github.com

同じ悲しみを背負っている方に共有したいとおもいます。

-- select codeblock text
local function move_cursor_to_above_codeblock()
  local current_line_number = vim.api.nvim_win_get_cursor(0)[1]
  local start_line = current_line_number
  -- search above
  for i = current_line_number, 0, -1 do
    local _line = vim.fn.getline(i)
    if string.match(_line, "^```") then
      start_line = i - 1
      break
    end
    if i <= 1 then
      start_line = 1
      break
    end
  end
  vim.api.nvim_win_set_cursor(0, {start_line, 0})
end

local function select_codeblock_text(cursor_position)
  local current_line_number = vim.api.nvim_win_get_cursor(0)[1]
  local start_line = current_line_number
  local end_line = current_line_number
  local max_line = vim.api.nvim_buf_line_count(0)
  -- search above
  for i = current_line_number, 0, -1 do
    local _line = vim.fn.getline(i)
    if string.match(_line, "^```") then
      start_line = i + 1
      break
    end
    if i == 1 then
      start_line = 1
      break
    end
  end
  -- search below
  for i = current_line_number, max_line do
    local _line = vim.fn.getline(i)
    if string.match(_line, "^```") then
      end_line = i - 1
      break
    end
  end
  if start_line <= 1 then
    print("no codeblock text")
    -- restore cursor position
    vim.api.nvim_win_set_cursor(0, {cursor_position, 0})
  else
    -- select lines
    vim.api.nvim_win_set_cursor(0, {start_line, 0})
    vim.cmd("normal! V")
    vim.api.nvim_win_set_cursor(0, {end_line, 0})
  end
end

local function select_last_codeblock_text()
  -- save cursor position
  local cursor_position = vim.api.nvim_win_get_cursor(0)[1]
  vim.cmd("normal! G")
  move_cursor_to_above_codeblock()
  select_codeblock_text(cursor_position)
end
vim.keymap.set("n", "<leader>vmm", select_last_codeblock_text, {desc = "select codeblock text (last)", noremap = true})

copilot chatの提案を受けたあと、<leader> + v + m + mで最後のコードブロックの中身が選択されます。

また、以下を追加することで<leader> + v + m + dとすると変更前、変更後のdiffを取ることが出来ます。

注意点としては/tmpにファイルを吐き出すことと、無名レジスタを使用するところです。

※tmpなのでファイルのお掃除もせず、毎回上書きです

local function save_yanked_text(path, reg)
  local text = vim.fn.getreg(reg)
  if text == nil or text == "" then
    print("no text in register")
    return false
  end
  local file = io.open(path, "w")
  if file == nil then
    print("cannot open file")
    return false
  end
  file:write(text)
  file:close()
  return true
end

local function get_filetype_from_codeblock()
  vim.cmd("normal! G")
  move_cursor_to_above_codeblock()
  local current_line_number = vim.api.nvim_win_get_cursor(0)[1]
  local block_line = current_line_number
  for i = current_line_number, 0, -1 do
    local _line = vim.fn.getline(i)
    if string.match(_line, "^```") then
      block_line = i
      break
    end
  end
  vim.api.nvim_win_set_cursor(0, {block_line, 0})

  local filetype = vim.api.nvim_get_current_line()
  -- extract filetype from codeblock
  filetype = filetype:match("^```(%w+)")
  return filetype
end

local function diff_codeblock_text()
  local cursor_position = vim.api.nvim_win_get_cursor(0)[1]
  vim.cmd("normal! G")
  move_cursor_to_above_codeblock()
  local current_line_number = vim.api.nvim_win_get_cursor(0)[1]
  local start_line = current_line_number
  for i = current_line_number, 0, -1 do
    local _line = vim.fn.getline(i)
    if string.match(_line, "^```") then
      start_line = i + 1
      break
    end
    if i == 1 then
      start_line = 1
      break
    end
  end
  if start_line <= 1 then
    print("no codeblock in this buffer")
    -- restore cursor position
    vim.api.nvim_win_set_cursor(0, {cursor_position, 0})
    return
  end
  local target_text = "/tmp/_target_text"
  local copilot_text = "/tmp/_copilot_suggestion"
  local function save_and_check(path, register)
    local result = save_yanked_text(path, register)
    if not result then
      error("Failed to save yanked text to " .. path)
    end
  end
  local function diff_texts(target_text, copilot_text, filetype)
    -- open copilot_text text to new tab
    vim.cmd("tabnew " .. copilot_text)
    vim.cmd("setlocal filetype=" .. filetype)
    vim.cmd("vertical diffsplit " .. target_text)
    vim.cmd("setlocal filetype=" .. filetype)
  end
  save_and_check(target_text, '"')
  local filetype = get_filetype_from_codeblock()
  select_last_codeblock_text()
  vim.cmd('normal! y')
  save_and_check(copilot_text, '"')
  diff_texts(target_text, copilot_text, filetype)
end
vim.keymap.set("n", "<leader>vmd", diff_codeblock_text, {desc = "diff codeblock text", noremap = true})
vim.keymap.set("n", "<leader>vmc", ":tabclose<CR>", {desc = "close diff tab", noremap = true})

ついさっき思いついたものなので作りは荒いですが、参考になれば幸いです。

VSCodeをVimmer好みにする

皆さんはVSCodeを使用していますでしょうか。 私は普段Vim(Neovim)ですが、誰かと共通の何かを作成するときはVSCodeを使用しています。 また、初心者の方に何かを教えるときは同じエディタを使わないと伝わりが良くないので、みんなが使ってるエディタを勧めることが多く、その上教える際も初心者に方にはこのボタンを押してみましょうみたいに教えるのでこうなってしまいますね。

そして、画面共有をしながらプログラミングして見せたりするのにコーディングをするわけですが、残念ながらVSCodeはそのままだと使い物になりません。 とはいえNeovimの拡張機能を入れてinit.luaを共通で使うというのもあんまりしたくありませんので、私はVSCodeの設定を別で管理することにしています。 今回はよく使う機能をショートカットに設定して、vimっぽく使おうということでお話していきます。

拡張機能を入れる

まずは拡張機能vimを入れます。これがないと始まりません。

marketplace.visualstudio.com

これでキーバインドVimになって使いやすくなるのですが、これだけだと悲しい事件が起きてしまいます。 なので色々設定していきましょう。

設定 (settings.json)

まず、settings.jsonを開いて以下の設定を入れます。 leaderの設定とクリップボード共有の設定は入れておくといいでしょう。 また、Cursorにあるようなインラインなチャット入力の呼び出しもここで設定できます。(GitHub Copilot) (書き換えてDiffとってAcceptしたら書き換えてくれるやつのこと)

Macではこれを設定しなくてもCommand + iで呼べますが、Ubuntuの場合は設定しないとショートカットに割り当てられていません。今回は<C-i>に割り当てています。 他にもundoとredovscode由来のものを使用するように変更しています。これをしないとundo/redoしただけなのに未保存扱いになってしまい面倒です。 他にも色々書いていますが、興味があればKeyboard Shortcutsを開いてcommandsの部分を検索してみるとなんのことか解ると思います。

以下設定例です。

    "vim.useSystemClipboard": true,
    "vim.hlsearch": true,
    "vim.incsearch": true,
    "vim.leader": ",",
    "vim.useCtrlKeys": true,
    // normal mode
    "vim.normalModeKeyBindingsNonRecursive": [
        {
            "before": [
                "<C-i>"
            ],
            // CopilotChat inline editor
            "commands": [
                "interactiveEditor.start"
            ]
        },{
            "before": [
                "<leader>",
                "c"
            ],
            // CopilotChat
            "commands": [
                "workbench.panel.chat.view.copilot.focus"
            ]
        },{
            "before": [
                "<leader>",
                "e"
            ],
            // open explorer
            "commands": [
                "workbench.view.explorer"
            ]
        },{
            "before": [
                "<leader>",
                "u"
            ],
            // timeline
            "commands": [
                "timeline.focus"
            ]
        },{
            "before": [
                "<leader>",
                "s",
            ],
            // find in files
            "commands": [
                "workbench.action.findInFiles"
            ]
        },{
            "before": [
                "<leader>",
                "g",
            ],
            // open source control
            "commands": [
                "workbench.view.scm"
            ]
        },{
            "before": [
                "<leader>",
                " ",
            ],
            // search highlight
            "commands": [
                ":noh"
            ]
        },{
            "before": [
                "u",
            ],
            "commands": [
                "undo"
            ]
        },{
            "before": [
                "<C-r>",
            ],
            "commands": [
                "redo"
            ]
        },{
            "before": [
                "<leader>",
                "r",
            ],
            // search and replace
            "commands": [
                "editor.action.startFindReplaceAction"
            ]
        },{
            "before": [
                "<leader>",
                "d",
            ],
            // git diff on current file
            "commands": [
                "git.openChange"
            ]
        },{
            "before": [
                "<leader>",
                "f",
            ],
            // format document
            "commands": [
                "editor.action.formatDocument"
            ]
        },{
            "before": [
                "K",
            ],
            // definition preview
            "commands": [
                "editor.action.showDefinitionPreviewHover"
            ]
        },{
            "before": [
                "<C-t>",
            ],
            // jump to definition
            "commands": [
                "editor.action.revealDefinition"
            ]
        }
    ],
    // visual mode
    "vim.visualModeKeyBindingsNonRecursive": [
        {
            "before": [
                "<C-i>"
            ],
            // CopilotChat inline editor
            "commands": [
                "interactiveEditor.start"
            ]
        },{
            "before": [
                "<leader>",
                "c"
            ],
            // CopilotChat inline editor
            "commands": [
                "workbench.panel.chat.view.copilot.focus"
            ]
        },{
            "before": [
                "<leader>",
                "e"
            ],
            // open explorer
            "commands": [
                "workbench.view.explorer"
            ]
        },{
            "before": [
                "<leader>",
                "u"
            ],
            // timeline
            "commands": [
                "timeline.focus"
            ]
        },{
            "before": [
                ">",
            ],
            "commands": [
                "editor.action.indentLines"
            ]
        },{
            "before": [
                "<",
            ],
            "commands": [
                "editor.action.outdentLines"
            ]
        }
    ],
    // insert mode
    "vim.insertModeKeyBindingsNonRecursive": [
        {
            "before": [
                "<C-i>"
            ],
            // CopilotChat inline editor
            "commands": [
                "interactiveEditor.start"
            ]
        },{
            "before": [
                "<C-j>"
            ],
            // Copilot suggestion(next)
            "commands": [
                "editor.action.inlineSuggest.showNext"
            ]
        },{
            "before": [
                "<C-k>"
            ],
            // Copilot suggestion(previous)
            "commands": [
                "editor.action.inlineSuggest.showPrevious"
            ]
        }
    ],

あとはショートカットの設定を別途好みのものにすれば良いと思います。 これで結構使える感じになるのではないでしょうか。

おすすめの設定

ファイル名検索

開いているフォルダの中のファイルからファイル名検索

        },{
            "before": [
                "<leader>",
                "s",
                "f",
            ],
            "commands": [
                "workbench.action.quickOpen"
            ]
        },{

grep

開いているフォルダの中のファイルの中身をgrep

        },{
            "before": [
                "<leader>",
                "g",
                "r",
            ],
            "commands": [
                "workbench.action.findInFiles"
            ]
        },{

検索ハイライトを解除

        },{
            "before": [
                "<leader>",
                " ",
            ],
            "commands": [
                ":noh"
            ]
        },{

開いているバッファを表示(の代わり)

拡張機能vimで:buffersをしようとするとPRヨロと言われるのでとりあえずこれで代用

        },{
            "before": [
                "<leader>",
                "l",
                "s",
            ],
            "commands": [
                "workbench.files.action.focusOpenEditorsView"
            ]
        },{

コメントイン/アウト

コレがないと生きられない

        },{
            "before": [
                "g",
                "c",
                "c",
            ],
            commands": [
                "editor.action.commentLine"
            ]
        },{

quick fix(Copilot)

次のエラーに飛んでインラインチャットでエラーメッセージを拾う

            "before": [
                "<leader>",
                "q",
                "f",
            ],
            "commands": [
                "editor.action.marker.nextInFiles",
                "interactiveEditor.start",
            ]

ショートカット設定(keybindings.json)

併せてこちらも設定しておくといい感じになります。

Enterをctrl+m、ESCをctrl+[で出来るようにする

    {
        "key": "ctrl+m",
        "command": "type",
        "args": {
            "text": "\n"
        },
        "when": "editorTextFocus"
    },
    {
        "key": "ctrl+[",
        "command": "extension.vim_escape",
    },

Buffresの代わりに開いたOPEN EDITORSの選択したファイルを閉じる

    {
        "key": "alt+x",
        "command": "workbench.action.closeActiveEditor"
    },

nvim-treeみたいにファイルの作成や削除をキー入力で出来るようにする(Explorerにフォーカスしているとき)

    // create new file
    {
        "key": "a",
        "command": "explorer.newFile",
        "when": "filesExplorerFocus && foldersViewVisible && !explorerResourceIsRoot && !explorerResourceReadonly && !inputFocus"
    },
    // create new dir
    {
        "key": "shift+a",
        "command": "explorer.newFolder",
        "when": "filesExplorerFocus && foldersViewVisible && !explorerResourceIsRoot && !explorerResourceReadonly && !inputFocus"
    },
    // rename
    {
        "key": "r",
        "command": "renameFile",
        "when": "filesExplorerFocus && foldersViewVisible && !explorerResourceIsRoot && !explorerResourceReadonly && !inputFocus"
    },
    // open with vertical split window
    {
        "key": "ctrl+v",
        "command": "explorer.openToSide",
        "when": "explorerViewletFocus && foldersViewVisible && !inputFocus"
    },
    // delete file
    {
        "key": "d",
        "command": "moveFileToTrash",
        "when": "explorerResourceMoveableToTrash && filesExplorerFocus && foldersViewVisible && !explorerResourceReadonly && !inputFocus"
    },

ちなみに

NeovimでCopilotChat使いたいんだがって方はこちらを参照してください。

github.com

環境によっては「copilot.luaとか見つからないんだが?」って怒られる方もいると思います。 その場合はcopilot.luaとplugin.luaimport osの直下に

import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))

を追記すると使えるようになります。 copolot.luaとplugin.lua

~/.local/share/nvim/lazy/CopilotChat.nvim/rplugin/python3

にあると思います。

また、根本解決したい場合は以下を実行するとよいです。

pip install pynvim==0.5.0

余談ですがnvimでcopilot chatを使用したquick fix(っぽいこと)をしたい場合はこんな感じになります。

local function quick_fix_next_error_with_ai()
  if vim.diagnostic.get(0, {severity = vim.diagnostic.severity.ERROR})[1] == nil then
    print("no error")
    return
  end
  -- jump to next error/warn
  vim.diagnostic.goto_next({severity = vim.diagnostic.severity.ERROR})
  -- fix with Copilot
  -- copy diagnostic message and current line
  local diagnostic_message = vim.diagnostic.get(0, {severity = vim.diagnostic.severity.ERROR})[1].message
  local current_line_text = vim.api.nvim_get_current_line()
  -- open Copilot chat window
  vim.cmd("vertical rightbelow new")
  vim.cmd("setlocal filetype=markdown")
  vim.cmd("CopilotChat ".. "error message : " .. diagnostic_message .. " | current line text : " .. current_line_text .. " | your job : how to fix it?")
end
vim.keymap.set("n", "<leader>xn", vim.diagnostic.goto_next, {desc="Jump to Next Error/Warn"})
vim.keymap.set("n", "<leader>qf", quick_fix_next_error_with_ai, {desc="Jump to Next Error and fix with Copilot Chat"})

NeoVimのテーマをTmuxのpaneIDによって決める

お久しぶりです。 昨今ではAIを使用したプログラミングが流行りに流行って、AI搭載のテキストエディタがたくさん出てきましたね。 私も色々使ってみたのですが最終的にNeoVimに戻ってきてしまいました。 NeoVimでもCopilotChat使えますしね。

色々試した中でもCursorはかなり良い線行っていたのですが、Remote Explorer周りが動かなくて断念しました。 やはり、自分で色々設定できるエディタのほうが使いやすいですね。

そんな感じで色々設定を作っていくなかでこれは微妙…ってなったものが多数あります。 今回はそんな設定の中でも特に使っていない機能を供養します。

作った経緯

これを作った経緯はTmuxでpaneを切り替えているときに、 どの画面のnvimだっけ?となるのが嫌だなと思ったからです。

しかし実際にはこのテーマしか勝たんみたいなものが切り替えまくれるほど存在せず 結局はランダムで決める形になってしまいました。 その設定が以下です。

-- ランダムにテーマを決める
local function set_random_color_scheme(color_schemes)
  local color_scheme = color_schemes[math.random(#color_schemes)]
  vim.cmd("colorscheme " .. color_scheme)
  print("color scheme: " .. color_scheme)
end
-- paneIDによってテーマを決める
local function set_color_scheme_from_tmux_pane(color_scheme)
  vim.cmd("colorscheme " .. color_scheme)
  print("color scheme: " .. color_scheme)
end
local function set_color_scheme()
  local tmux_pane_id = vim.fn.system("tmux run \"echo '#{pane_id}'\"")
  tmux_pane_id = string.gsub(tmux_pane_id, "%%", "")
  -- "colorscheme XXX"で設定するXXXをここに並べる
  local color_schemes = {
    "rose-pine-main",
    "vscode",
    "tokyonight-night",
  }
  -- paneID は数値か
  if not tonumber(tmux_pane_id) then
    set_random_color_scheme(color_schemes)
  else
    -- tmux pane id(+1) は要素数を上回るか?
    if tmux_pane_id+1 > #color_schemes then
      set_random_color_scheme(color_schemes)
    else
      set_color_scheme_from_tmux_pane(color_schemes[tmux_pane_id+1])
    end
  end
end

set_color_scheme()

今見てみるともっと効率よく書けそうですがもはやその気力はありません。

ワクワクをありがとう。

さくっとpackerからlazyに乗り換える

さくっとpackerからlazyに乗り換える

最近nvimを使い始めてしばらく経ちますが、

packerメンテされてない問題に気がついたのでパッケージ管理をlazy.nvimに変更しました。

その際、さくっと乗り換えられる方法がよく分からなかったので自分用メモを残します。

似たような構成で乗り換えたい方の助けになれば幸いです。

1. after/をtmp/にリネーム

このような構成になっている場合は最初にafterをリネームする。

packerからlazyに書き換えた直後にpluginが無い状態で読み込まれてエラーが出ないようにする。

nvim
├── after # これを`mv after tmp`
│   └── plugin
│       :
│       :
│       ├── lsp.lua
│       ├── treesitter.lua
│       ├── trouble.lua
│       ├── undotree.lua
│       :
│       :
│       ├── vim-illuminate.lua
│       └── zenmode.lua
├── init.lua
├── lua
│   └── setup
│       ├── init.lua
│       ├── packer.lua
│       ├── remap.lua
│       └── set.lua

2. packer.luaをlazy.luaにリネーム

nvim
:
├── tmp
│   └── plugin
│       :
│
├── init.lua
├── lua
│   └── setup
│       ├── init.lua
│       ├── packer.lua # これを`mv packer.lua lazy.lua`
│       ├── remap.lua
│       └── set.lua

3. init.luaでlazy.luaを読み込む

├── lua
│   └── setup
│       ├── init.lua # 私の場合はここでremap等を読み込んでいるのでここに追加
│       ├── lazy.lua
│       ├── remap.lua
require("setup.lazy") -- こんな感じで追加
require("setup.set")
require("setup.remap")

4. lazy.luaファイル書き換え

lazy.nvimのGitHubページに従ってPackerでのpluginの定義から書き換える

これを(packer)

-- Only required if you have packer configured as `opt`
vim.cmd.packadd('packer.nvim')

return require('packer').startup(function(use)
  -- Packer can manage itself
  use 'wbthomason/packer.nvim'

こうしたり(lazy)

local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
if not vim.loop.fs_stat(lazypath) then
  vim.fn.system({
    "git",
    "clone",
    "--filter=blob:none",
    "https://github.com/folke/lazy.nvim.git",
    "--branch=stable", -- latest stable release
    lazypath,
  })
end
vim.opt.rtp:prepend(lazypath)

vim.g.mapleader = ","

local plugins = {

これを(packer)

  use({
    'folke/tokyonight.nvim',
    as = 'tokyonight'
  })
  use({
    'Mofiqul/vscode.nvim',
    as = 'vscode',
    config = function()
      vim.cmd('colorscheme vscode')
    end
  })

こうしたりする(lazy)

  {
    'folke/tokyonight.nvim',
    name = 'tokyonight'
  },
  {
    'Mofiqul/vscode.nvim',
    name = 'vscode',
    config = function()
      vim.cmd('colorscheme vscode')
    end
  },

置き換えに関して詳しくはGitHubのREADMEに記載があるので確認する

github.com

5. nvimを閉じて再起動

Lazyが走ってプラグインのインストールが始まる

6. checkhealthを実行してエラーを確認

コマンドモードでcheckhealth lazyを実行してエラーが無いか確認する。

:checkhealth lazy

packerから乗り換える場合はここでpackerあるから消してねみたいなメッセージを確認できるので書いてある通りのディレクトリを削除する。

7. 最後に

最初に変更したtmp/をafter/にリネームしてnvimを再起動で移行完了です。

nvimでオススメのプラグイン

nvimでオススメのプラグイン

最近vimからneovimに乗り換え気味なのですが、その原因となっているプラグインを紹介します。

基本的にnvimでしか出来ないものは無いのですが、自分で作らなくても仕組みを用意してくれているものがたくさんあるので今回はそのあたりを紹介したいと思います。

特に有名なtelescopeやtreesitter等の全員が使ってそうなものは紹介しません。(主観)

また、色系のものは除外しています。

1. mbbill/undotree

github.com

vimにはundoやredo機能がありますが、それを管理しやすくするツールです。

nvimでファイルを色々編集して終了したあとでも再度開けばundo可能です。

また、可視化も優秀で操作も直感的なのでオススメです。(キャプチャが微妙)

undotree

特に特別な設定はしていませんがToggleを設定しておくと使いやすいです

vim.keymap.set("n", "<leader>u", vim.cmd.UndotreeToggle)

2. NeogitOrg/neogit

github.com

git管理が超かんたんになる神ツールです。

nvimから抜けたりターミナルを開いて操作しなくてもぱぱっと確認しながらpushまで出来てしまいます。

ついでにDiffを見たりLogを見たりもできてとても凄いです。

neogit

3. sindrets/diffview.nvim

github.com

こちらもgit系のツールです。

変更点をツリーの一覧でかんたんに確認できて素晴らしい。

一覧で見る機能はtelescopeのgit_statusでも実現できますが、編集できるのがいいです。

diffview

4. akinsho/toggleterm.nvim

github.com

今回紹介するプラグインの中でも群を抜いて一番おすすめしたいものです。

これはもはやnvimという枠を超えたツールだと言えます。(過言)

このツールはtui系ツールと連携させると化学反応が起きます。

例えばdockerを扱えるtuiを例に見てみます。

このようにtuiをnvimのプラグインかのように起動できてしまいます。神。

ただし注意点としてnvimを通す分動作がモッサリします。便利なので目を瞑っています。

toggleterm x docui

今回の例とさせていただいたtuiです。すごい。(あとbrewのほう使えるようにしてほしいです)

github.com

設定例です

local Terminal = require("toggleterm.terminal").Terminal
local docui = Terminal:new({
    cmd = "docui",
    direction = "float",
    hidden = true
})

function _docui_toggle()
    docui:toggle()
end
vim.api.nvim_set_keymap("n", "<leader>do", "<cmd>lua _docui_toggle()<CR>", { noremap = true, silent = true })

おわりに

nvimへの乗り換えが現実味を帯びてきました。