goroutine 中的 ANSI 光标移动问题
Issue with ANSI cursor movement in goroutine
背景
受 Node 库的启发,我正在尝试编写一个用于创建终端任务列表的 Go 库 listr。
我的图书馆 golist 在后台 goroutine 中打印任务列表并使用 ANSI 转义序列更新文本和状态字符。
问题
列表的最终打印 偶尔会 包含额外的空格,这会导致一些空格或重复行。这里有两个例子——一个正确,一个不正确——都来自 相同的代码 (here's a link to the code).
的运行
例子
下面是它的外观示例:
(Here's a gist of the raw text output for the correct output)
下面是它有时看起来像的一个例子:
(Here's a gist of the raw text output for the incorrect output)
如果您查看 lines 184 and 185 in the gist of the incorrect version,有两个空行的版本不正确。
为什么会发生这种情况,为什么它只在有时发生?
代码
我在以下循环中将列表打印到终端:
go func() {
defer donePrinting() // Tell the Stop function that we're done printing
ts := l.getTaskStates()
l.print(ts)
for {
select {
case <-ctx.Done(): // Check if the print loop should stop
// Perform a final clear and an optional print depending on `ClearOnComplete`
ts := l.getTaskStates()
if l.ClearOnComplete {
l.clear(ts)
return
}
l.clearThenPrint(ts)
return
case s := <-l.printQ: // Check if there's a message to print
fmt.Fprintln(l.Writer, s)
default: // Otherwise, print the list
ts := l.getTaskStates()
l.clearThenPrint(ts)
l.StatusIndicator.Next()
time.Sleep(l.Delay)
}
}
}()
列表被格式化为字符串,然后被打印出来。以下函数格式化字符串:
// fmtPrint returns the formatted list of messages
// and statuses, using the supplied TaskStates
func (l *List) fmtPrint(ts []*TaskState) string {
s := make([]string, 0)
for _, t := range ts {
s = append(s, l.formatMessage(t))
}
return strings.Join(s, "\n")
}
并且以下函数构建 ANSI 转义字符串以清除行:
// fmtClear returns a string of ANSI escape characters
// to clear the `n` lines previously printed.
func (l *List) fmtClear(n int) string {
s := "3[1A" // Move up a line
s += "3[K" // Clear the line
s += "\r" // Move back to the beginning of the line
return strings.Repeat(s, n)
}
我使用 this site 作为 ANSI 代码的参考。
提前感谢您对发生这种情况的原因提出的任何建议!
如果我可以添加任何其他有用的信息,请告诉我。
我认为 ANSI 代码只是转移注意力。我下载了库并在本地尝试 运行ning,发现以下部分是导致此问题的原因:
case s := <-l.printQ: // Check if there's a message to print
fmt.Fprintln(l.Writer, s)
当 printQ
通道关闭时,这种情况有时是 运行ning,这似乎是将光标向下移动,即使没有打印任何内容。当我在调用 l.printDone
后移动调用以关闭频道时,此行为消失了。
...
// Wait for the print loop to finish
<-l.printDone
if l.printQ != nil {
close(l.printQ)
}
...
这确保当通道关闭时循环不再运行ning,因此s := <-l.printQ
情况不会运行。
背景
受 Node 库的启发,我正在尝试编写一个用于创建终端任务列表的 Go 库 listr。
我的图书馆 golist 在后台 goroutine 中打印任务列表并使用 ANSI 转义序列更新文本和状态字符。
问题
列表的最终打印 偶尔会 包含额外的空格,这会导致一些空格或重复行。这里有两个例子——一个正确,一个不正确——都来自 相同的代码 (here's a link to the code).
的运行例子
下面是它的外观示例:
(Here's a gist of the raw text output for the correct output)
下面是它有时看起来像的一个例子:
(Here's a gist of the raw text output for the incorrect output)
如果您查看 lines 184 and 185 in the gist of the incorrect version,有两个空行的版本不正确。
为什么会发生这种情况,为什么它只在有时发生?
代码
我在以下循环中将列表打印到终端:
go func() {
defer donePrinting() // Tell the Stop function that we're done printing
ts := l.getTaskStates()
l.print(ts)
for {
select {
case <-ctx.Done(): // Check if the print loop should stop
// Perform a final clear and an optional print depending on `ClearOnComplete`
ts := l.getTaskStates()
if l.ClearOnComplete {
l.clear(ts)
return
}
l.clearThenPrint(ts)
return
case s := <-l.printQ: // Check if there's a message to print
fmt.Fprintln(l.Writer, s)
default: // Otherwise, print the list
ts := l.getTaskStates()
l.clearThenPrint(ts)
l.StatusIndicator.Next()
time.Sleep(l.Delay)
}
}
}()
列表被格式化为字符串,然后被打印出来。以下函数格式化字符串:
// fmtPrint returns the formatted list of messages
// and statuses, using the supplied TaskStates
func (l *List) fmtPrint(ts []*TaskState) string {
s := make([]string, 0)
for _, t := range ts {
s = append(s, l.formatMessage(t))
}
return strings.Join(s, "\n")
}
并且以下函数构建 ANSI 转义字符串以清除行:
// fmtClear returns a string of ANSI escape characters
// to clear the `n` lines previously printed.
func (l *List) fmtClear(n int) string {
s := "3[1A" // Move up a line
s += "3[K" // Clear the line
s += "\r" // Move back to the beginning of the line
return strings.Repeat(s, n)
}
我使用 this site 作为 ANSI 代码的参考。
提前感谢您对发生这种情况的原因提出的任何建议!
如果我可以添加任何其他有用的信息,请告诉我。
我认为 ANSI 代码只是转移注意力。我下载了库并在本地尝试 运行ning,发现以下部分是导致此问题的原因:
case s := <-l.printQ: // Check if there's a message to print
fmt.Fprintln(l.Writer, s)
当 printQ
通道关闭时,这种情况有时是 运行ning,这似乎是将光标向下移动,即使没有打印任何内容。当我在调用 l.printDone
后移动调用以关闭频道时,此行为消失了。
...
// Wait for the print loop to finish
<-l.printDone
if l.printQ != nil {
close(l.printQ)
}
...
这确保当通道关闭时循环不再运行ning,因此s := <-l.printQ
情况不会运行。