🦈

weztermのEmojiがなんか思ってたのと違うな...というときの設定の仕方

2023/03/03に公開

TL;DR

Emojiが含まれている合成フォントを使いたいときはこう書くといい

wezterm.lua
return {
  font = wezterm.font_with_fallback({
    { family = "Cica" },
    { family = "Cica", assume_emoji_presentation = true },
  })
}

最近、paizaのエンジニア間でじわじわとweztermが流行っています。
ずっとalacritty + tmuxを使っていたんですが、みんなよさげよさげと言うので私も使い始めてみました。

https://wezfurlong.org/wezterm/

macではhomebrewでweztermをインストールできます。

$ brew install --cask wezterm

fontの設定

私はCica fontが好きなので、エディタにもターミナルにもCicaを設定しています。当然alacrittyでもCicaを使っており、weztermもCicaで行こうとおもいます。
https://github.com/miiton/Cica

Cicaもhomebrewからインストールできます。

$ brew install --cask font-cica

これをweztermで使うように設定します。

wezterm.lua
return {
  font = wezterm.font 'Cica',
}

CicaはNerd FontsやNoto EmojiやIcons for Devsが合成されたフォントなのでこれらのGlyphも持っており、これ一つでいい感じに絵文字を表示してくれる素敵なフォントです。
Powerlineもstarshipもこれでいい感じに決まるはず。

なんかいつもと違くない...?

これはstarshipで表示しているpromptです。
上がwezterm、下がalacrittyの表示になります。(カラースキームやフォントレンダリングの微妙な違いがありますがいったん気にしないことにします)

Emojiが違う...どうして...

alacrittyの方で表示されているemojiはCicaに合成されたNerd Fontに含まれているものですが、weztermの方ではNoto Color Emoji のものが表示されてしまっているようです。

fontのfallbackを確認する

多くのエディタやターミナルではfallback fontを設定することで、「Primaryのfontに存在しない文字はSecondaryのfontを利用する」という設定が可能です。
weztermもfont fallbackに対応しているので、(Cicaに含まれているからCicaから使われるはず...と思いながらも)まずはfont fallbackの状況を確認してみます。

weztermではcliから wezterm ls-fonts コマンドでどのフォントが表示に選択されているかのfallback状況を確認することができます。

以下は wezterm ls-font の出力からPrimary fontの部分を抜粋しました。

$ wezterm ls-fonts
Primary font:
wezterm.font_with_fallback({
  -- /Users/akira.shinohara/Library/Fonts/Cica-Regular.ttf, CoreText
  {family="Cica"},

  -- <built-in>, BuiltIn
  "JetBrains Mono",

  -- <built-in>, BuiltIn
  -- Assumed to have Emoji Presentation
  -- Pixel sizes: [128]
  "Noto Color Emoji",

  -- <built-in>, BuiltIn
  "Symbols Nerd Font Mono",

})

これを見る限りだとCicaが最優先になっているものの、Emojiの表示にはCicaをすっ飛ばして Noto Color Emoji にfallbackされてしまっていそうです。

明示的にNerd Fontのfallbackを設定してみる

先の通り、何故かCicaからはEmojiが使われないことがわかったので一旦 Noto Color Emoji よりNerd Fontを優先すべく明示的にfallback設定をしてみます。

wezterm.lua
return {
  font = wezterm.font_with_fallback({
    { family = "Cica" },
    { family = "Symbols Nerd Font Mono" },
  }
}

Symbols Nerd Font Monoはweztermに組み込まれているfontなので、マシンへのインストールをせずに利用できます)

結果は...

変わらん...

wezterm ls-font の出力を見ると、ちゃんとNerd Fontのほうが優先されているみたいですが...

wezterm ls-fonts
Primary font:
wezterm.font_with_fallback({
  -- /Users/akira.shinohara/Library/Fonts/Cica-Regular.ttf, CoreText
  "Cica",

  -- <built-in>, BuiltIn
  "Symbols Nerd Font Mono",

  -- <built-in>, BuiltIn
  "JetBrains Mono",

  -- <built-in>, BuiltIn
  -- Assumed to have Emoji Presentation
  -- Pixel sizes: [128]
  "Noto Color Emoji",

})

どうして...

別のEmoji Fontをfallbackに設定してみる

切り分けのために別のEmoji対応fontをfallbackに設定してみます。
今回はTwemoji Mozilla を設定してみました。

https://github.com/mozilla/twemoji-colr

wezterm.lua
return {
  font = wezterm.font_with_fallback({
    { family = "Cica" },
    { family = "Symbols Nerd Font Mono" },
    { family = "Twemoji Mozilla" },
  }
}

変わった!

wezterm ls-font の出力は以下のとおりです。

Primary font:
wezterm.font_with_fallback({
  -- /Users/akira.shinohara/Library/Fonts/Cica-Regular.ttf, CoreText
  "Cica",

  -- <built-in>, BuiltIn
  "Symbols Nerd Font Mono",

  -- /Users/akira.shinohara/Library/Fonts/Twemoji.Mozilla.ttf, CoreText
  -- Assumed to have Emoji Presentation
  "Twemoji Mozilla",

  -- <built-in>, BuiltIn
  "JetBrains Mono",

  -- <built-in>, BuiltIn
  -- Assumed to have Emoji Presentation
  -- Pixel sizes: [128]
  "Noto Color Emoji",

})

CicaとNerd Fontをすっ飛ばした上でTwemoji Mozillaが選択されているようですね。

つまり、「特定のフォントをEmojiの表示に利用するか」には何らかの条件があるようです。

weztermがEmoji表示用fontを選択する仕組み

weztermのソースコードを読んでみました。

https://github.com/wez/wezterm/blob/main/wezterm-font/src/parser.rs#L681

wezterm-font/src/parser.rc
                // If they explicitly list an emoji font, assume that they
                // want it to be used for emoji presentation.
                // We match on "moji" rather than "emoji" as there are
                // emoji fonts that are moji rather than emoji :-/
                // This heuristic is awful, TBH.
                if !self.is_built_in_fallback
                    && !attr.is_synthetic
                    && self.names.full_name.to_lowercase().contains("moji")
                {
                    self.assume_emoji_presentation = true;
                }

どうやらweztermではfontの名前に case unsensitiveで moji が含まれているときにそのfontをEmoji表示のために選択するようです。 これじゃ〜ん!

これで「Cica」と「Nerd Font」が無視されて「Twemoji Mozilla」にfallbackされた理由がわかりました。

明示的にCicaをEmoji用fontとしてfallbackに設定する

先の通り、命名に「moji」が含まれていないと自動的には絵文字表示に利用してくれないことがわかりました。

weztermでfont fallbackに設定できる項目に assume_emoji_presentation というプロパティがあります。これをtrueに設定することで明示的にEmoji用fontとして認識させることができるようです。(ドキュメントにバッチリ書いてあったのを見落としてた...)

assume_emoji_presentation = true or assume_emoji_presentation = false to control whether a font is considered to have emoji (rather than text) presentation glyphs for emoji. (Since: 20220807-113146-c2fee766)
https://wezfurlong.org/wezterm/config/lua/wezterm/font_with_fallback.html?highlight=assume_emoji_presentation#weztermfont_with_fallbackfamilies--attributes

このようにweztermに設定します。(Emoji用fontはテキスト表示用fontとは独立しているようで、両方記述してfallbackさせないとダメでした)

wezterm.lua
return {
  font = wezterm.font_with_fallback({
    { family = "Cica" },
    { family = "Cica", assume_emoji_presentation = true },
  })
}

こちらがこの設定でのターミナルの表示です。

完璧

おかえり...

before

after

(このサメくんはCicaには存在しないGlyphなので、weztermデフォルトの Noto Color Emoji にfallbackされて表示されています)

これでお気に入りのpromptが帰ってきたのでハッピーで埋め尽くしてレストインピースまで行けるようになりました。

weztermのfont fallbackはfontごとにscaleが変更できたり(英字fontから和文fontへfallbackさせたいときに微妙なサイズのズレを調整できます)結構かゆいところに手が届くのでとてもよいですね。


luaでシンプルに設定が書けて、仮に足りなくても自分でそういう機能を書いちゃおうというのがweztermの素敵なところです。個人的にもtmuxにもたせていた機能をどんどん移行していってるのでこのままweztermを使っていこうという機運が高まりました。

とてもおすすめのターミナルなので、「思ったとおりにfontが出ないからなあ...」と諦めていた方はぜひ触ってみてください。あとドキュメントはちゃんと読もう。

paiza

Discussion