mirror of
https://github.com/walles/moar.git
synced 2024-10-26 21:13:11 +03:00
Show file name and line numbers
This commit is contained in:
parent
007112e896
commit
70500056af
3
go.mod
3
go.mod
@ -4,5 +4,8 @@ go 1.12
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/gdamore/tcell v1.1.2
|
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
|
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5
|
||||||
|
gotest.tools v2.2.0+incompatible
|
||||||
)
|
)
|
||||||
|
6
go.sum
6
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/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 h1:Afe8cU6SECC06UmvaJ55Jr3Eh0tz/ywLjqWYqjGZp3s=
|
||||||
github.com/gdamore/tcell v1.1.2/go.mod h1:h3kq4HO9l2On+V9ed8w8ewqQEmGCSSHOgQ+2h8uzurE=
|
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 h1:mCMFu6PgSozg9tDNMMK3g18oJBX7oYGrC09mS6CXfO4=
|
||||||
github.com/lucasb-eyer/go-colorful v1.0.2/go.mod h1:0MS4r+7BZKSJ5mw4/S5MPN+qHFF1fYclkSPilDOKW0s=
|
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 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
|
||||||
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
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-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 h1:58fnuSXlxZmFdJyvtTFVmVhcMLU6v5fEb/ok4wyqtNU=
|
||||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
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.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 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
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=
|
||||||
|
@ -50,7 +50,7 @@ func (p *_Pager) _AddLines(logger *log.Logger) {
|
|||||||
|
|
||||||
pos := 0
|
pos := 0
|
||||||
footerStyle := tcell.StyleDefault.Reverse(true)
|
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)
|
p.screen.SetContent(pos, height-1, token, nil, footerStyle)
|
||||||
pos++
|
pos++
|
||||||
}
|
}
|
||||||
@ -168,6 +168,9 @@ func (p *_Pager) StartPaging(logger *log.Logger, screen tcell.Screen) {
|
|||||||
logger.Printf("Unhandled event type: %v", ev)
|
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)
|
p._Redraw(logger)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
36
m/reader.go
36
m/reader.go
@ -2,9 +2,12 @@ package m
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"math"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"path"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Reader reads a file into an array of strings.
|
// 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
|
// FIXME: Make the reader read in the background, independently of what the pager is showing
|
||||||
type Reader struct {
|
type Reader struct {
|
||||||
lines []string
|
lines []string
|
||||||
name string
|
name *string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lines contains a number of lines from the reader, plus metadata
|
// Lines contains a number of lines from the reader, plus metadata
|
||||||
type Lines struct {
|
type Lines struct {
|
||||||
lines []string
|
lines []string
|
||||||
|
|
||||||
// One-based number of the first of the lines
|
// One-based line number of the first line returned
|
||||||
firstLineOneBased int
|
firstLineOneBased int
|
||||||
|
|
||||||
|
// "monkey.txt: 1-23/45 51%"
|
||||||
|
statusText string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewReaderFromStream creates a new stream reader
|
// NewReaderFromStream creates a new stream reader
|
||||||
func NewReaderFromStream(reader io.Reader) (*Reader, error) {
|
func NewReaderFromStream(reader io.Reader) (*Reader, error) {
|
||||||
// FIXME: Close the stream when done reading it?
|
// 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)
|
scanner := bufio.NewScanner(reader)
|
||||||
var lines []string
|
var lines []string
|
||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
@ -84,10 +91,30 @@ func NewReaderFromFilename(filename string) (*Reader, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
reader.name = filename
|
reader.name = &filename
|
||||||
return reader, err
|
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 + "<empty>"
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
// GetLines gets the indicated lines from the input
|
||||||
func (r *Reader) GetLines(firstLineOneBased int, wantedLineCount int) *Lines {
|
func (r *Reader) GetLines(firstLineOneBased int, wantedLineCount int) *Lines {
|
||||||
if firstLineOneBased < 1 {
|
if firstLineOneBased < 1 {
|
||||||
@ -100,6 +127,8 @@ func (r *Reader) GetLines(firstLineOneBased int, wantedLineCount int) *Lines {
|
|||||||
|
|
||||||
// FIXME: What line number should we set here?
|
// FIXME: What line number should we set here?
|
||||||
firstLineOneBased: firstLineOneBased,
|
firstLineOneBased: firstLineOneBased,
|
||||||
|
|
||||||
|
statusText: r._CreateStatus(0, 0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,5 +154,6 @@ func (r *Reader) GetLines(firstLineOneBased int, wantedLineCount int) *Lines {
|
|||||||
return &Lines{
|
return &Lines{
|
||||||
lines: r.lines[firstLineZeroBased : lastLineZeroBased+1],
|
lines: r.lines[firstLineZeroBased : lastLineZeroBased+1],
|
||||||
firstLineOneBased: firstLineOneBased,
|
firstLineOneBased: firstLineOneBased,
|
||||||
|
statusText: r._CreateStatus(firstLineOneBased, lastLineZeroBased+1),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,11 +5,14 @@ import (
|
|||||||
"math"
|
"math"
|
||||||
"path"
|
"path"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"gotest.tools/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func _TestGetLines(t *testing.T, reader *Reader) {
|
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)
|
lines := reader.GetLines(1, 10)
|
||||||
if len(lines.lines) > 10 {
|
if len(lines.lines) > 10 {
|
||||||
@ -99,3 +102,39 @@ func TestGetLines(t *testing.T) {
|
|||||||
_TestGetLines(t, reader)
|
_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, "<empty>")
|
||||||
|
_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: <empty>")
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: Add test for opening .gz files
|
||||||
|
// FIXME: Add test for opening .xz files
|
||||||
|
// FIXME: Add test for opening .bz2 files
|
||||||
|
2
moar.go
2
moar.go
@ -36,6 +36,8 @@ func main() {
|
|||||||
|
|
||||||
// FIXME: Support --version
|
// FIXME: Support --version
|
||||||
|
|
||||||
|
// FIXME: Support --no-highlight
|
||||||
|
|
||||||
if len(os.Args) != 2 {
|
if len(os.Args) != 2 {
|
||||||
// FIXME: Improve this message
|
// FIXME: Improve this message
|
||||||
fmt.Fprintf(os.Stderr, "ERROR: Expected exactly one parameter, got: %v\n", os.Args[1:])
|
fmt.Fprintf(os.Stderr, "ERROR: Expected exactly one parameter, got: %v\n", os.Args[1:])
|
||||||
|
Loading…
Reference in New Issue
Block a user