Files
wehub-resource-sync 5357c39144
Fuzzer / Run Fuzzer (push) Has been cancelled
Race tests / Go race tests (ubuntu-22.04) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:01:40 +08:00

258 lines
5.3 KiB
Go

// Copyright 2019 Dolthub, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cli
import (
"fmt"
"io"
"os"
"strings"
"sync"
"sync/atomic"
"github.com/fatih/color"
"github.com/vbauerster/mpb/v8/cwriter"
"github.com/dolthub/dolt/go/libraries/utils/iohelp"
)
var colorOutput = color.Output
var CliOut = colorOutput
var CliErr = color.Error
var outputClosed uint64
var InStream io.ReadCloser = os.Stdin
var OutStream io.WriteCloser = os.Stdout
var ExecuteWithStdioRestored func(userFunc func())
func CloseOutput() {
if atomic.CompareAndSwapUint64(&outputClosed, 0, 1) {
fmt.Fprintln(CliOut)
}
}
func SetIOStreams(inStream io.ReadCloser, outStream io.WriteCloser) {
InStream = inStream
OutStream = outStream
}
func InitIO() (restoreIO func()) {
stdOut, stdErr := os.Stdout, os.Stderr
f, err := os.OpenFile(os.DevNull, os.O_WRONLY, 0)
if err == nil {
os.Stdout = f
os.Stderr = f
SetIOStreams(os.Stdin, iohelp.NopWrCloser(CliOut))
}
restoreIO = func() {
if f != nil {
f.Close()
}
os.Stdout = stdOut
os.Stderr = stdErr
SetIOStreams(os.Stdin, os.Stdout)
}
ExecuteWithStdioRestored = func(userFunc func()) {
initialNoColor := color.NoColor
color.NoColor = true
os.Stdout = stdOut
os.Stderr = stdErr
SetIOStreams(os.Stdin, os.Stdout)
userFunc()
os.Stdout = f
os.Stderr = f
color.NoColor = initialNoColor
SetIOStreams(os.Stdin, iohelp.NopWrCloser(CliOut))
}
return restoreIO
}
func Println(a ...interface{}) {
if outputIsClosed() {
return
}
fmt.Fprintln(CliOut, a...)
}
func Print(a ...interface{}) {
if outputIsClosed() {
return
}
fmt.Fprint(CliOut, a...)
}
func Printf(format string, a ...interface{}) {
if outputIsClosed() {
return
}
fmt.Fprintf(CliOut, format, a...)
}
func PrintErrln(a ...interface{}) {
if outputIsClosed() {
return
}
fmt.Fprintln(CliErr, a...)
}
func PrintErr(a ...interface{}) {
if outputIsClosed() {
return
}
fmt.Fprint(CliErr, a...)
}
func PrintErrf(format string, a ...interface{}) {
if outputIsClosed() {
return
}
fmt.Fprintf(CliErr, format, a...)
}
// DeleteAndPrint prints a new message and deletes the old one given the
// previous messages length. It returns the length of the printed message that
// should be passed as prevMsgLen on the next call of DeleteAndPrint.
//
// DeleteAndPrint does not work for multiline messages.
func DeleteAndPrint(prevMsgLen int, msg string) int {
if outputIsClosed() {
return 0
}
msgLen := len(msg)
backspacesAndMsg := make([]byte, prevMsgLen+msgLen, 2*prevMsgLen+msgLen)
for i := 0; i < prevMsgLen; i++ {
backspacesAndMsg[i] = '\b'
}
for i, c := range []byte(msg) {
backspacesAndMsg[i+prevMsgLen] = c
}
diff := prevMsgLen - msgLen
if diff > 0 {
for i := 0; i < diff; i++ {
backspacesAndMsg = append(backspacesAndMsg, ' ')
}
for i := 0; i < diff; i++ {
backspacesAndMsg = append(backspacesAndMsg, '\b')
}
}
PrintErr(string(backspacesAndMsg))
return msgLen
}
// EphemeralPrinter is tool than you can use to print temporary line(s) to the
// console. Each time Display is called, the output is reset, and you can begin
// writing anew.
type EphemeralPrinter struct {
w *cwriter.Writer
mu sync.Mutex // protects the following
lines int
atLineStart bool
}
// NewEphemeralPrinter creates a new EphemeralPrinter.
func NewEphemeralPrinter() *EphemeralPrinter {
var out io.Writer
// Bypass the colored writer.
if CliOut == colorOutput {
if ExecuteWithStdioRestored != nil {
ExecuteWithStdioRestored(func() {
out = os.Stdout
})
} else {
out = os.Stdout
}
} else {
out = CliOut
}
w := cwriter.New(out)
e := &EphemeralPrinter{w: w, mu: sync.Mutex{}, atLineStart: true}
return e
}
// Printf formats and prints a string to the printer. If no newline character is
// provided, one will be added to ensure that the output can be cleared in the
// future. You can print multiple lines.
func (e *EphemeralPrinter) Printf(format string, a ...interface{}) {
e.mu.Lock()
defer e.mu.Unlock()
if outputIsClosed() {
return
}
str := fmt.Sprintf(format, a...)
lines := strings.Split(str, "\n")
for i, line := range lines {
if i != 0 {
_, _ = e.w.Write([]byte("\n"))
e.lines++
e.atLineStart = true
}
_, _ = e.w.Write([]byte(line))
if len(line) > 0 {
e.atLineStart = false
}
}
}
// Display clears the previous output and displays the new text.
func (e *EphemeralPrinter) Display() {
e.mu.Lock()
defer e.mu.Unlock()
if outputIsClosed() {
return
}
if !e.atLineStart {
// Cursor needs to be at the start of a line. This will ensure that the
// output is cleared properly.
_, _ = e.w.Write([]byte("\n"))
e.lines++
e.atLineStart = true
}
_ = e.w.Flush(e.lines)
e.lines = 0
}
func outputIsClosed() bool {
isClosed := atomic.LoadUint64(&outputClosed)
return isClosed == 1
}