mirror of
https://github.com/walles/moar.git
synced 2024-08-16 15:30:34 +03:00
parent
4b7c112364
commit
4bd2d70a53
48
m/pager.go
48
m/pager.go
@ -2,6 +2,7 @@ package m
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"regexp"
|
||||
"time"
|
||||
|
||||
@ -59,7 +60,9 @@ type Pager struct {
|
||||
searchString string
|
||||
searchPattern *regexp.Regexp
|
||||
gotoLineString string
|
||||
Following bool
|
||||
|
||||
// We used to have a "Following" field here. If you want to follow, set
|
||||
// TargetLineNumberOneBased to math.MaxInt instead, see below.
|
||||
|
||||
isShowingHelp bool
|
||||
preHelpState *_PreHelpState
|
||||
@ -83,16 +86,20 @@ type Pager struct {
|
||||
|
||||
SideScrollAmount int // Should be positive
|
||||
|
||||
// If non-zero, scroll to this line number as soon as possible. Set to
|
||||
// math.MaxInt to follow the end of the input (tail).
|
||||
TargetLineNumberOneBased int
|
||||
|
||||
// If true, pager will clear the screen on return. If false, pager will
|
||||
// clear the last line, and show the cursor.
|
||||
DeInit bool
|
||||
}
|
||||
|
||||
type _PreHelpState struct {
|
||||
reader *Reader
|
||||
scrollPosition scrollPosition
|
||||
leftColumnZeroBased int
|
||||
following bool
|
||||
reader *Reader
|
||||
scrollPosition scrollPosition
|
||||
leftColumnZeroBased int
|
||||
targetLineNumberOneBased int
|
||||
}
|
||||
|
||||
const _EofMarkerFormat = "\x1b[7m" // Reverse video
|
||||
@ -210,7 +217,7 @@ func (p *Pager) Quit() {
|
||||
p.reader = p.preHelpState.reader
|
||||
p.scrollPosition = p.preHelpState.scrollPosition
|
||||
p.leftColumnZeroBased = p.preHelpState.leftColumnZeroBased
|
||||
p.Following = p.preHelpState.following
|
||||
p.TargetLineNumberOneBased = p.preHelpState.targetLineNumberOneBased
|
||||
p.preHelpState = nil
|
||||
}
|
||||
|
||||
@ -235,11 +242,15 @@ func (p *Pager) moveRight(delta int) {
|
||||
}
|
||||
|
||||
func (p *Pager) handleScrolledUp() {
|
||||
p.Following = false
|
||||
p.TargetLineNumberOneBased = 0
|
||||
}
|
||||
|
||||
func (p *Pager) handleScrolledDown() {
|
||||
p.Following = p.isScrolledToEnd()
|
||||
if p.isScrolledToEnd() {
|
||||
p.TargetLineNumberOneBased = math.MaxInt
|
||||
} else {
|
||||
p.TargetLineNumberOneBased = 0
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Pager) onKey(keyCode twin.KeyCode) {
|
||||
@ -326,15 +337,15 @@ func (p *Pager) onRune(char rune) {
|
||||
case '?':
|
||||
if !p.isShowingHelp {
|
||||
p.preHelpState = &_PreHelpState{
|
||||
reader: p.reader,
|
||||
scrollPosition: p.scrollPosition,
|
||||
leftColumnZeroBased: p.leftColumnZeroBased,
|
||||
following: p.Following,
|
||||
reader: p.reader,
|
||||
scrollPosition: p.scrollPosition,
|
||||
leftColumnZeroBased: p.leftColumnZeroBased,
|
||||
targetLineNumberOneBased: p.TargetLineNumberOneBased,
|
||||
}
|
||||
p.reader = _HelpReader
|
||||
p.scrollPosition = newScrollPosition("Pager scroll position")
|
||||
p.leftColumnZeroBased = 0
|
||||
p.Following = false
|
||||
p.TargetLineNumberOneBased = 0
|
||||
p.isShowingHelp = true
|
||||
}
|
||||
|
||||
@ -539,8 +550,15 @@ func (p *Pager) StartPaging(screen twin.Screen) {
|
||||
return
|
||||
|
||||
case eventMoreLinesAvailable:
|
||||
if p.mode.isViewing() && p.Following {
|
||||
p.scrollToEnd()
|
||||
if p.mode.isViewing() && p.TargetLineNumberOneBased > 0 {
|
||||
// The user wants to scroll down to a specific line number
|
||||
if p.reader.GetLineCount() >= p.TargetLineNumberOneBased {
|
||||
p.scrollPosition = NewScrollPositionFromLineNumberOneBased(p.TargetLineNumberOneBased, "goToTargetLineNumber")
|
||||
p.TargetLineNumberOneBased = 0
|
||||
} else {
|
||||
// Not there yet, keep scrolling
|
||||
p.scrollToEnd()
|
||||
}
|
||||
}
|
||||
|
||||
case eventMaybeDone:
|
||||
|
@ -1,6 +1,9 @@
|
||||
package m
|
||||
|
||||
import "fmt"
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
)
|
||||
|
||||
// Please create using newScrollPosition(name)
|
||||
type scrollPosition struct {
|
||||
@ -305,7 +308,13 @@ func (p *Pager) scrollToEnd() {
|
||||
// lines than the number of characters it contains.
|
||||
p.scrollPosition.internalDontTouch.deltaScreenLines = len(lastInputLine.raw)
|
||||
|
||||
p.Following = true
|
||||
if p.TargetLineNumberOneBased == 0 {
|
||||
// Start following the end of the file
|
||||
//
|
||||
// Otherwise, if we're already aiming for some place, don't overwrite
|
||||
// that.
|
||||
p.TargetLineNumberOneBased = math.MaxInt
|
||||
}
|
||||
}
|
||||
|
||||
// Can be either because Pager.scrollToEnd() was just called or because the user
|
||||
|
@ -117,7 +117,7 @@ func (p *Pager) scrollToNextSearchHit() {
|
||||
p.scrollPosition = *firstHitPosition
|
||||
|
||||
// Don't let any search hit scroll out of sight
|
||||
p.Following = false
|
||||
p.TargetLineNumberOneBased = 0
|
||||
}
|
||||
|
||||
func (p *Pager) scrollToPreviousSearchHit() {
|
||||
@ -155,7 +155,7 @@ func (p *Pager) scrollToPreviousSearchHit() {
|
||||
p.scrollPosition = *firstHitPosition
|
||||
|
||||
// Don't let any search hit scroll out of sight
|
||||
p.Following = false
|
||||
p.TargetLineNumberOneBased = 0
|
||||
}
|
||||
|
||||
func (p *Pager) updateSearchPattern() {
|
||||
|
4
moar.1
4
moar.1
@ -95,6 +95,10 @@ Print trace logs after exiting, more verbose than
|
||||
\fB\-\-wrap\fR
|
||||
Wrap long lines, toggle with
|
||||
.B w
|
||||
.TP
|
||||
\fB\+\1234\fR
|
||||
Immediately scroll to line
|
||||
.B 1234
|
||||
.SH ENVIRONMENT
|
||||
Having
|
||||
.B PAGER=moar
|
||||
|
48
moar.go
48
moar.go
@ -4,6 +4,7 @@ import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
@ -80,6 +81,9 @@ func printUsage(output io.Writer, flagSet *flag.FlagSet, printCommandline bool)
|
||||
_, _ = fmt.Fprintln(output, "Options:")
|
||||
|
||||
flagSet.PrintDefaults()
|
||||
|
||||
_, _ = fmt.Fprintln(output, " +1234")
|
||||
_, _ = fmt.Fprintln(output, " \tImmediately scroll to line 1234")
|
||||
}
|
||||
|
||||
// "moar" if we're in the $PATH, otherwise an absolute path
|
||||
@ -271,6 +275,35 @@ func tryOpen(filename string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Parses an argument like "+123" anywhere on the command line into a one-based
|
||||
// line number, and returns the remaining args.
|
||||
//
|
||||
// Returns 0 on no target line number specified.
|
||||
func getTargetLineNumberOneBased(flagSet *flag.FlagSet) (int, []string) {
|
||||
args := flagSet.Args()
|
||||
for i, arg := range args {
|
||||
if !strings.HasPrefix(arg, "+") {
|
||||
continue
|
||||
}
|
||||
|
||||
lineNumber, err := strconv.ParseInt(arg[1:], 10, 32)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Remove the target line number from the args
|
||||
//
|
||||
// Ref: https://stackoverflow.com/a/57213476/473672
|
||||
remainingArgs := make([]string, 0)
|
||||
remainingArgs = append(remainingArgs, args[:i]...)
|
||||
remainingArgs = append(remainingArgs, args[i+1:]...)
|
||||
|
||||
return int(lineNumber), remainingArgs
|
||||
}
|
||||
|
||||
return 0, args
|
||||
}
|
||||
|
||||
func main() {
|
||||
// FIXME: If we get a CTRL-C, get terminal back into a useful state before terminating
|
||||
|
||||
@ -370,8 +403,10 @@ func main() {
|
||||
TimestampFormat: time.StampMicro,
|
||||
})
|
||||
|
||||
if len(flagSet.Args()) > 1 {
|
||||
fmt.Fprintln(os.Stderr, "ERROR: Expected exactly one filename, or data piped from stdin, got:", flagSet.Args())
|
||||
targetLineNumberOneBased, remainingArgs := getTargetLineNumberOneBased(flagSet)
|
||||
|
||||
if len(remainingArgs) > 1 {
|
||||
fmt.Fprintln(os.Stderr, "ERROR: Expected exactly one filename, or data piped from stdin, got:", remainingArgs)
|
||||
fmt.Fprintln(os.Stderr)
|
||||
printUsage(os.Stderr, flagSet, true)
|
||||
|
||||
@ -381,7 +416,7 @@ func main() {
|
||||
stdinIsRedirected := !term.IsTerminal(int(os.Stdin.Fd()))
|
||||
stdoutIsRedirected := !term.IsTerminal(int(os.Stdout.Fd()))
|
||||
var inputFilename *string
|
||||
if len(flagSet.Args()) == 1 {
|
||||
if len(remainingArgs) == 1 {
|
||||
word := flagSet.Arg(0)
|
||||
inputFilename = &word
|
||||
|
||||
@ -444,7 +479,6 @@ func main() {
|
||||
|
||||
pager := m.NewPager(reader)
|
||||
pager.WrapLongLines = *wrap
|
||||
pager.Following = *follow
|
||||
pager.ShowLineNumbers = !*noLineNumbers
|
||||
pager.ShowStatusBar = !*noStatusBar
|
||||
pager.DeInit = !*noClearOnExit
|
||||
@ -454,6 +488,12 @@ func main() {
|
||||
pager.ScrollLeftHint = *scrollLeftHint
|
||||
pager.ScrollRightHint = *scrollRightHint
|
||||
pager.SideScrollAmount = int(*shift)
|
||||
|
||||
pager.TargetLineNumberOneBased = targetLineNumberOneBased
|
||||
if *follow && pager.TargetLineNumberOneBased == 0 {
|
||||
pager.TargetLineNumberOneBased = math.MaxInt
|
||||
}
|
||||
|
||||
startPaging(pager, screen)
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user