1
1
mirror of https://github.com/walles/moar.git synced 2024-08-16 15:30:34 +03:00

Add support for +1234 command line option

Fixes #155.
This commit is contained in:
Johan Walles 2023-09-27 20:23:27 +02:00
parent 4b7c112364
commit 4bd2d70a53
5 changed files with 94 additions and 23 deletions

View File

@ -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:

View File

@ -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

View File

@ -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
View File

@ -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
View File

@ -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)
}