はじめに
pecoのソースコード読みが少し進んだので、そのことについて少し書きたいと思います。
どんなふうに進んだのかといいますと、pecoパッケージにBufferReaderという構造体があるのですが、この構造体を使わずに標準入力を内部状態に保存してみました。そして、pecoと同じような動作になるようにがんばってみました。
少し期待しない結果になっていますが、一応、同じような動作になっています。
実装
昨日のjessevdk/go-flagsを使ってみるに引き続き、サンプルとして少し実装しました。
ソースコードのほとんどの部分はpeco.goのソースコードです。
変更しているところはいくつかあります。
まず、オプションの機能がありません。main関数をみていただければ納得していただけると思います。
次に、ファイル操作がありません。ターゲットは標準入力だけです。しかも、元のエラー処理をほとんど壊してしまっている可能性が高いです。
そして、私がコードを追加したところは、コメントで「ここから」「ここまで」となっているところと、peco/ctx.goです。peco/ctx.goにAppendLine関数を追加しました。
package main
import (
"bufio"
"fmt"
"os"
"runtime"
"github.com/jessevdk/go-flags"
"github.com/nsf/termbox-go"
"github.com/peco/peco"
)
type cmdOptions struct {
OptHelp bool `short:"h" long:"help"`
OptTTY string `long:"tty"`
OptQuery string `long:"query"`
OptRcfile string `long:"rcfile"`
OptNoIgnoreCase bool `long:"no-ignore-case"`
OptVersion bool `long:"version"`
OptBufferSize int `long:"buffer-size" short:"b"`
OptEnableNullSep bool `long:"null"`
OptInitialIndex int `long:"initial-index"`
OptInitialMatcher string `long:"initial-matcher"`
OptPrompt string `long:"prompt"`
}
// BufferSize returns the specified buffer size. Fulfills peco.CtxOptions
func (o cmdOptions) BufferSize() int {
return o.OptBufferSize
}
// EnableNullSep returns tru if --null was specified. Fulfills peco.CtxOptions
func (o cmdOptions) EnableNullSep() bool {
return o.OptEnableNullSep
}
func (o cmdOptions) InitialIndex() int {
if o.OptInitialIndex >= 0 {
return o.OptInitialIndex + 1
}
return 1
}
func main() {
opts := &cmdOptions{}
p := flags.NewParser(opts, flags.PrintErrors)
p.Parse()
ctx := peco.NewCtx(opts)
defer func() {
if err := recover(); err != nil {
fmt.Fprintf(os.Stderr, "Error:\n%s", err)
}
if result := ctx.Result(); result != nil {
for _, match := range result {
line := match.Output()
if line[len(line)-1] != '\n' {
line = line + "\n"
}
fmt.Fprint(os.Stdout, line)
}
}
}()
// ここから
in := os.Stdin
scanner := bufio.NewScanner(in)
for scanner.Scan() {
// fmt.Println(scanner.Text())
// ctx.lines = append(ctx.lines, NewNoMatch(scanner.Text(), ctx.enableSep))
ctx.AppendLine(scanner.Text())
}
// ここまで
var err error
err = peco.TtyReady()
if err != nil {
fmt.Fprintln(os.Stderr, err)
return
}
defer peco.TtyTerm()
err = termbox.Init()
if err != nil {
fmt.Fprintln(os.Stderr, err)
return
}
defer termbox.Close()
// Windows handle Esc/Alt self
if runtime.GOOS == "windows" {
termbox.SetInputMode(termbox.InputEsc | termbox.InputAlt)
}
view := ctx.NewView()
filter := ctx.NewFilter()
input := ctx.NewInput()
sig := ctx.NewSignalHandler()
loopers := []interface {
Loop()
}{
view,
filter,
input,
sig,
}
for _, looper := range loopers {
ctx.AddWaitGroup(1)
go looper.Loop()
}
ctx.WaitDone()
}
func (c *Ctx) AppendLine(line string) {
c.lines = append(c.lines, NewNoMatch(line, c.enableSep))
}
だめだろうと思いながらpeco/ctx.goに関数を追加しました。関数を追加して、次のようにコマンドラインを実行しました。
$ go get github.com/peco/peco
$ go build sample.go
$ ls | ./sample
真っ暗な画面になりました。やっぱりだめだったかと思いながら、キーボードをタイプすると、pecoと同じような画面に!!
まとめ
カーネルハッカー・小崎資広の「コードを読む技術」を読んで、目からウロコでした。
この記事を読んでコンプレックスみたいなものが少し解消されました。
記事の内容と少し違うかもしれませんが、今回みたいな感じでコード・リーディングを続けたいと思います。