Neovimでlspのエラー(現在の行)などをコピーする

以下を設定として読み込まれるようにする。

vim.keymap.set("n", "dc", function()
  local diags = vim.diagnostic.get(0, { lnum = vim.fn.line(".") - 1 })
  local cmap = {
    [1] = "Error",
    [2] = "Warn",
    [3] = "Info",
    [4] = "Hint",
  }

  if #diags > 0 then
    local d = diags[1]
    local ebuf =
      string.format("%s: %s, %s, by %s at %d:%d-%d:%d", cmap[d.severity], d.message, d.code, d.source, d.lnum, d.col, d.end_lnum, d.end_col)
    vim.fn.setreg("+", ebuf)
    vim.notify("Diagnostic copied!", vim.log.levels.INFO)
  end
end, { desc = "Copy diagnostic message" })

仕組み

lspの結果

lspの結果はlocal diags = vim.diagnostic.get(0, { lnum = vim.fn.line(".") - 1 })で取得できる。vim.fn.line(".") - 1は現在の行番号(indexの関係で-1する)を使って現在の行をvim.diagnostic.getに渡す。

lspの結果は以下の構造だそう。

{
  bufnr = 8,                  -- バッファ番号
  code = "undefined-global",  -- エラーコード
  col = 6,                    -- 開始列(0-indexed)
  end_col = 9,                -- 終了列(0-indexed)
  end_lnum = 25,              -- 終了行(0-indexed)
  lnum = 25,                  -- 開始行(0-indexed)
  message = "Undefined global `vim`.",  -- メッセージ本文
  namespace = 45,             -- diagnostic namespace ID
  severity = 2,               -- 1=Error 2=Warn 3=Info 4=Hint
  source = "Lua Diagnostics.", -- 診断元LSP名

  user_data = {               -- LSP生データ(より詳細)
    lsp = {
      code = "undefined-global",
      message = "Undefined global `vim`.",
      range = {               -- LSPのrange(こちらも0-indexed)
        start     = { line = 25, character = 6 },
        ["end"]   = { line = 25, character = 9 },
      },
      severity = 2,
      source = "Lua Diagnostics.",
    }
  }
}

結果のフォーマット

さっきの構造から必要そうなフィールドの値をstring.format()でフォーマットする。

local d = diags[1]
local ebuf = string.format(
  "%s: %s, %s, by %s at %d:%d-%d:%d",
  cmap[d.severity],d.message, d.code,
  d.source, d.lnum, d.col, d.end_lnum,
  d.end_col)

上記のようにフォーマットすると以下のような結果が得られる。

Warn: Undefined global `vim`., undefined-global, by Lua Diagnostics. at 38:4-38:7

severityの変換

severityは数字なので、以下のマップを使って数字を文字に変換する。

local cmap = {
  [1] = "Error",
  [2] = "Warn",
  [3] = "Info",
  [4] = "Hint",
}

コピーする

vim.fn.setreg()を使ってvimのクリップボードにコピーする。”+“を使うことでシステムのクリップボードに入れる。あと通知も一応。

vim.fn.setreg("+", ebuf)
vim.notify("Diagnostic copied!", vim.log.levels.INFO)

demo