こんにちは、フロントエンド開発部のcw-suetake 🐧です。 この記事はChatwork Advent Calendar 2022 6日目の記事になります。
今回は僕が業務中にも使っているEmacsの話をしたいと思います。
emacsの機能拡張がLispではなくTypeScriptで書けたらいいのにと思ったことはありませんか?僕はあります。
一口にemacsといってもFork含め様々なプロジェクトがありますが、僕は普段その一つであるemacs-ngというエディターを利用しています。
このエディターは通常のemacsに加えてDenoランタイムを組み込んだものになっています、なのでEmacsLispに加えてTypeScriptを用いてエディターの機能拡張を作ることができるといった特徴があります。
今回はemacs Lispで作られたchatworkクライアントを元ネタとしてTypeScript版を作ってみたのでご紹介したいと思います。
↑ 参考にさせていただいた 元ネタはこちら
↓ 今回作ったTypeScript版はこちら
今回作った拡張はLispを使わずに全てTypeScriptで書かれています。インストール方法も特徴的で(eval-js)の中でDenoのimport文を書きます。
(setq chatwork-ng-token "XXXXXXXXXXXXXXXX") (eval-js "import 'https://raw.githubusercontent.com/magcho/chatwork.el-ng/main/chatwork-el-ng.ts'")
今回は簡単のためにトークンをinit.elに記載していますが、auth-source等に切り出して管理する方が好ましそうです。 Emacs の設定ファイルにプライベートトークンを記述する方法 - chatwork-mobile
demo
試しに1行のメッセージを任意のルームに送る機能を実装しています。
Chatwork apiの呼び出しはDeno(TS)側で行い、メッセージの入力や送信先選択などのインターフェースはEmacs(Lisp)側の機能を利用してみました。
設定をすれば部屋選択でカーソル選択やインクリメンタルサーチも簡単に用意できました。
パッケージの書き味
emacs-ngに内蔵されているDenoからTypeScriptを実行した時にglobalスコープにlisp
というオブジェクトが定義されています、どうやらこのオブジェクトが内部的にemacsにつながっておりLispの処理の呼び出しができるようになっているようです。
以下に同じような処理をlispとTypeScript実装したものを並べてみました。
M-x hoge-function
で処理を呼び出すとMessageの入力を求め、その値をそのままprintする流れになっています。TS上のlispオブジェクトの使い方を見ると、実際のlispの命名を踏襲しているのがなんとなく見えてきてとっつきやすいです。
(defun hoge-function (interactive) (print (read-string "sMessage: ")))
declare var lisp: any; lisp.defun({ name: "hoge-function", interactive: true, args: "sMessage: ", func: async (message: string) => { lisp.print(message) }, });
特にLispでApi呼び出し等の非同期処理を実装しようとすると覚えることが多くて挫折してしまった経験があるのですが、今回はTypeScriptのAsync/Awaitだけでemacsの操作をブロックしない非同期処理を行うことができました。
まとめ
Lispの書き方はよくわからない & TSならなんとか書けそうといった僕でも簡単な機能を作ることができそうでした。
エンジニアの中にはチャットも開発も全てエディターの中で完結させたいという野望をお持ちの方もいらっしゃるかと思います。今回はChatwork apiの一部機能しか利用しませんでしたが他にも様々な機能が用意されていますのでChatworkをエディターから便利に使いたいけれど難しそうと感じている方にぜひおすすめです。