Macのターミナルで全角記号を綺麗に表示したかった

## はじめに
Macのデフォルトのターミナルや Iterm2 等のコンソール上で一部全角記号を表示すると挙動が怪しくなります。
<img width="381" alt="スクリーンショット 2019-12-06 17.59.50.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/31481/99623af5-1ccc-6e18-ae1c-70d9c2df746b.png">
`echo` 程度なら文字が重なって読みにくいだけで済みますが、vim等のエディタ上ではカーソルの制御が狂って意図しない挙動になったりと結構不便です。
何とかならないものかと色々と調べてみたのでまとめてみました。

## 環境
- Mac OS: Sierra (version 10.12.6)
- iTerm2: 3.3.7

## tl;dr
- iTerm2の設定は役に立たない
- `/usr/share/locale/{locale値}/LC_CTYPE` を書き換えれば解決するが、OS X 10.11 El Capitan 以降のMacの場合リカバリーモードに入らないと書き換え不可
- 上記以外の方法として、全角記号を半角で表示するフォントを使えば一応ごまかせる (今回はこちらで対応)

## なんで文字が重なってしまうのか
コンソール上で文字を表現するときに、その文字の幅が半角英数字何文字分なのかを判断する必要があります。
かつて東アジア圏では1バイト文字に1桁の幅（いわゆる半角）の文字を、2バイト文字に2桁の幅（いわゆる全角）の文字を割り当てることが行われおり、今現在普及されている文字規格Unicodeでもその特性との互換性を残すため `East_Asian_Width` 特性が定められています。
(詳細は [東アジアの文字幅 - Wikipedia](https://ja.wikipedia.org/wiki/%E6%9D%B1%E3%82%A2%E3%82%B8%E3%82%A2%E3%81%AE%E6%96%87%E5%AD%97%E5%B9%85) にまとまっているので割愛)

この `East_Asian_Width` プロパティの中で `East Asian Ambiguous (A)` 属性の文字は文脈や言語によって文字幅が異なり、コンソールではロケール `LC_CTYPE` ファイルによって文字幅が制御されます。
この `LC_CTYPE` で設定されている文字幅とフォントファイルで指定されている文字幅が一致しないと、意図しない余白や文字同士が重なってしまう現象が発生します。
上記例ですと、文字 `◯`(`U+25ef`), `☆`(`U+2606`), `◎`(`U+25ce`) が当てはまります。 文字フォント上は全角(2桁幅)であるにもかかわらず、Unicode 上では半角(1桁幅)として扱われ、重なった表記になる様子です。[^1]

## [**非推奨**] 解決策1: iTerm2の設定で Ambiguous characters are double-width を設定
iTerm2 の機能で Unicode の `East Asian Ambiguous (A)` 属性の文字を強引に全角(2桁幅)にするオプションです。
...が、shellの履歴を遡ると表示がバグったり[^2]、vimのカーソルの位置はやっぱり狂いまくりなので対応としてはイマイチ。
Preferences > Profile > Text > Unicode の Ambiguous characters are double-width で設定できますが、やめておいたほうが良さそうです。

## 解決策2: Ambiguous の適応範囲を強引に書き換えて全角表記にする
`/usr/share/locale/{locale値}/LC_CTYPE` を書き換え不具合のある文字の文字幅を矯正する手法です。 
該当文字コードを1つ1つ指定するのはめんどくさい...と思ったのですが、既に同様の問題を対応している mutt-ja というプロジェクトのリポジトリに良さそうなコードがあったのでこちらを利用していきます。

```sh:LC_CTYPEを書き換える
# 良さそうなファイルをダウンロード
curl -L "http://sourceforge.jp/projects/mutt-j/scm/svn/blobs/146/mutt-ja-doc/trunk/samples/UTF-9.src?export=raw" > $WORK_DIR/ja_JP.UTF-8.src
# メモ:
#   URLが UTF-9 だしファイルの文頭も "UCS-4 used here as the internal representation, not UTF-8." と書いてあり怪しいが、
#   https://ja.osdn.net/projects/mutt-j/scm/svn/blobs/head/mutt-ja-doc/trunk/UTF-8.txt によると
#   UTF-8 を元に UTF-9 というファイル名で書き換えた、とあるので多分大丈夫なはず。

# LC_CTYPE ファイルを作成
mklocale -o $WORK_DIR/LC_CTYPE $WORK_DIR/ja_JP.UTF-8.src

# 念の為既存の LC_TYPE ファイルをバックアップ
sudo cp /usr/share/locale/UTF-8/LC_CTYPE /usr/share/locale/UTF-8/LC_TYPE.bak

# 作成した LC_TYPE ファイルに上書き
sudo cp $WORK_DIR/LC_CTYPE /usr/share/locale/UTF-8/LC_TYPE
```

これで文字が重なる問題からオサラバだぜ！よっしゃー今年のAdventCalendarのネタ終了！！
...と思っていましたが `sudo cp` の部分で `Operation not permitted` が出て進められないようです。
どうやら OS X 10.11 El Capitanより追加されたセキュリティ機能、SIP（System Integrity Protection）によって `/usr` 配下の書き込みは `root` 権限を持ってしても書き込みができないらしい。まじか。

今私は会社から貸与されたPCでこの記事を書いているのですが、このPCで参考記事「[初心者向け　MacでOperation not permittedの解決方法](https://qiita.com/iwaseasahi/items/9d2e29b02df5cce7285d)」にある通りリカバリーモードでセーフティー外してまで試すのは気が引ける...
別の手段を考えるしか無いのか...ぐぬぬ

## 解決策3: Ambiguous 文字を半角で表示するフォントを使う
フォント側を修正し、`LC_CTYPE` 側に文字幅を合わせる手法です。

https://github.com/tomonic-x/Illusion
<img width="400" alt="Illusion screenshot.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/31481/7173b30b-3888-1e48-803d-a84a39056270.png">

上記リポジトリのフォントを使うことで解消できそうです。(画像はリポジトリのものをお借りしました)
おおー、なかなか良さそうじゃないですか！！
Illusionのフォントファミリーは3種類あり (詳細は上記リポジトリ参照)、その中の `Illusion N (Narrow)` が求めていたフォントになります。
<img width="266" alt="スクリーンショット 2019-12-06 18.02.01.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/31481/bbaafdeb-f3b4-0ec0-f2ca-7824b2970ef4.png">
全角記号文字を半角サイズで表示するので小さくなって見にくいですが、vimのカーソルがずれて編集できなくなる方が死活問題なので及第点といったところでしょうか。


### Tips1: Nerd font と併用 (fontforgeでフォントを合成する)
特にフォントにこだわりのない方は `Illusion N (Narrow)` をそのまま使うことで解決できるかと思います。
ただ私は [Nerd Fonts](https://www.nerdfonts.com/) というリッチなアイコンが詰まったフォントを使用しており、これらによってtmuxやvimをデコレーションしているのです。Nerd Fonts以外のフォントをそのまま利用すると豆腐だらけの画面になってしまい、生産効率が当社比半分以下になってしまいます。
c(`Д´と⌒c)つ彡ﾔﾀﾞﾔﾀﾞ

そこで fontforge を使い `Illusion N (Narrow)` と Nerd font を合成し、両取りを目指してみます。

```sh
# fontforgeを取得 (既にインストール済みの場合は不要)
brew install fontforge
# 以下のコマンドを実行してAplicationにFontForgeを紐付け (Path通す)
brew cask install fontforge

# https://github.com/tomonic-x/Illusion/releases から良さげなフォントファイルをDL
curl -L -O "https://github.com/tomonic-x/Illusion/releases/download/v0.2.5/Illusion-0.2.5.zip"
# 必要あるファイルだけ解凍 (結構ファイル多くて邪魔になるので)
unzip Illusion-0.2.5.zip hinted/Illusion-Regular.ttc  # このttcファイルに3種類のフォントファミリーが入ってる

# Nerd Fonts を clone
#   リポジトリのサイズが大きいので shallow clone する。 通常の clone は公式非推奨
git clone --depth 1 git@github.com:ryanoasis/nerd-fonts.git

# Nerd Fonts リポジトリ上の font-patcher でフォントを合成
# オプション -s: Nerd Fonts のアイコン (glaph) を半角として定義
fontforge -script nerd-fonts/font-patcher hinted/Illusion-Regular.ttc -s --careful --complete -ext ttf
```
- メモ: 試したバージョン
  - fontforge: Version 20190801
  - Illusion: v0.2.5
  - Nerd Fonts: v2.0.0

ここまでを実行すると、作業ディレクトリ直下に `Illusion N Nerd Font Complete Mono.ttf` なるフォントファイルが生成されます。
<img width="371" alt="スクリーンショット 2019-12-06 18.07.07.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/31481/17f4efc6-1c6c-46c6-c04e-78cc164d2281.png">
デモ用にフォントサイズを24pxとしていますが、やっぱりアイコン部分が小さい...
`nerd-fonts/font-patcher` の `-s` オプションを外すことで大きくできるけど、彼方立てれば此方が立たぬ...

### Tips2: フォント変えたけどvim上での文字がやっぱりずれる場合
vimrc に以下を追記すれば良くなるはず。

```vimscript
set ambiwidth=single
```

今回、 `East Asian Ambiguous (A)` 属性の文字を半角として扱うようにしたのですが、vim上の設定も同様に設定しておきましょう。

## 最後に
普段の業務で扱うvimの気になったことをAdventCalendarに取り上げて5行程度の記事で済ませようかなーと思ってたのですが、蓋を開けたらvimとはほとんど関係のない話でかつ想像以上に沼で文量多くなってしまいました (ヽ´ω`)
タイトルの「綺麗に表示したかった」という、当初思い描いていた完成形に着地できていないので「表示した」と言い切れない、モヤモヤする記事となってしまいました。力不足で申し訳ないです。
もっといい方法があるよ！という方がいらっしゃいましたらコメント欄にて連絡いただければ幸いです。
ターミナル上のvimでテキスト編集する人は大勢いるかと思いますが、全角記号を使いまくるケースはあまり想像できないのでちょっとニッチな記事なのかなと思ったりもしてます。

どこかの誰かさまにお役に立てれば光栄です。

## 参考になった文献
- [東アジアの文字幅 - Wikipedia](https://ja.wikipedia.org/wiki/%E6%9D%B1%E3%82%A2%E3%82%B8%E3%82%A2%E3%81%AE%E6%96%87%E5%AD%97%E5%B9%85)
  - 諸悪の根源の把握に役立ちました。 
- [Unicode® Standard Annex #11 EAST ASIAN WIDTH](http://www.unicode.org/reports/tr11/tr11-36.html)
  - ↑の原文。google翻訳と上記Wikiの記事併せて読むことでなんとなく意味を理解できました。
- [glibc の wcwidth() の「曖昧な文字幅」についての動作 - diary of a madman](https://macks.hatenadiary.org/entry/20090304/p2)
  - ↑の概念を理解するのに役立ちました。 
- [East Asian WidthとiTerm2 - Qiita](https://qiita.com/amedama/items/58af3e26809456a15c57)
  - 先人様の記事。でも解決までには至ってない様なので、この記事を書こうと思い立ちました。 
- [MacのLocal環境でTerminal作業するときに，東アジア文字幅問題の沼から抜け出す - 考え中](http://no-communication.hatenablog.com/entry/20140709/1404933402)
  - これを書く過程で見つけた記事。ほぼ完成形が書かれているが、 SIP によってやりにくくなった。
- [初心者向け　MacでOperation not permittedの解決方法 - Qiita](https://qiita.com/iwaseasahi/items/9d2e29b02df5cce7285d)
  - SIP（System Integrity Protection） という仕様を教えていただきました。ありがとうございます

[^1]: そもそも [Unicode® Standard Annex #11 EAST ASIAN WIDTH](http://www.unicode.org/reports/tr11/tr11-36.html#Scope) に記載の通り、 `East_Asian_Width` プロパティ自体が端末エミュレータでの動作を保証していないらしい。悲しい。
[^2]: 参考: [East Asian WidthとiTerm2](https://qiita.com/amedama/items/58af3e26809456a15c57) (そもそもiTerm2公式的にも非推奨らしい)