GoのTUIで表示が崩れる場合

Posted on:

結論

gnome-terminalを使用している場合は、設定の「曖昧幅の文字(W)」と環境変数RUNEWIDTH_EASTASIANを一致させよう。

Ambiguous width(曖昧幅)

ターミナル上のアプリケーション(TUI)では、GUIと違って文字単位で描画されます。 そして1文字の幅は固定されていて、アルファベットは1文字分とすると日本語などは2文字分使用する、いわゆる半角全角の世界です。 ただし、既にUnicode(UTF-8)が標準となっているので、バイト数と文字幅は関係しないようになっています。

Unicodeでは幅が決まっている文字がほとんどですが、一部に「Ambiguous; 曖昧」とされている文字があります。

以前は英語圏のアプリケーションではASCIIの範囲内のみを使用していて「Ambiguous」な範囲の文字を使用するのは、それ以外の地域の人だったため、全角幅で問題になることは無かったのですが、Unicodeの使用が拡大するにつれて英語圏の方が作るアプリケーションでも「Ambiguous」な幅の文字が使用されることが増えてきました。

特にTUIアプリケーションでは、罫線「┌ ├ ─ ┘等」を使用して枠線を表現することがあります。これが「Ambiguous」な幅として、英語圏では1文字幅で表示できる様になっているため、2文字幅と解釈して表示しようとすると表示が崩れてしまいます。

ターミナル上で罫線を使用したプログラムがズレる場合はこれが原因です。

runewide0.png runewide0.png

そのための対応として、gnome-terminalではPreferencesから「曖昧幅の文字(W)」を半角/全角で変更出来るようになっています。

ambiguous_width.png ambiguous_width.png

これを半角にすれば、罫線が1文字幅で表示されるため、表示が直ります。

runewide2.png runewide2.png

例えば、psql\pset linestyle unicodeをgnome-terminalで使用するには、ここを半角にしておかないと縦の線が揃わなくなります。

ただし、これはアプリケーションが幅を半角幅と仮定しているのに合わせているだけなので、別のアプリケーションでは合わなくなるといったことが起こります。

go-runewidth のAmbiguous width

go言語では、1文字がターミナルで半角なのか全角なのかを判断するには、go-runewidthで判断するのがデファクトスタンダードになっていると思います。

go-runewidthでは、ロケールに従ってAmbiguousな幅を決定していて日本語を使用している場合(ja_JP.UTF-8等)は、全角(2文字幅)になります。

この場合、上記のgnome-terminalで「曖昧幅の文字(W)」を半角にしていた場合は、余分な隙間が空いて表示が崩れたようになります。 LANG=Cでアプリケーションを起動し直してみて表示が正常になる場合は、この「曖昧幅の文字(W)」とgo-runewidthが解釈するロケールに齟齬が起こっていることになります。

runewide1.png runewide1.png

go-runewideth ではロケールの他に環境変数RUNEWIDTH_EASTASIANによりAmbiguousな幅を変更できますので、gnome-terminalではPreferencesの「曖昧幅の文字(W)」が半角の場合は、環境変数RUNEWIDTH_EASTASIANを0に全角の場合は1に設定しておくとgo言語のTUIアプリケーションの表示の乱れが無くなるのでは無いかと思います。