perf(messageslist): stop copying the model and message to the heap per item build

This commit is contained in:
ayn2op 2026-10-15 18:58:10 -04:00
parent 60aa5f52f1
commit 871f36759c
3 changed files with 20 additions and 13 deletions

2
go.mod
View file

@ -15,7 +15,7 @@ require (
github.com/andybalholm/brotli v1.2.6 github.com/andybalholm/brotli v1.2.6
github.com/ayn2op/arikawa/v3 v3.0.0-20261014035141-32b58d8f3755 github.com/ayn2op/arikawa/v3 v3.0.0-20261014035141-32b58d8f3755
github.com/ayn2op/ningen/v3 v3.0.1-0.20261014035153-5e43590a1426 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/fhttp v0.6.9
github.com/bogdanfinn/tls-client v1.16.0 github.com/bogdanfinn/tls-client v1.16.0
github.com/gdamore/tcell/v3 v3.6.0 github.com/gdamore/tcell/v3 v3.6.0

4
go.sum
View file

@ -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/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 h1:0ndRWpAiSSgR0z9b7K+pwMlCHEk3VzQeS5ls+zWtbr0=
github.com/ayn2op/ningen/v3 v3.0.1-0.20261014035153-5e43590a1426/go.mod h1:4S2B9frwrjELeAldoqPgasA/VfnOySgRlektwYLuVks= 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-20261015233535-ab884c65f4fe h1:XBuBkzSmoomT6agxYt2d5+1WNF4T+AFuI52lZsrD8/I=
github.com/ayn2op/tview v0.0.0-20261015215920-c33abb9e4eb2/go.mod h1:3aWH6S5uOqOkZv7JFePEVHukTj9hPb6Lbynm59Sks2E= 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 h1:WdFv/oukjTJCLa79UfkGmwX7ZxONAihKu4V0mLIs11Q=
github.com/bdandy/go-errors v1.2.2/go.mod h1:NkYHl4Fey9oRRdbB1CoC6e84tuqQHiqrOcZpqFEkBxM= github.com/bdandy/go-errors v1.2.2/go.mod h1:NkYHl4Fey9oRRdbB1CoC6e84tuqQHiqrOcZpqFEkBxM=
github.com/bdandy/go-socks4 v1.2.3 h1:Q6Y2heY1GRjCtHbmlKfnwrKVU/k81LS8mRGLRlmDlic= github.com/bdandy/go-socks4 v1.2.3 h1:Q6Y2heY1GRjCtHbmlKfnwrKVU/k81LS8mRGLRlmDlic=

View file

@ -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)} 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 { if item.text == nil {
grouped := item.grouped item.text, item.reply = m.messageViews(*message, 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()
}}
} }
body := column.New(item.text).Height(layout.Shrink) body := column.New(item.text).Height(layout.Shrink)
if isVoiceMessage(message) { if isVoiceMessage(*message) {
body = body.Push(m.voiceMessageView(message.Attachments[0])) 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 { if preview, ok := item.previews[s.proxy]; ok {
body = body.Push(preview) body = body.Push(preview)
} }
@ -306,6 +300,19 @@ func (m Model) buildItem(index int) tview.Widget {
return body 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 { func (m Model) blocked(user discord.User) bool {
return m.cfg.HideBlockedUsers && m.state.UserIsBlocked(user.ID) return m.cfg.HideBlockedUsers && m.state.UserIsBlocked(user.ID)
} }