Introduce Tree Sitter to Emacs

近年、テキストエディタ/IDEの機能はますます複雑となり、プログラミング言語の数は増え、その組み合わせは膨大なものとなっています。元来テキストエディタはそれぞれがそれぞれの実装でそれらの組み合わせを実現してきましたが、自動補完、定義ジャンプ、ドキュメントの表示など、概ねその基本的な機能は同じであるにもかかわらず、すべてのエディタ開発者はそれぞれが車輪の再発明を行う必要があったのです。その問題を解決するために設計開発されたのがLanguage Server Protocol(LSP)であり、ここ数年で多くのテキストエディタが採用したことにより名前くらいは知っているという人も多いのではないでしょうか。 Tree-sitter はある意味でLSPあるいはLanguage Serverと呼ばれるものに似ています。Tree-sitterは、プログラミング言語の文法から構文解析器を自動生成するパーサージェネレーターツールであり、またそれを使用した構文解析ライブラリです。 Tree-sitterは任意の言語を解析できる汎用的なツールで、動作が速く、依存が少ないツールです。2018年にGitHubからリリースされ、emacs 29.1ではbuilt-inの機能として使えるようになりました。 Emacs x Tree-sitterの仕組みと導入 emacsでTree-sitterを使うには、2つの条件があります: 使用したい言語のパーサーが実装されている Tree-sitterに対応したmajor modeが実装されている 使用したい言語のパーサーが実装されている 当然のことながら、Tree-sitter用に文法が定義されている必要があります。使用したい言語のパーサーが既に誰かによって実装されているかは、available-parsers のリストを参照すると良いでしょう。概ね、メジャーどころの言語は実装されておりほとんどの場合困ることはないと思います。各言語の文法定義(grammar)はJavaScriptで定義し、tree-sitter CLIを使用してC言語のソースコードに変換し、最終的にはそれをコンパイルして使用します。 emacsの場合、~/.emacs.d/tree-sitter/ディレクトリにコンパイルされたダイナミックリンクライブラリを置くことで読み込むことができます。予めコンパイルされたものを置く、でも良いのですが、treesit-install-language-grammarを使用すると文法をダウンロードしてきて配置してくれるため簡単です。treesit-install-language-grammarを使用するには、まずtreesit-language-source-alistに言語とgitリポジトリのペアを定義します。その後treesit-install-language-grammarに言語を渡すことでgit cloneからコンパイル、配置まで行ってくれます。 例えば、YAML用の文法を導入するには次の様に定義します: 1 2 (setq treesit-language-source-alist '((yaml "https://github.com/ikatyang/tree-sitter-yaml"))) そして、(treesit-install-language-grammar yaml)(もちろんM-xでやっても良いと思います)とするとYAML用の文法がインストールされます。私はこの手のものはemacsの起動時に自動で導入されてほしいので、次の様にしました: 1 2 3 4 5 6 7 8 9 (setq treesit-language-source-alist '((yaml "https://github.com/ikatyang/tree-sitter-yaml"))) (dolist (element treesit-language-source-alist) (let* ((lang (car element))) (if (treesit-language-available-p lang) (message "tree-sistter: %s is already installed" lang) (message "tree-sitter: %s is not installed" lang) (treesit-install-language-grammar lang)))) Tree-sitterに対応したmajor modeが実装されている emacsでTree-sitterを使用したい場合、残念ながら、LSPを使用するときのように、単純にパッケージを追加すればすべての言語でよしなに動く・・・という風にはなりません。emacsでは今までのところ、major modeがシンタックスハイライトを提供するためには主に正規表現などが使用されてきました。正規表現を利用したシンタックスハイライトと、Tree-sitterを使用したシンタックスハイライトは大きく違うため、ほとんどのmajor modeではそのままTree-sitterを採用する形にはならず、Tree-sitter専用のmajor modeが提供される形になっています。emacs29ではメジャーどころの言語に対するTree-sitter対応モードが提供されています 。...

2024-02-14 · nasa9084

Tall Titlebar Issue on macOS Sonoma

私は普段、railwaycat/emacsmacport を使ってインストールしたemacs-mac を使用しているのですが、何故かmacOSをSonomaにアップグレードしたらタイトルバーが太くなりました。 macOS SonomaにしたらEmacsのタイトルバーが太くなった…! によると、次の設定を入れるとなおるようです: 1 2 (tool-bar-mode 1) (tool-bar-mode 0) うーん、起動も遅くなったっぽいし、workaround感がすごい。わかんないですけどなんかフレームの初期化に失敗しているとかそういう感じなんでしょうか。 emacs-macの方で修正を入れてくれている様子 なので、リリースされるまではこのworkaroundでしのいでおくくらいしかできなさそうです。

