neovimの設定をvimscriptからluaにしたときの覚書とか

> 覚書なので随時追加していく系かもです

# 注意事項

- **三文記事です** 
- 内容はほぼ日記と覚書です
- 基本的には公式ドキュメント嫁案件ではあります
- なんか間違えていたら教えてくださるとありがたいです
- 逐次追記するかもしれません

# とりあえず設定の書き方で迷ったときは…

githubでとにかく `<プラグイン名/機能名> lang:lua` で検索した結果をとにかく持ってきて動きを確認したりしていました

方法的にはマイコンのころからある、既存コードをコピペして使いやすく改変でしかありませんが、おおよそすでにきちんと動くコードから、luaの適切な書き方なども学ぶことができたので、とてもよかったです

# `vimscript` から `lua` に変更したときに戸惑ったあれこれ

## 文字列結合 (lua)

luaの文字列結合は `..` です、 `+` ではありません

```lua
local hoge = "ho" +  "ge" -- error
local fuga = "fu" .. "ga" -- OK
```

## 配列関係 (lua)

luaの配列やハッシュは `テーブル` というひとつの機能となっています

また、テーブルを表す文字は `{}` となっています

```lua
local ngtable = [] -- error
local oktable = {} -- OK
```

### 配列キーアクセス (lua)

また、配列キーにいろいろな文字を指定できますが、指定されている文字列によってはアクセス方法を変えないとならないです

```lua
local table = {
  one = 1,
  _ = "underscore",
  ["hoge-fuga"] = "hoge-fuga",
}
```

また、普通の連番の配列が存在する場合、 **添え字(要素数)の始点は `0` ではなく、 `1` です**

```lua
local table = { "one", "two", "three" }
table[0] = "ONE" -- error
table[1] = "ONE" -- OK
```

# luaでの `vimscript` 機能呼び出しあれこれ

## `set hogehoge` 系 (`set colorscheme` など)

残念ながら `vim.cmd()` を使用します

諦めが必要

```init.lua
local my_colorscheme = "onehalfdark"
vim.cmd("colorscheme" .. my_colorscheme)
```

## グローバル変数 (`g:xxx` のようなやつ)

- `vim.g.xxx` の形式で呼べます

## vimscript定義？の関数の呼び出し (`expand("%:h")` など)

以下のいずれかで呼び出せます

- `vim.fn.expand()`
- `vim.fn["expand"]()`

私はもっぱら後者を使っていました

理由としては、プラグイン固有の関数呼び出しの場合に楽に呼び出せるためです

例えば `ddu.vim` を使用している場合は `vim.fn["ddu#start"]()` という形式で呼び出しを記述できます

## vim固有の変数の呼び出し (`&lines` や `&columns` など)

- `vim.api.nvim_eval("&lines")` の形式で取得できます

# 個人的に作っておいて嬉しかったユーティリティ関数

## `echo` 系

私が不勉強なだけの可能性が高いですが、 `vim.fn["echo"]` の形で `echo` を呼び出すことができず、 `vim.cmd("echo " .. echo_message)` の形式で記述することが多かったですので、ユーティリティとしてまとめておきました

また、vim設定の `printfデバッグ` 用の変数をグローバルに定義しており、それを参照することでデバッグモードがONの時にのみ動作するechoも別途実装しておりました

:::note info
こちらについては `vim.print()` を利用されることを強く推奨します
デバッグモードかどうか？の検出はあると便利ですが、 `vim.print()` を使用することで `変数内のテーブル情報を表示する` など、自作するよりもはるかに便利です
:::

<details>

```utils/init.lua
local M = {}

function M._echo(type, mes)
  vim.cmd("echo" .. type .. " '" .. mes .. "'")
end

function M.echo( mes)
  M._echo("", mes)
end
function M.echom(mes)
  M._echo("m", mes)
end

function M.echoerr(mes)
  M._echo("err", mes)
end

function M.echoe(mes)
  M.echoerr(mes)
end

-- debug preference
function M.debug_echo(mes, args)
  if vim.g.is_enable_my_debug then
    M.echom(mes)

    if args then
      for i, v in ipairs(args) do
        M.echom(i .. " : " .. v)
      end
    end
  end
end

function M.begin_debug(mes)
  M.debug_echo("begin " .. mes)
end
function M.end_debug(mes)
  M.debug_echo("end " .. mes)
end
```

</details>


## `try-catch`

