diff --git a/go.mod b/go.mod index c99a742..5bf35ef 100644 --- a/go.mod +++ b/go.mod @@ -4,5 +4,8 @@ go 1.12 require ( github.com/gdamore/tcell v1.1.2 + github.com/google/go-cmp v0.3.0 // indirect + github.com/pkg/errors v0.8.1 // indirect golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5 + gotest.tools v2.2.0+incompatible ) diff --git a/go.sum b/go.sum index 36b3aee..62c2713 100644 --- a/go.sum +++ b/go.sum @@ -3,10 +3,14 @@ github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdk github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= github.com/gdamore/tcell v1.1.2 h1:Afe8cU6SECC06UmvaJ55Jr3Eh0tz/ywLjqWYqjGZp3s= github.com/gdamore/tcell v1.1.2/go.mod h1:h3kq4HO9l2On+V9ed8w8ewqQEmGCSSHOgQ+2h8uzurE= +github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/lucasb-eyer/go-colorful v1.0.2 h1:mCMFu6PgSozg9tDNMMK3g18oJBX7oYGrC09mS6CXfO4= github.com/lucasb-eyer/go-colorful v1.0.2/go.mod h1:0MS4r+7BZKSJ5mw4/S5MPN+qHFF1fYclkSPilDOKW0s= github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5 h1:58fnuSXlxZmFdJyvtTFVmVhcMLU6v5fEb/ok4wyqtNU= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -18,3 +22,5 @@ golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c h1:qgOY6WgZOaTkIIMiVjBQcw93 golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= +gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= diff --git a/m/pager.go b/m/pager.go index cb6245c..f8f8d55 100644 --- a/m/pager.go +++ b/m/pager.go @@ -50,7 +50,7 @@ func (p *_Pager) _AddLines(logger *log.Logger) { pos := 0 footerStyle := tcell.StyleDefault.Reverse(true) - for _, token := range "Press ESC / q to exit" { + for _, token := range lines.statusText + " Press ESC / q to exit" { p.screen.SetContent(pos, height-1, token, nil, footerStyle) pos++ } @@ -168,6 +168,9 @@ func (p *_Pager) StartPaging(logger *log.Logger, screen tcell.Screen) { logger.Printf("Unhandled event type: %v", ev) } + // FIXME: If more events are ready, skip this redraw, that + // should speed up mouse wheel scrolling + p._Redraw(logger) } } diff --git a/m/reader.go b/m/reader.go index be97f4a..6f71acc 100644 --- a/m/reader.go +++ b/m/reader.go @@ -2,9 +2,12 @@ package m import ( "bufio" + "fmt" "io" + "math" "os" "os/exec" + "path" ) // Reader reads a file into an array of strings. @@ -18,20 +21,24 @@ import ( // FIXME: Make the reader read in the background, independently of what the pager is showing type Reader struct { lines []string - name string + name *string } // Lines contains a number of lines from the reader, plus metadata type Lines struct { lines []string - // One-based number of the first of the lines + // One-based line number of the first line returned firstLineOneBased int + + // "monkey.txt: 1-23/45 51%" + statusText string } // NewReaderFromStream creates a new stream reader func NewReaderFromStream(reader io.Reader) (*Reader, error) { // FIXME: Close the stream when done reading it? + // FIXME: If we have a filter process, wait for that after done reading the stream scanner := bufio.NewScanner(reader) var lines []string for scanner.Scan() { @@ -84,10 +91,30 @@ func NewReaderFromFilename(filename string) (*Reader, error) { return nil, err } - reader.name = filename + reader.name = &filename return reader, err } +func (r *Reader) _CreateStatus(firstLineOneBased int, lastLineOneBased int) string { + prefix := "" + if r.name != nil { + prefix = path.Base(*r.name) + ": " + } + + if len(r.lines) == 0 { + return prefix + "" + } + + percent := int(math.Floor(100.0 * float64(lastLineOneBased) / float64(len(r.lines)))) + + return fmt.Sprintf("%s%d-%d/%d %d%%", + prefix, + firstLineOneBased, + lastLineOneBased, + len(r.lines), + percent) +} + // GetLines gets the indicated lines from the input func (r *Reader) GetLines(firstLineOneBased int, wantedLineCount int) *Lines { if firstLineOneBased < 1 { @@ -100,6 +127,8 @@ func (r *Reader) GetLines(firstLineOneBased int, wantedLineCount int) *Lines { // FIXME: What line number should we set here? firstLineOneBased: firstLineOneBased, + + statusText: r._CreateStatus(0, 0), } } @@ -125,5 +154,6 @@ func (r *Reader) GetLines(firstLineOneBased int, wantedLineCount int) *Lines { return &Lines{ lines: r.lines[firstLineZeroBased : lastLineZeroBased+1], firstLineOneBased: firstLineOneBased, + statusText: r._CreateStatus(firstLineOneBased, lastLineZeroBased+1), } } diff --git a/m/reader_test.go b/m/reader_test.go index 33bf4bc..ee62000 100644 --- a/m/reader_test.go +++ b/m/reader_test.go @@ -5,11 +5,14 @@ import ( "math" "path" "runtime" + "strings" "testing" + + "gotest.tools/assert" ) func _TestGetLines(t *testing.T, reader *Reader) { - t.Logf("Testing file: %s...", reader.name) + t.Logf("Testing file: %s...", *reader.name) lines := reader.GetLines(1, 10) if len(lines.lines) > 10 { @@ -99,3 +102,39 @@ func TestGetLines(t *testing.T) { _TestGetLines(t, reader) } } + +func _GetReaderWithLineCount(totalLines int) *Reader { + reader, err := NewReaderFromStream(strings.NewReader(strings.Repeat("x\n", totalLines))) + if err != nil { + panic(err) + } + return reader +} + +func _TestStatusText(t *testing.T, fromLine int, toLine int, totalLines int, expected string) { + testMe := _GetReaderWithLineCount(totalLines) + linesRequested := toLine - fromLine + 1 + statusText := testMe.GetLines(fromLine, linesRequested).statusText + assert.Equal(t, statusText, expected) +} + +func TestStatusText(t *testing.T) { + _TestStatusText(t, 1, 10, 20, "1-10/20 50%") + _TestStatusText(t, 1, 5, 5, "1-5/5 100%") + _TestStatusText(t, 998, 999, 1000, "998-999/1000 99%") + + _TestStatusText(t, 0, 0, 0, "") + _TestStatusText(t, 1, 1, 1, "1-1/1 100%") + + // Test with filename + testMe, err := NewReaderFromFilename("/dev/null") + if err != nil { + panic(err) + } + statusText := testMe.GetLines(0, 0).statusText + assert.Equal(t, statusText, "null: ") +} + +// FIXME: Add test for opening .gz files +// FIXME: Add test for opening .xz files +// FIXME: Add test for opening .bz2 files diff --git a/moar.go b/moar.go index 74217a7..9371f2c 100644 --- a/moar.go +++ b/moar.go @@ -36,6 +36,8 @@ func main() { // FIXME: Support --version + // FIXME: Support --no-highlight + if len(os.Args) != 2 { // FIXME: Improve this message fmt.Fprintf(os.Stderr, "ERROR: Expected exactly one parameter, got: %v\n", os.Args[1:])