Denoで作る初めてのVimプラグイン
この記事は「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のトークン取得はこちらの記事を参考にしました。
実装
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>])`,
);
}
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.ts
のmain
が呼び出されるようになっているため、この中に処理を追記していけばOKです。
各関数がAPIとして登録されるため、この場合postMsg
関数がvim-slack
というプラグインのAPIとして登録されます。
設定の読み込み
Slackのトークンやメッセージを投稿したいチャンネル名をユーザー自身が定義しておく必要があります。それを以下で読み込んでいます。
let token = await vars.g.get(denops, "vim_slack_usertoken", "")
let channel = await vars.g.get(denops, "vim_slack_channel", "")
.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で実行することができます。
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