LoginSignup
3
2

More than 5 years have passed since last update.

lolcatやNeo Cowsayみたいに文字を色付けするgolorコマンドをはじめてGo言語を触りながら作った

Last updated at Posted at 2018-02-19

前書き

busyloop/lolcat: Rainbows and unicorns!
に惚れ惚れしながら色々カラフルにしていたのですが、Rubyをインストールするのにちと時間がかかる。
また、短い文字列に色合いを固定してカラフルにするのは少し面倒。

chalk/chalk: 🖍 Terminal string styling done right
にもグラデーションできそうなものがあるが、モジュール式でちょいと面倒そう。

ということでバイナリだけで似たことができるものはないかなと調べたら、
Code-Hex/Neo-cowsay: 🐮 cowsay written in Go
がなかなか良さそうだった。
ただし牛は余計なので、コードを盗みつつ牛をドナドナするように頑張ってみた。

toiletはよくわからなかった。

スクリーンショット

image.png

image.png

パイプとファイル指定が出来れば個人的によし。
ついでに引数渡しも。

完成品

khsk/golor: Rainbows as lolcat

zip落として解凍すれば使えるのは嬉しい。
go get -u github.com/khsk/golor
でも取れる…はず…。

肝心なところはここぐらい

func rainbow(text string, c *cli.Context) string {
    const (
        red = iota + 31
        green
        yellow
        blue
        magenta
        cyan
    )
    rainbow := []int{magenta, red, yellow, green, cyan, blue}
    buf := make([]rune, 0, len(text))

    var line int
    attribute := getVisualDistinction(c)

    i := 0
    for _, char := range text {
        if char == '\n' {
            if c.Int("deviation") != 0 {
                line++
                i = line * c.Int("deviation")
            } else {
                i = 0
            }
            buf = append(buf, char)
            continue
        }
        buf = append(buf, []rune(fmt.Sprintf("\x1b[%d%sm%c\x1b[0m", rainbow[i%6], attribute, char))...)
        i++
    }

    return string(buf)
}

func aurora(text string, c *cli.Context) string {

    buf := make([]rune, 0, len(text))
    attribute := getVisualDistinction(c)
    if c.Int64("seed") == 0 {
        rand.Seed(time.Now().UnixNano())
    } else {
        rand.Seed(c.Int64("seed"))
    }
    i := rand.Intn(256)

    freq := c.Float64("freq")
    spread := c.Float64("spread")

    for _, char := range text {
        if char == '\n' {
            buf = append(buf, char)
            continue
        }

        buf = append(buf, []rune(fmt.Sprintf("\033[38;5;%d%sm%c\033[0m", rgb(float64(i)/spread, freq), attribute, char))...)
        i++
    }

    return string(buf)

}

func rgb(i float64, freq float64) int {
    red := int(6*((math.Sin(freq*i+0)*127+128)/256)) * 36
    green := int(6*((math.Sin(freq*i+2*math.Pi/3)*127+128)/256)) * 6
    blue := int(6*((math.Sin(freq*i+4*math.Pi/3)*127+128)/256)) * 1
    return 16 + red + green + blue
}

(コピペミックスなので式の意味するところがあまりわかっていない)

Goのいいところ

簡単

並列1処理使っていないのでなぜGoと言われそうですが、単純に書きやすい。
A Tour of Go
は最初のHello Worldで面倒になってやめたんですが、
見よう見まねと
golang チートシート - Qiita
などを見てなんとかかんとか動くレベルまで行くのは良かった。

go コマンド便利

go runはまあ普通として、
go fmtのおかげで2スペースインデントとか自由にガシガシ書けた。

特にgo getがよくて、簡単にgithubなどから引っ張ってこれる。
これがパッケージ管理のnpmなど別コマンドじゃなくてgoでできるのが良かったと思った。
…しかしpackage.jsonらしきものはできないので依存パッケージはgo getでは管理不可?
開発者じゃなくて利用者として便利?なのかな?

追記:
Go & Versioning(vgo)を読んで大きな変更が入ったなと思った - Qiita
でバージョン管理できそう。
ただし、vgoとコマンド名が変わっちゃうのはちょっと残念?npmみたいな感じに落ち着くのだろうか。

リリース環境整っている

作ったは良いけれど、環境毎のビルドが標準方法ではかなり不便そうだった。

go buildとクロスコンパイル - Qiita

いちいち環境変数設定するなんてうへーとなったけど、良いものがあった。

mitchellh/gox: A dead simple, no frills Go cross compile tool
golangでクロスコンパイルするならgoxライブラリを使うと便利! - エンジニアはこわくない

自動で全部作ってくれるすごいやつだよ。

…いやいやでもでもGithubのリリース作るの面倒だしな…と凄い不精じみていると、

tcnksm/ghr: Upload multiple artifacts to GitHub Release in parallel
高速に自作パッケージをGithubにリリースするghrというツールをつくった | SOTA

自動でリリースしてアップロードもしてくれるすごいやつだよ。

…いやいやいやいやでもでもでも2つのツール使うなんて面倒だしー!zip化も手動でしなきゃいけないしなー!

gox して ghr するツール ggallin 作った - Qiita

すごい

どんなに面倒くさがりでもこれならリリースできると思います。
できました。
ありがとうございます。

CLIもテンプレがある

これもGoならではではないですが、
urfave/cli: A simple, fast, and fun package for building command line apps in Go
で簡単にCLIが作れるのが良い。
面倒なヘルプも自動で作ってくれる。楽。


Goは面倒くさがりの利用者に優しく、
Goは面倒くさがりの開発者に優しい、
言語・環境なのかなと感じました。

Goのわるいところ(別方法あるのかも)

しっかり触っていないのでなんともですが…
String型から一文字取り出すとRuneになってしまって、Stringを受け付ける処理に(キャストしても)渡せないことが困りました。

最初は一文字ずつ切り取って
GoでANSIエスケープコードを扱うライブラリを作った(色付け・カーソル移動等) - Qiita
を使い色付けしようと考えていたのですが、それで断念。
手動でコードをつけていく方式(それでもコピペ)になりました。

Qiitaのタグ

Go言語の悪いところではないのですが

Gogolangの2つのタグがある…
Goが良さそうですが、2つともつけている記事が多そうです。

参考


バイナリファイル名は恐らくディレクトリ名が使われるので注意。
ggallinは設定したバージョンのプレフィックスとしてvが付くので、バージョンにvを含めるとvv1.0.0となっちゃう。



課題

  • lolcatのようにヘルプに色付け
    カスタマイズしようにもそのまま出力してるっぽいので標準出力をバッファしないといけない?
  • ライブラリ化
    他のコード書くときの出力にも使えたら便利そう
  • もっと楽しいカラフルさの表現

オマケ

fish_prompt.fish
function fish_prompt
    #set_color $fish_color_cwd
    #echo -n (prompt_pwd)
    #set_color normal
    echo -n (prompt_pwd) | golor
    echo -n ' > '
end

image.png

余談

Neo Cowsayという偉大なお手本があったのでGoにしましたが、Nimもなかなか良さそうでした。


  1. 並行? 

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2