Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(output): export output writer #122

Merged
merged 1 commit into from
Aug 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 18 additions & 8 deletions output.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
)

// File represents a file descriptor.
//
// Deprecated: Use *os.File instead.
type File interface {
io.ReadWriter
Fd() uintptr
Expand All @@ -23,7 +25,7 @@
// Output is a terminal output.
type Output struct {
Profile
tty io.Writer
w io.Writer
environ Environ

assumeTTY bool
Expand Down Expand Up @@ -61,10 +63,10 @@
output = o
}

// NewOutput returns a new Output for the given file descriptor.
func NewOutput(tty io.Writer, opts ...OutputOption) *Output {
// NewOutput returns a new Output for the given writer.
func NewOutput(w io.Writer, opts ...OutputOption) *Output {
o := &Output{
tty: tty,
w: w,
environ: &osEnviron{},
Profile: -1,
fgSync: &sync.Once{},
Expand All @@ -73,8 +75,8 @@
bgColor: NoColor{},
}

if o.tty == nil {
o.tty = os.Stdout
if o.w == nil {
o.w = os.Stdout
}
for _, opt := range opts {
opt(o)
Expand Down Expand Up @@ -180,15 +182,23 @@

// TTY returns the terminal's file descriptor. This may be nil if the output is
// not a terminal.
//
// Deprecated: Use Writer() instead.
func (o Output) TTY() File {
if f, ok := o.tty.(File); ok {
if f, ok := o.w.(File); ok {
return f
}
return nil
}

// Writer returns the underlying writer. This may be of type io.Writer,
// io.ReadWriter, or *os.File.
func (o Output) Writer() io.Writer {
return o.w
}

func (o Output) Write(p []byte) (int, error) {
return o.tty.Write(p)
return o.w.Write(p)

Check failure on line 201 in output.go

View workflow job for this annotation

GitHub Actions / lint-soft

error returned from interface method should be wrapped: sig: func (io.Writer).Write(p []byte) (n int, err error) (wrapcheck)
}

// WriteString writes the given string to the output.
Expand Down
88 changes: 44 additions & 44 deletions screen.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,234 +71,234 @@

// Reset the terminal to its default style, removing any active styles.
func (o Output) Reset() {
fmt.Fprint(o.tty, CSI+ResetSeq+"m")
fmt.Fprint(o.w, CSI+ResetSeq+"m")
}

// SetForegroundColor sets the default foreground color.
func (o Output) SetForegroundColor(color Color) {
fmt.Fprintf(o.tty, OSC+SetForegroundColorSeq, color)
fmt.Fprintf(o.w, OSC+SetForegroundColorSeq, color)
}

// SetBackgroundColor sets the default background color.
func (o Output) SetBackgroundColor(color Color) {
fmt.Fprintf(o.tty, OSC+SetBackgroundColorSeq, color)
fmt.Fprintf(o.w, OSC+SetBackgroundColorSeq, color)
}

// SetCursorColor sets the cursor color.
func (o Output) SetCursorColor(color Color) {
fmt.Fprintf(o.tty, OSC+SetCursorColorSeq, color)
fmt.Fprintf(o.w, OSC+SetCursorColorSeq, color)
}

// RestoreScreen restores a previously saved screen state.
func (o Output) RestoreScreen() {
fmt.Fprint(o.tty, CSI+RestoreScreenSeq)
fmt.Fprint(o.w, CSI+RestoreScreenSeq)
}

// SaveScreen saves the screen state.
func (o Output) SaveScreen() {
fmt.Fprint(o.tty, CSI+SaveScreenSeq)
fmt.Fprint(o.w, CSI+SaveScreenSeq)
}

// AltScreen switches to the alternate screen buffer. The former view can be
// restored with ExitAltScreen().
func (o Output) AltScreen() {
fmt.Fprint(o.tty, CSI+AltScreenSeq)
fmt.Fprint(o.w, CSI+AltScreenSeq)
}

// ExitAltScreen exits the alternate screen buffer and returns to the former
// terminal view.
func (o Output) ExitAltScreen() {
fmt.Fprint(o.tty, CSI+ExitAltScreenSeq)
fmt.Fprint(o.w, CSI+ExitAltScreenSeq)
}

// ClearScreen clears the visible portion of the terminal.
func (o Output) ClearScreen() {
fmt.Fprintf(o.tty, CSI+EraseDisplaySeq, 2)
fmt.Fprintf(o.w, CSI+EraseDisplaySeq, 2)

Check failure on line 116 in screen.go

View workflow job for this annotation

GitHub Actions / lint-soft

mnd: Magic number: 2, in <argument> detected (gomnd)
o.MoveCursor(1, 1)
}

// MoveCursor moves the cursor to a given position.
func (o Output) MoveCursor(row int, column int) {
fmt.Fprintf(o.tty, CSI+CursorPositionSeq, row, column)
fmt.Fprintf(o.w, CSI+CursorPositionSeq, row, column)
}

// HideCursor hides the cursor.
func (o Output) HideCursor() {
fmt.Fprint(o.tty, CSI+HideCursorSeq)
fmt.Fprint(o.w, CSI+HideCursorSeq)
}

// ShowCursor shows the cursor.
func (o Output) ShowCursor() {
fmt.Fprint(o.tty, CSI+ShowCursorSeq)
fmt.Fprint(o.w, CSI+ShowCursorSeq)
}

// SaveCursorPosition saves the cursor position.
func (o Output) SaveCursorPosition() {
fmt.Fprint(o.tty, CSI+SaveCursorPositionSeq)
fmt.Fprint(o.w, CSI+SaveCursorPositionSeq)
}

// RestoreCursorPosition restores a saved cursor position.
func (o Output) RestoreCursorPosition() {
fmt.Fprint(o.tty, CSI+RestoreCursorPositionSeq)
fmt.Fprint(o.w, CSI+RestoreCursorPositionSeq)
}

// CursorUp moves the cursor up a given number of lines.
func (o Output) CursorUp(n int) {
fmt.Fprintf(o.tty, CSI+CursorUpSeq, n)
fmt.Fprintf(o.w, CSI+CursorUpSeq, n)
}

// CursorDown moves the cursor down a given number of lines.
func (o Output) CursorDown(n int) {
fmt.Fprintf(o.tty, CSI+CursorDownSeq, n)
fmt.Fprintf(o.w, CSI+CursorDownSeq, n)
}

// CursorForward moves the cursor up a given number of lines.
func (o Output) CursorForward(n int) {
fmt.Fprintf(o.tty, CSI+CursorForwardSeq, n)
fmt.Fprintf(o.w, CSI+CursorForwardSeq, n)
}

// CursorBack moves the cursor backwards a given number of cells.
func (o Output) CursorBack(n int) {
fmt.Fprintf(o.tty, CSI+CursorBackSeq, n)
fmt.Fprintf(o.w, CSI+CursorBackSeq, n)
}

// CursorNextLine moves the cursor down a given number of lines and places it at
// the beginning of the line.
func (o Output) CursorNextLine(n int) {
fmt.Fprintf(o.tty, CSI+CursorNextLineSeq, n)
fmt.Fprintf(o.w, CSI+CursorNextLineSeq, n)
}

// CursorPrevLine moves the cursor up a given number of lines and places it at
// the beginning of the line.
func (o Output) CursorPrevLine(n int) {
fmt.Fprintf(o.tty, CSI+CursorPreviousLineSeq, n)
fmt.Fprintf(o.w, CSI+CursorPreviousLineSeq, n)
}

// ClearLine clears the current line.
func (o Output) ClearLine() {
fmt.Fprint(o.tty, CSI+EraseEntireLineSeq)
fmt.Fprint(o.w, CSI+EraseEntireLineSeq)
}

// ClearLineLeft clears the line to the left of the cursor.
func (o Output) ClearLineLeft() {
fmt.Fprint(o.tty, CSI+EraseLineLeftSeq)
fmt.Fprint(o.w, CSI+EraseLineLeftSeq)
}

// ClearLineRight clears the line to the right of the cursor.
func (o Output) ClearLineRight() {
fmt.Fprint(o.tty, CSI+EraseLineRightSeq)
fmt.Fprint(o.w, CSI+EraseLineRightSeq)
}

// ClearLines clears a given number of lines.
func (o Output) ClearLines(n int) {
clearLine := fmt.Sprintf(CSI+EraseLineSeq, 2)
cursorUp := fmt.Sprintf(CSI+CursorUpSeq, 1)
fmt.Fprint(o.tty, clearLine+strings.Repeat(cursorUp+clearLine, n))
fmt.Fprint(o.w, clearLine+strings.Repeat(cursorUp+clearLine, n))
}

// ChangeScrollingRegion sets the scrolling region of the terminal.
func (o Output) ChangeScrollingRegion(top, bottom int) {
fmt.Fprintf(o.tty, CSI+ChangeScrollingRegionSeq, top, bottom)
fmt.Fprintf(o.w, CSI+ChangeScrollingRegionSeq, top, bottom)
}

// InsertLines inserts the given number of lines at the top of the scrollable
// region, pushing lines below down.
func (o Output) InsertLines(n int) {
fmt.Fprintf(o.tty, CSI+InsertLineSeq, n)
fmt.Fprintf(o.w, CSI+InsertLineSeq, n)
}

// DeleteLines deletes the given number of lines, pulling any lines in
// the scrollable region below up.
func (o Output) DeleteLines(n int) {
fmt.Fprintf(o.tty, CSI+DeleteLineSeq, n)
fmt.Fprintf(o.w, CSI+DeleteLineSeq, n)
}

// EnableMousePress enables X10 mouse mode. Button press events are sent only.
func (o Output) EnableMousePress() {
fmt.Fprint(o.tty, CSI+EnableMousePressSeq)
fmt.Fprint(o.w, CSI+EnableMousePressSeq)
}

// DisableMousePress disables X10 mouse mode.
func (o Output) DisableMousePress() {
fmt.Fprint(o.tty, CSI+DisableMousePressSeq)
fmt.Fprint(o.w, CSI+DisableMousePressSeq)
}

// EnableMouse enables Mouse Tracking mode.
func (o Output) EnableMouse() {
fmt.Fprint(o.tty, CSI+EnableMouseSeq)
fmt.Fprint(o.w, CSI+EnableMouseSeq)
}

// DisableMouse disables Mouse Tracking mode.
func (o Output) DisableMouse() {
fmt.Fprint(o.tty, CSI+DisableMouseSeq)
fmt.Fprint(o.w, CSI+DisableMouseSeq)
}

// EnableMouseHilite enables Hilite Mouse Tracking mode.
func (o Output) EnableMouseHilite() {
fmt.Fprint(o.tty, CSI+EnableMouseHiliteSeq)
fmt.Fprint(o.w, CSI+EnableMouseHiliteSeq)
}

// DisableMouseHilite disables Hilite Mouse Tracking mode.
func (o Output) DisableMouseHilite() {
fmt.Fprint(o.tty, CSI+DisableMouseHiliteSeq)
fmt.Fprint(o.w, CSI+DisableMouseHiliteSeq)
}

// EnableMouseCellMotion enables Cell Motion Mouse Tracking mode.
func (o Output) EnableMouseCellMotion() {
fmt.Fprint(o.tty, CSI+EnableMouseCellMotionSeq)
fmt.Fprint(o.w, CSI+EnableMouseCellMotionSeq)
}

// DisableMouseCellMotion disables Cell Motion Mouse Tracking mode.
func (o Output) DisableMouseCellMotion() {
fmt.Fprint(o.tty, CSI+DisableMouseCellMotionSeq)
fmt.Fprint(o.w, CSI+DisableMouseCellMotionSeq)
}

// EnableMouseAllMotion enables All Motion Mouse mode.
func (o Output) EnableMouseAllMotion() {
fmt.Fprint(o.tty, CSI+EnableMouseAllMotionSeq)
fmt.Fprint(o.w, CSI+EnableMouseAllMotionSeq)
}

// DisableMouseAllMotion disables All Motion Mouse mode.
func (o Output) DisableMouseAllMotion() {
fmt.Fprint(o.tty, CSI+DisableMouseAllMotionSeq)
fmt.Fprint(o.w, CSI+DisableMouseAllMotionSeq)
}

// EnableMouseExtendedMotion enables Extended Mouse mode (SGR). This should be
// enabled in conjunction with EnableMouseCellMotion, and EnableMouseAllMotion.
func (o Output) EnableMouseExtendedMode() {
fmt.Fprint(o.tty, CSI+EnableMouseExtendedModeSeq)
fmt.Fprint(o.w, CSI+EnableMouseExtendedModeSeq)
}

// DisableMouseExtendedMotion disables Extended Mouse mode (SGR).
func (o Output) DisableMouseExtendedMode() {
fmt.Fprint(o.tty, CSI+DisableMouseExtendedModeSeq)
fmt.Fprint(o.w, CSI+DisableMouseExtendedModeSeq)
}

// EnableMousePixelsMotion enables Pixel Motion Mouse mode (SGR-Pixels). This
// should be enabled in conjunction with EnableMouseCellMotion, and
// EnableMouseAllMotion.
func (o Output) EnableMousePixelsMode() {
fmt.Fprint(o.tty, CSI+EnableMousePixelsModeSeq)
fmt.Fprint(o.w, CSI+EnableMousePixelsModeSeq)
}

// DisableMousePixelsMotion disables Pixel Motion Mouse mode (SGR-Pixels).
func (o Output) DisableMousePixelsMode() {
fmt.Fprint(o.tty, CSI+DisableMousePixelsModeSeq)
fmt.Fprint(o.w, CSI+DisableMousePixelsModeSeq)
}

// SetWindowTitle sets the terminal window title.
func (o Output) SetWindowTitle(title string) {
fmt.Fprintf(o.tty, OSC+SetWindowTitleSeq, title)
fmt.Fprintf(o.w, OSC+SetWindowTitleSeq, title)
}

// EnableBracketedPaste enables bracketed paste.
func (o Output) EnableBracketedPaste() {
fmt.Fprintf(o.tty, CSI+EnableBracketedPasteSeq)
fmt.Fprintf(o.w, CSI+EnableBracketedPasteSeq)
}

// DisableBracketedPaste disables bracketed paste.
func (o Output) DisableBracketedPaste() {
fmt.Fprintf(o.tty, CSI+DisableBracketedPasteSeq)
fmt.Fprintf(o.w, CSI+DisableBracketedPasteSeq)
}

// Legacy functions.
Expand Down
2 changes: 1 addition & 1 deletion screen_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func tempOutput(t *testing.T) *Output {

func verify(t *testing.T, o *Output, exp string) {
t.Helper()
tty := o.tty.(*os.File)
tty := o.w.(*os.File)

if _, err := tty.Seek(0, 0); err != nil {
t.Fatal(err)
Expand Down
7 changes: 4 additions & 3 deletions termenv.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import (
"errors"
"os"

"github.com/mattn/go-isatty"
)
Expand All @@ -12,13 +13,13 @@
)

const (
// Escape character

Check failure on line 16 in termenv.go

View workflow job for this annotation

GitHub Actions / lint-soft

Comment should end in a period (godot)
ESC = '\x1b'
// Bell

Check failure on line 18 in termenv.go

View workflow job for this annotation

GitHub Actions / lint-soft

Comment should end in a period (godot)
BEL = '\a'
// Control Sequence Introducer

Check failure on line 20 in termenv.go

View workflow job for this annotation

GitHub Actions / lint-soft

Comment should end in a period (godot)
CSI = string(ESC) + "["
// Operating System Command

Check failure on line 22 in termenv.go

View workflow job for this annotation

GitHub Actions / lint-soft

Comment should end in a period (godot)
OSC = string(ESC) + "]"
// String Terminator
ST = string(ESC) + `\`
Expand All @@ -31,11 +32,11 @@
if len(o.environ.Getenv("CI")) > 0 {
return false
}
if o.TTY() == nil {
return false
if f, ok := o.Writer().(*os.File); ok {
return isatty.IsTerminal(f.Fd())
}

return isatty.IsTerminal(o.TTY().Fd())
return false
}

// ColorProfile returns the supported color profile:
Expand Down
5 changes: 3 additions & 2 deletions termenv_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package termenv

import (
"fmt"
"os"
"strconv"

"golang.org/x/sys/windows"
Expand Down Expand Up @@ -103,8 +104,8 @@ func EnableVirtualTerminalProcessing(o *Output) (restoreFunc func() error, err e
}

// If o is not a tty, then there is nothing to do.
tty := o.TTY()
if tty == nil {
tty, ok := o.Writer().(*os.File)
if tty == nil || !ok {
return
}

Expand Down
Loading