@@ -2,21 +2,28 @@ package tui
2
2
3
3
import (
4
4
"strings"
5
+ "sync"
5
6
"time"
6
7
7
- "github.com/pterm/pterm "
8
+ "atomicgo.dev/cursor "
8
9
)
9
10
10
11
var (
11
- defaultDuration = 200 * time .Millisecond
12
+ loopDelay = 200 * time .Millisecond
12
13
)
13
14
15
+ type displayState struct {
16
+ area area
17
+ lastPrint string
18
+ content string
19
+ finish bool
20
+ }
21
+
14
22
type display struct {
15
- area area
16
- prompter * prompter
17
- last time.Time
18
- lastDuration time.Duration
19
- stopped bool
23
+ displayState
24
+ prompter * prompter
25
+ contentLock sync.Mutex
26
+ closer func ()
20
27
}
21
28
22
29
func newDisplay (tool string ) (* display , error ) {
@@ -25,25 +32,43 @@ func newDisplay(tool string) (*display, error) {
25
32
return nil , err
26
33
}
27
34
28
- return & display {
29
- prompter : prompter ,
30
- lastDuration : defaultDuration ,
31
- }, nil
35
+ t := time .NewTicker (loopDelay )
36
+ d := & display {
37
+ prompter : prompter ,
38
+ closer : t .Stop ,
39
+ }
40
+
41
+ go func () {
42
+ for range t .C {
43
+ d .paint ()
44
+ }
45
+ }()
46
+
47
+ return d , nil
48
+ }
49
+
50
+ func (a * display ) readline (f func () (string , bool )) (string , bool ) {
51
+ a .paint ()
52
+ cursor .Show ()
53
+ defer cursor .Hide ()
54
+ return f ()
32
55
}
33
56
34
57
func (a * display ) Ask (text string , sensitive bool ) (string , bool ) {
35
58
a .setMultiLinePrompt (text )
36
59
if sensitive {
37
- return a .prompter .ReadPassword ( )
60
+ return a .readline ( a . prompter .ReadPassword )
38
61
}
39
- return a .prompter .Readline ( )
62
+ return a .readline ( a . prompter .Readline )
40
63
}
41
64
42
65
func (a * display ) setMultiLinePrompt (text string ) {
43
66
lines := strings .Split (text , "\n " )
44
67
a .prompter .SetPrompt (lines [len (lines )- 1 ])
45
68
if len (lines ) > 1 {
46
- a .area .Update (a .area .content + "\n " + strings .Join (lines [:len (lines )- 1 ], "\n " ) + "\n " )
69
+ a .contentLock .Lock ()
70
+ defer a .contentLock .Unlock ()
71
+ a .content = a .area .content + "\n " + strings .Join (lines [:len (lines )- 1 ], "\n " ) + "\n "
47
72
}
48
73
}
49
74
@@ -58,7 +83,7 @@ const (
58
83
func (a * display ) AskYesNo (text string ) (Answer , bool , error ) {
59
84
a .setMultiLinePrompt (text )
60
85
for {
61
- line , ok := a .prompter .Readline ( )
86
+ line , ok := a .readline ( a . prompter .Readline )
62
87
if ! ok {
63
88
return No , ok , nil
64
89
}
@@ -75,49 +100,49 @@ func (a *display) AskYesNo(text string) (Answer, bool, error) {
75
100
76
101
func (a * display ) Prompt (text string ) (string , bool ) {
77
102
a .prompter .SetPrompt (text )
78
- return a .prompter .Readline ( )
103
+ return a .readline ( a . prompter .Readline )
79
104
}
80
105
81
- func (a * display ) Progress (text string ) error {
82
- if text == "" {
83
- return nil
106
+ func (a * display ) paint () {
107
+ a .contentLock .Lock ()
108
+ if a .finish {
109
+ a .area .Update (a .content )
110
+ cursor .Show ()
111
+ a .displayState = displayState {}
112
+ a .contentLock .Unlock ()
113
+ return
84
114
}
85
115
86
- if a .stopped {
87
- a .area = area {}
88
- a .stopped = false
89
- a .last = time.Time {}
90
- a .lastDuration = defaultDuration
91
- }
116
+ newContent := a .content
117
+ a .contentLock .Unlock ()
92
118
93
- start := time .Now ()
94
- if start .Sub (a .last ) > a .lastDuration {
95
- lines := strings .Split (text , "\n " )
96
- height := pterm .GetTerminalHeight ()
97
- if len (lines ) > height {
98
- lines = lines [len (lines )- height :]
99
- }
100
- newText := strings .Join (lines , "\n " )
101
- a .area .Update (newText )
102
- done := time .Now ()
103
- delta := done .Sub (start )
104
- if delta > a .lastDuration {
105
- a .lastDuration = delta
106
- }
107
- a .last = done
119
+ if newContent == a .lastPrint {
120
+ return
108
121
}
109
122
110
- return nil
123
+ a .area .Update (newContent )
124
+ a .lastPrint = newContent
125
+ }
126
+
127
+ func (a * display ) Progress (text string ) {
128
+ a .contentLock .Lock ()
129
+ defer a .contentLock .Unlock ()
130
+ a .content = text
111
131
}
112
132
113
133
func (a * display ) Close () error {
134
+ a .closer ()
114
135
return a .prompter .Close ()
115
136
}
116
137
117
138
func (a * display ) Finished (text string ) {
139
+ defer a .paint ()
140
+ a .contentLock .Lock ()
141
+ defer a .contentLock .Unlock ()
142
+
118
143
if ! strings .HasSuffix (text , "\n " ) {
119
144
text += "\n "
120
145
}
121
- a .stopped = true
122
- a .area . Finish ( text )
146
+ a .finish = true
147
+ a .content = text
123
148
}
0 commit comments