標準愚痴出力

個人的なIT作業ログです。もしかしたら一般的に参考になることが書いているかもしれません(弱気

go-readline-ny:カラー化の手引き

(2023/04/14:追記)
v0.10.0 にて非互換性を含む修正を行ったため、その変更点を反映しました。


go-readline-ny は v0.6.1 から文字ごとに色を変更できるようになりました。

この配色は固定ではなく、readline.Editor のフィールド Coloring に設定されたオブジェクトに問い合わせて決定するようになっています。同オブジェクトは、次のインターフェイスを満たしている必要があります。

type Coloring interface {
    // Reset has to initialize receiver's fields and return default color.
    Init() ColorSequence // <-- v0.10.0 にて int より変更
    // Next has to return color for the given rune.
    Next(rune) ColorSequence // <-- v0.10.0 にて int より変更
}

※ v0.10.0 より、戻り値を int より readline.ColorSequence (実態は int64)に変更しました。

このインターフェイスは次のように使われます(コードはイメージです。本物はもっとゴチャゴチャしてます)

func (editor *Editor) printLine(){
    X = editor.Coloring
    defaultColor := X.Init()
    last := ColorSequence(-1)
    for i, r := range テキスト全体 {
        c := X.Next(r) // 表示範囲外でも二重引用符の内外など判断をNextの中ですることがあるので一応呼ばれる
        if 表示開始位置 <= i && i < 表示終了位置 {
            if c != last {
                colorSet(c) ; last = c
            }
            printRune(r)
        }
    }
    if last != defaultColor {
        colorSet(defaultColor) // 表示が終わったあと、標準色に戻すため Init の戻り値が使われる
    }
}

Init や Next で返すべき色コード cANSIエスケープシーケンスの SGR、いわゆる 「ESC[𝑛₁;𝑛₂;...m」の 数列 𝑛ₖ を一つの 32bit 64bit コードにパックしたコードになります。パックには SGR1~SGR4 という関数を用います。

  • ESC[39mreadline.SGR1(39)
  • ESC[1;39mreadline.SGR2(1, 39)
  • ESC[37;22;41mreadline.SGR3(37, 22, 41)

また、よく使うものについては readline の本体側でも readline.Black , readline.Red というような形で定義しています。

これを実際に実装したものは

になります。VimBatch では

  • デフォルトは端末デフォルト文字色+端末デフォルト背景色+高輝度(ESC[1;49;39m
  • 全角空白\u3000 は文字色「白」、背景色「赤」
  • 環境変数 %% の間はシアン
  • 文字列 "" の間はマゼンダ
  • & は暗い黄色

という4種類のみを定義しています(以下、VimBatch の定義)

こうして実装した Coloring オブジェクトは、github.com/nyaosorg/go-readline-ny/examples/example2.go のように設定しておけばOKです。