2024-02-06 · nasa9084

y-or-n for kill modified buffer

emacs 29.1はuse-packageやeglotの同梱、tree-sitterのネイティブサポートなどが大きな変更として話題ですが、他にも非常に多くの変更があります。 そのうちの一つ が、変更が加えられた後保存されていないbufferをkillしようとしたときの質問です。これまで、編集があるbufferのkillはyesまたはnoの二択でしたが、第三の選択肢、save and then killが加えられました。 もともと(fset 'yes-or-no-p 'y-or-n-p)としてyまたはnで返事をすることが記憶された私の体はこれに順応することができず、毎回yで止まってしまいます。そこでこれを再びy-or-nに置き換える方法を探したのですが、まだまだみんなemacs 29.1を使っていないのか、情報が全然ありませんでした。しかし(多分)唯一の記事として、Kill Unsaved Emacs Buffers UX: Replacing Yes/No/Save with Meaningful Options というブログ記事を見つけました。この記事は単純にy-or-nにするのではなく、もう少し踏み込んでいるのですが、それはさておき、これによるとkill-buffer--possibly-save という関数で実装されているようです。 adviceをしても良いけれど、できれば関数全体をコピペして置き換えるみたいなことはやりたくないがどうしたモノか、と思っていたところ、use-short-answersという、何やらよさげな変数名が目に入りました。 describe-variableで説明を見てみると、non-nilの時にyes-or-no-pの代わりにy-or-n-pを使うための変数、ということらしいということが分かりました(どうやら28.1で導入された様子。知らなかった)。(setq use-short-answers t)として試したところ、上手い具合にy/n/sで確定できるようになりましたので、正解だったようです。 yes-or-no-pの説明を確認してみても、 If the ‘use-short-answers’ variable is non-nil, instead of asking for “yes” or “no”, this function will ask for “y” or “n”. と書いてありました。 というわけで、(fset 'yes-or-no-p 'y-or-n-p)を(setq use-short-answers t)に置き換えて解決、という感じでした。めでたしめでたし。

2023-09-26 · nasa9084

Migrate Helm to Vertico Stack

世の流れ的には、HelmといえばKubernetes用のパッケージマネージャー を思い浮かべる人が多いと思いますが、私にとってはHelmといえばやはりemacsのパッケージ を思い浮かべます。emacs-helm(以下helm)は所謂fuzzy finder的なモノの一種で、emacsを使っていない人は雑にfzfとかpecoみたいな絞り込みをemacsでできるようにするやつ、と思えば想像しやすいと思います。 helmはなかなか歴史の長いパッケージで、2012年5月15日にanything.elからForkをしたというコミット が打たれています。私個人としては2015年7月にhelmを導入したっぽい記録(commit /tweet )が残っています。そこから数日前まで、かれこれ8年余りの期間にわたってhelmを使ってきました。使ってきた、とは言っても導入したときに多少使い勝手の調整をして以来、ほとんど設定は変えておらず、機能的にもhelm-M-x、helm-find-files、helm-show-kill-ring、helm-buffers-listくらいしか使っていませんでした。基本的にはMELPAからインストールされる最新版を使っていたのですが、8年使っていてもアップデートで壊れた記憶は多分1回くらいしかなくて、かなりの頻度でコミットが打たれているのに安定していた、という印象があります。 8年の間にも、Ivy/Counsel が話題になったり、icomplete-vertical-mode/fido-vertical-modeという補完UIが標準に追加されたりと、いろいろと流行が変わっていたのは認識はしていたのですが、まぁなんやかんやあり8年間helmを使い続けてきました。 そうこうしていたところ、最近emacs 29.1がリリースされ、同じく長らく使用してきたuse-package (どのタイミングで導入したのかは不明ですが、GitHub上でのfirst commit で既に導入済みだったっぽいのでhelmより長く使っていることになります)がemacs標準搭載となりました。use-package以外のパッケージに関する設定はuse-packageを使ってやっていて、他のパッケージがインストールされていないときにインストールするというのもuse-packageでやっていて、という感じなのですが、そうなるとuse-packageは誰が入れてくれるんだ、ということになり結局use-packageだけはpackage-installを直接呼び出すことでインストールしていた のですが、use-packageがbuilt-inとなったことで、晴れてpackage-installの直接呼び出しも不要となりました 。その変更ついでに、いろいろと設定を見直したりとか、ローカルでのみ設定されていた諸諸をGitHubにpushしたりとか、そんなことをやったのですが、その一環として(?)helmからvertico に乗り換えてみました。 helmからverticoに移行して一番の大きな違いは、helmはそれ自体が巨大パッケージで、いろいろなモノがhelmでまかなわれていましたが、verticoはそれ自体はかなり小さく保たれていて、いろいろなパッケージを組み合わせることで同じような機能を実現する、というところです。個人的にはでかいフレームワークより小さいライブラリ類を組み合わせてアプリを書く方が好きなので、そういった意味でもverticoは好きになることができそうです。 今のところ組み合わせているものはmarginalia とorderless の二つ。 marginaliaはminibuffer completionに何やら情報を足すやつでM-xでコマンドの簡単な説明が見られたり、find-fileでファイルのパーミッションやら最終更新時間やらが見られるようになります。インストールしただけでも情報が見れるようになって便利なのですが、そのままだとファイルの更新時間が2023 Sep 10みたいなかんじで月名を使った表示になっており、分からなくはないんですけど日本人としてはやはり2023-09-10と数字を使って表現してくれた方がぱっと見でわかりやすいので、次の様にadviceでmarginalia--time-absoluteを置き換えることで数字表記に修正しました。 1 2 3 4 5 6 7 8 9 10 (defun marginalia--time-absolute--month-number (time) "Format TIME as an absolute age but use month number instead of month name." (let ((system-time-locale "C")) (format-time-string (if (> (decoded-time-year (decode-time (current-time))) (decoded-time-year (decode-time time))) " %Y-%m-%d" "%m-%d %H:%M") time))) (advice-add 'marginalia--time-absolute :override #'marginalia--time-absolute--month-number) orderlessは候補の絞り込みをするときに、完全一致や前方一致だけでなく、fuzzy matchをしてくれるようになるやつで、たとえばabcと入力するとa....

