🤟

Denoで作る初めてのVimプラグイン

2021/12/04に公開

この記事は「Hamee Advent Calendar 2021」5日目の記事です。

はじめに

わたしは普段Neovimを使って開発しています。
ある日、vimmerならプラグインの一つくらい自分で作れるよな?と、どこからか言われた気がしたのでdenoでプラグインを作ってみようと思いました。
Vim scriptでのプラグイン開発と比べて取っ付きやすいのではないのでしょうか。
こんなに簡単にプラグイン作れたよっていうのが伝わったら良いなー。

この記事で触れないこと

  • denoの詳細
  • denopsの詳細

denops.vim

denoとはJavaScript/TypeScriptのランタイムです。その上で動作するdenops.vimというvimのプラグインを使うことで、vim/Neovimで動作するプラグインをTypeScriptで開発できます。
TypeScriptで書けるので、プラグイン開発やってみようって気持ちになれました。

作っていく

つくるもの

今回は、Slack APIを使って、Vimのコマンドモードから文字列を特定のチャンネルに投稿するだけのプラグインを作ります。こんな感じ。

リポジトリはこちら。shoooout/vim-slack

事前準備

Alisueさんの記事を参考に事前準備をしていきます。この通りに準備を進めます。
実際にdenopsの開発をされている方なのでより詳しく知りたい方はこちらを読んでください。

denoのインストール

dneops.vimを動かすにはdenoがインストールされていなければなりません。
macであればこれでOK

$brew install deno

その他のOSの場合は公式を参考にしてください。

denops.vimのインストール

今回の主役のdenops.vimをインストールしていきます。
私はdein.vimを使ってtomlファイルで管理しています。

[[plugins]]
repo = 'vim-denops/denops.vim'

denops.vimの設定を追加

開発中のプラグインを読み込むために、開発中はdenops.vimの設定を追加しておきます。
今回作成するプラグインはvim-slackという名前にしておきます。

set runtimepath^=~/vim-slack

ディレクトリ構成

vim-slack
├── denops
│  └── vim-slack
│      └── main.ts

denopsは作業ディレクトリ下のdenops/*/main.tsを読み込むので↑のように構成し、main.tsに処理を追記していきます。

Slackのトークンを取得

今回は自分自身が投稿しているようにしたいのでuser tokenを取得します。
slackのトークン取得はこちらの記事を参考にしました。

実装

main.ts
import type { Denops } from "./deps.ts";
import { vars, execute } from "./deps.ts";
import { WebClient } from "https://deno.land/x/slack_web_api@1.0.3/mod.ts";

export async function main(denops: Denops): Promise<void> {
  denops.dispatcher = {
    async postMsg(text: unknown): Promise<unknown> {
      let token = await vars.g.get(denops, "vim_slack_usertoken", "")
      let channel = await vars.g.get(denops, "vim_slack_channel", "")
      let message: string = `${text}`
      let slack = new WebClient(token)
      await slack.chat.postMessage({
        channel: "#"+channel,
        text: message,
      })
      return await Promise.resolve(text);
    }
  }

  await execute(
    denops,
    `command! -nargs=1 SV echomsg denops#request('${denops.name}', 'postMsg', [<q-args>])`,
   );
}
deps.ts
export type { Denops } from "https://deno.land/x/denops_std@v1.0.0/mod.ts";
export * as vars from "https://deno.land/x/denops_std@v1.0.0/variable/mod.ts";
export { execute } from "https://deno.land/x/denops_std@v1.0.0/helper/mod.ts";

deinで管理しているので、vimを開き直すとこの開発中のプラグインが呼び出されます。
上記のコードでの処理を簡単に説明します。

APIの登録

main.tsmainが呼び出されるようになっているため、この中に処理を追記していけばOKです。
各関数がAPIとして登録されるため、この場合postMsg関数がvim-slackというプラグインのAPIとして登録されます。

設定の読み込み

Slackのトークンやメッセージを投稿したいチャンネル名をユーザー自身が定義しておく必要があります。それを以下で読み込んでいます。

main.ts
let token = await vars.g.get(denops, "vim_slack_usertoken", "")
let channel = await vars.g.get(denops, "vim_slack_channel", "")

.vimrcではこのように設定してください。

.vimrc
let g:vim_slack_channel = "{投稿したいチャンネル名}"
let g:vim_slack_usertoken = "{Slackのユーザートークン}"

あとはSlackAPIの仕様に沿って、チャンネル名とユーザートークンを渡すだけです。

APIの呼び出し

ここまでできれば、作成したAPIを呼び出すことができます。

:echo denops#request('vim-slack', 'postMsg', ["Hello World!"])

denops#requestでAPIを呼び出すことができるのですが、こんなに長々と打つくらいならslackアプリを開いて投稿した方が早いです。
executeを使うと、引数の文字列をVim scriptで実行することができます。

main.ts
  await execute(
    denops,
    `command! -nargs=1 SV echomsg denops#request('${denops.name}', 'postMsg', [<q-args>])`,
   );

SVというコマンドを定義し、引数の文字列を実際にSlackに投稿するようにしています。

:SV VimからSlackに投稿できます。

おわりに

読んでいただきありがとうございました。
内容に間違いがあればご指摘ください。

#times_hogehogeみたいな分報チャンネルのあるSlackワークスペースは多いと思うのですが、思い立ったその場で投稿したいのに、Slackアプリ開く > timesチャンネル開く > 投稿するっていうこの時間の間にその投稿をしたい熱は冷めていることが結構ありました。既存のプラグインほどリッチじゃなくて良い、登録している一つのチャンネルにのみ投稿できればそれで良いという経験が建前きっかけで作ってみることにしました。
これでvimでコードを書きながらSlackに投稿できますね!

この程度のプラグインではTSの良さを全く活かせませんでしたが、30行程度でvimからSlackに投稿するプラグインを自作することができました。
denops.vimを使って見慣れた言語でプラグインを開発できるのは、開発体験としてとても良いものでした。

これを機にTypeScriptもちゃんと勉強しよ...
まだまだvim弱者なので鍛えていきます。

参考

Discussion