@@ -4,13 +4,12 @@ import (
4
4
"encoding/json"
5
5
"fmt"
6
6
"io"
7
- "os"
8
7
"strings"
9
8
"time"
10
9
11
- "github.com/Nvveen/Gotty"
12
10
"github.com/docker/docker/pkg/term"
13
11
"github.com/docker/go-units"
12
+ "github.com/morikuni/aec"
14
13
)
15
14
16
15
// RFC3339NanoFixed is time.RFC3339Nano with nanoseconds padded using zeros to
@@ -151,69 +150,32 @@ type JSONMessage struct {
151
150
Aux * json.RawMessage `json:"aux,omitempty"`
152
151
}
153
152
154
- /* Satisfied by gotty.TermInfo as well as noTermInfo from below */
155
- type termInfo interface {
156
- Parse (attr string , params ... interface {}) (string , error )
153
+ func clearLine (out io.Writer ) {
154
+ eraseMode := aec .EraseModes .All
155
+ cl := aec .EraseLine (eraseMode )
156
+ fmt .Fprint (out , cl )
157
157
}
158
158
159
- type noTermInfo struct {} // canary used when no terminfo.
160
-
161
- func (ti * noTermInfo ) Parse (attr string , params ... interface {}) (string , error ) {
162
- return "" , fmt .Errorf ("noTermInfo" )
163
- }
164
-
165
- func clearLine (out io.Writer , ti termInfo ) {
166
- // el2 (clear whole line) is not exposed by terminfo.
167
-
168
- // First clear line from beginning to cursor
169
- if attr , err := ti .Parse ("el1" ); err == nil {
170
- fmt .Fprintf (out , "%s" , attr )
171
- } else {
172
- fmt .Fprintf (out , "\x1b [1K" )
173
- }
174
- // Then clear line from cursor to end
175
- if attr , err := ti .Parse ("el" ); err == nil {
176
- fmt .Fprintf (out , "%s" , attr )
177
- } else {
178
- fmt .Fprintf (out , "\x1b [K" )
179
- }
180
- }
181
-
182
- func cursorUp (out io.Writer , ti termInfo , l int ) {
183
- if l == 0 { // Should never be the case, but be tolerant
184
- return
185
- }
186
- if attr , err := ti .Parse ("cuu" , l ); err == nil {
187
- fmt .Fprintf (out , "%s" , attr )
188
- } else {
189
- fmt .Fprintf (out , "\x1b [%dA" , l )
190
- }
159
+ func cursorUp (out io.Writer , l uint ) {
160
+ fmt .Fprint (out , aec .Up (l ))
191
161
}
192
162
193
- func cursorDown (out io.Writer , ti termInfo , l int ) {
A3DB
div>
194
- if l == 0 { // Should never be the case, but be tolerant
195
- return
196
- }
197
- if attr , err := ti .Parse ("cud" , l ); err == nil {
198
- fmt .Fprintf (out , "%s" , attr )
199
- } else {
200
- fmt .Fprintf (out , "\x1b [%dB" , l )
201
- }
163
+ func cursorDown (out io.Writer , l uint ) {
164
+ fmt .Fprint (out , aec .Down (l ))
202
165
}
203
166
204
- // Display displays the JSONMessage to `out`. `termInfo` is non-nil if `out`
205
- // is a terminal. If this is the case, it will erase the entire current line
206
- // when displaying the progressbar.
207
- func (jm * JSONMessage ) Display (out io.Writer , termInfo termInfo ) error {
167
+ // Display displays the JSONMessage to `out`. If `isTerminal` is true, it will erase the
168
+ // entire current line when displaying the progressbar.
169
+ func (jm * JSONMessage ) Display (out io.Writer , isTerminal bool ) error {
208
170
if jm .Error != nil {
209
171
if jm .Error .Code == 401 {
210
172
return fmt .Errorf ("authentication is required" )
211
173
}
212
174
return jm .Error
213
175
}
214
176
var endl string
215
- if termInfo != nil && jm .Stream == "" && jm .Progress != nil {
216
- clearLine (out , termInfo )
177
+ if isTerminal && jm .Stream == "" && jm .Progress != nil {
178
+ clearLine (out )
217
179
endl = "\r "
218
180
fmt .Fprintf (out , endl )
219
181
} else if jm .Progress != nil && jm .Progress .String () != "" { //disable progressbar in non-terminal
@@ -230,7 +192,7 @@ func (jm *JSONMessage) Display(out io.Writer, termInfo termInfo) error {
230
192
if jm .From != "" {
231
193
fmt .Fprintf (out , "(from %s) " , jm .From )
232
194
}
233
- if jm .Progress != nil && termInfo != nil {
195
+ if jm .Progress != nil && isTerminal {
234
196
fmt .Fprintf (out , "%s %s%s" , jm .Status , jm .Progress .String (), endl )
235
197
} else if jm .ProgressMessage != "" { //deprecated
236
198
fmt .Fprintf (out , "%s %s%s" , jm .Status , jm .ProgressMessage , endl )
@@ -248,25 +210,11 @@ func (jm *JSONMessage) Display(out io.Writer, termInfo termInfo) error {
248
210
func DisplayJSONMessagesStream (in io.Reader , out io.Writer , terminalFd uintptr , isTerminal bool , auxCallback func (JSONMessage )) error {
249
211
var (
250
212
dec = json .NewDecoder (in )
251
- ids = make (map [string ]int )
213
+ ids = make (map [string ]uint )
252
214
)
253
215
254
- var termInfo termInfo
255
-
256
- if isTerminal {
257
- term := os .Getenv ("TERM" )
258
- if term == "" {
259
- term = "vt102"
260
- }
261
-
262
- var err error
263
- if termInfo , err = gotty .OpenTermInfo (term ); err != nil {
264
- termInfo = & noTermInfo {}
265
- }
266
- }
267
-
268
216
for {
269
- diff := 0
217
+ var diff uint
270
218
var jm JSONMessage
271
219
if err := dec .Decode (& jm ); err != nil {
272
220
if err == io .EOF {
@@ -294,27 +242,27 @@ func DisplayJSONMessagesStream(in io.Reader, out io.Writer, terminalFd uintptr,
294
242
// when we output something that's not
295
243
// accounted for in the map, such as a line
296
244
// with no ID.
297
- line = len (ids )
245
+ line = uint ( len (ids ) )
298
246
ids [jm .ID ] = line
299
- if termInfo != nil {
247
+ if isTerminal {
300
248
fmt .Fprintf (out , "\n " )
301
249
}
302
250
}
303
- diff = len (ids ) - line
304
- if termInfo != nil {
305
- cursorUp (out , termInfo , diff )
251
+ diff = uint ( len (ids ) ) - line
252
+ if isTerminal {
253
+ cursorUp (out , diff )
306
254
}
307
255
} else {
308
256
// When outputting something that isn't progress
309
257
// output, clear the history of previous lines. We
310
258
// don't want progress entries from some previous
311
259
// operation to be updated (for example, pull -a
312
260
// with multiple tags).
313
- ids = make (map [string ]int )
261
+ ids = make (map [string ]uint )
314
262
}
315
- err := jm .Display (out , termInfo )
316
- if jm .ID != "" && termInfo != nil {
317
- cursorDown (out , termInfo , diff )
263
+ err := jm .Display (out , isTerminal )
264
+ if jm .ID != "" && isTerminal {
265
+ cursorDown (out , diff )
318
266
}
319
267
if err != nil {
320
268
return err
0 commit comments