Procházet zdrojové kódy

Squash merge commands into main

ayn2op před 10 měsíci
rodič
revize
a58ee99917

+ 5 - 5
cmd/application.go

@@ -24,7 +24,7 @@ type application struct {
 	pages        *tview.Pages
 	flex         *tview.Flex
 	guildsTree   *guildsTree
-	messagesText *messagesText
+	messagesList *messagesList
 	messageInput *messageInput
 }
 
@@ -36,7 +36,7 @@ func newApplication(cfg *config.Config) *application {
 		pages:        tview.NewPages(),
 		flex:         tview.NewFlex(),
 		guildsTree:   newGuildsTree(cfg),
-		messagesText: newMessagesText(cfg),
+		messagesList: newMessagesList(cfg),
 		messageInput: newMessageInput(cfg),
 	}
 
@@ -82,7 +82,7 @@ func (a *application) init() {
 
 	right := tview.NewFlex().
 		SetDirection(tview.FlexRow).
-		AddItem(a.messagesText, 0, 1, false).
+		AddItem(a.messagesList, 0, 1, false).
 		AddItem(a.messageInput, 3, 1, false)
 
 	// The guilds tree is always focused first at start-up.
@@ -112,9 +112,9 @@ func (a *application) onFlexInputCapture(event *tcell.EventKey) *tcell.EventKey
 		a.messageInput.removeMentionsList()
 		a.SetFocus(app.guildsTree)
 		return nil
-	case a.cfg.Keys.FocusMessagesText:
+	case a.cfg.Keys.FocusMessagesList:
 		a.messageInput.removeMentionsList()
-		a.SetFocus(app.messagesText)
+		a.SetFocus(app.messagesList)
 		return nil
 	case a.cfg.Keys.FocusMessageInput:
 		a.SetFocus(app.messageInput)

+ 3 - 3
cmd/guilds_tree.go

@@ -192,9 +192,9 @@ func (gt *guildsTree) onSelected(node *tview.TreeNode) {
 			return
 		}
 
-		app.messagesText.reset()
-		app.messagesText.drawMsgs(channel.ID)
-		app.messagesText.
+		app.messagesList.reset()
+		app.messagesList.drawMsgs(channel.ID)
+		app.messagesList.
 			ScrollToEnd().
 			SetTitle(gt.channelToString(*channel))
 

+ 6 - 6
cmd/message_input.go

@@ -160,8 +160,8 @@ func (mi *messageInput) send() {
 	}()
 
 	mi.reset()
-	app.messagesText.Highlight()
-	app.messagesText.ScrollToEnd()
+	app.messagesList.Highlight()
+	app.messagesList.ScrollToEnd()
 }
 
 func processText(cID discord.ChannelID, src []byte) string {
@@ -331,10 +331,10 @@ func (mi *messageInput) searchMember(gID discord.GuildID, name string) {
 		return
 	}
 	mi.lastSearch = time.Now()
-	app.messagesText.waitForChunkEvent()
-	app.messagesText.setFetchingChunk(true, 0)
+	app.messagesList.waitForChunkEvent()
+	app.messagesList.setFetchingChunk(true, 0)
 	discordState.MemberState.SearchMember(gID, name)
-	mi.cache.Create(key, app.messagesText.waitForChunkEvent())
+	mi.cache.Create(key, app.messagesList.waitForChunkEvent())
 }
 
 func isValidUserRune(x rune) bool {
@@ -352,7 +352,7 @@ func (mi *messageInput) showMentionList(col int) {
 	l := mi.autocomplete
 	x, _, _, _ := mi.GetInnerRect()
 	_, y, _, _ := mi.GetRect()
-	_, _, maxW, maxH := app.messagesText.GetInnerRect()
+	_, _, maxW, maxH := app.messagesList.GetInnerRect()
 	if t := int(mi.cfg.Theme.Autocomplete.MaxHeight); t != 0 {
 		maxH = min(maxH, t)
 	}

+ 160 - 160
cmd/messages_text.go → cmd/messages_list.go

@@ -27,7 +27,7 @@ import (
 	"github.com/yuin/goldmark/text"
 )
 
-type messagesText struct {
+type messagesList struct {
 	*tview.TextView
 	cfg               *config.Config
 	selectedMessageID discord.MessageID
@@ -40,50 +40,50 @@ type messagesText struct {
 	}
 }
 
-func newMessagesText(cfg *config.Config) *messagesText {
-	mt := &messagesText{
+func newMessagesList(cfg *config.Config) *messagesList {
+	ml := &messagesList{
 		TextView: tview.NewTextView(),
 		cfg:      cfg,
 	}
 
-	mt.Box = ui.ConfigureBox(mt.Box, &cfg.Theme)
-	mt.
+	ml.Box = ui.ConfigureBox(ml.Box, &cfg.Theme)
+	ml.
 		SetDynamicColors(true).
 		SetRegions(true).
 		SetWordWrap(true).
 		ScrollToEnd().
-		SetHighlightedFunc(mt.onHighlighted).
+		SetHighlightedFunc(ml.onHighlighted).
 		SetTextColor(tcell.ColorDefault).
 		SetTitle("Messages").
-		SetInputCapture(mt.onInputCapture)
+		SetInputCapture(ml.onInputCapture)
 
-	markdown.DefaultRenderer.AddOptions(renderer.WithOption("theme", cfg.Theme.MessagesText))
-	return mt
+	markdown.DefaultRenderer.AddOptions(renderer.WithOption("theme", cfg.Theme.MessagesList))
+	return ml
 }
 
-func (mt *messagesText) drawMsgs(cID discord.ChannelID) {
-	msgs, err := discordState.Messages(cID, uint(mt.cfg.MessagesLimit))
+func (ml *messagesList) drawMsgs(cID discord.ChannelID) {
+	msgs, err := discordState.Messages(cID, uint(ml.cfg.MessagesLimit))
 	if err != nil {
 		slog.Error("failed to get messages", "err", err, "channel_id", cID)
 		return
 	}
 
-	if app.cfg.Theme.MessagesText.ShowNicknames || app.cfg.Theme.MessagesText.ShowUsernameColors {
+	if app.cfg.Theme.MessagesList.ShowNicknames || app.cfg.Theme.MessagesList.ShowUsernameColors {
 		if ch, _ := discordState.Cabinet.Channel(cID); ch.GuildID.IsValid() {
-			mt.requestGuildMembers(ch.GuildID, msgs)
+			ml.requestGuildMembers(ch.GuildID, msgs)
 		}
 	}
 
 	for _, m := range slices.Backward(msgs) {
-		mt.createMsg(m)
+		ml.createMsg(m)
 	}
 }
 
-func (mt *messagesText) reset() {
-	mt.selectedMessageID = 0
+func (ml *messagesList) reset() {
+	ml.selectedMessageID = 0
 	app.messageInput.replyMessageID = 0
 
-	mt.
+	ml.
 		Clear().
 		Highlight().
 		SetTitle("")
@@ -91,60 +91,60 @@ func (mt *messagesText) reset() {
 
 // Region tags are square brackets that contain a region ID in double quotes
 // https://pkg.go.dev/github.com/ayn2op/tview#hdr-Regions_and_Highlights
-func (mt *messagesText) startRegion(msgID discord.MessageID) {
-	fmt.Fprintf(mt, `["%s"]`, msgID)
+func (ml *messagesList) startRegion(msgID discord.MessageID) {
+	fmt.Fprintf(ml, `["%s"]`, msgID)
 }
 
 // Tags with no region ID ([""]) don't start new regions. They can therefore be used to mark the end of a region.
-func (mt *messagesText) endRegion() {
-	fmt.Fprint(mt, `[""]`)
+func (ml *messagesList) endRegion() {
+	fmt.Fprint(ml, `[""]`)
 }
 
-func (mt *messagesText) createMsg(msg discord.Message) {
-	mt.startRegion(msg.ID)
-	defer mt.endRegion()
+func (ml *messagesList) createMsg(msg discord.Message) {
+	ml.startRegion(msg.ID)
+	defer ml.endRegion()
 
-	if mt.cfg.HideBlockedUsers {
+	if ml.cfg.HideBlockedUsers {
 		isBlocked := discordState.UserIsBlocked(msg.Author.ID)
 		if isBlocked {
-			io.WriteString(mt, "[:red:b]Blocked message[:-:-]")
+			io.WriteString(ml, "[:red:b]Blocked message[:-:-]")
 			return
 		}
 	}
 
 	// reset
-	io.WriteString(mt, "[-:-:-]")
+	io.WriteString(ml, "[-:-:-]")
 
 	switch msg.Type {
 	case discord.DefaultMessage:
 		if msg.Reference != nil && msg.Reference.Type == discord.MessageReferenceTypeForward {
-			mt.createForwardedMsg(msg)
+			ml.createForwardedMsg(msg)
 		} else {
-			mt.createDefaultMsg(msg)
+			ml.createDefaultMsg(msg)
 		}
 	case discord.InlinedReplyMessage:
-		mt.createReplyMsg(msg)
+		ml.createReplyMsg(msg)
 	case discord.ChannelPinnedMessage:
-		fmt.Fprintf(mt, "%s pinned a message", msg.Author.DisplayOrUsername())
+		fmt.Fprintf(ml, "%s pinned a message", msg.Author.DisplayOrUsername())
 	default:
-		mt.drawTimestamps(msg.Timestamp)
-		mt.drawAuthor(msg)
+		ml.drawTimestamps(msg.Timestamp)
+		ml.drawAuthor(msg)
 	}
 
-	fmt.Fprintln(mt)
+	fmt.Fprintln(ml)
 }
 
-func (mt *messagesText) formatTimestamp(ts discord.Timestamp) string {
-	return ts.Time().In(time.Local).Format(mt.cfg.Timestamps.Format)
+func (ml *messagesList) formatTimestamp(ts discord.Timestamp) string {
+	return ts.Time().In(time.Local).Format(ml.cfg.Timestamps.Format)
 }
 
-func (mt *messagesText) drawTimestamps(ts discord.Timestamp) {
-	fmt.Fprintf(mt, "[::d]%s[::D] ", mt.formatTimestamp(ts))
+func (ml *messagesList) drawTimestamps(ts discord.Timestamp) {
+	fmt.Fprintf(ml, "[::d]%s[::D] ", ml.formatTimestamp(ts))
 }
 
-func (mt *messagesText) drawAuthor(msg discord.Message) {
+func (ml *messagesList) drawAuthor(msg discord.Message) {
 	name := msg.Author.DisplayOrUsername()
-	style := mt.cfg.Theme.MessagesText.AuthorStyle
+	style := ml.cfg.Theme.MessagesList.AuthorStyle
 
 	if msg.GuildID.IsValid() {
 		member, err := discordState.Cabinet.Member(msg.GuildID, msg.Author.ID)
@@ -153,11 +153,11 @@ func (mt *messagesText) drawAuthor(msg discord.Message) {
 			return
 		}
 
-		if app.cfg.Theme.MessagesText.ShowNicknames && member.Nick != "" {
+		if app.cfg.Theme.MessagesList.ShowNicknames && member.Nick != "" {
 			name = member.Nick
 		}
 
-		if app.cfg.Theme.MessagesText.ShowUsernameColors {
+		if app.cfg.Theme.MessagesList.ShowUsernameColors {
 			color, ok := state.MemberColor(member, func(id discord.RoleID) *discord.Role {
 				r, _ := discordState.Cabinet.Role(msg.GuildID, id)
 				return r
@@ -170,77 +170,77 @@ func (mt *messagesText) drawAuthor(msg discord.Message) {
 	}
 
 	fg, bg, _ := style.Decompose()
-	_, _ = fmt.Fprintf(mt, "[%s:%s]%s[-] ", fg.String(), bg.String(), name)
+	_, _ = fmt.Fprintf(ml, "[%s:%s]%s[-] ", fg.String(), bg.String(), name)
 }
 
-func (mt *messagesText) drawContent(msg discord.Message) {
+func (ml *messagesList) drawContent(msg discord.Message) {
 	c := []byte(tview.Escape(msg.Content))
 	ast := discordmd.ParseWithMessage(c, *discordState.Cabinet, &msg, false)
 	if app.cfg.Markdown {
-		markdown.DefaultRenderer.Render(mt, c, ast)
+		markdown.DefaultRenderer.Render(ml, c, ast)
 	} else {
-		mt.Write(c) // write the content as is
+		ml.Write(c) // write the content as is
 	}
 }
 
-func (mt *messagesText) drawSnapshotContent(msg discord.MessageSnapshotMessage) {
+func (ml *messagesList) drawSnapshotContent(msg discord.MessageSnapshotMessage) {
 	c := []byte(tview.Escape(msg.Content))
 	// discordmd doesn't support MessageSnapshotMessage, so we just use write it as is. todo?
-	mt.Write(c)
+	ml.Write(c)
 }
 
-func (mt *messagesText) createDefaultMsg(msg discord.Message) {
-	if mt.cfg.Timestamps.Enabled {
-		mt.drawTimestamps(msg.Timestamp)
+func (ml *messagesList) createDefaultMsg(msg discord.Message) {
+	if ml.cfg.Timestamps.Enabled {
+		ml.drawTimestamps(msg.Timestamp)
 	}
 
-	mt.drawAuthor(msg)
-	mt.drawContent(msg)
+	ml.drawAuthor(msg)
+	ml.drawContent(msg)
 
 	if msg.EditedTimestamp.IsValid() {
-		io.WriteString(mt, " [::d](edited)[::D]")
+		io.WriteString(ml, " [::d](edited)[::D]")
 	}
 
 	for _, a := range msg.Attachments {
-		fmt.Fprintln(mt)
+		fmt.Fprintln(ml)
 
-		fg, bg, _ := mt.cfg.Theme.MessagesText.AttachmentStyle.Decompose()
-		if mt.cfg.ShowAttachmentLinks {
-			fmt.Fprintf(mt, "[%s:%s][%s]:\n%s[-]", fg, bg, a.Filename, a.URL)
+		fg, bg, _ := ml.cfg.Theme.MessagesList.AttachmentStyle.Decompose()
+		if ml.cfg.ShowAttachmentLinks {
+			fmt.Fprintf(ml, "[%s:%s][%s]:\n%s[-]", fg, bg, a.Filename, a.URL)
 		} else {
-			fmt.Fprintf(mt, "[%s:%s][%s][-]", fg, bg, a.Filename)
+			fmt.Fprintf(ml, "[%s:%s][%s][-]", fg, bg, a.Filename)
 		}
 	}
 }
 
-func (mt *messagesText) createReplyMsg(msg discord.Message) {
+func (ml *messagesList) createReplyMsg(msg discord.Message) {
 	// reply
-	fmt.Fprintf(mt, "[::d]%s ", mt.cfg.Theme.MessagesText.ReplyIndicator)
+	fmt.Fprintf(ml, "[::d]%s ", ml.cfg.Theme.MessagesList.ReplyIndicator)
 	if refMsg := msg.ReferencedMessage; refMsg != nil {
 		refMsg.GuildID = msg.GuildID
-		mt.drawAuthor(*refMsg)
-		mt.drawContent(*refMsg)
+		ml.drawAuthor(*refMsg)
+		ml.drawContent(*refMsg)
 	}
 
-	io.WriteString(mt, tview.NewLine)
+	io.WriteString(ml, tview.NewLine)
 	// main
-	mt.createDefaultMsg(msg)
+	ml.createDefaultMsg(msg)
 }
 
-func (mt *messagesText) createForwardedMsg(msg discord.Message) {
-	mt.drawTimestamps(msg.Timestamp)
-	mt.drawAuthor(msg)
-	fmt.Fprintf(mt, "[::d]%s [::-]", mt.cfg.Theme.MessagesText.ForwardedIndicator)
-	mt.drawSnapshotContent(msg.MessageSnapshots[0].Message)
-	fmt.Fprintf(mt, " [::d](%s)[-:-:-] ", mt.formatTimestamp(msg.MessageSnapshots[0].Message.Timestamp))
+func (ml *messagesList) createForwardedMsg(msg discord.Message) {
+	ml.drawTimestamps(msg.Timestamp)
+	ml.drawAuthor(msg)
+	fmt.Fprintf(ml, "[::d]%s [::-]", ml.cfg.Theme.MessagesList.ForwardedIndicator)
+	ml.drawSnapshotContent(msg.MessageSnapshots[0].Message)
+	fmt.Fprintf(ml, " [::d](%s)[-:-:-] ", ml.formatTimestamp(msg.MessageSnapshots[0].Message.Timestamp))
 }
 
-func (mt *messagesText) selectedMsg() (*discord.Message, error) {
-	if !mt.selectedMessageID.IsValid() {
+func (ml *messagesList) selectedMsg() (*discord.Message, error) {
+	if !ml.selectedMessageID.IsValid() {
 		return nil, errors.New("no message is currently selected")
 	}
 
-	msg, err := discordState.Cabinet.Message(app.guildsTree.selectedChannelID, mt.selectedMessageID)
+	msg, err := discordState.Cabinet.Message(app.guildsTree.selectedChannelID, ml.selectedMessageID)
 	if err != nil {
 		return nil, fmt.Errorf("could not retrieve selected message: %w", err)
 	}
@@ -248,14 +248,14 @@ func (mt *messagesText) selectedMsg() (*discord.Message, error) {
 	return msg, nil
 }
 
-func (mt *messagesText) selectedMsgIndex() (int, error) {
+func (ml *messagesList) selectedMsgIndex() (int, error) {
 	ms, err := discordState.Cabinet.Messages(app.guildsTree.selectedChannelID)
 	if err != nil {
 		return -1, err
 	}
 
 	for i, m := range ms {
-		if m.ID == mt.selectedMessageID {
+		if m.ID == ml.selectedMessageID {
 			return i, nil
 		}
 	}
@@ -263,89 +263,89 @@ func (mt *messagesText) selectedMsgIndex() (int, error) {
 	return -1, nil
 }
 
-func (mt *messagesText) onInputCapture(event *tcell.EventKey) *tcell.EventKey {
+func (ml *messagesList) onInputCapture(event *tcell.EventKey) *tcell.EventKey {
 	switch event.Name() {
-	case mt.cfg.Keys.MessagesText.Cancel:
-		mt.selectedMessageID = 0
+	case ml.cfg.Keys.MessagesList.Cancel:
+		ml.selectedMessageID = 0
 		app.messageInput.replyMessageID = 0
-		mt.Highlight()
-
-	case mt.cfg.Keys.MessagesText.SelectPrevious, mt.cfg.Keys.MessagesText.SelectNext, mt.cfg.Keys.MessagesText.SelectFirst, mt.cfg.Keys.MessagesText.SelectLast, mt.cfg.Keys.MessagesText.SelectReply:
-		mt._select(event.Name())
-	case mt.cfg.Keys.MessagesText.YankID:
-		mt.yankID()
-	case mt.cfg.Keys.MessagesText.YankContent:
-		mt.yankContent()
-	case mt.cfg.Keys.MessagesText.YankURL:
-		mt.yankURL()
-	case mt.cfg.Keys.MessagesText.Open:
-		mt.open()
-	case mt.cfg.Keys.MessagesText.Reply:
-		mt.reply(false)
-	case mt.cfg.Keys.MessagesText.ReplyMention:
-		mt.reply(true)
-	case mt.cfg.Keys.MessagesText.Delete:
-		mt.delete()
+		ml.Highlight()
+
+	case ml.cfg.Keys.MessagesList.SelectPrevious, ml.cfg.Keys.MessagesList.SelectNext, ml.cfg.Keys.MessagesList.SelectFirst, ml.cfg.Keys.MessagesList.SelectLast, ml.cfg.Keys.MessagesList.SelectReply:
+		ml._select(event.Name())
+	case ml.cfg.Keys.MessagesList.YankID:
+		ml.yankID()
+	case ml.cfg.Keys.MessagesList.YankContent:
+		ml.yankContent()
+	case ml.cfg.Keys.MessagesList.YankURL:
+		ml.yankURL()
+	case ml.cfg.Keys.MessagesList.Open:
+		ml.open()
+	case ml.cfg.Keys.MessagesList.Reply:
+		ml.reply(false)
+	case ml.cfg.Keys.MessagesList.ReplyMention:
+		ml.reply(true)
+	case ml.cfg.Keys.MessagesList.Delete:
+		ml.delete()
 	}
 
 	return nil
 }
 
-func (mt *messagesText) _select(name string) {
+func (ml *messagesList) _select(name string) {
 	ms, err := discordState.Cabinet.Messages(app.guildsTree.selectedChannelID)
 	if err != nil {
 		slog.Error("failed to get messages", "err", err, "channel_id", app.guildsTree.selectedChannelID)
 		return
 	}
 
-	msgIdx, err := mt.selectedMsgIndex()
+	msgIdx, err := ml.selectedMsgIndex()
 	if err != nil {
 		slog.Error("failed to get selected message", "err", err)
 		return
 	}
 
 	switch name {
-	case mt.cfg.Keys.MessagesText.SelectPrevious:
+	case ml.cfg.Keys.MessagesList.SelectPrevious:
 		// If no message is currently selected, select the latest message.
-		if len(mt.GetHighlights()) == 0 {
-			mt.selectedMessageID = ms[0].ID
+		if len(ml.GetHighlights()) == 0 {
+			ml.selectedMessageID = ms[0].ID
 		} else if msgIdx < len(ms)-1 {
-			mt.selectedMessageID = ms[msgIdx+1].ID
+			ml.selectedMessageID = ms[msgIdx+1].ID
 		} else {
 			return
 		}
-	case mt.cfg.Keys.MessagesText.SelectNext:
+	case ml.cfg.Keys.MessagesList.SelectNext:
 		// If no message is currently selected, select the latest message.
-		if len(mt.GetHighlights()) == 0 {
-			mt.selectedMessageID = ms[0].ID
+		if len(ml.GetHighlights()) == 0 {
+			ml.selectedMessageID = ms[0].ID
 		} else if msgIdx > 0 {
-			mt.selectedMessageID = ms[msgIdx-1].ID
+			ml.selectedMessageID = ms[msgIdx-1].ID
 		} else {
 			return
 		}
-	case mt.cfg.Keys.MessagesText.SelectFirst:
-		mt.selectedMessageID = ms[len(ms)-1].ID
-	case mt.cfg.Keys.MessagesText.SelectLast:
-		mt.selectedMessageID = ms[0].ID
-	case mt.cfg.Keys.MessagesText.SelectReply:
-		if mt.selectedMessageID == 0 {
+	case ml.cfg.Keys.MessagesList.SelectFirst:
+		ml.selectedMessageID = ms[len(ms)-1].ID
+	case ml.cfg.Keys.MessagesList.SelectLast:
+		ml.selectedMessageID = ms[0].ID
+	case ml.cfg.Keys.MessagesList.SelectReply:
+		if ml.selectedMessageID == 0 {
 			return
 		}
 
 		if ref := ms[msgIdx].ReferencedMessage; ref != nil {
 			for _, m := range ms {
 				if ref.ID == m.ID {
-					mt.selectedMessageID = m.ID
+					ml.selectedMessageID = m.ID
 				}
 			}
 		}
 	}
 
-	mt.Highlight(mt.selectedMessageID.String())
-	mt.ScrollToHighlight()
+	ml.Highlight(ml.selectedMessageID.String())
+	ml.ScrollToHighlight()
 }
 
-func (mt *messagesText) onHighlighted(added, removed, remaining []string) {
+func (ml *messagesList) onHighlighted(added, removed, remaining []string) {
 	if len(added) > 0 {
 		id, err := discord.ParseSnowflake(added[0])
 		if err != nil {
@@ -353,12 +353,12 @@ func (mt *messagesText) onHighlighted(added, removed, remaining []string) {
 			return
 		}
 
-		mt.selectedMessageID = discord.MessageID(id)
+		ml.selectedMessageID = discord.MessageID(id)
 	}
 }
 
-func (mt *messagesText) yankID() {
-	msg, err := mt.selectedMsg()
+func (ml *messagesList) yankID() {
+	msg, err := ml.selectedMsg()
 	if err != nil {
 		slog.Error("failed to get selected message", "err", err)
 		return
@@ -369,8 +369,8 @@ func (mt *messagesText) yankID() {
 	}
 }
 
-func (mt *messagesText) yankContent() {
-	msg, err := mt.selectedMsg()
+func (ml *messagesList) yankContent() {
+	msg, err := ml.selectedMsg()
 	if err != nil {
 		slog.Error("failed to get selected message", "err", err)
 		return
@@ -381,8 +381,8 @@ func (mt *messagesText) yankContent() {
 	}
 }
 
-func (mt *messagesText) yankURL() {
-	msg, err := mt.selectedMsg()
+func (ml *messagesList) yankURL() {
+	msg, err := ml.selectedMsg()
 	if err != nil {
 		slog.Error("failed to get selected message", "err", err)
 		return
@@ -393,8 +393,8 @@ func (mt *messagesText) yankURL() {
 	}
 }
 
-func (mt *messagesText) open() {
-	msg, err := mt.selectedMsg()
+func (ml *messagesList) open() {
+	msg, err := ml.selectedMsg()
 	if err != nil {
 		slog.Error("failed to get selected message", "err", err)
 		return
@@ -416,7 +416,7 @@ func (mt *messagesText) open() {
 			go openURL(msg.Attachments[0].URL)
 		}
 	} else {
-		mt.showAttachmentsList(urls, msg.Attachments)
+		ml.showAttachmentsList(urls, msg.Attachments)
 	}
 }
 
@@ -443,31 +443,31 @@ func extractURLs(content string) []string {
 	return urls
 }
 
-func (mt *messagesText) showAttachmentsList(urls []string, attachments []discord.Attachment) {
+func (ml *messagesList) showAttachmentsList(urls []string, attachments []discord.Attachment) {
 	list := tview.NewList().
 		SetWrapAround(true).
 		SetHighlightFullLine(true).
 		ShowSecondaryText(false).
 		SetDoneFunc(func() {
 			app.pages.RemovePage(attachmentsListPageName).SwitchToPage(flexPageName)
-			app.SetFocus(mt)
+			app.SetFocus(ml)
 		})
 	list.
 		SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
 			switch event.Name() {
-			case mt.cfg.Keys.MessagesText.SelectPrevious:
+			case ml.cfg.Keys.MessagesList.SelectPrevious:
 				return tcell.NewEventKey(tcell.KeyUp, 0, tcell.ModNone)
-			case mt.cfg.Keys.MessagesText.SelectNext:
+			case ml.cfg.Keys.MessagesList.SelectNext:
 				return tcell.NewEventKey(tcell.KeyDown, 0, tcell.ModNone)
-			case mt.cfg.Keys.MessagesText.SelectFirst:
+			case ml.cfg.Keys.MessagesList.SelectFirst:
 				return tcell.NewEventKey(tcell.KeyHome, 0, tcell.ModNone)
-			case mt.cfg.Keys.MessagesText.SelectLast:
+			case ml.cfg.Keys.MessagesList.SelectLast:
 				return tcell.NewEventKey(tcell.KeyEnd, 0, tcell.ModNone)
 			}
 
 			return event
 		})
-	list.Box = ui.ConfigureBox(list.Box, &mt.cfg.Theme)
+	list.Box = ui.ConfigureBox(list.Box, &ml.cfg.Theme)
 
 	for i, a := range attachments {
 		list.AddItem(a.Filename, "", rune('a'+i), func() {
@@ -492,7 +492,7 @@ func openURL(url string) {
 	}
 }
 
-func (mt *messagesText) reply(mention bool) {
+func (ml *messagesList) reply(mention bool) {
 	var title string
 	if mention {
 		title += "[@] Replying to "
@@ -500,7 +500,7 @@ func (mt *messagesText) reply(mention bool) {
 		title += "Replying to "
 	}
 
-	msg, err := mt.selectedMsg()
+	msg, err := ml.selectedMsg()
 	if err != nil {
 		slog.Error("failed to get selected message", "err", err)
 		return
@@ -515,19 +515,19 @@ func (mt *messagesText) reply(mention bool) {
 			return
 		}
 
-		if app.cfg.Theme.MessagesText.ShowNicknames && member.Nick != "" {
+		if app.cfg.Theme.MessagesList.ShowNicknames && member.Nick != "" {
 			name = member.Nick
 		}
 	}
 
 	title += name
 	app.messageInput.SetTitle(title)
-	app.messageInput.replyMessageID = mt.selectedMessageID
+	app.messageInput.replyMessageID = ml.selectedMessageID
 	app.SetFocus(app.messageInput)
 }
 
-func (mt *messagesText) delete() {
-	msg, err := mt.selectedMsg()
+func (ml *messagesList) delete() {
+	msg, err := ml.selectedMsg()
 	if err != nil {
 		slog.Error("failed to get selected message", "err", err)
 		return
@@ -554,9 +554,9 @@ func (mt *messagesText) delete() {
 		return
 	}
 
-	mt.selectedMessageID = 0
+	ml.selectedMessageID = 0
 	app.messageInput.replyMessageID = 0
-	mt.Highlight()
+	ml.Highlight()
 
 	if err := discordState.MessageRemove(app.guildsTree.selectedChannelID, msg.ID); err != nil {
 		slog.Error("failed to delete message", "err", err, "channel_id", app.guildsTree.selectedChannelID, "message_id", msg.ID)
@@ -567,7 +567,7 @@ func (mt *messagesText) delete() {
 	// its work after the event returns
 }
 
-func (mt *messagesText) requestGuildMembers(gID discord.GuildID, ms []discord.Message) {
+func (ml *messagesList) requestGuildMembers(gID discord.GuildID, ms []discord.Message) {
 	var usersToFetch []discord.UserID
 	for _, m := range ms {
 		if member, _ := discordState.Cabinet.Member(gID, m.Author.ID); member == nil {
@@ -585,37 +585,37 @@ func (mt *messagesText) requestGuildMembers(gID discord.GuildID, ms []discord.Me
 			return
 		}
 
-		mt.setFetchingChunk(true, 0)
-		mt.waitForChunkEvent()
+		ml.setFetchingChunk(true, 0)
+		ml.waitForChunkEvent()
 	}
 }
 
-func (mt *messagesText) setFetchingChunk(value bool, count uint) {
-	mt.fetchingMembers.mu.Lock()
-	defer mt.fetchingMembers.mu.Unlock()
+func (ml *messagesList) setFetchingChunk(value bool, count uint) {
+	ml.fetchingMembers.mu.Lock()
+	defer ml.fetchingMembers.mu.Unlock()
 
-	if mt.fetchingMembers.value == value {
+	if ml.fetchingMembers.value == value {
 		return
 	}
 
-	mt.fetchingMembers.value = value
+	ml.fetchingMembers.value = value
 
 	if value {
-		mt.fetchingMembers.done = make(chan struct{})
+		ml.fetchingMembers.done = make(chan struct{})
 	} else {
-		mt.fetchingMembers.count = count
-		close(mt.fetchingMembers.done)
+		ml.fetchingMembers.count = count
+		close(ml.fetchingMembers.done)
 	}
 }
 
-func (mt *messagesText) waitForChunkEvent() uint {
-	mt.fetchingMembers.mu.Lock()
-	if !mt.fetchingMembers.value {
-		mt.fetchingMembers.mu.Unlock()
+func (ml *messagesList) waitForChunkEvent() uint {
+	ml.fetchingMembers.mu.Lock()
+	if !ml.fetchingMembers.value {
+		ml.fetchingMembers.mu.Unlock()
 		return 0
 	}
-	mt.fetchingMembers.mu.Unlock()
+	ml.fetchingMembers.mu.Unlock()
 
-	<-mt.fetchingMembers.done
-	return mt.fetchingMembers.count
+	<-ml.fetchingMembers.done
+	return ml.fetchingMembers.count
 }

+ 4 - 4
cmd/state.go

@@ -37,7 +37,7 @@ func openState(token string) error {
 	discordState.AddHandler(onMessageDelete)
 
 	discordState.AddHandler(func(event *gateway.GuildMembersChunkEvent) {
-		app.messagesText.setFetchingChunk(false, uint(len(event.Members)))
+		app.messagesList.setFetchingChunk(false, uint(len(event.Members)))
 	})
 
 	discordState.AddHandler(func(event *gateway.GuildMemberRemoveEvent) {
@@ -109,7 +109,7 @@ func onReady(r *gateway.ReadyEvent) {
 func onMessageCreate(msg *gateway.MessageCreateEvent) {
 	if app.guildsTree.selectedChannelID.IsValid() &&
 		app.guildsTree.selectedChannelID == msg.ChannelID {
-		app.messagesText.createMsg(msg.Message)
+		app.messagesList.createMsg(msg.Message)
 		app.Draw()
 	}
 
@@ -120,8 +120,8 @@ func onMessageCreate(msg *gateway.MessageCreateEvent) {
 
 func onMessageDelete(msg *gateway.MessageDeleteEvent) {
 	if app.guildsTree.selectedChannelID == msg.ChannelID {
-		app.messagesText.reset()
-		app.messagesText.drawMsgs(msg.ChannelID)
+		app.messagesList.reset()
+		app.messagesList.drawMsgs(msg.ChannelID)
 		app.Draw()
 	}
 }

+ 3 - 3
internal/config/config.toml

@@ -41,7 +41,7 @@ user_agent = "default"
 # Esc: Reset message selection or close the channel selection popup.
 [keys]
 focus_guilds_tree = "Ctrl+G"
-focus_messages_text = "Ctrl+T"
+focus_messages_list = "Ctrl+T"
 focus_message_input = "Ctrl+P"
 # Hide/show the guilds tree.
 toggle_guilds_tree = "Ctrl+B"
@@ -63,7 +63,7 @@ collapse_parent_node = "Rune[-]"
 move_to_parent_node = "Rune[p]"
 
 # Only while focusing on sent messages
-[keys.messages_text]
+[keys.messages_list]
 select_previous = "Rune[k]"
 select_next = "Rune[j]"
 select_first = "Rune[g]"
@@ -129,7 +129,7 @@ private_channel_style = { foreground = "default", background = "default" }
 guild_style = { foreground = "default", background = "default" }
 channel_style = { foreground = "default", background = "default" }
 
-[theme.messages_text]
+[theme.messages_list]
 # Set to false to show messages with usernames instead of nicknames
 show_user_nicks = true
 show_user_colors = true

+ 3 - 3
internal/config/keys.go

@@ -10,12 +10,12 @@ type (
 
 	Keys struct {
 		FocusGuildsTree   string `toml:"focus_guilds_tree"`
-		FocusMessagesText string `toml:"focus_messages_text"`
+		FocusMessagesList string `toml:"focus_messages_list"`
 		FocusMessageInput string `toml:"focus_message_input"`
 		ToggleGuildsTree  string `toml:"toggle_guilds_tree"`
 
 		GuildsTree   GuildsTreeKeys   `toml:"guilds_tree"`
-		MessagesText MessagesTextKeys `toml:"messages_text"`
+		MessagesList MessagesListKeys `toml:"messages_list"`
 		MessageInput MessageInputKeys `toml:"message_input"`
 		Autocomplete AutocompleteKeys `toml:"autocomplete"`
 
@@ -32,7 +32,7 @@ type (
 		MoveToParentNode   string `toml:"move_to_parent_node"`
 	}
 
-	MessagesTextKeys struct {
+	MessagesListKeys struct {
 		NavigationKeys
 		SelectReply  string `toml:"select_reply"`
 		Reply        string `toml:"reply"`

+ 2 - 2
internal/config/theme.go

@@ -113,7 +113,7 @@ type (
 		ChannelStyle        StyleWrapper `toml:"channel_style"`
 	}
 
-	MessagesTextTheme struct {
+	MessagesListTheme struct {
 		ShowNicknames      bool `toml:"show_user_nicks"`
 		ShowUsernameColors bool `toml:"show_user_colors"`
 
@@ -142,7 +142,7 @@ type (
 		Border BorderTheme `toml:"border"`
 
 		GuildsTree   GuildsTreeTheme   `toml:"guilds_tree"`
-		MessagesText MessagesTextTheme `toml:"messages_text"`
+		MessagesList MessagesListTheme `toml:"messages_list"`
 		Autocomplete AutocompleteTheme `toml:"autocomplete"`
 	}
 )

+ 16 - 18
internal/markdown/renderer.go

@@ -1,3 +1,4 @@
+// Package markdown defines a renderer for tview style tags.
 package markdown
 
 import (
@@ -8,6 +9,7 @@ import (
 
 	"github.com/ayn2op/discordo/internal/config"
 	"github.com/diamondburned/ningen/v3/discordmd"
+	"github.com/gdamore/tcell/v2"
 	"github.com/yuin/goldmark/ast"
 	gmr "github.com/yuin/goldmark/renderer"
 )
@@ -34,6 +36,7 @@ func (r *renderer) AddOptions(opts ...gmr.Option) {
 }
 
 func (r *renderer) Render(w io.Writer, source []byte, node ast.Node) error {
+	theme := r.config.Options["theme"].(config.MessagesListTheme)
 	return ast.Walk(node, func(node ast.Node, entering bool) (ast.WalkStatus, error) {
 		switch node := node.(type) {
 		case *ast.Document:
@@ -45,9 +48,9 @@ func (r *renderer) Render(w io.Writer, source []byte, node ast.Node) error {
 		case *ast.FencedCodeBlock:
 			r.renderFencedCodeBlock(w, node, entering, source)
 		case *ast.AutoLink:
-			r.renderAutoLink(w, node, entering, source)
+			r.renderAutoLink(w, node, entering, source, theme.URLStyle.Style)
 		case *ast.Link:
-			r.renderLink(w, node, entering)
+			r.renderLink(w, node, entering, theme.URLStyle.Style)
 		case *ast.List:
 			r.renderList(w, node, entering)
 		case *ast.ListItem:
@@ -56,9 +59,9 @@ func (r *renderer) Render(w io.Writer, source []byte, node ast.Node) error {
 		case *discordmd.Inline:
 			r.renderInline(w, node, entering)
 		case *discordmd.Mention:
-			r.renderMention(w, node, entering)
+			r.renderMention(w, node, entering, theme.ShowNicknames, theme.MentionStyle.Style)
 		case *discordmd.Emoji:
-			r.renderEmoji(w, node, entering)
+			r.renderEmoji(w, node, entering, theme.EmojiStyle.Style)
 		}
 
 		return ast.WalkContinue, nil
@@ -95,10 +98,9 @@ func (r *renderer) renderFencedCodeBlock(w io.Writer, node *ast.FencedCodeBlock,
 	}
 }
 
-func (r *renderer) renderAutoLink(w io.Writer, node *ast.AutoLink, entering bool, source []byte) {
+func (r *renderer) renderAutoLink(w io.Writer, node *ast.AutoLink, entering bool, source []byte, urlStyle tcell.Style) {
 	if entering {
-		theme := r.config.Options["theme"].(config.MessagesTextTheme)
-		fg, bg, _ := theme.URLStyle.Decompose()
+		fg, bg, _ := urlStyle.Decompose()
 		_, _ = fmt.Fprintf(w, "[%s:%s]", fg, bg)
 		w.Write(node.URL(source))
 	} else {
@@ -106,10 +108,9 @@ func (r *renderer) renderAutoLink(w io.Writer, node *ast.AutoLink, entering bool
 	}
 }
 
-func (r *renderer) renderLink(w io.Writer, node *ast.Link, entering bool) {
+func (r *renderer) renderLink(w io.Writer, node *ast.Link, entering bool, urlStyle tcell.Style) {
 	if entering {
-		theme := r.config.Options["theme"].(config.MessagesTextTheme)
-		fg, bg, _ := theme.URLStyle.Decompose()
+		fg, bg, _ := urlStyle.Decompose()
 		_, _ = fmt.Fprintf(w, "[%s:%s::%s]", fg, bg, node.Destination)
 	} else {
 		io.WriteString(w, "[-:-::-]")
@@ -189,10 +190,9 @@ func (r *renderer) renderInline(w io.Writer, node *discordmd.Inline, entering bo
 	}
 }
 
-func (r *renderer) renderMention(w io.Writer, node *discordmd.Mention, entering bool) {
+func (r *renderer) renderMention(w io.Writer, node *discordmd.Mention, entering bool, showNicknames bool, mentionStyle tcell.Style) {
 	if entering {
-		theme := r.config.Options["theme"].(config.MessagesTextTheme)
-		fg, bg, _ := theme.MentionStyle.Decompose()
+		fg, bg, _ := mentionStyle.Decompose()
 		_, _ = fmt.Fprintf(w, "[%s:%s:b]", fg, bg)
 
 		switch {
@@ -200,8 +200,7 @@ func (r *renderer) renderMention(w io.Writer, node *discordmd.Mention, entering
 			io.WriteString(w, "#"+node.Channel.Name)
 		case node.GuildUser != nil:
 			username := node.GuildUser.DisplayOrUsername()
-			theme := r.config.Options["theme"].(config.MessagesTextTheme)
-			if theme.ShowNicknames && node.GuildUser.Member != nil && node.GuildUser.Member.Nick != "" {
+			if showNicknames && node.GuildUser.Member != nil && node.GuildUser.Member.Nick != "" {
 				username = node.GuildUser.Member.Nick
 			}
 			io.WriteString(w, "@"+username)
@@ -213,10 +212,9 @@ func (r *renderer) renderMention(w io.Writer, node *discordmd.Mention, entering
 	}
 }
 
-func (r *renderer) renderEmoji(w io.Writer, node *discordmd.Emoji, entering bool) {
+func (r *renderer) renderEmoji(w io.Writer, node *discordmd.Emoji, entering bool, emojiStyle tcell.Style) {
 	if entering {
-		theme := r.config.Options["theme"].(config.MessagesTextTheme)
-		fg, bg, _ := theme.EmojiStyle.Decompose()
+		fg, bg, _ := emojiStyle.Decompose()
 		fmt.Fprintf(w, "[%s:%s]", fg, bg)
 		io.WriteString(w, ":"+node.Name+":")
 	} else {