/
stream.go
80 lines (71 loc) · 2.88 KB
/
stream.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
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)
}