1 Commits
Author SHA1 Message Date
sneak 16bf9992af Keep neoirc-cli responding when a key or a typed line updates the screen (closes #223)
check / check (push) Canceled after 0s
The Enter handler of the input line and the Alt and digit key handler
ran the typed line on the tview event loop. Every UI method that
changes the screen waits for that loop, so the client stopped
responding. Each line now runs on its own goroutine, which first waits
for the line before it, so lines keep the order they were typed in.

/window now runs off the event loop. It reads the buffer name through
the new BufferName, which runs on the loop, and switches only when
that buffer exists.

One new test drives a running UI on a simulation screen with Alt and
1, then a typed line and Enter. Another holds the first line and
checks that the second waits for it.

Model: opus-5-5
2026-11-01 23:49:11 +00:00
2 changed files with 45 additions and 4 deletions
+5 -2
View File
@@ -610,14 +610,17 @@ func (a *App) cmdWindow(args string) {
_, _ = fmt.Sscanf(args, "%d", &bufIndex)
a.ui.SwitchBuffer(bufIndex)
a.mu.Lock()
nick := a.nick
a.mu.Unlock()
// Read the name before switching. A message from the server can
// add a buffer at any time, but only at the end, so bufIndex
// still names this buffer when SwitchBuffer runs.
name, found := a.ui.BufferName(bufIndex)
if found {
a.ui.SwitchBuffer(bufIndex)
if name != "(status)" {
a.mu.Lock()
a.target = name
+40 -2
View File
@@ -25,6 +25,10 @@ const (
// be handled, or for the UI to stop, before it fails.
const eventLoopTimeout = 5 * time.Second
// holdTime is how long TestLinesKeepOrder holds the first line,
// which gives the second line time to start too early.
const holdTime = 200 * time.Millisecond
func TestMemberPrefix(t *testing.T) {
t.Parallel()
@@ -283,8 +287,8 @@ func TestAltDigit(t *testing.T) {
}
// TestEventLoopKeepsRunning sends Alt and 1, then a typed line and
// Enter, to a running UI. It checks that both are handled, in that
// order, so the event loop did not stop on either.
// Enter, to a running UI. It checks that both are handled, so the
// event loop did not stop on either.
func TestEventLoopKeepsRunning(t *testing.T) {
t.Parallel()
@@ -310,6 +314,40 @@ func TestEventLoopKeepsRunning(t *testing.T) {
waitHandled(t, handled, testText)
}
// TestLinesKeepOrder holds the handling of a first line until a
// second line has been passed in. It fails if the second line is
// handled while the first is still held.
func TestLinesKeepOrder(t *testing.T) {
t.Parallel()
const firstLine, secondLine = "one", "two"
ui := NewUI()
handled := make(chan string)
release := make(chan struct{})
ui.OnInput(func(text string) {
if text == firstLine {
<-release
}
handled <- text
})
ui.callOnInput(firstLine)
ui.callOnInput(secondLine)
select {
case got := <-handled:
t.Fatalf("handled %q while the first line was held", got)
case <-time.After(holdTime):
}
close(release)
waitHandled(t, handled, firstLine)
waitHandled(t, handled, secondLine)
}
// TestOwnEventFromOtherClient checks that a message, notice,
// join or part that the user's session makes from another
// client shows as one line in the buffer it belongs to.