Show square brackets in the neoirc-cli message window as written (closes #233)
check / check (push) Canceled after 0s

The message window has colour tags on, so tview read square-bracket
text in nicks, channel names, reasons, topics and message text as
tags. It dropped that text or changed the colour of the rest of the
line.

A new escape in internal/cli/ui.go writes each "[" as "[[:]", and
app.go applies it to every part of a line that comes from the user or
the server. tview.Escape is not used: it misses bracket pairs holding
symbols such as "|", and tags with a URL.

Lines for other server commands now show the command, such as
"[MODE]", which tview had read as a tag.

Model: opus-5-5
This commit is contained in:
2026-11-02 05:28:25 +00:00
parent 1e2f0de877
commit 1f445f286a
3 changed files with 210 additions and 55 deletions
+54 -52
View File
@@ -108,7 +108,7 @@ func (a *App) handleInput(text string) {
})
if err != nil {
a.ui.AddStatus(
"[red]Send error: " + err.Error(),
"[red]Send error: " + escape(err.Error()),
)
}
}
@@ -166,7 +166,7 @@ func (a *App) dispatchInfoCommand(cmd, args string) {
a.cmdWhois(args)
default:
a.ui.AddStatus(
"[red]Unknown command: " + cmd,
"[red]Unknown command: " + escape(cmd),
)
}
}
@@ -182,7 +182,7 @@ func (a *App) cmdConnect(serverURL string) {
serverURL = strings.TrimRight(serverURL, "/")
a.ui.AddStatus("Connecting to " + serverURL + "...")
a.ui.AddStatus("Connecting to " + escape(serverURL) + "...")
a.mu.Lock()
nick := a.nick
@@ -192,9 +192,9 @@ func (a *App) cmdConnect(serverURL string) {
resp, err := client.CreateSession(nick)
if err != nil {
a.ui.AddStatus(fmt.Sprintf(
"[red]Connection failed: %v", err,
))
a.ui.AddStatus(
"[red]Connection failed: " + escape(err.Error()),
)
return
}
@@ -218,7 +218,7 @@ func (a *App) cmdConnect(serverURL string) {
a.ui.AddStatus(fmt.Sprintf(
"[green]Connected! Nick: %s, Session: %d",
resp.Nick, resp.ID,
escape(resp.Nick), resp.ID,
))
a.ui.SetStatus(resp.Nick, "", "connected")
@@ -244,7 +244,7 @@ func (a *App) cmdNick(nick string) {
a.mu.Unlock()
a.ui.AddStatus(
"Nick set to " + nick +
"Nick set to " + escape(nick) +
" (will be used on connect)",
)
@@ -256,9 +256,9 @@ func (a *App) cmdNick(nick string) {
Body: []string{nick},
})
if err != nil {
a.ui.AddStatus(fmt.Sprintf(
"[red]Nick change failed: %v", err,
))
a.ui.AddStatus(
"[red]Nick change failed: " + escape(err.Error()),
)
return
}
@@ -269,7 +269,7 @@ func (a *App) cmdNick(nick string) {
a.mu.Unlock()
a.ui.SetStatus(nick, target, "connected")
a.ui.AddStatus("Nick changed to " + nick)
a.ui.AddStatus("Nick changed to " + escape(nick))
}
func (a *App) cmdJoin(channel string) {
@@ -297,9 +297,9 @@ func (a *App) cmdJoin(channel string) {
err := a.client.JoinChannel(channel)
if err != nil {
a.ui.AddStatus(fmt.Sprintf(
"[red]Join failed: %v", err,
))
a.ui.AddStatus(
"[red]Join failed: " + escape(err.Error()),
)
return
}
@@ -337,9 +337,9 @@ func (a *App) cmdPart(channel string) {
err := a.client.PartChannel(channel)
if err != nil {
a.ui.AddStatus(fmt.Sprintf(
"[red]Part failed: %v", err,
))
a.ui.AddStatus(
"[red]Part failed: " + escape(err.Error()),
)
return
}
@@ -384,9 +384,9 @@ func (a *App) cmdMsg(args string) {
Body: []string{text},
})
if err != nil {
a.ui.AddStatus(fmt.Sprintf(
"[red]Send failed: %v", err,
))
a.ui.AddStatus(
"[red]Send failed: " + escape(err.Error()),
)
}
}
@@ -439,9 +439,9 @@ func (a *App) cmdTopic(args string) {
Body: []string{args},
})
if err != nil {
a.ui.AddStatus(fmt.Sprintf(
"[red]Topic set failed: %v", err,
))
a.ui.AddStatus(
"[red]Topic set failed: " + escape(err.Error()),
)
}
}
@@ -465,9 +465,9 @@ func (a *App) cmdNames() {
members, err := a.client.GetMembers(target)
if err != nil {
a.ui.AddStatus(fmt.Sprintf(
"[red]Names failed: %v", err,
))
a.ui.AddStatus(
"[red]Names failed: " + escape(err.Error()),
)
return
}
@@ -480,7 +480,7 @@ func (a *App) cmdNames() {
a.ui.AddLine(target, fmt.Sprintf(
"[cyan]*** Members of %s: %s",
target, strings.Join(nicks, " "),
escape(target), escape(strings.Join(nicks, " ")),
))
}
@@ -511,9 +511,9 @@ func (a *App) cmdList() {
channels, err := a.client.ListChannels()
if err != nil {
a.ui.AddStatus(fmt.Sprintf(
"[red]List failed: %v", err,
))
a.ui.AddStatus(
"[red]List failed: " + escape(err.Error()),
)
return
}
@@ -522,7 +522,7 @@ func (a *App) cmdList() {
for _, ch := range channels {
a.ui.AddStatus(fmt.Sprintf(
" %s %s", ch.Name, ch.Topic,
" %s %s", escape(ch.Name), escape(ch.Topic),
))
}
@@ -544,9 +544,9 @@ func (a *App) cmdMotd() {
&api.Message{Command: irc.CmdMotd}, //nolint:exhaustruct
)
if err != nil {
a.ui.AddStatus(fmt.Sprintf(
"[red]MOTD failed: %v", err,
))
a.ui.AddStatus(
"[red]MOTD failed: " + escape(err.Error()),
)
}
}
@@ -582,9 +582,9 @@ func (a *App) cmdWho(args string) {
},
)
if err != nil {
a.ui.AddStatus(fmt.Sprintf(
"[red]WHO failed: %v", err,
))
a.ui.AddStatus(
"[red]WHO failed: " + escape(err.Error()),
)
}
}
@@ -613,9 +613,9 @@ func (a *App) cmdWhois(args string) {
},
)
if err != nil {
a.ui.AddStatus(fmt.Sprintf(
"[red]WHOIS failed: %v", err,
))
a.ui.AddStatus(
"[red]WHOIS failed: " + escape(err.Error()),
)
}
}
@@ -818,7 +818,7 @@ func (a *App) handlePrivmsgEvent(
a.ui.AddLine(target, fmt.Sprintf(
"[gray]%s [green]<%s>[white] %s",
timestamp, msg.From, text,
timestamp, escape(msg.From), escape(text),
))
}
@@ -831,7 +831,7 @@ func (a *App) handleJoinEvent(
a.ui.AddLine(msg.To, fmt.Sprintf(
"[gray]%s [yellow]*** %s has joined %s",
timestamp, msg.From, msg.To,
timestamp, escape(msg.From), escape(msg.To),
))
}
@@ -850,11 +850,11 @@ func (a *App) handlePartEvent(
line := fmt.Sprintf(
"[gray]%s [yellow]*** %s has left %s",
timestamp, msg.From, msg.To,
timestamp, escape(msg.From), escape(msg.To),
)
if reason != "" {
line += " (" + reason + ")"
line += " (" + escape(reason) + ")"
}
if msg.From == myNick {
@@ -875,12 +875,12 @@ func (a *App) handleQuitEvent(
if reason != "" {
a.ui.AddStatus(fmt.Sprintf(
"[gray]%s [yellow]*** %s has quit (%s)",
timestamp, msg.From, reason,
timestamp, escape(msg.From), escape(reason),
))
} else {
a.ui.AddStatus(fmt.Sprintf(
"[gray]%s [yellow]*** %s has quit",
timestamp, msg.From,
timestamp, escape(msg.From),
))
}
}
@@ -907,7 +907,7 @@ func (a *App) handleNickEvent(
a.ui.AddStatus(fmt.Sprintf(
"[gray]%s [yellow]*** %s is now known as %s",
timestamp, msg.From, newNick,
timestamp, escape(msg.From), escape(newNick),
))
}
@@ -919,7 +919,7 @@ func (a *App) handleNoticeEvent(
a.ui.AddStatus(fmt.Sprintf(
"[gray]%s [magenta]--%s-- %s",
timestamp, msg.From, text,
timestamp, escape(msg.From), escape(text),
))
}
@@ -935,7 +935,7 @@ func (a *App) handleTopicEvent(
a.ui.AddLine(msg.To, fmt.Sprintf(
"[gray]%s [cyan]*** %s set topic: %s",
timestamp, msg.From, text,
timestamp, escape(msg.From), escape(text),
))
}
@@ -945,10 +945,12 @@ func (a *App) handleDefaultEvent(
lines := msg.BodyLines()
text := strings.Join(lines, " ")
// The brackets around the command are escaped with it, as tview
// reads a command such as "[MODE]" as a tag.
if text != "" {
a.ui.AddStatus(fmt.Sprintf(
"[gray]%s [white][%s] %s",
timestamp, msg.Command, text,
"[gray]%s [white]%s %s",
timestamp, escape("["+msg.Command+"]"), escape(text),
))
}
}