1
1
mirror of https://github.com/walles/moar.git synced 2024-09-11 12:15:43 +03:00

Support retaining screen contents after exit

Fixes #76.
This commit is contained in:
Johan Walles 2021-11-09 18:56:02 +01:00
parent 871cf523c0
commit b96c88c4d1
5 changed files with 59 additions and 32 deletions

View File

@ -1,8 +1,6 @@
package m
import (
"fmt"
"github.com/walles/moar/twin"
)
@ -13,20 +11,12 @@ func (p *Pager) Page() error {
// Screen setup failed
return e
}
defer func() {
screen.Close()
if p.DeInit {
return
}
// FIXME: Consider moving this logic into the twin package, or into Pager.
_, height := p.screen.Size()
lines := p.reader.GetLines(p.firstLineOneBased, height-1).lines
for _, line := range lines {
fmt.Println(line.raw)
}
}()
p.StartPaging(screen)
return nil
screen.Close()
if p.DeInit {
return nil
}
return p.ReprintAfterExit()
}

View File

@ -710,3 +710,18 @@ func (p *Pager) StartPaging(screen twin.Screen) {
log.Warnf("Reader reported an error: %s", p.reader.err.Error())
}
}
// After the pager has exited and the normal screen has been restored, you can
// call this method to print the pager contents to screen again, faking
// "leaving" pager contents on screen after exit.
func (p *Pager) ReprintAfterExit() error {
// Figure out how many screen lines are used by pager contents
_, height := p.screen.Size()
heightWithoutFooter := height - 1
lineCount := len(p.reader.GetLines(p.firstLineOneBased, heightWithoutFooter).lines)
if lineCount > 0 {
p.screen.ShowNLines(lineCount)
}
return nil
}

23
moar.go
View File

@ -150,6 +150,7 @@ func main() {
"Highlighting style from https://xyproto.github.io/splash/docs/longer/all.html")
colorsOption := flagSet.String("colors", "16M", "Highlighting palette size: 8, 16, 256, 16M")
noLineNumbers := flagSet.Bool("no-linenumbers", false, "Hide line numbers on startup, press left arrow key to show")
noClearOnExit := flagSet.Bool("no-clear-on-exit", false, "Retain screen contents when exiting moar")
// Combine flags from environment and from command line
flags := os.Args[1:]
@ -246,7 +247,7 @@ func main() {
if stdinIsRedirected {
// Display input pipe contents
reader := m.NewReaderFromStream("", os.Stdin)
startPaging(reader, *wrap, *noLineNumbers)
startPaging(reader, *wrap, *noLineNumbers, *noClearOnExit)
return
}
@ -256,16 +257,21 @@ func main() {
fmt.Fprintf(os.Stderr, "ERROR: %v\n", err)
os.Exit(1)
}
startPaging(reader, *wrap, *noLineNumbers)
startPaging(reader, *wrap, *noLineNumbers, *noClearOnExit)
}
func startPaging(reader *m.Reader, wrapLongLines bool, noLineNumbers bool) {
func startPaging(reader *m.Reader, wrapLongLines bool, noLineNumbers bool, noClearOnExit bool) {
screen, e := twin.NewScreen()
if e != nil {
panic(e)
}
var loglines strings.Builder
log.SetOutput(&loglines)
pager := m.NewPager(reader)
pager.WrapLongLines = wrapLongLines
pager.ShowLineNumbers = !noLineNumbers
defer func() {
// Restore screen...
screen.Close()
@ -276,6 +282,13 @@ func startPaging(reader *m.Reader, wrapLongLines bool, noLineNumbers bool) {
panic(err)
}
if noClearOnExit {
err := pager.ReprintAfterExit()
if err != nil {
log.Error("Failed reprinting pager view after exit", err)
}
}
if len(loglines.String()) > 0 {
printProblemsHeader()
@ -286,9 +299,5 @@ func startPaging(reader *m.Reader, wrapLongLines bool, noLineNumbers bool) {
}
}()
log.SetOutput(&loglines)
pager := m.NewPager(reader)
pager.WrapLongLines = wrapLongLines
pager.ShowLineNumbers = !noLineNumbers
pager.StartPaging(screen)
}

View File

@ -64,6 +64,10 @@ func (screen *FakeScreen) Show() {
// This method intentionally left blank
}
func (screen *FakeScreen) ShowNLines(int) {
// This method intentionally left blank
}
func (screen *FakeScreen) Size() (int, int) {
return screen.width, screen.height
}

View File

@ -25,6 +25,10 @@ type Screen interface {
// Render our contents into the terminal window
Show()
// Can be called after Close()ing the screen to fake retaining its output.
// Plain Show() is what you'd call during normal operation.
ShowNLines(lineCountToShow int)
// Returns screen width and height.
//
// NOTE: Never cache this response! On window resizes you'll get an
@ -444,19 +448,24 @@ func renderLine(row []Cell) (string, int) {
return builder.String(), headerLength
}
// Render our contents into the terminal window
//
// Note that we start by prepping everything we want to write, then write it all
// in one go. This is to make the screen update experience as atomic and flicker
// free as possible.
func (screen *UnixScreen) Show() {
_, height := screen.Size()
screen.showNLines(height, true)
}
func (screen *UnixScreen) ShowNLines(height int) {
screen.showNLines(height, false)
}
func (screen *UnixScreen) showNLines(height int, clearFirst bool) {
var builder strings.Builder
// Start in the top left corner:
// https://en.wikipedia.org/wiki/ANSI_escape_code#CSI_(Control_Sequence_Introducer)_sequences
builder.WriteString("\x1b[1;1H")
if clearFirst {
// Start in the top left corner:
// https://en.wikipedia.org/wiki/ANSI_escape_code#CSI_(Control_Sequence_Introducer)_sequences
builder.WriteString("\x1b[1;1H")
}
_, height := screen.Size()
for row := 0; row < height; row++ {
rendered, lineLength := renderLine(screen.cells[row])
builder.WriteString(rendered)