diff --git a/cmd/moor/moor.go b/cmd/moor/moor.go index db17a2dd..ea7bb58f 100644 --- a/cmd/moor/moor.go +++ b/cmd/moor/moor.go @@ -131,11 +131,7 @@ func parseStyleOption(styleOption string) (*chroma.Style, error) { func parseColorsOption(colorsOption string) (twin.ColorCount, error) { if strings.ToLower(colorsOption) == "auto" { - colorsOption = "16M" - if os.Getenv("COLORTERM") != "truecolor" && strings.Contains(os.Getenv("TERM"), "256") { - // Covers "xterm-256color" as used by the macOS Terminal - colorsOption = "256" - } + return twin.DefaultColorCount(), nil } switch strings.ToUpper(colorsOption) { @@ -397,7 +393,7 @@ func getVersion() string { // Can return a nil pager on --help or --version, or if pumping to stdout. func pagerFromArgs( args []string, - newScreen func(mouseMode twin.MouseMode, terminalColorCount twin.ColorCount) (twin.Screen, error), + newScreen func(options twin.ScreenOptions) (twin.Screen, error), stdinIsRedirected bool, stdoutIsRedirected bool, ) ( @@ -438,6 +434,8 @@ func pagerFromArgs( flagSet.Bool("no-reformat", true, "No effect, kept for compatibility. See --reformat") quitIfOneScreen := flagSet.Bool("quit-if-one-screen", false, "Don't page if contents fits on one screen. Affected by --no-clear-on-exit-margin.") noClearOnExit := flagSet.Bool("no-clear-on-exit", false, "Retain screen contents when exiting moor") + noAlternateScreen := flagSet.Bool("no-alternate-screen", false, "Don't use the alternate screen buffer (like less -X)") + flagSet.BoolVar(noAlternateScreen, "X", false, "Don't use the alternate screen buffer (like less -X)") noClearOnExitMargin := flagSet.Int("no-clear-on-exit-margin", 1, "Number of lines to leave for your shell prompt, defaults to 1") statusBarStyle := flagSetFunc(flagSet, "statusbar", internal.STATUSBAR_STYLE_INVERSE, @@ -619,7 +617,11 @@ func pagerFromArgs( // We got the first byte, this means sudo is done (if it was used) and we // can set up the UI. - screen, err := newScreen(*mouseMode, *terminalColorsCount) + screen, err := newScreen(twin.ScreenOptions{ + MouseMode: *mouseMode, + ColorCount: *terminalColorsCount, + UseAlternateScreen: !*noAlternateScreen, + }) if err != nil { // Ref: https://github.com/walles/moor/issues/149 log.Info("Failed to set up screen for paging, pumping to stdout instead: ", err) @@ -710,7 +712,7 @@ func main() { pager, screen, style, formatter, _logsRequested, err := pagerFromArgs( os.Args, - twin.NewScreenWithMouseModeAndColorCount, + twin.NewScreenWithOptions, stdinIsRedirected, stdoutIsRedirected, ) diff --git a/cmd/moor/moor_test.go b/cmd/moor/moor_test.go index 3e9328e9..c547f7e0 100644 --- a/cmd/moor/moor_test.go +++ b/cmd/moor/moor_test.go @@ -32,7 +32,7 @@ func TestUnescapeManPn(t *testing.T) { func TestPageOneInputFile(t *testing.T) { pager, screen, _, formatter, _, err := pagerFromArgs( []string{"", "moor_test.go"}, - func(_ twin.MouseMode, _ twin.ColorCount) (twin.Screen, error) { + func(_ twin.ScreenOptions) (twin.Screen, error) { return twin.NewFakeScreen(80, 24), nil }, false, // stdin is redirected diff --git a/moor.1 b/moor.1 index 9083ae7d..928cb0c1 100644 --- a/moor.1 +++ b/moor.1 @@ -60,6 +60,10 @@ Guarantee selecting text with the mouse works but maybe not mouse scrolling. Or guarantee mouse scrolling works but selecting text requiring extra effort. Details here: https://github.com/walles/moor/blob/master/MOUSE.md .TP +\fB\-\-no\-alternate\-screen\fR, \fB\-X\fR +Don't use the alternate screen buffer. This is useful for seeing prompts +written to the main terminal screen by concurrent processes. +.TP \fB\-\-no\-clear\-on\-exit\fR Retain screen contents when exiting moor. Affected by \fB--no-clear-on-exit-margin\fP. diff --git a/pkg/moor/embed-api.go b/pkg/moor/embed-api.go index 688ced4b..d87f5424 100644 --- a/pkg/moor/embed-api.go +++ b/pkg/moor/embed-api.go @@ -44,6 +44,10 @@ type Options struct { // The default is to always start the pager. If this is set to true, short // input will just be printed, and no paging will happen. QuitIfOneScreen bool + + // The default is to use the alternate screen buffer. Set this to true to + // disable the alternate screen buffer. + NoAlternateScreen bool } // PageFromStream reads the contents of the given reader and presents it in a pager. @@ -161,7 +165,11 @@ func pageFromReader(reader *internalReader.ReaderImpl, options Options) error { pager.ShowLineNumbers = !options.NoLineNumbers pager.QuitIfOneScreen = options.QuitIfOneScreen - screen, e := twin.NewScreen() + screen, e := twin.NewScreenWithOptions(twin.ScreenOptions{ + MouseMode: twin.MouseModeAuto, + ColorCount: twin.DefaultColorCount(), + UseAlternateScreen: !options.NoAlternateScreen, + }) if e != nil { // Screen setup failed return e diff --git a/twin/screen.go b/twin/screen.go index fa44738a..7f9b5780 100644 --- a/twin/screen.go +++ b/twin/screen.go @@ -84,6 +84,12 @@ type lastRendered struct { cells [][]StyledRune } +type ScreenOptions struct { + MouseMode MouseMode + ColorCount ColorCount + UseAlternateScreen bool +} + type UnixScreen struct { widthAccessFromSizeOnly int // Access from Size() method only heightAccessFromSizeOnly int // Access from Size() method only @@ -116,6 +122,7 @@ type UnixScreen struct { terminalColorCount ColorCount mouseMode MouseMode + useAlternateScreen bool } // Example event: "\x1b[<65;127;41M" @@ -135,22 +142,34 @@ func NewScreen() (Screen, error) { } func NewScreenWithMouseMode(mouseMode MouseMode) (Screen, error) { - terminalColorCount := ColorCount24bit + return NewScreenWithMouseModeAndColorCount(mouseMode, DefaultColorCount()) +} + +func DefaultColorCount() ColorCount { if os.Getenv("COLORTERM") != "truecolor" && strings.Contains(os.Getenv("TERM"), "256") { // Covers "xterm-256color" as used by the macOS Terminal - terminalColorCount = ColorCount256 + return ColorCount256 } - return NewScreenWithMouseModeAndColorCount(mouseMode, terminalColorCount) + return ColorCount24bit } func NewScreenWithMouseModeAndColorCount(mouseMode MouseMode, terminalColorCount ColorCount) (Screen, error) { + return NewScreenWithOptions(ScreenOptions{ + MouseMode: mouseMode, + ColorCount: terminalColorCount, + UseAlternateScreen: true, + }) +} + +func NewScreenWithOptions(options ScreenOptions) (Screen, error) { if !term.IsTerminal(int(os.Stdout.Fd())) { return nil, fmt.Errorf("stdout (fd=%d) must be a terminal for paging to work", os.Stdout.Fd()) } screen := UnixScreen{ - terminalColorCount: terminalColorCount, - mouseMode: mouseMode, + terminalColorCount: options.ColorCount, + mouseMode: options.MouseMode, + useAlternateScreen: options.UseAlternateScreen, } // The number "80" here is from manual testing on my MacBook: @@ -177,7 +196,7 @@ func NewScreenWithMouseModeAndColorCount(mouseMode MouseMode, terminalColorCount go func() { defer func() { - panicHandler("NewScreenWithMouseModeAndColorCount()/mainLoop()", recover(), debug.Stack()) + panicHandler("NewScreenWithOptions()/mainLoop()", recover(), debug.Stack()) }() screen.mainLoop() @@ -271,7 +290,9 @@ func (screen *UnixScreen) enterAlternateScreenSession() { screen.renderLock.Lock() defer screen.renderLock.Unlock() - screen.setAlternateScreenModeLocked(true) + if screen.useAlternateScreen { + screen.setAlternateScreenModeLocked(true) + } screen.enableMouseTrackingLocked(screen.shouldEnableMouseTracking()) screen.hideCursorLocked(true) @@ -288,7 +309,9 @@ func (screen *UnixScreen) leaveAlternateScreenSession() { screen.writeLocked("\x1b[m") screen.hideCursorLocked(false) screen.enableMouseTrackingLocked(false) - screen.setAlternateScreenModeLocked(false) + if screen.useAlternateScreen { + screen.setAlternateScreenModeLocked(false) + } } func (screen *UnixScreen) shouldEnableMouseTracking() bool {