EmacsでTree-sitterを利用してシンタックスハイライトする
Emacs 29以降の設定方法
Emacs 29以降の設定方法は こちら の記事で説明しています!
Tree-sitterとは
Tree-sitter自体はパーサ生成ツールと、かつそれを利用して作成された様々な言語のパーサのセットのようです。
Tree-sitterのパーサを利用することで高速かつ正確なシンタックスハイライトを適用することができます。
AtomではTree-sitterをベースにしたシンタックスハイライトシステムを使用しているようです。
emacsではELisp Tree-sitterというEmacs Lispバインディングパッケージを通じてシンタックスハイライトを適用できます。
Tree-sitterそのものについての解説は下記の記事がわかりやすかったです。
インストール
インストール手順に則ってElisp Tree-sitterパッケージを追加します。
併せて、tree-sitter-langs パッケージも追加します。
tree-sitter-langsには、言語用の文法バイナリとハイライトのパターンの定義を行っているSchemeのファイルなどが含まれており、これを導入することで様々な言語でハイライトできるようになる。といった感じです。
tree-sitter-langsでは現状下記の言語に対応しているようです。
- agda
- bash
- c
- c-sharp
- cpp
- css
- elm
- fluent
- go
- hcl
- html
- janet-simple
- java
- javascript
- jsdoc
- json
- julia
- ocaml
- pgn
- php
- python
- ruby
- rust
- scala
- swift
- typescript
設定
設定を以下に記載します。
自分はleaf.el
を利用しているので、下記のように設定し、
melpa経由でtree-sitterとtree-sitter-langsを追加します。
(leaf tree-sitter
:ensure (t tree-sitter-langs)
:require tree-sitter-langs
:config
(global-tree-sitter-mode)
(add-hook 'tree-sitter-after-on-hook #'tree-sitter-hl-mode)
;; TSXの対応
(tree-sitter-require 'tsx)
(add-to-list 'tree-sitter-major-mode-language-alist '(typescript-tsx-mode . tsx))
;; ハイライトの追加
(tree-sitter-hl-add-patterns 'tsx
[
;; styled.div``
(call_expression
function: (member_expression
object: (identifier) @function.call
(.eq? @function.call "styled"))
arguments: ((template_string) @property.definition
(.offset! @property.definition 0 1 0 -1)))
;; styled(Component)``
(call_expression
function: (call_expression
function: (identifier) @function.call
(.eq? @function.call "styled"))
arguments: ((template_string) @property.definition
(.offset! @property.definition 0 1 0 -1)))
])
)
上記の設定を入れることで下図のようにハイライトされるようになりました。
-
Before(typescript-modeのみ)
-
After(typescript-mode + tree-sitter-mode)
TSXの対応
TSXを書くことが多いので、.tsx
ファイルでハイライトされるようになっていて欲しいです。
tree-sitter-langsではTypeScript自体には対応しているのですが、
major-modeに応じてどのハイライトを適用するか決定しているようで、その中にTSXハイライトへの対応はありません。
そこで、下記のワークアラウンドでTSXファイルがハイライトできるようにしてます。
以下がtypescript-mode
の設定です。
(leaf typescript-mode
:ensure t
:init
(define-derived-mode typescript-tsx-mode typescript-mode "TSX")
(add-to-list 'auto-mode-alist '("\\.tsx\\'" . typescript-tsx-mode)))
define-derived-modeマクロを利用してtypescript-tsx-mode
を作成し、.tsx
ファイルの場合はtypescript-tsx-mode
を利用するようにします。
あとは設定で記載した通りtree-sitter
の設定の中で
(tree-sitter-require 'tsx)
(add-to-list 'tree-sitter-major-mode-language-alist '(typescript-tsx-mode . tsx))
することでTSXファイルのハイライトに対応できるようになります。
ハイライトパターンの追加
Customizationを参考にハイライトパターンの追加をしています。
自分はstyled-componentsを利用するので、簡単なそれ用の設定を入れています。
勉強不足で自信はないのですが、下記のIssueに対応していただけたらmmm-mode
を利用する感じでcssのハイライトに適用できるようになるのかなと期待しています。
クエリはTree-sitterのplaygroundで構文木とクエリの確認ができるのでこれを参照しつつ書きました。
ハイライトするためのワードなどは
elisp-tree-sitterのtree-sitter-hl.elや、
tree-sitter-langのhighlights.scmが参考になりました。
終わりに
Tree-sitterの紹介とEmacsへの導入について説明しました。
Tree-sitter自体わかってないところも多いですが、
major-modeによらずいい感じでハイライトできるようになったのと、コーディング時にハイライトの処理の遅延を感じることもないのでよかったです。
個人的にこれで一番恩恵を受けたのは、TSXを描く際に元々利用していたweb-mode
を利用しなくてもいい感じでハイライトできるようになったため、それだけ早くなったと感じます。。(typescript-mode
だとHTML部のインデントは正しく行なってくれませんが…)
Discussion