日記作成アプリを作りつつ denops で Vim/neovim プラグイン作る勉強をした

# はじめに

作ったのはこれ。

https://github.com/takavfx/dailynote.vim

## モチベーション

もともと日記をずっと markdown で特定のディレクトリに吐き出して作成していく癖があって、かつ、最近は Vim を使うことがかなり増えている。
ということで、Python + bat で実装していた仕組みを Vim の中でやってしまえばシームレスだしいいなと思ってプラグインを作ろうと思った。

# 実装

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51857/43b97985-d04a-916d-54b1-97cc141087b6.png)

まず、**そもそも Vim のプラグインを書いたことがない**し、Vim Script もなんかよくなじめない。
ということで調べてたら、[denops.vim](https://github.com/vim-denops/denops.vim) なるものを少し前に見つけていた。
これを使うと TypeScritp で書けるらしいということで、もともと Web UI を作るのに TypeScript を使っていたというのもあり、かつ、TypeScript + deno のライブラリパワーを使えるだろうと、このフレームワークをベースに開発することに決めた。


## 1, denops プラグインのインストール

まずは、denops.vim のインストールとセットアップを行う。
僕は普段は [dein.vim](https://github.com/Shougo/dein.vim) を使っていて、toml で管理しているので、以下の項目をその .dein.toml に追加する。

```toml:.dein.toml
[[plugins]]
repo = 'vim-denops/denops.vim' # Deno Base Plugin
hook_add = '''
let g:denops_disable_version_check = 1 # こっちは一旦おまけ
set runtimepath^=F:\Develop\Git\dailynote.vim
let g:denops#debug = 1
'''
```

:::note info
この際、 `hook_add` に `set runtimepath^=<特定の単一の開発ディレクトリ>` を追加してもいいのだが、最終的にプロジェクトを GitHub に公開してかつ、実際に読み込みながら開発するには最初からプロジェクトごとに分けた方がいいので、以下の「プロジェクトディレクトリの作成」の度に追加していくようにした。
:::

## 2, プロジェクトディレクトリの作成

とりあえず以下のようにフォルダと最初のファイルを作成する。

```
<project_root>
    |--denops/<project_name>
        |- main.ts    <----- エントリーポイントの実行ファイル
        |- deps.ts    <----- 外部ライブラリを一旦ここでインポートして管理する様のファイル。
```

既存のコードを眺める限り `deps.ts` でサードパーティライブラリをインポートして管理している様子。
確かにこうしておいた方が便利だし、自分以外の人が参照する時にも便利なので、それに倣う事にする。

### main.ts/deps.ts のスケルトン

とりあえずコマンドを実行してログを出すだけの最小構成のスケルトンコード。

```typescript:main.ts
import {
  Denops,
  ensureString,
} from './deps.ts';

export async function main(denops: Denops): Promise<void> {
  denops.dispatcher = {
    async echo(text: unknown): Promise<unknown> {
      ensureString(text);
      return await Promise.resolve(text);
    },
  };

  await denops.cmd(
    `command! -nargs=1 HelloWorldEcho echomsg denops#request('${denops.name}', 'echo', [<q-args>])`,
  );
};
```

```typescript:deps.ts
export type { Denops } from "https://deno.land/x/denops_std@v4.1.0/mod.ts";
export { ensureString } from "https://deno.land/x/unknownutil@v2.1.0/mod.ts";
```

あとはこれを編集していくだけ。

### 変数のアクセス方法

Vim のインストールの際に、グローバル変数でコンフィグを設定して、それをもとにプラグインの挙動を変えたりする。
これはよくあることだし、先の toml ファイルの中で一緒に管理できるのはともていいこと。

グローバル変数を設定する場合は、「.vimrc」や先の「.dein.toml」といったファイルで管理できる。
「.dein.toml」のでの書き方は次の項目に任せるとして、基本的には以下のように定義できる。

```vim
let g:dailynote_pathfmt = 'YYYY/YYYY-m/YYYY-m-d'
```

そして、これをもとに denops のプラグインの中で取得して使いたいわけである。
それには、 [denops_std の `variable`](https://deno.land/x/denops_std@v4.1.0/variable/mod.ts) を使う。

まず、ライブラリを取得しておく。

```typescript:deps.ts
export * as vars from "https://deno.land/x/denops_std@v4.1.0/variable/mod.ts";
```

そして、中にある `vars.globals.get(denops, '<変数名>')` を取得して使用する。

```typescript:main.ts
import {
  vars,
} from './deps.ts';

let pathfmt = await dpstdVariable.globals.get(denops, "dailynote_pathfmt") as string;
```


## 3, インストール方法の準備

基本的にプロジェクトファイルを GitHub レポジトリとしてプッシュしてしまえば、あとはユーザー側で以下のようにインストールすることができる。

```toml:.dein.toml
[[plugin]]
repo = 'takavfx/dailynote.vim'
depends = 'denops.vim'
hook_add = '''
let g:dailynote_pathfmt = 'YYYY/YYYY-m/YYYY-m-d' # こちらはオプション
'''
```

### Vim ヘルプドキュメントの準備

ドキュメントを `doc` 以下にプロジェクト名で作成しておく。

```
<project_root>
    |- denops/<project_name>
    |- doc/<project_name>.txt
```

これで、ユーザーが `:h dailynote` でヘルプを参照することができるようになる。

# まとめ

とりあえず、いつかの自分のために、再度プラグインを1から書く必要になったら参照できるように書き残してみた。
誰かの参考になれば尚いいなぁ～～