Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
11201: [cli] Abstract out terminal interactions r=pgavlin a=pgavlin Replace direct interaction with the terminal with an abstraction. This abstraction is tightly constrained to the capabilities needed for the CLI's display. Using this abstraction allows for straightforward testing of the interactive renderers. 11302: Changelog and go.mod updates for v3.46.1 r=pulumi-bot a=pulumi-bot bors merge Co-authored-by: Pat Gavlin <pat@pulumi.com> Co-authored-by: github-actions <github-actions@github.com>
- Loading branch information
Showing
119 changed files
with
25,125 additions
and
311 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
4 changes: 0 additions & 4 deletions
4
changelog/pending/20221020--pkg--add-deletedwith-as-a-resource-option.yaml
This file was deleted.
Oops, something went wrong.
4 changes: 0 additions & 4 deletions
4
...-improve-the-usability-of-the-interactive-dipslay-by-making-the-treetable-scrollable.yaml
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
4 changes: 0 additions & 4 deletions
4
.../pending/20221102--sdk-python--handle-none-being-passed-to-register_resource_outputs.yaml
This file was deleted.
Oops, something went wrong.
4 changes: 0 additions & 4 deletions
4
...ending/20221103--sdkgen-go--allow-resource-names-that-conflict-with-additional-types.yaml
This file was deleted.
Oops, something went wrong.
4 changes: 0 additions & 4 deletions
4
changelog/pending/20221104--sdkgen-go--guard-against-conflicting-field-names.yaml
This file was deleted.
Oops, something went wrong.
4 changes: 0 additions & 4 deletions
4
changelog/pending/20221107--engine--disable-auto-parenting-to-see-if-that-fixes-10950.yaml
This file was deleted.
Oops, something went wrong.
4 changes: 0 additions & 4 deletions
4
changelog/pending/20221108--pkg--allow-case-insensitive-prop-typecheck-on-traversal.yaml
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
//nolint:goconst | ||
package display | ||
|
||
import ( | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
package terminal | ||
|
||
import ( | ||
"fmt" | ||
"io" | ||
|
||
gotty "github.com/ijc/Gotty" | ||
) | ||
|
||
type Info interface { | ||
Parse(attr string, params ...interface{}) (string, error) | ||
|
||
ClearLine(out io.Writer) | ||
CursorUp(out io.Writer, count int) | ||
CursorDown(out io.Writer, count int) | ||
} | ||
|
||
/* Satisfied by gotty.TermInfo as well as noTermInfo from below */ | ||
type termInfo interface { | ||
Parse(attr string, params ...interface{}) (string, error) | ||
} | ||
|
||
type noTermInfo int // canary used when no terminfo. | ||
|
||
func (ti noTermInfo) Parse(attr string, params ...interface{}) (string, error) { | ||
return "", fmt.Errorf("noTermInfo") | ||
} | ||
|
||
type info struct { | ||
termInfo | ||
} | ||
|
||
var _ = Info(info{}) | ||
|
||
func OpenInfo(terminal string) Info { | ||
if i, err := gotty.OpenTermInfo(terminal); err == nil { | ||
return info{i} | ||
} | ||
return info{noTermInfo(0)} | ||
} | ||
|
||
func (i info) ClearLine(out io.Writer) { | ||
// el2 (clear whole line) is not exposed by terminfo. | ||
|
||
// First clear line from beginning to cursor | ||
if attr, err := i.Parse("el1"); err == nil { | ||
fmt.Fprintf(out, "%s", attr) | ||
} else { | ||
fmt.Fprintf(out, "\x1b[1K") | ||
} | ||
// Then clear line from cursor to end | ||
if attr, err := i.Parse("el"); err == nil { | ||
fmt.Fprintf(out, "%s", attr) | ||
} else { | ||
fmt.Fprintf(out, "\x1b[K") | ||
} | ||
} | ||
|
||
func (i info) CursorUp(out io.Writer, count int) { | ||
if count == 0 { // Should never be the case, but be tolerant | ||
return | ||
} | ||
if attr, err := i.Parse("cuu", count); err == nil { | ||
fmt.Fprintf(out, "%s", attr) | ||
} else { | ||
fmt.Fprintf(out, "\x1b[%dA", count) | ||
} | ||
} | ||
|
||
func (i info) CursorDown(out io.Writer, count int) { | ||
if count == 0 { // Should never be the case, but be tolerant | ||
return | ||
} | ||
if attr, err := i.Parse("cud", count); err == nil { | ||
fmt.Fprintf(out, "%s", attr) | ||
} else { | ||
fmt.Fprintf(out, "\x1b[%dB", count) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
package terminal | ||
|
||
import ( | ||
"io" | ||
"sync" | ||
) | ||
|
||
type MockTerminal struct { | ||
m sync.Mutex | ||
|
||
width, height int | ||
raw bool | ||
info Info | ||
|
||
keys chan string | ||
|
||
dest io.Writer | ||
} | ||
|
||
func NewMockTerminal(dest io.Writer, width, height int, raw bool) *MockTerminal { | ||
return &MockTerminal{ | ||
width: width, | ||
height: height, | ||
raw: raw, | ||
info: info{noTermInfo(0)}, | ||
keys: make(chan string), | ||
dest: dest, | ||
} | ||
} | ||
|
||
func (t *MockTerminal) IsRaw() bool { | ||
return t.raw | ||
} | ||
|
||
func (t *MockTerminal) Close() error { | ||
close(t.keys) | ||
return nil | ||
} | ||
|
||
func (t *MockTerminal) Size() (width, height int, err error) { | ||
t.m.Lock() | ||
defer t.m.Unlock() | ||
|
||
return t.width, t.height, nil | ||
} | ||
|
||
func (t *MockTerminal) Write(b []byte) (int, error) { | ||
return t.dest.Write(b) | ||
} | ||
|
||
func (t *MockTerminal) ClearLine() { | ||
t.info.ClearLine(t) | ||
} | ||
|
||
func (t *MockTerminal) CursorUp(count int) { | ||
t.info.CursorUp(t, count) | ||
} | ||
|
||
func (t *MockTerminal) CursorDown(count int) { | ||
t.info.CursorDown(t, count) | ||
} | ||
|
||
func (t *MockTerminal) ReadKey() (string, error) { | ||
k, ok := <-t.keys | ||
if !ok { | ||
return "", io.EOF | ||
} | ||
return k, nil | ||
} | ||
|
||
func (t *MockTerminal) SetSize(width, height int) { | ||
t.m.Lock() | ||
defer t.m.Unlock() | ||
|
||
t.width, t.height = width, height | ||
} | ||
|
||
func (t *MockTerminal) SendKey(key string) { | ||
t.keys <- key | ||
} |
Oops, something went wrong.