Helix Editorで新しいテーマの作り方

## まとめ

背景が透過のテーマを作成してそれがマージされたのでうれしかった

ただ，作るのが大変でドキュメントとにらめっこしながら作ることになった

ドキュメントの記述にない箇所や，個人的に名前と変更される場所の名前がミスマッチかなあと思う箇所がある(Issue~~を作ろうかなと思っている~~[作った](https://github.com/helix-editor/helix/issues/3335))

個人的にはデフォルトで組み込みのテーマがあるのは良いと思っている

~~なので，これらテーマのメンテナンスをしやすいようなツールをどうにか作ろうかなとかんがえている~~

[themelint](https://github.com/helix-editor/helix/pull/3234)がマージされていた

```sh
cargo xtask themelint dark_high_contrast # input theme name
```

これを使って必要な項目が満たされているかどうかを静的に解析することができる

ハイコントラステーマを作った際にスコープの範囲を大まかに分類したのでとりあえず下に置いておく

ハイコントラストなので`black`とか`white`をマイルドにしたり，省略している箇所を追加したり、過剰な装飾もあるので`modifiers`を削ったり、カラーコードを変更したりいろいろやってみるのをお勧めする

version12.12からmodifier=["underline"]は非推奨になった(利用はできる)

`underline = {color="", style="line"}`が推奨される書き方

下線に色付けと、ターミナルに依存するが波線や二重線など複数の種類を適応できるようになった

```toml
# author name : theme name <e-mail@dom>
# original theme : github URL or something

# Interface
"ui.background" = { bg = "black" } # ウィンドウの背景
"ui.window" = { fg = "aqua" } # 分割したときのウィンドウの境界線
"ui.text" = "white" # 恐らくフォールバック用のテキストの色，シンタックスハイライトにも適用される

"special" = "orange" # ファイルピッカーのファジーマッチの色，シンタックスハイライトのspecialと共通?
"ui.background.separator" = { fg = "white" } # ファイルピッカーの検索箇所との境界線
"ui.text.focus" = { modifiers = ["reversed"] } # ファイルピッカーで選択している行の色

"ui.virtual.ruler" = { fg = "gray", bg = "white", modifiers = ["reversed"] }
"ui.virtual.whitespace" = "gray"
"ui.virtual.indent-guide" = "white"

"ui.statusline" = { fg = "white", bg = "deep_blue" } # アクティブウィンドウのみ
"ui.statusline.inactive" = { fg = "gray" } # アクティブじゃない方のウィンドウ
# アクティブウィンドウだけに表示される
"ui.statusline.normal" = { fg = "black", bg = "aqua" }
"ui.statusline.insert" = { fg = "black", bg = "orange" }
"ui.statusline.select" = { fg = "black", bg = "purple" }
# "ui.statusline.separator" = { fg = "aqua", bg = "white" } # どこが変わっているかわからない

"ui.cursor" = { fg = "black", bg = "white" }
"ui.cursor.insert" = { fg = "black", bg = "white" }
"ui.cursor.select" = { fg = "black", bg = "white" }
"ui.cursor.match" = { bg = "white", modifiers = ["dim"] }
# カーソルがある箇所の色
"ui.cursor.primary" = { fg = "black", bg = "white", modifiers = ["slow_blink"] }
"ui.cursor.secondary" = "white"
# カーソルがある行の色
"ui.cursorline.primary" = { fg = "orange", underline = { style = "line" } }
"ui.cursorline.secondary" = { fg = "orange", underline = { style = "line" } }
# カーソルとカーソルがある行とは別の選択されている箇所の色
"ui.selection" = { fg = "deep_blue", bg = "white" }

"ui.menu" = { fg = "white", bg = "deep_blue" }
"ui.menu.selected" = { fg = "orange", underline = { style = "line" } }
# fgがスクロールバー，bgがスクロールエリア
"ui.menu.scroll" = { fg = "aqua", bg = "black" }
"ui.help" = { fg = "white", bg = "deep_blue" } # メニューを開いているときにその上部に出る説明を表示する場所
"ui.popup.info" = { fg = "white", bg = "deep_blue" } # マイナーモードのポップアップ

# エディターの左側にあるデバッグやエラーなどの●が出る場所の色
"ui.gutter" = { bg = "black" } # fgは恐らく意味が無いと思われる
"ui.linenr" = { fg = "white", bg = "black" } # 行番号のある位置
# カーソルがある箇所は全て選択されるのでcursorlineで背景を統一するなどすると過剰になりすぎない
"ui.linenr.selected" = { fg = "orange", bg = "black", modifiers = ["bold"] }

# Diagnostic
# フォールバック用以下のが設定されていなければ使われる
"diagnostic" = "white" 
# コードの該当箇所に対して適用され，シンタックスハイライトなどを上書きする
"diagnostic.info" = { bg = "white", underline = { style = "line" } }
"diagnostic.hint" = { fg = "yellow", underline = { style = "line" } }
"diagnostic.warning" = { fg = "orange", underline = { style = "line" } }
"diagnostic.error" = { fg = "red", underline = { style = "line" } }
# ガターの●，ステータスラインの●，コード上部の説明の３箇所に適用されるので注意
# コード上部のみに背景や下線を適用したくても他の2箇所にも適用されれるのでこの書き方が一番無難な気がする
"info" = "white"
"hint" = "yellow"
"warning" = "orange"
"error" = "red"
"debug" = "red"

# (git)diff color
"diff.plus" = "green"
"diff.delta" ="blue"
"diff.minus" = "pink"

# Syntax high light
"type" = { fg = "emerald_green" }
"type.buildin" = "emerald_green"
"comment" = { fg = "gray", modifiers = ["italic"] }
"operator" = "white"
"variable" = { fg = "aqua" }
"variable.other.member" = "brown"
"constant" = "aqua"
"constant.numeric" = "emerald_green"
"attributes" = { fg = "blue" }
"namespace" = "blue"
"string" = "brown"
"string.special.url" = { fg = "brown", underline = { style = "line" } }
"constant.character.escape" = "yellow"
"constructor" = "aqua"
"keyword" = { fg = "blue", underline = { style = "line" } }
"keyword.control" = "purple"
"function" = "yellow"
"label" = "blue"

# Markup 
"ui.popup" = { bg = "deep_blue" } # マークアップや入力補完のポップアップの色
"markup.heading" = { fg = "yellow", modifiers = ["bold"], underline = { style = "line" }  }
"markup.list" = { fg = "pink" }
"markup.bold" = { fg = "emerald_green", modifiers = ["bold"] }
"markup.italic" = { fg = "blue", modifiers = ["italic"] }
"markup.link.url" = { fg = "blue", underline = { style = "line" } }
"markup.link.text" = "pink"
"markup.quote" = "yellow"
"markup.raw" = "brown"

[palette]
black = "#000000"
gray = "#585858"
white = "#ffffff"
blue = "#66a4ff"
deep_blue = "#142743"
aqua = "#6fc3df"
purple = "#c586c0"
red = "#b65f5f"
pink = "#ff5c8d"
orange = "#f38518"
brown = "#ce9178"
yellow = "#cedc84"
green = "#427a2d"
emerald_green = "#4ec9b0"
````

[こんなIssueもある](https://github.com/helix-editor/helix/issues/2454)ので将来的にはもう少し楽に個人的なテーマを作成できるようになるかもしれない

[これがマージされた](https://github.com/helix-editor/helix/pull/3067)ので既存のテーマを継承してオリジナルのテーマを作成できるようになった

```toml:onedark_transparent.toml
inherits = "onedark"
"ui.background" = {}
```

これを`runtime/themes/`に配置するだけで良い

あとは`config.toml`で名前を指定する

```toml
theme = "onedark_transparent"
```

上記のだとポップアップなどは透過になっていないので、少し一貫性に欠けるテーマになってしまうので調整は必要

## Helix用に透過テーマを作成した

![popup_ex.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2630694/3e63c6ac-6097-f3c4-e198-eac0ce2281a8.png)

ルーラーが3本あるのは、線が被ったときの見やすさを確認するためのもの

あとポップアップが出たときにかぶったところが透過されるのを確認するため

Windows Terminalに背景画像を設定し，それが見やすくなるようにテーマを作成した

もともとはayuテーマを作成する予定だった(ほかの人が先に作成していた)

元となっているテーマが背景が透過されるものだったので透過テーマを作成した

Heilx editorでは背景を設定しないとデフォルトの色が設定されるのではなく色がないとして扱われる

これによりターミナル側が透過や画像が設定されていると画像のようにすることができる

欠点としてはすべての背景を透過にしたので画像にあるようにポップアップも透過されてしまい、コードやポップアップドキュメント、エラーなどがごちゃごちゃに感じる

透過するかしないかしかできないが将来的にはできるかもしれない

背景に黒を選択すると背景が暗めの画像であればうまくマッチするが一応透過がメインなので設定しないことにした

## Heilxでの新しいテーマの追加方法

~~カラーコードが必須のところと必須じゃないところまではわからなかった~~

`cargo xtask themelint theme_name`これでエラーが出たのを直せば良くなった

tomlのデバッグができないので，[構文のエラーなどは全て`ui.selection`として表示される](https://github.com/helix-editor/helix/issues/2247)ので注意が必要

vimやvscodeなどからテーマを移植する場合はそれに近い配色を先にテーマを選定してそれをcpを使ってコピーしておくのが多分楽になると思う(それか上のひな形から作り直すか)

それから[omni補完などがhelixにはまだない](https://github.com/helix-editor/helix/issues/1015)ので，nvimやvscodeなどの単語の補完ができるエディタでやった方が良い

実際に私はvscodeで編集して統合ターミナルでhelixを起動してリロードをするという形でやっていた

```bash
# windows
cd ~\AppData\Roming\helix\runtime\themes
cp original_theme.toml my_theme.toml
code my_theme.toml # nvim my_theme.toml
# linux
cd ~/.config/helix/runtime/themes
cp original_theme.toml my_theme.toml
code my_theme.toml # nvim my_theme.toml
```

続いて自分のテーマをとりあえず読み込むようにする

config.tomlを開き自分のテーマを設定する

```toml
theme = "my_theme" # 自分のテーマを設定する

[editor]
```

### my_theme.tomlの書き方

基本的には階層化されてい`key`はダブルクォーテーションをつけずに記述することができる

階層化されている(`.`ドットつきの)場合はダブルクォーテーションで囲う必要がある

基本的にダブルクォーテーションをつけていた方が無難な感じがする

```toml
# authorname <e-mail@dom>
# refarentce_theme <e-mail@do> # 既存のテーマのガイドラインに従ってください

"uiの箇所" = { fg = "fg_color", bg = "bg_color", modifier = ["modi00", "modi01"] } # 基本的な記述方法
uiの箇所 = "bg_color" # fgだけの時は直接書ける，ドットがないからダブルクォーテーションがいらない
"uiの箇所.枠組み" = "fg_color" # ドットがあるときはダブルクォーテーションで囲む

# ユーザーの色を設定して定数としておきたい場合は[palettes]を書く
[palettes]
fg_color = "#ffffff"
bg_color = "#000000"
```

基本的な書き方はこうなる

一番上に製作者の情報

オリジナルのテーマではなく既存のテーマの場合にはきちんとそれを明記する

現在のところテーマに関しては[これといったガイドラインがない](https://github.com/helix-editor/helix/issues/3257)ため変なものじゃなければ元のガイドラインに従っていなくてもマージさる

ですがオリジナルがあるときにはきちんとガイドラインに従って追加をする

それ以降はドキュメントにある変更した箇所の名前を記述

{}波括弧内に記述する

波括弧を書かなければデフォルトでfgとして扱われる

自分でカラーパレットを書きたければ[palettes]と記述しそれ以降に書かないと無効になる

文字には修飾を複数つけることができる

修飾の種類はcrosstermというバックエンドで制御しているのでこれ以外の修飾が欲しければそちらにPRを出さないといけない

また、細かく文字を変えたりとかもこれではできなく、あくまでも色と下記の修飾までである

修飾は使った方が割と見やすいので、割と複数個つけても良いと思う

commentをdimとitalicにするとか

| 英語 | 日本語 |
| --- | --- |
| bold | 太字 |
| dim | 薄い |
| italic | 斜体 |
| underlined | 下線 |
| slow_blink | 遅い点滅 |
| rapid_blink | 速い点滅 |
| reversed | 反転 |
| hidden | 隠す |
| crossed_out | 取り消し線 |

slow_blink(遅い点滅)とreversed(反転)は割と使えるので覚えておいた方が良い

dimが[透明度を調整できる](https://github.com/helix-editor/helix/pull/2751)ように頑張っている途中らしい

かなりハッカブルらしくいろいろ大変そうだなあとコミットを見て思った次第です

## ハイライト早見表

今回はテーブルの形で記述した

基本的に一列目がuiやシンタックスハイライトといった大きなくくりになっている

続いて2列目と3列目とより細かい箇所の色分けになっている
| key | subkey| description |
| --- | --- | --- |
| type |  | タイプ |
|  | buildin | 組み込み型 |
| constructor |  | コンストラクタ |

このようなテーブルの配置になっていたら下記のように書き直せる

```toml
# 以下の3つは色の指定の仕方が異なるが意味が同じ
type = "fg_color"
"type.buildin" = "type_color"
"constructor" =  { fg = "const_color" }
```

一行目がtypeが広い範囲の型全般を色付けし、次の行で組込み型のみを特定の色に上書きできる

3行目以降にはまた別の範囲の色付けが続くというような形になっている

最後の列が変更される場所の説明と個人的な感想が載っている

### シンタックスハイライト

helixではtree-sitterが使われておりこれが上手く働いている言語では細かく色付けをすることができる

たまにバグっているのでIssueが上がったりしてる

そのため，上手くいっていない気がしたらtree-sitterのパースが間違がっている可能性がある

一番左のが元の色になりそれに対して、上書きするように細かく色付けをすることができる

逆に言えば、元の色だけで良い時には細かいところを記述しなくても大丈夫

| key | subkey | subkey | description |
| --- | --- | --- | --- |
| type |  |  | 型全般 |
|  | buildin |  | 組込みの型(int, usizeなど) |
| constructor |  |  | コンストラクタ |
| constant |  |  | 定数全般 |
|  | buildin |  | 組込みの定数 |
|  |  | boolean | 真偽値 |
|  | character |  | 文字列 |
|  |  | escape | エスケープ文字 |
|  | numeric |  | 数値 |
|  |  | inter | 整数 |
|  |  | float | 浮動小数 |
| String |  |  | 文字列<br>’’と””で分けるかrawで分けるかで検討中らしい |
|  | regexp |  | 正規表現 |
|  | special |  | 特殊文字 |
|  |  | path | パス |
|  |  | url | URL |
|  |  | symbol | シンボル変数?<br>よくわかっていない |
| comment | line |  | ラインコメント |
|  | block |  | ブロックコメント |
|  |  | documentation | Rustの///などでかかれたドキュメント |
| variable |  |  | 変数全般 |
|  | buildin |  | 組込み変数、self,this,superなど |
|  | parameter |  | 関数やメソッドの引数 |
|  | other |  | 変数内の他全般 |
|  |  | member | フィールドや複合データタイプ<br>structやunions, enumなど |
|  | function |  | 関数やメソッド |
| label |  |  | htmlなどのラベル |
| punctuation |  |  | 記号系だが，指定しないとui.textで指定した色で表示される |
|  | delimiter |  | カンマやコロン |
|  | bracket |  | 括弧全般 |
| keyword |  |  | プログラミング言語ごとに設定されている予約語全般 |
|  | control |  | 制御構文系 |
|  |  | conditional | if elseなどの比較 |
|  |  | repeat | for, while loopなどの繰り返し処理 |
|  |  | import | import、exportなどのロード系 |
|  |  | return | リターン |
|  |  | exception | 例外処理 |
|  | operator |  | and, or, inなどの記号じゃない演算子 |
|  | directive |  | わからない |
|  | function |  | 関数やメソッドの宣言系、これとvariable.functionがバグっている可能性がある |
|  | storage |  | letやvarとかの変数用のストレージ全般 |
|  |  | type | let, var, classなどの大きいタイプ用 |
|  |  | modirier | staticとかの変数用の予約語の修飾語 |
| operator |  |  | +-=などの予約語にある文字じゃない記号の演算子 |
| function |  |  | 関数全般 |
|  | buildin |  | 組込み関数全般 |
|  | method |  | メソッド全般 |
|  | macro |  | マクロ全般 |
|  | special |  | Cとかのプロセッサ？<br>よくわかっていない |
| tag |  |  | htmlなどのタグ |
| namaspace |  |  | 名前空間全般 |

### マークアップ

画像のこの部分

![popup_ex_(2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2630694/bae21aa1-2854-9596-bede-0358f1a34ac5.png)

ホップアップのマークダウン記法で書かれている箇所のハイライト

フォントの大きさや種類などを細かく制御できないため、それぞれを色分けする必要がある

そもそも太字や斜体で表現されるところも、ここできちんと修飾をつけてあげないと太字や斜体などに変わらない

| key | subkey | subkey | description |
| --- | --- | --- | --- |
| markup |  |  |  |
|  | heading |  | 見出し全般 |
|  |  | marker | 見出しのマーカー線 |
|  |  | 1,2,3,4,5,6 | 強調のところ<br>数字ごとに設定可能 |
|  | list |  | +, * などのリスト箇所 |
|  | bold |  | 太字箇所 |
|  | link |  | リンク系 |
|  |  | url | URL |
|  |  | label | ラベル |
|  |  | text | urlや画像の説明文 |
|  | quote |  | 引用箇所 |
|  | raw |  | 文字全般 |
|  |  | inline | インライン |
|  |  | block | ブロック |

インターフェースだが古いため必要ないそうな

### Gitなどのバージョン系

Gitの変更や移動などのよくあるバージョン変化の内容も同様にできる

まだ、マージされていないが[これ](https://github.com/helix-editor/helix/pull/1623)もあるのでガターも最終的にもっとリッチにできるようになるかも
| key | subkey | subkey | description |
| --- | --- | --- | --- |
| diff |  |  | Gitなどの変更系の表示 |
|  | plus |  | 追加 |
|  | minus |  | 削除 |
|  | delta |  | 変更 |
|  |  | moved | 移動 |

### エディターの見た目系

シンタックスだけではなくステータスラインや枠、タガーもここで見た目を変えられる

| key | subkey | subkey | desctiption |
| --- | --- | --- | --- |
| special |  |  | ファイルピッカーのあいまい検索のマッチング<br>[ドキュメントに記述がなく、かつ名前が変更される予定](https://github.com/helix-editor/helix/issues/2380) |
| ui ||| これ単体はフォールバック用に宣言できないみたい |
|  | backgorund |  | 現在開いているウィンドウの背景 |
|  |  | separator | ファイルピッカーの入力部分の下の線<br>なぜバックグランドなのかは不明 |
|  | cursor |  | ノーマルモードのカーソルの色 |
|  |  | insert | インサートモード同上 |
|  |  | select | セレクトモード同上 |
|  |  | match | 対応している括弧などの箇所 |
|  |  | primary | 複数のカーソルあったときのメインで操作しているカーソル |
|  |  | secondary | プライマリーカーソル以外のカーソルすべて |
|  | linener |  | 左側の番号の箇所 |
|  |  | selected | 左側の番号のprimayとsecondaryのある行全て |
|  | statusline |  | ステータスライン全般 |
|  |  | normal | ノーマルモードの時のステータスラインのlightlineみたいな形になる箇所 |
|  |  | insert | インサートモード同上 |
|  |  | select | セレクトモード同上 |
|  |  | separator | ステータスラインの各左、中央、右と設定できる箇所の区切り |
|  | popup |  | 先に説明した部分の背景とかの全般の色のポップアップの色<br>fgを設定した場合にどこが変更されるかわからない |
|  |  | info | スペースキーとかのマイナーモードの時にでる情報ポップアップ |
|  | window |  | ウィンドウ画面のボーダー線<br>fgで細い線、bgで太い線になる |
|  | help |  | menuの選択したときに出る上の説明部分の色 |
|  | text |  | UIのテキスト全般をこれで統一できる、定義していない箇所はこれで統一した色を設定可能 |
|  |  | focus | ファイルピッカーの選択したところの色、またはデバッグの進行している箇所の色 |
|  | virtual |  | 空白、ルーラー、インデントガイドそれぞれのもとの色 |
|  |  | ruler | ルーラー |
|  |  | whitespace | 空白 |
|  |  | indent-guide | インデントガイド |
|  | menu |  | コードとコマンドモードの補完の時に出るポップアップのベースになる色 |
|  |  | selected | 補完の選択する箇所 |
|  |  | scroll | コード補完のポップアップの設定<br>fgはスクロールボックスのところ,bgはスクロールエリア |
|  | selection |  | 選択モードで選択される箇所 |
|  |  | primary | プライマリーカーソルがある箇所 |
|  |  | secondary | プライマリーカーソル以外のカーソルがある箇所 |
|  | cursorline |  | カーソルがある行 |
|  |  | primary | プライマリーカーソルのある行 |
|  |  | secondary | プライマリーカーソル以外のカーソルがある行 |

### 診断系

エラー時のメッセージやタガー側の色などを設定できる

errorで背景を設定すると上部の情報のみだけでなくガターとステータスラインの上の●にも背景が上書きされる

そのためガターとステータスラインの背景が違う場合には統一感が下がるので気を付ける

![diagnostic.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2630694/285dd71c-657e-32ee-6337-cd367528b4bc.png)

| key | subkey | description |
| --- | --- | --- |
| diagnostic |  | 左側の●箇所のフォールバック用 |
|  | hint | ヒントがあるコードの該当箇所 |
|  | info | 同上のインフォ |
|  | warning | 同上のワーニング |
|  | error | 同上のエラー |
| hint |  | 開いているウィンドウの上部に表示される文字と<br>ガターの●，ステータスライン上の●の3箇所 |
| info |  | 同上のインフォ |
| warning |  | 同上のワーニング |
| error |  | 同上のエラー |

## 終わりに

書いている途中で気づいたが，自分が作ったひな形順にしておけば見やすくなるなあと今更ながら思った

とりあえずはこのままにして，自分がよく参照するようなら再度書き直すかも

説明をもう少し明瞭化しないといけないなあと思うのでたびたび変わるかも

テーマの作成は難しい