mirror of
https://github.com/diamondburned/cchat-gtk.git
synced 2024-10-31 19:44:23 +00:00
bumped discord; partial message reference support
This commit is contained in:
parent
744f59cf38
commit
c8f5446710
4
go.mod
4
go.mod
|
@ -4,8 +4,8 @@ go 1.14
|
|||
|
||||
replace github.com/gotk3/gotk3 => github.com/diamondburned/gotk3 v0.0.0-20201230071527-a77c32eb3876
|
||||
|
||||
// replace github.com/diamondburned/gotk3-tcmalloc => ../../gotk3-tcmalloc
|
||||
// replace github.com/diamondburned/cchat-discord => ../cchat-discord
|
||||
// replace github.com/diamondburned/gotk3-tcmalloc => ../../gotk3-tcmalloc
|
||||
// replace github.com/diamondburned/ningen/v2 => ../../ningen
|
||||
// replace github.com/diamondburned/arikawa/v2 => ../../arikawa
|
||||
|
||||
|
@ -13,7 +13,7 @@ require (
|
|||
github.com/Xuanwo/go-locale v1.0.0
|
||||
github.com/alecthomas/chroma v0.7.3
|
||||
github.com/diamondburned/cchat v0.3.15
|
||||
github.com/diamondburned/cchat-discord v0.0.0-20201227035212-6beff5225092
|
||||
github.com/diamondburned/cchat-discord v0.0.0-20201231025836-96e97aa11705
|
||||
github.com/diamondburned/cchat-mock v0.0.0-20201115033644-df8d1b10f9db
|
||||
github.com/diamondburned/gspell v0.0.0-20201229064336-e43698fd5828
|
||||
github.com/diamondburned/handy v0.0.0-20201229063418-ec23c1370374
|
||||
|
|
2
go.sum
2
go.sum
|
@ -66,6 +66,8 @@ github.com/diamondburned/cchat-discord v0.0.0-20201227023505-c4e360010fb8 h1:eyK
|
|||
github.com/diamondburned/cchat-discord v0.0.0-20201227023505-c4e360010fb8/go.mod h1:i3y8dyAFrtigpGOwunBdoJK/phwt9Gp/wfpVJb4imV0=
|
||||
github.com/diamondburned/cchat-discord v0.0.0-20201227035212-6beff5225092 h1:oxY7APUclLgaWjaTK++7kHBdl0GdVyqOvHQv68TcpHw=
|
||||
github.com/diamondburned/cchat-discord v0.0.0-20201227035212-6beff5225092/go.mod h1:rFBGZYLq0g6Pb/WGN/K0++kXrhCYlQQ1nc2FX4r8CO0=
|
||||
github.com/diamondburned/cchat-discord v0.0.0-20201231025836-96e97aa11705 h1:g0hwnUpeJ3yo7WaVZjWBQw875tnKVjCz4YofnamE9Fg=
|
||||
github.com/diamondburned/cchat-discord v0.0.0-20201231025836-96e97aa11705/go.mod h1:rFBGZYLq0g6Pb/WGN/K0++kXrhCYlQQ1nc2FX4r8CO0=
|
||||
github.com/diamondburned/cchat-mock v0.0.0-20201115033644-df8d1b10f9db h1:VQI2PdbsdsRJ7d669kp35GbCUO44KZ0Xfqdu4o/oqVg=
|
||||
github.com/diamondburned/cchat-mock v0.0.0-20201115033644-df8d1b10f9db/go.mod h1:M87kjNzWVPlkZycFNzpGPKQXzkHNnZphuwMf3E9ckgc=
|
||||
github.com/diamondburned/gotk3 v0.0.0-20201209182406-e7291341a091 h1:lQpSWzbi3rQf66aMSip/rIypasIFwqCqF0Wfn5og6gw=
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
package cozy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"runtime/pprof"
|
||||
|
||||
"github.com/diamondburned/cchat"
|
||||
"github.com/diamondburned/cchat-gtk/internal/gts"
|
||||
"github.com/diamondburned/cchat-gtk/internal/ui/messages/container"
|
||||
|
@ -85,7 +82,7 @@ func (c *Container) NewMessage(msg cchat.MessageCreate) container.GridMessage {
|
|||
func (c *Container) NewPresendMessage(msg input.PresendMessage) container.PresendGridMessage {
|
||||
// We can do the check here since we're never using NewPresendMessage for
|
||||
// backlog messages.
|
||||
if c.lastMessageIsAuthor(msg.AuthorID(), 0) {
|
||||
if c.lastMessageIsAuthor(msg.AuthorID(), msg.Author().String(), 0) {
|
||||
return NewCollapsedSendingMessage(msg)
|
||||
}
|
||||
|
||||
|
@ -123,18 +120,20 @@ func (c *Container) reuseAvatar(authorID, avatarURL string, full *FullMessage) {
|
|||
}
|
||||
}
|
||||
|
||||
func (c *Container) lastMessageIsAuthor(id string, offset int) bool {
|
||||
func (c *Container) lastMessageIsAuthor(id cchat.ID, name string, offset int) bool {
|
||||
// Get the offfsetth message from last.
|
||||
var last = c.GridStore.NthMessage((c.GridStore.MessagesLen() - 1) + offset)
|
||||
return last != nil && last.AuthorID() == id
|
||||
return gridMessageIsAuthor(last, id, name)
|
||||
}
|
||||
|
||||
var createMessageLabel = pprof.Labels("cozy", "createMessage")
|
||||
func gridMessageIsAuthor(gridMsg container.GridMessage, id cchat.ID, name string) bool {
|
||||
return gridMsg != nil &&
|
||||
gridMsg.AuthorID() == id &&
|
||||
gridMsg.AuthorName() == name
|
||||
}
|
||||
|
||||
func (c *Container) CreateMessage(msg cchat.MessageCreate) {
|
||||
gts.ExecAsync(func() {
|
||||
pprof.Do(context.Background(), createMessageLabel, func(context.Context) {
|
||||
|
||||
// Create the message in the parent's handler. This handler will also
|
||||
// wipe old messages.
|
||||
c.GridContainer.CreateMessageUnsafe(msg)
|
||||
|
@ -151,7 +150,8 @@ func (c *Container) CreateMessage(msg cchat.MessageCreate) {
|
|||
// Should we collapse this message? Yes, if the current message is
|
||||
// inserted at the end and its author is the same as the last author.
|
||||
case c.GridContainer.LastMessage().ID():
|
||||
if c.lastMessageIsAuthor(msg.Author().ID(), -1) {
|
||||
author := msg.Author()
|
||||
if c.lastMessageIsAuthor(author.ID(), author.Name().String(), -1) {
|
||||
c.compact(c.GridContainer.LastMessage())
|
||||
}
|
||||
|
||||
|
@ -159,17 +159,13 @@ func (c *Container) CreateMessage(msg cchat.MessageCreate) {
|
|||
// second message.
|
||||
case c.GridContainer.FirstMessage().ID():
|
||||
if sec := c.NthMessage(1); sec != nil {
|
||||
// If the author isn't the same, then ignore.
|
||||
if sec.AuthorID() != msg.Author().ID() {
|
||||
return
|
||||
}
|
||||
|
||||
// The author is the same; collapse.
|
||||
author := msg.Author()
|
||||
if gridMessageIsAuthor(sec, author.ID(), author.Name().String()) {
|
||||
c.compact(sec)
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -34,6 +34,8 @@ func WrapCollapsedMessage(gc *message.GenericContainer) *CollapsedMessage {
|
|||
// Set Content's padding accordingly to FullMessage's main box.
|
||||
gc.Content.ToWidget().SetMarginEnd(container.ColumnSpacing * 2)
|
||||
|
||||
gc.Username.SetMaxWidthChars(30)
|
||||
|
||||
return &CollapsedMessage{
|
||||
GenericContainer: gc,
|
||||
}
|
||||
|
|
|
@ -78,6 +78,8 @@ func WrapFullMessage(gc *message.GenericContainer) *FullMessage {
|
|||
gc.Timestamp.SetVAlign(gtk.ALIGN_END) // bottom-align
|
||||
gc.Timestamp.SetMarginStart(0) // clear margins
|
||||
|
||||
gc.Username.SetMaxWidthChars(75)
|
||||
|
||||
// Attach the class and CSS for the left avatar.
|
||||
avatarCSS(avatar)
|
||||
|
||||
|
|
|
@ -364,6 +364,10 @@ func NewMember(member cchat.ListMember) *Member {
|
|||
return m
|
||||
}
|
||||
|
||||
var noMentionLinks = markup.RenderConfig{
|
||||
NoMentionLinks: true,
|
||||
}
|
||||
|
||||
func (m *Member) Update(member cchat.ListMember) {
|
||||
m.ListBoxRow.SetName(member.Name().Content)
|
||||
|
||||
|
@ -371,7 +375,7 @@ func (m *Member) Update(member cchat.ListMember) {
|
|||
m.Avatar.AsyncSetIconer(iconer, "Failed to get member list icon")
|
||||
}
|
||||
|
||||
m.output = markup.RenderCmplxWithConfig(member.Name(), markup.NoMentionLinks)
|
||||
m.output = markup.RenderCmplxWithConfig(member.Name(), noMentionLinks)
|
||||
txt := strings.Builder{}
|
||||
txt.WriteString(fmt.Sprintf(
|
||||
`<span color="#%06X">●</span> %s`,
|
||||
|
|
|
@ -19,6 +19,7 @@ type Container interface {
|
|||
ID() string
|
||||
Time() time.Time
|
||||
AuthorID() string
|
||||
AuthorName() string
|
||||
AvatarURL() string // avatar
|
||||
Nonce() string
|
||||
|
||||
|
@ -50,6 +51,7 @@ type GenericContainer struct {
|
|||
id string
|
||||
time time.Time
|
||||
authorID string
|
||||
authorName string
|
||||
avatarURL string // avatar
|
||||
nonce string
|
||||
|
||||
|
@ -94,7 +96,6 @@ func NewEmptyContainer() *GenericContainer {
|
|||
ts.Show()
|
||||
|
||||
user := labeluri.NewLabel(text.Rich{})
|
||||
user.SetMaxWidthChars(35)
|
||||
user.SetLineWrap(true)
|
||||
user.SetLineWrapMode(pango.WRAP_WORD_CHAR)
|
||||
user.SetXAlign(1) // right align
|
||||
|
@ -168,6 +169,10 @@ func (m *GenericContainer) AuthorID() string {
|
|||
return m.authorID
|
||||
}
|
||||
|
||||
func (m *GenericContainer) AuthorName() string {
|
||||
return m.authorName
|
||||
}
|
||||
|
||||
func (m *GenericContainer) AvatarURL() string {
|
||||
return m.avatarURL
|
||||
}
|
||||
|
@ -189,8 +194,11 @@ func (m *GenericContainer) UpdateAuthor(author cchat.Author) {
|
|||
}
|
||||
|
||||
func (m *GenericContainer) UpdateAuthorName(name text.Rich) {
|
||||
var out = markup.RenderCmplxWithConfig(name, markup.NoMentionLinks)
|
||||
m.Username.SetOutput(out)
|
||||
cfg := markup.RenderConfig{}
|
||||
cfg.SetForegroundAnchor(m.ContentBody)
|
||||
|
||||
m.authorName = name.String()
|
||||
m.Username.SetOutput(markup.RenderCmplxWithConfig(name, cfg))
|
||||
}
|
||||
|
||||
func (m *GenericContainer) UpdateContent(content text.Rich, edited bool) {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package attrmap
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"html"
|
||||
"sort"
|
||||
|
@ -10,15 +11,15 @@ import (
|
|||
)
|
||||
|
||||
type AppendMap struct {
|
||||
appended map[int]string // for opening tags
|
||||
prepended map[int]string // for closing tags
|
||||
appended map[int][]byte // for opening tags
|
||||
prepended map[int][]byte // for closing tags
|
||||
indices []int
|
||||
}
|
||||
|
||||
func NewAppendedMap() AppendMap {
|
||||
return AppendMap{
|
||||
appended: map[int]string{},
|
||||
prepended: map[int]string{},
|
||||
appended: map[int][]byte{},
|
||||
prepended: map[int][]byte{},
|
||||
indices: []int{},
|
||||
}
|
||||
}
|
||||
|
@ -40,7 +41,7 @@ func (a *AppendMap) Anchor(start, end int, href string) {
|
|||
|
||||
// AnchorNU makes a new <a> tag without underlines and colors.
|
||||
func (a *AppendMap) AnchorNU(start, end int, href string) {
|
||||
a.Openf(start, `<a href="`+html.EscapeString(href)+`%s">`)
|
||||
a.Openf(start, `<a href="`+html.EscapeString(href)+`">`)
|
||||
a.Close(end, "</a>")
|
||||
// a.Anchor(start, end, href)
|
||||
a.Span(start, end, `underline="none"`)
|
||||
|
@ -63,7 +64,7 @@ func (a *AppendMap) Pad(start, end int) {
|
|||
}
|
||||
}
|
||||
|
||||
func posHaveSpace(tags map[int]string, pos int) bool {
|
||||
func posHaveSpace(tags map[int][]byte, pos int) bool {
|
||||
tg, ok := tags[pos]
|
||||
if !ok || len(tg) == 0 {
|
||||
return false
|
||||
|
@ -78,7 +79,7 @@ func posHaveSpace(tags map[int]string, pos int) bool {
|
|||
}
|
||||
|
||||
// Check spaces mid-tag. This works because strings are always escaped.
|
||||
return strings.Contains(tg, "> <")
|
||||
return bytes.Contains(tg, []byte("> <"))
|
||||
}
|
||||
|
||||
func (a *AppendMap) Pair(start, end int, open, close string) {
|
||||
|
@ -92,32 +93,31 @@ func (a *AppendMap) Openf(ind int, f string, argv ...interface{}) {
|
|||
|
||||
func (a *AppendMap) Open(ind int, attr string) {
|
||||
if str, ok := a.appended[ind]; ok {
|
||||
a.appended[ind] = str + attr // append
|
||||
a.appended[ind] = append(str, []byte(attr)...) // append
|
||||
return
|
||||
}
|
||||
|
||||
a.appended[ind] = attr
|
||||
a.appended[ind] = []byte(attr)
|
||||
a.appendIndex(ind)
|
||||
}
|
||||
|
||||
func (a *AppendMap) Close(ind int, attr string) {
|
||||
if str, ok := a.prepended[ind]; ok {
|
||||
a.prepended[ind] = attr + str // prepend
|
||||
a.prepended[ind] = append([]byte(attr), str...) // prepend
|
||||
return
|
||||
}
|
||||
|
||||
a.prepended[ind] = attr
|
||||
a.prepended[ind] = []byte(attr)
|
||||
a.appendIndex(ind)
|
||||
}
|
||||
|
||||
func (a AppendMap) Get(ind int) (tags string) {
|
||||
if t, ok := a.appended[ind]; ok {
|
||||
tags += t
|
||||
}
|
||||
if t, ok := a.prepended[ind]; ok {
|
||||
tags += t
|
||||
}
|
||||
return
|
||||
appended := a.appended[ind]
|
||||
prepended := a.prepended[ind]
|
||||
|
||||
// Borrowing appended's backing array to add prepended is probably fine, as
|
||||
// the length of the actual appended slice is going to stay the same.
|
||||
return string(append(appended, prepended...))
|
||||
}
|
||||
|
||||
func (a *AppendMap) Finalize(strlen int) []int {
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// Package hl provides a syntax highlighted renderer for the markup API.
|
||||
package hl
|
||||
|
||||
import (
|
||||
|
|
|
@ -9,10 +9,12 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/diamondburned/cchat-gtk/internal/ui/primitives"
|
||||
"github.com/diamondburned/cchat-gtk/internal/ui/rich/parser/attrmap"
|
||||
"github.com/diamondburned/cchat-gtk/internal/ui/rich/parser/hl"
|
||||
"github.com/diamondburned/cchat/text"
|
||||
"github.com/diamondburned/imgutil"
|
||||
"github.com/gotk3/gotk3/gtk"
|
||||
)
|
||||
|
||||
// Hyphenate controls whether or not texts should have hyphens on wrap.
|
||||
|
@ -27,6 +29,7 @@ type RenderOutput struct {
|
|||
Markup string
|
||||
Input string // useless to keep parts, as Go will keep all alive anyway
|
||||
Mentions []MentionSegment
|
||||
References []ReferenceSegment
|
||||
}
|
||||
|
||||
// MentionSegment is a type that satisfies both Segment and Mentioner.
|
||||
|
@ -35,24 +38,41 @@ type MentionSegment struct {
|
|||
text.Mentioner
|
||||
}
|
||||
|
||||
// f_Mention is used to print and parse mention URIs.
|
||||
const f_Mention = "cchat://mention/%d" // %d == Mentions[i]
|
||||
// ReferenceSegment is a type that satisfies both Segment and MessageReferencer.
|
||||
type ReferenceSegment struct {
|
||||
text.Segment
|
||||
text.MessageReferencer
|
||||
}
|
||||
|
||||
const (
|
||||
// f_Mention is used to print and parse mention URIs.
|
||||
f_Mention = "cchat://mention/%d" // %d == Mentions[i]
|
||||
f_Reference = "cchat://reference/%d" // %d == References[i]
|
||||
)
|
||||
|
||||
// IsMention returns the mention if the URI is correct, or nil if none.
|
||||
func (r RenderOutput) IsMention(uri string) text.Segment {
|
||||
var i int
|
||||
|
||||
if _, err := fmt.Sscanf(uri, f_Mention, &i); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if i >= len(r.Mentions) {
|
||||
_, err := fmt.Sscanf(uri, f_Mention, &i)
|
||||
if err != nil || i >= len(r.Mentions) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return r.Mentions[i]
|
||||
}
|
||||
|
||||
func (r RenderOutput) IsReference(uri string) text.Segment {
|
||||
var i int
|
||||
|
||||
_, err := fmt.Sscanf(uri, f_Reference, &i)
|
||||
if err != nil || i >= len(r.References) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return r.References[i]
|
||||
}
|
||||
|
||||
func Render(content text.Rich) string {
|
||||
return RenderCmplx(content).Markup
|
||||
}
|
||||
|
@ -63,15 +83,32 @@ func RenderCmplx(content text.Rich) RenderOutput {
|
|||
}
|
||||
|
||||
type RenderConfig struct {
|
||||
// NoMentionLinks prevents the renderer from wrapping mentions with a
|
||||
// hyperlink. This prevents invalid colors.
|
||||
// NoMentionLinks, if true, will not render any mentions.
|
||||
NoMentionLinks bool
|
||||
|
||||
// AnchorColor forces all anchors to be of a certain color. This is used if
|
||||
// the boolean is true. Else, all mention links will not work and regular
|
||||
// links will be of the default color.
|
||||
AnchorColor struct {
|
||||
uint32
|
||||
bool
|
||||
}
|
||||
}
|
||||
|
||||
// NoMentionLinks is the config to render author names. It disables author
|
||||
// mention links, as there's no way to make normal names not appear blue.
|
||||
var NoMentionLinks = RenderConfig{
|
||||
NoMentionLinks: true,
|
||||
// SetForegroundAnchor sets the AnchorColor of the render config to be that of
|
||||
// the regular text foreground color.
|
||||
func (c *RenderConfig) SetForegroundAnchor(styler primitives.StyleContexter) {
|
||||
styleCtx, _ := styler.GetStyleContext()
|
||||
|
||||
if rgba := styleCtx.GetColor(gtk.STATE_FLAG_NORMAL); rgba != nil {
|
||||
var color uint32
|
||||
for _, v := range rgba.Floats() { // [0.0, 1.0]
|
||||
color = (color << 8) + uint32(v*0xFF)
|
||||
}
|
||||
|
||||
c.AnchorColor.bool = true
|
||||
c.AnchorColor.uint32 = color
|
||||
}
|
||||
}
|
||||
|
||||
func RenderCmplxWithConfig(content text.Rich, cfg RenderConfig) RenderOutput {
|
||||
|
@ -104,15 +141,21 @@ func RenderCmplxWithConfig(content text.Rich, cfg RenderConfig) RenderOutput {
|
|||
// map to append strings to indices
|
||||
var appended = attrmap.NewAppendedMap()
|
||||
|
||||
// map to store mentions
|
||||
// map to store mentions and references
|
||||
var mentions []MentionSegment
|
||||
var references []ReferenceSegment
|
||||
|
||||
// Parse all segments.
|
||||
for _, segment := range content.Segments {
|
||||
start, end := segment.Bounds()
|
||||
|
||||
// hasAnchor is used to determine if the current segment has inserted
|
||||
// any anchor tags; it is used for AnchorColor.
|
||||
var hasAnchor bool
|
||||
|
||||
if linker := segment.AsLinker(); linker != nil {
|
||||
appended.Anchor(start, end, linker.Link())
|
||||
hasAnchor = true
|
||||
}
|
||||
|
||||
// Only inline images if start == end per specification.
|
||||
|
@ -127,19 +170,14 @@ func RenderCmplxWithConfig(content text.Rich, cfg RenderConfig) RenderOutput {
|
|||
}
|
||||
}
|
||||
|
||||
if colorer := segment.AsColorer(); colorer != nil {
|
||||
appended.Span(start, end, colorAttrs(colorer.Color(), false)...)
|
||||
}
|
||||
|
||||
// Mentioner needs to be before colorer, as we'd want the below color
|
||||
// segment to also highlight the full mention as well as make the
|
||||
// padding part of the hyperlink.
|
||||
if mentioner := segment.AsMentioner(); mentioner != nil {
|
||||
if mentioner := segment.AsMentioner(); mentioner != nil && !cfg.NoMentionLinks {
|
||||
// Render the mention into "cchat://mention:0" or such. Other
|
||||
// components will take care of showing the information.
|
||||
if !cfg.NoMentionLinks {
|
||||
appended.AnchorNU(start, end, fmt.Sprintf(f_Mention, len(mentions)))
|
||||
}
|
||||
hasAnchor = true
|
||||
|
||||
// Add the mention segment into the list regardless of hyperlinks.
|
||||
mentions = append(mentions, MentionSegment{
|
||||
|
@ -147,15 +185,44 @@ func RenderCmplxWithConfig(content text.Rich, cfg RenderConfig) RenderOutput {
|
|||
Mentioner: mentioner,
|
||||
})
|
||||
|
||||
// TODO: figure out a way to readd Pad. Right now, backend
|
||||
// implementations can arbitrarily add multiple mentions onto the
|
||||
// author for overloading, which we don't want to break.
|
||||
|
||||
// // Determine if the mention segment covers the entire label.
|
||||
// // Only pad the name and add a dimmed background if the bounds do
|
||||
// // not cover the whole segment.
|
||||
// var cover = (start == 0) && (end == len(content.Content))
|
||||
// if !cover {
|
||||
// appended.Pad(start, end)
|
||||
// }
|
||||
|
||||
// // If we don't have a mention color for this segment, then try to
|
||||
// // use our own AnchorColor.
|
||||
// if !hasColor && cfg.AnchorColor.bool {
|
||||
// appended.Span(start, end, colorAttrs(cfg.AnchorColor.uint32, false)...)
|
||||
// }
|
||||
}
|
||||
|
||||
if colorer := segment.AsColorer(); colorer != nil {
|
||||
// Only pad the name and add a dimmed background if the bounds
|
||||
// do not cover the whole segment.
|
||||
var cover = (start == 0) && (end == len(content.Content))
|
||||
appended.Span(start, end, colorAttrs(colorer.Color(), !cover)...)
|
||||
if !cover {
|
||||
appended.Pad(start, end)
|
||||
}
|
||||
appended.Span(start, end, colorAttrs(colorer.Color(), false)...)
|
||||
} else if hasAnchor && cfg.AnchorColor.bool {
|
||||
appended.Span(start, end, colorAttrs(cfg.AnchorColor.uint32, false)...)
|
||||
}
|
||||
|
||||
// Don't use AnchorColor for the link, as we're technically just
|
||||
// borrowing the anchor tag for its use. We should also prefer the
|
||||
// username popover (Mention) over this.
|
||||
if reference := segment.AsMessageReferencer(); !hasAnchor && reference != nil {
|
||||
// Render the mention into "cchat://reference:0" or such. Other
|
||||
// components will take care of showing the information.
|
||||
appended.AnchorNU(start, end, fmt.Sprintf(f_Reference, len(references)))
|
||||
|
||||
// Add the mention segment into the list regardless of hyperlinks.
|
||||
references = append(references, ReferenceSegment{
|
||||
Segment: segment,
|
||||
MessageReferencer: reference,
|
||||
})
|
||||
}
|
||||
|
||||
if attributor := segment.AsAttributor(); attributor != nil {
|
||||
|
@ -165,7 +232,12 @@ func RenderCmplxWithConfig(content text.Rich, cfg RenderConfig) RenderOutput {
|
|||
if codeblocker := segment.AsCodeblocker(); codeblocker != nil {
|
||||
start, end := segment.Bounds()
|
||||
// Syntax highlight the codeblock.
|
||||
hl.Segments(&appended, content.Content, start, end, codeblocker.CodeblockLanguage())
|
||||
hl.Segments(
|
||||
&appended,
|
||||
content.Content,
|
||||
start, end,
|
||||
codeblocker.CodeblockLanguage(),
|
||||
)
|
||||
}
|
||||
|
||||
// TODO: make this not shit. Maybe make it somehow not rely on green
|
||||
|
@ -190,6 +262,7 @@ func RenderCmplxWithConfig(content text.Rich, cfg RenderConfig) RenderOutput {
|
|||
Markup: hyphenate(buf.String()),
|
||||
Input: content.Content,
|
||||
Mentions: mentions,
|
||||
References: references,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue