平衡点
2020/12/18
_ emacs -nw でこの先生きのこるには。
🍄
(2020/12/20 ひっそりと修正。 mintty, wsltty はファイルに設定を書くのであった)。
はじめに.
この文書はEmacs Advent Calendar 2020の12/18(金)分の記事です。 昨日はfiboさんのemacs -nw のコピペ事情でした。 手元では gpaste + xclip で生活してますが、 …ネットワークごしのコピペは面倒そうですね。
さて。
最近Emacs絡みで頑張った事と言えば,
  ターミナルでもall-the-icons.elしたくて,
  isfit-plusを作ったぐらいだったので, 特に記事を書く気は無かったのですが,
  Emacs JPのslack - emacs-jp.slack.com で行なわれた
  オンライン宴会の終了時点で, 何故か 記事を書く ハメ 事になっていました。
元々, …「CUIのEmacsを再考」されたら, 何か書こうかと思ってたのですが…マダァ?(・∀・ )っ/凵⌒☆チンチン
閑話休題. 本記事では「ターミナルで立ち上げたEmacsで生活するため」に じたばたした内容についてまとめています。 幾分内容が不正確であったり, 怨念(?)のこもったポエムがあるかもしれませんが, ご寛恕下さいますよう, よろしくお願いいたします。
TL;DR
ターミナルで生活したい(emacs -nw で生きていきたい)人は,
  以下の3つを なんとかして 揃えておきましょう。
- ロケールが定義する文字幅, ターミナルが表示する文字幅
 - フォントの文字幅
 - Emacsが表示・カーソル移動に利用する文字幅
 
ターミナルでの生活
さて。
時を遡ること Emacs 22→23 の頃でしょうか? Emacs で XFT がサポートとされ, GUI においてアンチエイリアスの効いたフォント表示がされる様になりました。 自分の過去の日記を漁ると emacs22, emacs-snapshot(with xft) for etch なんてことを頑張っていたりしてますね。
しかし, 私はその後 GUI で Emacs を使う事は殆どなくなりました。
  何故かと言えば,
  入力中に文字の大きさや幅・高さがヒョコヒョコ変わって嫌だった
  から, ですね。 入力している時に視線動かすのがストレスなんだよ。勝手に動くんじゃねぇよ。Wordかオマエは。
  多分, アンチエイリアスで表示した上で,
  global に variable pitch な機能を無効化できたのであれば,
  GUI を使っていたかもしれません(…誰かやりかたご存知ありませんか…?)。
結局どうしているのか, と言えば
  「 emacs -nw で生活する事にして, 表示は使っているターミナルに任せる」
  という方法を取っています。
  posframe? 何それ美味しいの?
  pdf-tools? …便利そうですよね(´・ω・`)
というわけで普段使いのターミナルで幸せになりたいわけですが, これが非常に面倒臭い状況になっています。
Unicodeの闇(?) East Asian Ambiguous Width
もはや文字コードは UTF-8 なのが「普通」となりつつあります
  ( 偶に古いシステムで =ja_JP.eucJP= とか見て砂吐きますが )。
  UTF-8はUnicodeの符号化の一つであり,
  Unicodeといえば「全ての言語を単一の文字コードで表わす」という
  バベルの塔 壮大な試みであることは
  周知の通りだと思います(CJK統合漢字やUnihanなんかは割と有名な話題かもしれませんね..。)
さて, Unicodeにおいて, 現在でも我々(私?)を困らせている話題が East Asian Width のお話です。
- Unicode Standard Annex #11(UAX#11)
 - 東アジアの文字幅
 
UAX#11 では(乱暴に言えば)文字の「幅」が半角幅か全角幅か が定められているのですが, 困った事に 文字幅が定まらない, 文脈に依存して文字幅が変わる 文字, East Asian Ambiguous character という文字が定義されています。 この文字(集合)の定まった経緯は
- 東アジア圏の文字コードでは文字幅が全角
 - 東アジア圏以外では文字幅は半角(主にギリシャ文字やキリル文字など)
 
という歴史的事情(と互換性)を考慮して, なんですが…。
  実際のところ libc6 の wcwidth とか、特段文脈を考慮しません(いや、「文脈」って何ですかね)。
  ロケールデータを参照して, そこにある通りに, East Asian Ambigous width は常に 1 が返ってきます。
というわけで, ターミナルで生活する際には, East Asian Ambigous Width は 常に全角(=2)である or 常に半角(=1)である かの選択を迫られます(少なくとも私は)。
ターミナル毎の対応
文字集合としての UTF-8 での文字幅は, ターミナルにおける「カーソルの横移動幅」の計算に必要不可欠です。 よって「文脈によって文字幅が変わる」とかシンドイので, 大抵は決め打ちです。 この実装はターミナルによって違います。
- libc6 を尊重するよ(それは libc6 の担当だよ) ⇒ rxvt-unicode
 - libvte で文字幅を計算するよ ⇒ vte 系のターミナル: gnome-terminal など
 - 自前で変更できるようにしておいたから設定書いてね
    ⇒ mlterm, mintty, wsltty
    
- (2020/12/20) mintty, wsltty はオプションじゃなくて設定ファイルでした.
 
 - 切り替えオプション付けておいたから設定してね
    ⇒ 
mintty, wsltty, iTerm2 など - CJKV とか知ったこっちゃないよ ⇒ Windows Terminal
 
まあ, ターミナル内で統一して処理されていれば問題無い筈ですね。
とはいえ, 二文字幅派は苦労が耐えません。 例えば 面倒なのが Unicode の Box Drawing Char - 罫線素片です。 なんとこれは Ambiguous なので、 二文字幅派は (n)curses を表示に使う アプリケーションにおいて表示が大幅に崩れたりします。
NCURSES_NO_UTF8_ACS という環境変数に 0 or 1 を
  与えることで「崩れたり崩れなかったりしろ」と設定できる筈なんですが,
  そもそもこの環境変数を読まないで独自に文字幅を判定していたり、
  折角指定して VT100 の alternate character set(ACS)で表示する様にしても,
  それをわざわざ Unicode の罫線素片に戻す(libvte系の)ターミナルもあったりして,
  なかなかシンドかったりします。
結局どうするの?
現在, 私は「常に2文字幅」で生活しています。 普段使いのターミナル群は
- Linux では rxvt-unicode.
 - Windows では wsltty (mintty)
 - macOS では iterm2
 
です(どっかのタイミングで辛くなって「1文字幅」にするかもしれませんけど…)。
これらのターミナルのうち,
  wsltty と
  iTerm2 には East Asian Ambiguous Width に関する設定があります。
  (2020/12/20 追記)
  mintty,wsltty
  は設定ファイルに書くことで挙動を変更できます
  (ex. %APPDATA%\wsltty\config に Charwidth=ambig-wide と書く)。
  また, rxvt-unicode はロケールデータを参照しているので,
  自分の手元の環境ではロケールデータを書き変えています。
なお, 後述の uwabami/isfit-plus のデータ領域も 2 文字幅にしています。
…そうそう, みんな大好き tmux ですが
  表示の文字幅の勘定にはロケールデータを使うことになったのですが,
  何故か setlocale(LC_CTYPE, "en_US.UTF-8") に決め打ちされています。
  2文字幅派は辛いですね。
良い感じのフォントを求めて
ターミナルでの 1 文字幅派, 2文字幅派は決まりましたか? その設定は済ませましたか?
では次はフォントを決めましょう。 ターミナルが処理する幅を決めたのであれば, 表示に使うデータ, つまりフォントの幅も 1 文字 or 2 文字に揃えておかないと幸せになれません。
Nerd Font と PUA
1文字幅 or 2 文字幅が選べて視認性の高いフォントは既に幾つかあります (例えば, みんな大好き Ricty はデフォルト 2 文字幅派ですね。生成時にオプションで 1 文字幅にもできます)。 ついでに, ターミナルでの表示といえば, みんな大好きNERD FONTSがあります。 これで色々なアイコンを表示できて, ターミナル生活が捗ります。
ですが, NERD FONTS は Unicode の Private Use Area(PUA) 以外 も使っているので 例えば, 異体字が使えません。 「かつしか」区民や「かつらぎ」市民にごめんなさいしないといけない(かもしれません)し, 「わたなべ」さんや「さいとう」さんにもごめんなさいしないといない(かもしれません。 一応, PUA以外を使っている事は issue に上がっていて(Suggestion Fix invalid code points for some glyphs #365) いつか解決するのかもしれません(し, しないのかもしれません)。
一応, 上記 issue の plan 2 に従って修正したアイコンフォントを 公開してあります
ついでに, このフォントのアイコンを利用するための all-the-icons.el のデータセットも同梱していますので, 適宜御活用下さい。
Emacsでの「文字幅」
ターミナルでのカーソル移動幅と表示するフォントの幅が整理された所で,
  ようやく emacs -nw での話題です。
  こちらもやることは単純で,
  「Emacs での表示・移動幅をターミナルに揃え」ます。
具体的には
(aset char-width-table #x2661 2)
等の様に, 2文字幅で表示したいコードポイント(レンジ)を更新しておけば良いことになります。 拙作の uwabami/locale-eaw-emoji では locale-eaw-emoji.el としてまとめていますので, 適宜御活用下さい.
最後に
というわけで。
1文字幅の場合は PUA も 1文字幅に強制される事が多い(=アイコンフォントが使えない)のですよね。 この辺が解消されたなら, 1文字幅派に改宗するかもしれません。
「こんな苦労をせずに GUI 使えば?」という意見もあるかもしれませんが,
  入力している時に視線動かすのがストレスなんだよ。勝手に動くんじゃ ry
明日は eggc さんの「ord-mode を使ったコード読むときの記録のとり方」の予定です。
参考文献
- 本日記内
    
- emacs22, emacs-snapshot(with xft) for etch: emacs22→emacs23 の頃のお話
 - (解決) tmux で EAW二文字幅としてこの先生きのこるには
 - all-the-icons-in-terminal
 
 - Emacs JP
    
- Emacs JP の slack: emacs-jp.slack.com …お気軽にどうぞ!
 
 - Emacs の素敵拡張あれこれ
 - Unicode, East Asian Ambiguous Width あれこれ
    
- Youtube: 強いUnicode #kernelvm
 - Unicode Standard Annex #11(UAX#11)
 - 東アジアの文字幅
 - 罫線素片
 - ncurses: alternate character set: ACS
 - ぼぎ〜てっく- ラベル:文字幅
        
- xterm, GNU screen, emacs23 の East Asian Ambiguous Width 対応
 
 
 - tmux
 - windows terminal
 - 拙作
    
- uwabami/locale-eaw-emoji
 - uwabami/isfit-plus
 - uwabami/fsmrmp: Patched Font: ‘Fantasque Sans Mono’ + ‘Rounded Mgen+’
        
- ちなみに rxvt-unicode はフォントの ASCII の領域で文字幅を算出するので、 単なるアイコンフォントを使うと文字幅が勘定できません。 結構ハマる事多いんじゃないかなぁ…。