Skip to content

Commit

Permalink
Merge #11695
Browse files Browse the repository at this point in the history
11695: [display] Overwrite terminal contents r=pgavlin a=pgavlin

Instead of clearing the contents of the display with each frame,
overwrite its contents by clearing to the end of each line prior to a
newline. This should eliminate flickering in the interactive display.

This also eliminates a couple of off-by-one errors that were hampering
usability.

Fixes #11691, https://github.com/pulumi/customer-support/issues/837.

Co-authored-by: Pat Gavlin <pat@pulumi.com>
  • Loading branch information
bors[bot] and pgavlin committed Dec 20, 2022
2 parents fc607c2 + f92cb3d commit 78c9bf4
Show file tree
Hide file tree
Showing 47 changed files with 23,558 additions and 23,488 deletions.
@@ -0,0 +1,4 @@
changes:
- type: fix
scope: cli/display
description: Fix flickering in the interactive display
24 changes: 24 additions & 0 deletions pkg/backend/display/internal/terminal/info.go
Expand Up @@ -10,9 +10,12 @@ import (
type Info interface {
Parse(attr string, params ...interface{}) (string, error)

ClearEnd(out io.Writer)
ClearLine(out io.Writer)
CursorUp(out io.Writer, count int)
CursorDown(out io.Writer, count int)
HideCursor(out io.Writer)
ShowCursor(out io.Writer)
}

/* Satisfied by gotty.TermInfo as well as noTermInfo from below */
Expand Down Expand Up @@ -56,6 +59,15 @@ func (i info) ClearLine(out io.Writer) {
}
}

func (i info) ClearEnd(out io.Writer) {
// 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
Expand All @@ -77,3 +89,15 @@ func (i info) CursorDown(out io.Writer, count int) {
fmt.Fprintf(out, "\x1b[%dB", count)
}
}

func (i info) HideCursor(out io.Writer) {
if attr, err := i.Parse("civis"); err == nil {
fmt.Fprintf(out, "%s", attr)
}
}

func (i info) ShowCursor(out io.Writer) {
if attr, err := i.Parse("cnorm"); err == nil {
fmt.Fprintf(out, "%s", attr)
}
}
12 changes: 12 additions & 0 deletions pkg/backend/display/internal/terminal/mock.go
Expand Up @@ -52,6 +52,10 @@ func (t *MockTerminal) ClearLine() {
t.info.ClearLine(t)
}

func (t *MockTerminal) ClearEnd() {
t.info.ClearEnd(t)
}

func (t *MockTerminal) CursorUp(count int) {
t.info.CursorUp(t, count)
}
Expand All @@ -60,6 +64,14 @@ func (t *MockTerminal) CursorDown(count int) {
t.info.CursorDown(t, count)
}

func (t *MockTerminal) HideCursor() {
t.info.HideCursor(t)
}

func (t *MockTerminal) ShowCursor() {
t.info.ShowCursor(t)
}

func (t *MockTerminal) ReadKey() (string, error) {
k, ok := <-t.keys
if !ok {
Expand Down
15 changes: 15 additions & 0 deletions pkg/backend/display/internal/terminal/term.go
Expand Up @@ -18,8 +18,11 @@ type Terminal interface {
Size() (width, height int, err error)

ClearLine()
ClearEnd()
CursorUp(count int)
CursorDown(count int)
HideCursor()
ShowCursor()

ReadKey() (string, error)
}
Expand Down Expand Up @@ -131,6 +134,10 @@ func (t *terminal) ClearLine() {
t.info.ClearLine(t.out)
}

func (t *terminal) ClearEnd() {
t.info.ClearEnd(t.out)
}

func (t *terminal) CursorUp(count int) {
t.info.CursorUp(t.out, count)
}
Expand All @@ -139,6 +146,14 @@ func (t *terminal) CursorDown(count int) {
t.info.CursorDown(t.out, count)
}

func (t *terminal) HideCursor() {
t.info.HideCursor(t.out)
}

func (t *terminal) ShowCursor() {
t.info.ShowCursor(t.out)
}

func (t *terminal) ReadKey() (string, error) {
if t.in == nil {
return "", io.EOF
Expand Down

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

35 changes: 25 additions & 10 deletions pkg/backend/display/tree.go
Expand Up @@ -58,6 +58,7 @@ func newInteractiveRenderer(term terminal.Terminal, opts Options) progressRender
if !term.IsRaw() {
return newInteractiveMessageRenderer(term, opts)
}
term.HideCursor()

r := &treeRenderer{
opts: opts,
Expand All @@ -75,6 +76,7 @@ func newInteractiveRenderer(term terminal.Terminal, opts Options) progressRender
}

func (r *treeRenderer) Close() error {
r.term.ShowCursor()
return r.term.Close()
}

Expand Down Expand Up @@ -110,6 +112,16 @@ func (r *treeRenderer) println(display *ProgressDisplay, text string) {
r.print("\n")
}

func (r *treeRenderer) over(text string) {
r.print(text)
r.term.ClearEnd()
}

func (r *treeRenderer) overln(text string) {
r.over(text)
r.print("\n")
}

func (r *treeRenderer) render(display *ProgressDisplay) {
r.m.Lock()
defer r.m.Unlock()
Expand Down Expand Up @@ -222,7 +234,7 @@ func (r *treeRenderer) frame(locked, done bool) {
}

treeTableHeight = termHeight - systemMessagesHeight - 1
r.maxTreeTableOffset = len(treeTableRows) - treeTableHeight - 1
r.maxTreeTableOffset = len(treeTableRows) - treeTableHeight + 1

treeTableRows = treeTableRows[r.treeTableOffset : r.treeTableOffset+treeTableHeight-1]

Expand All @@ -239,37 +251,40 @@ func (r *treeRenderer) frame(locked, done bool) {
footer := fmt.Sprintf("%smore%s", upArrow, downArrow)
padding := termWidth - uniseg.GraphemeClusterCount(footer)
treeTableFooter = strings.Repeat(" ", padding) + footer

if systemMessagesHeight > 0 {
treeTableFooter += "\n"
}
}

// Re-home the cursor.
r.term.ClearLine()
r.print("\r")
for ; r.rewind > 0; r.rewind-- {
r.term.CursorUp(1)
r.term.ClearLine()
}
r.rewind = totalHeight - 1

// Render the tree table.
r.println(nil, r.clampLine(treeTableHeader, termWidth))
r.overln(r.clampLine(treeTableHeader, termWidth))
for _, row := range treeTableRows {
r.println(nil, r.clampLine(row, termWidth))
r.overln(r.clampLine(row, termWidth))
}
if treeTableFooter != "" {
r.print(treeTableFooter)
r.over(treeTableFooter)
}

// Render the system messages.
if systemMessagesHeight > 0 {
r.println(nil, "")
r.println(nil, colors.Yellow+"System Messages"+colors.Reset)
r.overln("")
r.overln(colors.Yellow + "System Messages" + colors.Reset)

for _, line := range systemMessages {
r.println(nil, " "+line)
r.overln(" " + line)
}
}

if done && totalHeight > 0 {
r.println(nil, "")
r.overln("")
}
}

Expand Down

0 comments on commit 78c9bf4

Please sign in to comment.