| 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) |
| } |