81 lines
2.9 KiB
Go
81 lines
2.9 KiB
Go
|
package terminal
|
||
|
|
||
|
import (
|
||
|
"os"
|
||
|
)
|
||
|
|
||
|
const defaultColumns int = 78
|
||
|
const defaultIsTerminal bool = false
|
||
|
|
||
|
// OutputStream represents an output stream that might or might not be connected
|
||
|
// to a terminal.
|
||
|
//
|
||
|
// There are typically two instances of this: one representing stdout and one
|
||
|
// representing stderr.
|
||
|
type OutputStream struct {
|
||
|
File *os.File
|
||
|
|
||
|
// Interacting with a terminal is typically platform-specific, so we
|
||
|
// factor out these into virtual functions, although we have default
|
||
|
// behaviors suitable for non-Terminal output if any of these isn't
|
||
|
// set. (We're using function pointers rather than interfaces for this
|
||
|
// because it allows us to mix both normal methods and virtual methods
|
||
|
// on the same type, without a bunch of extra complexity.)
|
||
|
isTerminal func(*os.File) bool
|
||
|
getColumns func(*os.File) int
|
||
|
}
|
||
|
|
||
|
// Columns returns a number of character cell columns that we expect will
|
||
|
// fill the width of the terminal that stdout is connected to, or a reasonable
|
||
|
// placeholder value of 78 if the output doesn't seem to be a terminal.
|
||
|
//
|
||
|
// This is a best-effort sort of function which may give an inaccurate result
|
||
|
// in various cases. For example, callers storing the result will not react
|
||
|
// to subsequent changes in the terminal width, and indeed this function itself
|
||
|
// may not be able to either, depending on the constraints of the current
|
||
|
// execution context.
|
||
|
func (s *OutputStream) Columns() int {
|
||
|
if s.getColumns == nil {
|
||
|
return defaultColumns
|
||
|
}
|
||
|
return s.getColumns(s.File)
|
||
|
}
|
||
|
|
||
|
// IsTerminal returns true if we expect that the stream is connected to a
|
||
|
// terminal which supports VT100-style formatting and cursor control sequences.
|
||
|
func (s *OutputStream) IsTerminal() bool {
|
||
|
if s.isTerminal == nil {
|
||
|
return defaultIsTerminal
|
||
|
}
|
||
|
return s.isTerminal(s.File)
|
||
|
}
|
||
|
|
||
|
// InputStream represents an input stream that might or might not be a terminal.
|
||
|
//
|
||
|
// There is typically only one instance of this type, representing stdin.
|
||
|
type InputStream struct {
|
||
|
File *os.File
|
||
|
|
||
|
// Interacting with a terminal is typically platform-specific, so we
|
||
|
// factor out these into virtual functions, although we have default
|
||
|
// behaviors suitable for non-Terminal output if any of these isn't
|
||
|
// set. (We're using function pointers rather than interfaces for this
|
||
|
// because it allows us to mix both normal methods and virtual methods
|
||
|
// on the same type, without a bunch of extra complexity.)
|
||
|
isTerminal func(*os.File) bool
|
||
|
}
|
||
|
|
||
|
// IsTerminal returns true if we expect that the stream is connected to a
|
||
|
// terminal which can support interactive input.
|
||
|
//
|
||
|
// If this returns false, callers might prefer to skip elaborate input prompt
|
||
|
// functionality like tab completion and instead just treat the input as a
|
||
|
// raw byte stream, or perhaps skip prompting for input at all depending on the
|
||
|
// situation.
|
||
|
func (s *InputStream) IsTerminal() bool {
|
||
|
if s.isTerminal == nil {
|
||
|
return defaultIsTerminal
|
||
|
}
|
||
|
return s.isTerminal(s.File)
|
||
|
}
|