2023-09-10 · nasa9084

emacs/lsp-mode + goplsでGo用のLSP環境を設定する

Language Server Protocol (以下LSP)はこれまでエディタ/IDEが独自に実装する必要があった、補完や定義参照、静的解析によるエラー分析などをサービスとして実現するためのプロトコルです。 LSPを実装したクライアントは、Language Serverを提供している言語であれば何でも補完や定義参照、静的解析といった便利機能を使用することができます。 Microsoftが2016年にその仕様を公開してから、多くのエディタ用のLSPのクライアント実装が作られ、また各種言語用のLanguage Serverも公開されています。 Go言語も例に漏れずLanguage Serverの実装がいくつか存在します。今回は準公式提供のgopls を使用して設定してみます。 もちろんemacsにも複数のLSP Client実装がありますが、今回はlsp-mode を使用します。 まずはemacs用のパッケージをインストールします。次のモノをpackage-installかpackage-list-packagesか、そのあたりでよしなにインストールします。 lsp-mode lsp-ui company-lsp インストールできたら、(私はuse-packageを使っているので)設定ファイルにuse-packageの設定を入れておきます。ついでにgo用の設定も入れておきましょう。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 ;; Golang (defun lsp-go-install-save-hooks() (add-hook 'before-save-hook #'lsp-format-buffer t t) (add-hook 'before-save-hook #'lsp-organize-imports t t)) (use-package go-mode :ensure t :mode (("\\.go\\'" . go-mode)) :init (add-hook 'go-mode-hook #'lsp-go-install-save-hooks)) ;; Language Server (use-package lsp-mode :ensure t :hook (go-mode ....

2020-02-07 · nasa9084

emacs-macでtwittering-modeを使った際に毎回PINを聞かれる問題の解消

twitterのクライアントとして、日頃からemacs上で動くクライアントの"twittering-mode"を使用しています。 ところが最近、新しいmacにインストールしたemacs-macでtwittering-modeを起動すると、毎回twitterのPINを聞かれるようになってしまいました。 毎回、毎回、起動時の暗号化フェーズで Encrypt failed Exit と言われ・・・・ 以前使っていたmacではこのようなことが無かったため、困っていたのですが、以下の手順により解決できました。 背景 結論から言うと、これはGnuPGのバージョンが新しくなったことによる問題でした。 GnuPG2.1.0から、gpg-agentとpinentryと呼ばれる二つのソフトウェアの利用が必須となりました。 twittering-mode事態はgpg-agentやpinentryが必須でも基本的に問題なく動作するようにはなっているハズ・・・でした。 しかし、これらのソフトウェアの必須化に伴って、これまで標準入力から入力できていたパスフレーズが標準入力から入力できなくなっており、そのために暗号化に失敗して毎回PINを聞く・・・という状態になっていたようです。 解決策 みんな大好きarch wiki に解決策がありました。 まず、~/.gnupg/gpg-agent.confに以下のように記述します。 allow-loopback-pinentry つぎに、~/.gnupg/gpg.confに以下のように記述します。 pinentry-mode loopback 最後に、gpg-agentを再起動します。 コマンドラインから、 1 $ gpgconf --kill gpg-agent でgpg-agentを再起動できます。 以上で、twittering-modeが正常に使用できるようになるはずです。

2017-05-09 · nasa9084

pyorgというパッケージを作りました

この記事はEmacs Advent Calendar 2016 の、13日目の記事で、執筆者は @nasa9084 です。 前日はfujimisakariさんのプログラミングに役立つelisp10選 でした。 org記法でブログを書きたい 全国のEmacsユーザの皆さん、org-modeは使っていますか?私は使っています。 普段Pythonを書く私にとって、#はコメントアウトのイメージで、#を見出しに使うmarkdownは(ダメってほどじゃないですけど)なんとなく好きになれません。Emacsユーザということもあり、細かいメモを取るときや、スライドを作るときはorg-modeを使っています。 そんなわけで、慣れ親しんだorg記法。ブログを書くときも使いたいと考えました。 しかし、マークダウンで書くことができるブログや、或いはorgで書いたものをwordpressにアップすることができるelispなんかも有りましたが、どうもしっくり来ません。 そんな折に運用していたWordPressが使えなくなり、このブログシステムを作りました。 Pythonで使えるorg記法パーサも探したのですが、どれもブログで使えるようなものではなく、自分で変換機構も作成することにしました。 最初はブログシステムの中にorgをHTMLに変換するフィルターを作ればいいかな、と思って書き出したのですが、コレがなかなかうまく行かず。最終的にパーサとしてパッケージ化したので、そのお話をさせていただこうと思います。 正規表現の置換だけで実装してみた org記法を使いたいとは言っても、ブログ記事を書くのに使いたいだけなので、チェックボックスや時間に関する部分など、org-modeの大部分は必要ありません。文字の装飾部や見出し、リストが実装されれば十分です。 ですので、最初はわざわざ構文木などを作らずとも、単純に正規表現で置換すればいいのではと考えて実装を始めました。 しかしコレが大きな間違いだったのです。 リスト 問題の1つめは、ネストしたリストを使えないことでした。 単純に正規表現で行ごとに置換していく都合上、ネストしたリストを解析することができません。この段階で、ネストしたリストは使わないことにしよう、と考えました。 しかし、実際に実装してみてわかったのですが(気づくのが遅い)、問題はそれだけではありませんでした。 リストが始まったことや終わったことを判断できないのです。いま置換している行はリストの始めなのか(つまり<ul>が必要なのか)、真ん中なのか、終わりなのか、判断することができないのです。これは困りました。 スラッシュ 更に大きな問題が潜んでいました。斜体とリンクです。 ご存知のように、URLは区切り文字としてスラッシュを多用します。また、org-modeでは斜体をスラッシュで挟んだ形で表します。 このせいで、URLの一部が斜体としてマークアップされてしまう事態が発生しました。 これはちょっと困ったものです。とりあえず斜体を使わないこととしましたが、これには不満が残ります。 結局パーサとしてパッケージ化 更に、テーブルの実装ができないなど、org記法が使える!というには不満点が多すぎました。 そんなわけで、改めて外部モジュールとして、org記法のパーサを書きました。org記法で書かれたテキストを読み込み、解析し、抽象構文木を生成します。そこからHTMLを出力することも可能です。GitHub上でソースも公開しています(nasa9084/py-org )。 コレをブログシステムに組み込むことでorgでブログを書くことができるという寸法です。 また、当初予定してはいなかったのですが、gitのsubmoduleで管理するのはバージョンアップの際などいろいろと面倒なので、パッケージ化してPyPIへとアップしました(pyorg )。 現在はバージョン0.1.3として、見出し、リスト、テーブル、リンク、画像、引用、文字の強調を実装済みです。今後、少しずつ拡張予定ですが、当面はリファクタリングを進めようと思っています。 以上、pyorgのお話でした。

2016-12-12 · nasa9084