From 871f36759cbacdadc38cfe957cfdb693199f7609 Mon Sep 17 00:00:00 2001 From: ayn2op Date: Thu, 15 Oct 2026 18:58:10 -0400 Subject: [PATCH] perf(messageslist): stop copying the model and message to the heap per item build --- go.mod | 2 +- go.sum | 4 ++-- internal/ui/chat/messageslist/model.go | 27 ++++++++++++++++---------- 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/go.mod b/go.mod index 344d017..e3fd9f6 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( github.com/andybalholm/brotli v1.2.6 github.com/ayn2op/arikawa/v3 v3.0.0-20261014035141-32b58d8f3755 github.com/ayn2op/ningen/v3 v3.0.1-0.20261014035153-5e43590a1426 - github.com/ayn2op/tview v0.0.0-20261015215920-c33abb9e4eb2 + github.com/ayn2op/tview v0.0.0-20261015233535-ab884c65f4fe github.com/bogdanfinn/fhttp v0.6.9 github.com/bogdanfinn/tls-client v1.16.0 github.com/gdamore/tcell/v3 v3.6.0 diff --git a/go.sum b/go.sum index d24c256..e97bf98 100644 --- a/go.sum +++ b/go.sum @@ -14,8 +14,8 @@ github.com/ayn2op/arikawa/v3 v3.0.0-20261014035141-32b58d8f3755 h1:XHo9kizImaAEt github.com/ayn2op/arikawa/v3 v3.0.0-20261014035141-32b58d8f3755/go.mod h1:YARcqZmk7hDjXB+lexlXX4wDFHULafbGnF0gsD0tuNw= github.com/ayn2op/ningen/v3 v3.0.1-0.20261014035153-5e43590a1426 h1:0ndRWpAiSSgR0z9b7K+pwMlCHEk3VzQeS5ls+zWtbr0= github.com/ayn2op/ningen/v3 v3.0.1-0.20261014035153-5e43590a1426/go.mod h1:4S2B9frwrjELeAldoqPgasA/VfnOySgRlektwYLuVks= -github.com/ayn2op/tview v0.0.0-20261015215920-c33abb9e4eb2 h1:uGLp4qH9uegcYax3fruZ+FyadoXZpEIyYova0gK8nkM= -github.com/ayn2op/tview v0.0.0-20261015215920-c33abb9e4eb2/go.mod h1:3aWH6S5uOqOkZv7JFePEVHukTj9hPb6Lbynm59Sks2E= +github.com/ayn2op/tview v0.0.0-20261015233535-ab884c65f4fe h1:XBuBkzSmoomT6agxYt2d5+1WNF4T+AFuI52lZsrD8/I= +github.com/ayn2op/tview v0.0.0-20261015233535-ab884c65f4fe/go.mod h1:3aWH6S5uOqOkZv7JFePEVHukTj9hPb6Lbynm59Sks2E= github.com/bdandy/go-errors v1.2.2 h1:WdFv/oukjTJCLa79UfkGmwX7ZxONAihKu4V0mLIs11Q= github.com/bdandy/go-errors v1.2.2/go.mod h1:NkYHl4Fey9oRRdbB1CoC6e84tuqQHiqrOcZpqFEkBxM= github.com/bdandy/go-socks4 v1.2.3 h1:Q6Y2heY1GRjCtHbmlKfnwrKVU/k81LS8mRGLRlmDlic= diff --git a/internal/ui/chat/messageslist/model.go b/internal/ui/chat/messageslist/model.go index 5bee0dd..94ac4ec 100644 --- a/internal/ui/chat/messageslist/model.go +++ b/internal/ui/chat/messageslist/model.go @@ -267,23 +267,17 @@ func (m Model) buildItem(index int) tview.Widget { return dateSeparator{date: date, fill: m.cfg.DateSeparator.Character, style: m.cfg.Theme.MessagesList.MessageStyle.Style.Dim(true)} } - message, style := item.message, m.cfg.Theme.MessagesList.MessageStyle.Style + message := &item.message if item.text == nil { - grouped := item.grouped - item.text = &messageView{render: func(width int) richtext.Text { return m.renderMessage(message, style, width, grouped) }} - item.reply = &messageView{render: func(int) richtext.Text { - builder := new(richtext.Builder) - m.drawReply(builder, message, style) - return builder.Finish() - }} + item.text, item.reply = m.messageViews(*message, item.grouped) } body := column.New(item.text).Height(layout.Shrink) - if isVoiceMessage(message) { + if isVoiceMessage(*message) { body = body.Push(m.voiceMessageView(message.Attachments[0])) } - for _, s := range previewSources(message) { + for _, s := range previewSources(*message) { if preview, ok := item.previews[s.proxy]; ok { body = body.Push(preview) } @@ -306,6 +300,19 @@ func (m Model) buildItem(index int) tview.Widget { return body } +// messageViews returns the views of the text of message and of what it replies to. +// They capture the model and the message, so they are made here: capturing them in buildItem would copy both to the heap on every call, as the list builds every item each time it is laid out. +func (m Model) messageViews(message discord.Message, grouped bool) (text, reply *messageView) { + style := m.cfg.Theme.MessagesList.MessageStyle.Style + text = &messageView{render: func(width int) richtext.Text { return m.renderMessage(message, style, width, grouped) }} + reply = &messageView{render: func(int) richtext.Text { + builder := new(richtext.Builder) + m.drawReply(builder, message, style) + return builder.Finish() + }} + return text, reply +} + func (m Model) blocked(user discord.User) bool { return m.cfg.HideBlockedUsers && m.state.UserIsBlocked(user.ID) }