luaには `try-catch` の構文が存在しないらしく(調べ切れていない可能性が高いですが)、別途実装する必要がありましたので、こちらもユーティリティとして実装しております

なお、こちらのブログ記事がググると引っ掛かり、こちらを参考とさせていただきました(ありがとうございます)

http://bushimichi.blogspot.com/2016/11/lua-try-catch.html

```utils/init.lua
-- local M = {}

function M.try_catch(what)
  M.debug_echo("begin try --->")
  local status, exception = pcall(what.try)
  if not status then
    M.debug_echo("#### begin catch --->")
    what.catch(exception)
    M.debug_echo("<--- end catch ####")
  else
    M.debug_echo("<--- end try")
  end
  if what.finally then
    M.debug_echo("begin finally --->")
    what.finally()
    M.debug_echo("<--- end finally")
  end

  return exception
end
```

こちらは使うときはこのように使えます
うれしい

```usage.lua
local utils = require("utils")

utils.try_catch({
  try = function()
    -- watching target code
  end,
  catch = function()
    -- trapped code
  end,
  finally = function()
    -- finalize code
  end
})
```

## `eval` しないといけない系

人によっては `float window` を多用していて、あるいはその表示幅や高さをいろいろ変えたいことがあると思います

一方で、ウィンドウのサイズがコロコロ変わることがある人もいます、少なくとも私のことです

それは、私が `nvim` を `nvim-qt.exe` …つまり、WindowsのGUI環境で terminal を使用せずに neovim を使用していることを指しますが、この場合、ウィンドウをあちこち移動させたりすると、同様に幅がコロコロ変わります

そのため、 `&lines` や `&columns` を参照したいのですが、結構長ったらしく書かないとならないので、ユーティリティとして記述しました

```utils/init.lua
-- local M = {}

function M.get_vim_lines()
  return vim.api.nvim_eval("&lines")
end

function M.get_vim_columns()
  return vim.api.nvim_eval("&columns")
end
```

# `lua` 設定のデバッグのしかた

## `:che` / `:checkhealth`

**最強の強**

プラグイン関係のエラーもおおよそこれに表示されます

例えば、必要となっている実行バイナリや、必須となっているプラグインがインストールされていないなどを適切にエラーとして表示してくれます

まずはこれを見ろのレベルで多用します

しかし `:checkhealth` の実行は実行環境によっては非常に時間がかかります

それはつまり、欲しい情報がごく一部であったとしてもそれを閲覧するための時間が非常にかかってしまう…ということです

ですので、後述の通り `:checkhealth <check name>` で特定の部分だけ確認することができます

### `:checkhealth lazy`

::: note information
ここでは neovimのプラグイン管理 として `lazy` を利用している場合についてを紹介します
:::

`:checkhealth lazy` と入力することで プラグイン管理プラグインである `lazy.nvim` の `checkhealth` 結果だけを取得することができます

![{3E315855-4907-4048-A336-7EC08E1AFC77}.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/507143/40cd0b9c-fb4d-5f5f-83fb-bb5ad7ed0226.png)

## `:message`

一番下の行に現れるエラーの内容を表示できます

### `nvim -V9<filename>`

が、 `:message` だけだと何の因果でこの事象が発生してるかが分からないことが大半です

そのため、おおよそ `vim` にも存在すると思いますが、 `-V` という実行ログをファイルに保存するオプションがNeovimには存在します

これが使えるだけで問題解決力が段違いに上がります

## `:map [key_name]` / `:imap [key_name]` / `:nmap [key_name]`

現在のNeovimの設定ロード状態でマッピングされているキーを表示できます

`[key_name]` を指定しない場合は、現在設定されているマッピングの一覧を確認でき、設定した場合は、そのキーに今どのような動作が定義されているのかを確認することができます

e.g. `:imap <CR>`

こちらの記事で知りました、ありがとうございます…！

https://qiita.com/wakaba260/items/99ea1b8042c98fb7df93

# めっちゃどうでもいいけど git から設定をcloneするときに `~/AppData/Local/nvim` や `~/.cache` や `~/.config` 配下に配置したくない人向けのそういう設定つくるやつ

> 主対象者: 私
>
> 理由: Windows(pwsh) で neovim を使用する場合、設定が `~/.config` ではなく `~/AppData/Local/nvim` になることがあったりして管理が面倒くさい

こういう設定用スクリプトを記述して、実行すれば自動的に任意の設定フォルダを参照するようにしています (pwsh向けのスクリプトなのでそこだけお気を付けください)

```setup.ps1
Write-Output "make dir: ~/.config/nvim"

if (!(Test-Path '~/.config')) {
    mkdir ~/.config
    Write-Output "success mkdir ~/.config"
}

if (!(Test-Path '~/.config/nvim')) {
    mkdir ~/.config/nvim
    Write-Output "success mkdir ~/.config"
}

Write-Output "make dir: ~/AppData/Local/nvim"

if (!(Test-Path '~/AppData/Local/nvim')) {
    mkdir ~/AppData/Local/nvim
    Write-Output "success mkdir ~/AppData/Local/nvim"
}

Write-Output "remove nvim preference files what already exists"

if (Test-Path '~/.config/nvim/init.vim') {
    Remove-Item ~/.config/nvim/init.vim
}
if (Test-Path '~/.config/nvim/ginit.vim') {
    Remove-Item ~/.config/nvim/ginit.vim
}

if (Test-Path '~/AppData/Local/nvim/init.vim') {
    Remove-Item ~/AppData/Local/nvim/init.vim
}

if (Test-Path '~/AppData/Local/nvim/ginit.vim') {
    Remove-Item ~/AppData/Local/nvim/ginit.vim
}

if (Test-Path '~/.config/nvim/init.lua') {
    Remove-Item ~/.config/nvim/init.lua
}
if (Test-Path '~/.config/nvim/ginit.lua') {
    Remove-Item ~/.config/nvim/ginit.lua
}

if (Test-Path '~/AppData/Local/nvim/init.lua') {
    Remove-Item ~/AppData/Local/nvim/init.lua
}

if (Test-Path '~/AppData/Local/nvim/ginit.lua') {
    Remove-Item ~/AppData/Local/nvim/ginit.lua
}

Write-Output "write reference preference"

$preferencePathStr = [System.IO.Path]::GetDirectoryName($PSScriptRoot.ToString())

$preferencePathStr = $preferencePathStr.Replace('\', '/' )
$luaPathStr = $preferencePathStr + "/lua"

Write-Output $preferencePathStr

# ~/.config/nvim に設定作ってくれるやつ
Set-Content -Path ~/.config/nvim/init.lua  -Value "vim.opt.runtimepath:append(',${preferencePathStr}')"                 -Encoding UTF8
Add-Content -Path ~/.config/nvim/init.lua  -Value "vim.opt.runtimepath:append(',${luaPathStr}')"                        -Encoding UTF8
Add-Content -Path ~/.config/nvim/init.lua  -Value "vim.g.preference_path = vim.fn.expand('${preferencePathStr}')"       -Encoding UTF8
Add-Content -Path ~/.config/nvim/init.lua  -Value "vim.cmd([[luafile ${preferencePathStr}/init.lua]])"                  -Encoding UTF8
Set-Content -Path ~/.config/nvim/ginit.lua -Value "vim.cmd([[luafile ${preferencePathStr}/ginit.lua]])"                 -Encoding UTF8

# ~/AppData/Local/nvim に設定作ってくれるやつ
Set-Content -Path ~/AppData/Local/nvim/init.lua  -Value "vim.opt.runtimepath:append(',${preferencePathStr}')"           -Encoding UTF8
Add-Content -Path ~/AppData/Local/nvim/init.lua  -Value "vim.opt.runtimepath:append(',${luaPathStr}')"                  -Encoding UTF8
Add-Content -Path ~/AppData/Local/nvim/init.lua  -Value "vim.g.preference_path = vim.fn.expand('${preferencePathStr}')" -Encoding UTF8
Add-Content -Path ~/AppData/Local/nvim/init.lua  -Value "vim.cmd([[luafile ${preferencePathStr}/init.lua]])"            -Encoding UTF8
Set-Content -Path ~/AppData/Local/nvim/ginit.lua -Value "vim.cmd([[luafile ${preferencePathStr}/ginit.lua]])"           -Encoding UTF8

# luaの実行パスを通すやつ (あんまり意味ないかもしれない)
[System.Environment]::SetEnvironmentVariable("LUA_PATH",        $luaPathStr, "User")
[System.Environment]::SetEnvironmentVariable("LUA_EXE_PATH",    $luaPathStr, "User")

Write-Output "finished and all correct"
```

# 参考にしたやつ一覧

http://bushimichi.blogspot.com/2016/11/lua-try-catch.html

https://qiita.com/wakaba260/items/99ea1b8042c98fb7df93