Fixed minor bugs; improved appearances; removed some dead code

This commit is contained in:
diamondburned 2020-07-08 21:14:56 -07:00
parent ab4c949428
commit f2f59b3c2b
14 changed files with 182 additions and 382 deletions

2
go.mod
View File

@ -8,7 +8,7 @@ require (
github.com/Xuanwo/go-locale v0.2.0
github.com/alecthomas/chroma v0.7.3
github.com/diamondburned/cchat v0.0.42
github.com/diamondburned/cchat-discord v0.0.0-20200708091545-601e8abeb23b
github.com/diamondburned/cchat-discord v0.0.0-20200709041349-1e137df6de2c
github.com/diamondburned/cchat-mock v0.0.0-20200704044009-f587c4904aa3
github.com/diamondburned/imgutil v0.0.0-20200708012333-53c9e45dd28b
github.com/goodsign/monday v1.0.0

6
go.sum
View File

@ -60,6 +60,10 @@ github.com/diamondburned/cchat-discord v0.0.0-20200708083530-d0e43cc63b03 h1:Xx4
github.com/diamondburned/cchat-discord v0.0.0-20200708083530-d0e43cc63b03/go.mod h1:hjaCeQfhSqANH+ZtxXPMWzpgobb4syy2VcqDK4PXcPU=
github.com/diamondburned/cchat-discord v0.0.0-20200708091545-601e8abeb23b h1:c01OHXPE/HXcZdaPDgNNzmz3xN7MC0yIyL5i+6qILAc=
github.com/diamondburned/cchat-discord v0.0.0-20200708091545-601e8abeb23b/go.mod h1:IDBm0RqdQUASjPGP/RQkhmC1nGhGTTFoaHn9NbD/l7I=
github.com/diamondburned/cchat-discord v0.0.0-20200708212314-e08c31b895a6 h1:Nc9B6i7siInzh/uu3bTu7mcw7ckcFDTu4TtSNoxUMis=
github.com/diamondburned/cchat-discord v0.0.0-20200708212314-e08c31b895a6/go.mod h1:QHPtnxNrnMFCYB/b9kUP93D30Kf3AuGmkM91tScIpB8=
github.com/diamondburned/cchat-discord v0.0.0-20200709041349-1e137df6de2c h1:4F7IJqMfpF02/j6ZbVO9x29rWExQncLHFbxZrsAhhvM=
github.com/diamondburned/cchat-discord v0.0.0-20200709041349-1e137df6de2c/go.mod h1:QHPtnxNrnMFCYB/b9kUP93D30Kf3AuGmkM91tScIpB8=
github.com/diamondburned/cchat-mock v0.0.0-20200704044009-f587c4904aa3 h1:xr07/2cwINyrMqh92pQQJVDfQqG0u6gHAK+ZcGfpSew=
github.com/diamondburned/cchat-mock v0.0.0-20200704044009-f587c4904aa3/go.mod h1:SRu3OOeggELFr2Wd3/+SpYV1eNcvSk2LBhM70NOZSG8=
github.com/diamondburned/gotk3 v0.0.0-20200630065217-97aeb06d705d h1:Ha/I6PMKi+B4hpWclwlXj0tUMehR7Q0TNxPczzBwzPI=
@ -72,6 +76,8 @@ github.com/diamondburned/ningen v0.1.1-0.20200621014632-6babb812b249 h1:yP7kJ+xC
github.com/diamondburned/ningen v0.1.1-0.20200621014632-6babb812b249/go.mod h1:xW9hpBZsGi8KpAh10TyP+YQlYBo+Xc+2w4TR6N0951A=
github.com/diamondburned/ningen v0.1.1-0.20200708090333-227e90d19851 h1:xf1aLPnwK/Yn2z7dBIgQROSVOEc2wtivgnnwBItdEVM=
github.com/diamondburned/ningen v0.1.1-0.20200708090333-227e90d19851/go.mod h1:FNezDLQIhoDS+RkXLSQ7dJNrt6BW/nVl1krzDgWMQwg=
github.com/diamondburned/ningen v0.1.1-0.20200708211706-57c712372ede h1:qRmfQCOS+ZnH4G0+8O09PUx3HQTdQwzsDoo1ucTgm2E=
github.com/diamondburned/ningen v0.1.1-0.20200708211706-57c712372ede/go.mod h1:FNezDLQIhoDS+RkXLSQ7dJNrt6BW/nVl1krzDgWMQwg=
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
github.com/dlclark/regexp2 v1.2.0 h1:8sAhBGEM0dRWogWqWyQeIJnxjWO6oIjl8FKqREDsGfk=

View File

@ -1,6 +0,0 @@
# c
**HERE BE DRAGONS!!**
This package and all its subpackages contain either C code or Cgo code. This
bugs me and I hate it. PROCEED WITH CAUTION!

View File

@ -1,101 +0,0 @@
package labelutils
// #cgo pkg-config: gdk-3.0 gio-2.0 glib-2.0 gobject-2.0 gtk+-3.0
// #include <glib.h>
// #include <gtk/gtk.h>
// #include <pango/pango.h>
import "C"
import (
"unsafe"
"github.com/gotk3/gotk3/gtk"
"github.com/gotk3/gotk3/pango"
)
func gbool(b bool) C.gboolean {
if b {
return C.gboolean(C.TRUE)
} else {
return C.gboolean(C.FALSE)
}
}
type Attribute = func() *C.PangoAttribute
func InsertHyphens(hyphens bool) Attribute {
return func() *C.PangoAttribute {
return C.pango_attr_insert_hyphens_new(gbool(hyphens))
}
}
func Scale(factor float64) Attribute {
return func() *C.PangoAttribute {
return C.pango_attr_scale_new(C.double(factor))
}
}
func Underline(underline pango.Underline) Attribute {
return func() *C.PangoAttribute {
return C.pango_attr_underline_new(C.PangoUnderline(underline))
}
}
func Strikethrough(strikethrough bool) Attribute {
return func() *C.PangoAttribute {
return C.pango_attr_strikethrough_new(gbool(strikethrough))
}
}
const u16divu8 = 65535 / 255
func rgb(hex uint32) (r, g, b uint16) {
r = uint16(hex>>16&255) * u16divu8
g = uint16(hex>>8&255) * u16divu8
b = uint16(hex&255) * u16divu8
return
}
func Background(hex uint32) Attribute {
r, g, b := rgb(hex)
return func() *C.PangoAttribute {
return C.pango_attr_background_new(C.guint16(r), C.guint16(g), C.guint16(b))
}
}
func Foreground(hex uint32) Attribute {
r, g, b := rgb(hex)
return func() *C.PangoAttribute {
return C.pango_attr_foreground_new(C.guint16(r), C.guint16(g), C.guint16(b))
}
}
func Style(style pango.Style) Attribute {
return func() *C.PangoAttribute {
return C.pango_attr_style_new(C.PangoStyle(style))
}
}
func Family(family string) Attribute {
return func() *C.PangoAttribute {
str := C.CString(family)
defer C.free(unsafe.Pointer(str))
return C.pango_attr_family_new(str)
}
}
func AddAttr(l *gtk.Label, attrs ...Attribute) {
attrlist := C.pango_attr_list_new()
defer C.pango_attr_list_unref(attrlist)
for _, attr := range attrs {
// attr() should not unref; insert is transfer-full
// https://discourse.gnome.org/t/pango-how-to-turn-off-hyphenation-for-char-wrapping/2101/2
C.pango_attr_list_insert(attrlist, attr())
}
v := (*C.GtkLabel)(unsafe.Pointer(l.Native()))
C.gtk_label_set_attributes(v, attrlist)
}

View File

@ -7,7 +7,6 @@ import (
"path"
"strings"
"github.com/diamondburned/cchat-gtk/internal/c/labelutils"
"github.com/diamondburned/cchat-gtk/internal/gts/httputil"
"github.com/diamondburned/cchat-gtk/internal/log"
"github.com/diamondburned/cchat-gtk/internal/ui/dialog"
@ -87,7 +86,7 @@ func BindTooltip(connector WidgetConnector) {
}
const urlPrompt = `This link leads to the following URL:
<span weight="bold"><a href="%[1]s">%[1]s</a></span>
<span weight="bold" insert_hyphens="false"><a href="%[1]s">%[1]s</a></span>
Click <b>Open</b> to proceed.`
var warnLabelCSS = primitives.PrepareCSS(`
@ -109,9 +108,6 @@ func PromptOpen(uri string) {
// Style the label.
primitives.AttachCSS(l, warnLabelCSS)
// Disable hyphens on line wraps.
labelutils.AddAttr(l, labelutils.InsertHyphens(false))
open := func() {
if err := open.Start(uri); err != nil {
log.Error(errors.Wrap(err, "Failed to open URL after confirm"))

View File

@ -1,16 +1,21 @@
package completion
import (
"fmt"
"github.com/diamondburned/cchat"
"github.com/diamondburned/cchat-gtk/internal/gts/httputil"
"github.com/diamondburned/cchat-gtk/internal/ui/primitives/completion"
"github.com/diamondburned/cchat-gtk/internal/ui/rich"
"github.com/diamondburned/cchat-gtk/internal/ui/rich/parser"
"github.com/diamondburned/cchat/text"
"github.com/diamondburned/imgutil"
"github.com/gotk3/gotk3/gtk"
)
const (
ImageSize = 25
ImageSmall = 25
ImageLarge = 40
ImagePadding = 6
)
@ -51,30 +56,53 @@ func (v *View) Update(words []string, i int) []gtk.IWidget {
var widgets = make([]gtk.IWidget, len(v.entries))
for i, entry := range v.entries {
// Container that holds the label.
lbox, _ := gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 0)
lbox.SetVAlign(gtk.ALIGN_CENTER)
lbox.Show()
// Label for the primary text.
l := rich.NewLabel(entry.Text)
l.Show()
lbox.PackStart(l, false, false, 0)
img, _ := gtk.ImageNew()
// Get the iamge size so we can change and use if needed. The default
var size = ImageSmall
if !entry.Secondary.Empty() {
size = ImageLarge
s := rich.NewLabel(text.Rich{})
s.SetMarkup(fmt.Sprintf(
`<span alpha="50%%" size="small">%s</span>`,
parser.RenderMarkup(entry.Secondary),
))
s.Show()
lbox.PackStart(s, false, false, 0)
}
b, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0)
b.PackEnd(lbox, true, true, ImagePadding)
b.Show()
// Do we have an icon?
if entry.IconURL != "" {
img, _ := gtk.ImageNew()
img.SetMarginStart(ImagePadding)
img.SetSizeRequest(ImageSize, ImageSize)
img.SetSizeRequest(size, size)
img.Show()
// Prepend the image into the box.
b.PackEnd(img, false, false, 0)
var pps []imgutil.Processor
if !entry.Image {
pps = ppIcon
}
httputil.AsyncImageSized(img, entry.IconURL, ImageSize, ImageSize, pps...)
httputil.AsyncImageSized(img, entry.IconURL, size, size, pps...)
}
b, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0)
b.PackStart(img, false, false, 0) // image has pad left
b.PackStart(l, true, true, ImagePadding)
b.Show()
widgets[i] = b
}

View File

@ -4,7 +4,6 @@ import (
"time"
"github.com/diamondburned/cchat"
"github.com/diamondburned/cchat-gtk/internal/c/labelutils"
"github.com/diamondburned/cchat-gtk/internal/humanize"
"github.com/diamondburned/cchat-gtk/internal/ui/imgview"
"github.com/diamondburned/cchat-gtk/internal/ui/primitives"
@ -91,7 +90,6 @@ func NewEmptyContainer() *GenericContainer {
ts.SetEllipsize(pango.ELLIPSIZE_MIDDLE)
ts.SetXAlign(1) // right align
ts.SetVAlign(gtk.ALIGN_END)
// ts.SetSelectable(true)
ts.Show()
user, _ := gtk.LabelNew("")
@ -100,7 +98,6 @@ func NewEmptyContainer() *GenericContainer {
user.SetLineWrapMode(pango.WRAP_WORD_CHAR)
user.SetXAlign(1) // right align
user.SetVAlign(gtk.ALIGN_START)
// user.SetSelectable(true)
user.Show()
content, _ := gtk.LabelNew("")
@ -110,8 +107,14 @@ func NewEmptyContainer() *GenericContainer {
content.SetSelectable(true)
content.Show()
// Never insert hyphens on line breaks in the message content.
labelutils.AddAttr(content, labelutils.InsertHyphens(false))
// content.Connect("grab-notify", func(l *gtk.Label, grabbed bool) {
// if grabbed {
// // Hack to stop the label from selecting everything after being
// // refocused.
// content.SetSelectable(false)
// gts.ExecAsync(func() { content.SetSelectable(true) })
// }
// })
// Add CSS classes.
primitives.AddClass(ts, "message-time")

View File

@ -193,7 +193,10 @@ func (v *View) AddPresendMessage(msg input.PresendMessage) func(error) {
// AuthorEvent should be called on message create/update/delete.
func (v *View) AuthorEvent(author cchat.MessageAuthor) {
v.Typing.RemoveAuthor(author)
// Remove the author from the typing list if it's not nil.
if author != nil {
v.Typing.RemoveAuthor(author)
}
}
// LatestMessageFrom returns the last message ID with that author.

View File

@ -3,56 +3,82 @@ package attrmap
import (
"fmt"
"sort"
"strings"
)
type AppendMap struct {
appended map[int]string
indices []int
appended map[int]string // for opening tags
prepended map[int]string // for closing tags
indices []int
}
func NewAppendedMap() AppendMap {
return AppendMap{
appended: make(map[int]string),
indices: []int{},
appended: map[int]string{},
prepended: map[int]string{},
indices: []int{},
}
}
func (a *AppendMap) Span(start, end int, attr string) {
a.Add(start, `<span `+attr+`>`)
a.Add(end, "</span>")
func (a *AppendMap) appendIndex(ind int) {
// Backwards search which should make things faster.
for i := len(a.indices) - 1; i >= 0; i-- {
if a.indices[i] == ind {
return
}
}
a.indices = append(a.indices, ind)
}
func (a *AppendMap) Span(start, end int, attrs ...string) {
a.Openf(start, "<span %s>", strings.Join(attrs, " "))
a.Close(end, "</span>")
}
func (a *AppendMap) Pair(start, end int, open, close string) {
a.Add(start, open)
a.Add(end, close)
a.Open(start, open)
a.Close(end, close)
}
func (a *AppendMap) Addf(ind int, f string, argv ...interface{}) {
a.Add(ind, fmt.Sprintf(f, argv...))
func (a *AppendMap) Openf(ind int, f string, argv ...interface{}) {
a.Open(ind, fmt.Sprintf(f, argv...))
}
func (a *AppendMap) Pad(ind int) {
a.Add(ind, "\n")
}
func (a *AppendMap) Add(ind int, attr string) {
if _, ok := a.appended[ind]; ok {
a.appended[ind] += attr
func (a *AppendMap) Open(ind int, attr string) {
if str, ok := a.appended[ind]; ok {
a.appended[ind] = str + attr // append
return
}
a.appended[ind] = attr
a.indices = append(a.indices, ind)
a.appendIndex(ind)
}
func (a AppendMap) Get(ind int) string {
return a.appended[ind]
func (a *AppendMap) Close(ind int, attr string) {
if str, ok := a.prepended[ind]; ok {
a.prepended[ind] = attr + str // prepend
return
}
a.prepended[ind] = 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
}
func (a *AppendMap) Finalize(strlen int) []int {
// make sure there's always a closing tag at the end so the entire string
// gets flushed.
a.Add(strlen, "")
a.Close(strlen, "")
sort.Ints(a.indices)
return a.indices
}

View File

@ -42,7 +42,11 @@ func Tokenize(language, source string) chroma.Iterator {
func Segments(appendmap *attrmap.AppendMap, src string, seg text.Codeblocker) {
var start, end = seg.Bounds()
appendmap.Span(start, end, `font_family="monospace"`)
appendmap.Span(
start, end,
`font_family="monospace"`,
`insert_hyphens="false"`, // all my homies hate hyphens
)
if i := Tokenize(seg.CodeblockLanguage(), src[start:end]); i != nil {
fmtter.segments(appendmap, start, i)
@ -92,13 +96,13 @@ func (f *formatter) segments(appendmap *attrmap.AppendMap, offset int, iter chro
attr := f.styleAttr(token.Type)
if attr != "" {
appendmap.Addf(offset, `<span %s>`, attr)
appendmap.Openf(offset, `<span %s>`, attr)
}
offset += len(token.Value)
if attr != "" {
appendmap.Add(offset, "</span>")
appendmap.Close(offset, "</span>")
}
}
}

View File

@ -65,17 +65,26 @@ func RenderMarkup(content text.Rich) string {
start, end := segment.Bounds()
if segment, ok := segment.(text.Linker); ok {
appended.Addf(start, `<a href="%s">`, html.EscapeString(segment.Link()))
appended.Add(end, "</a>")
appended.Openf(start, `<a href="%s">`, html.EscapeString(segment.Link()))
appended.Close(end, "</a>")
}
if segment, ok := segment.(text.Imager); ok {
// Ends don't matter with images.
appended.Add(start, composeImageMarkup(segment))
appended.Open(start, composeImageMarkup(segment))
}
if segment, ok := segment.(text.Colorer); ok {
appended.Span(start, end, fmt.Sprintf(`color="#%06X"`, segment.Color()))
var attrs = []string{fmt.Sprintf(`color="#%06X"`, segment.Color())}
// If the color segment only covers a segment, then add some more
// formatting.
if start > 0 && end < len(content.Content) {
attrs = append(attrs,
`bgalpha="10%"`,
fmt.Sprintf(`bgcolor="#%06X"`, segment.Color()),
)
}
appended.Span(start, end, attrs...)
}
if segment, ok := segment.(text.Attributor); ok {

View File

@ -0,0 +1,57 @@
package parser
import (
"testing"
"github.com/diamondburned/cchat-mock/segments"
"github.com/diamondburned/cchat/text"
)
func TestRenderMarkup(t *testing.T) {
content := text.Rich{Content: "astolfo is the best trap"}
content.Segments = []text.Segment{
segments.NewColored(content.Content, 0x55CDFC),
}
expect := `<span color="#55CDFC">` + content.Content + "</span>"
if text := RenderMarkup(content); text != expect {
t.Fatal("Unexpected text:", text)
}
}
// Test no longer works, and should not work anyway.
// func TestRenderMarkupPartial(t *testing.T) {
// content := text.Rich{Content: "random placeholder text go brrr"}
// content.Segments = []text.Segment{
// // This is absolutely jankery that should not work at all, but we'll try
// // it anyway.
// coloredSegment{0, 4, 0x55CDFC},
// coloredSegment{2, 6, 0xFFFFFF}, // naive parsing, so spans close unexpectedly.
// coloredSegment{4, 6, 0xF7A8B8},
// }
// const expect = "" +
// <span color="#55CDFC">ra<span color="#FFFFFF" bgalpha="10%" bgcolor="#FFFFFF">nd<span color="#F7A8B8" bgalpha="10%" bgcolor="#F7A8B8"></span>om</span></span>
// `<span color="#55CDFC">ra<span color="#FFFFFF">nd</span>` +
// `<span color="#F7A8B8">om</span></span>`
// if text := RenderMarkup(content); !strings.HasPrefix(text, expect) {
// t.Fatal("Unexpected text:", text)
// }
// }
type coloredSegment struct {
start int
end int
color uint32
}
var _ text.Colorer = (*coloredSegment)(nil)
func (c coloredSegment) Bounds() (start, end int) {
return c.start, c.end
}
func (c coloredSegment) Color() uint32 {
return c.color
}

View File

@ -1,170 +0,0 @@
package parser
import (
"fmt"
"sort"
"time"
"github.com/diamondburned/cchat-gtk/internal/log"
"github.com/diamondburned/cchat-gtk/internal/ui/primitives"
"github.com/diamondburned/cchat/text"
"github.com/gotk3/gotk3/gdk"
"github.com/gotk3/gotk3/gtk"
"github.com/gotk3/gotk3/pango"
"github.com/pkg/errors"
"github.com/skratchdot/open-golang/open"
)
func AppendEditBadge(b *gtk.TextBuffer, editedAt time.Time) {
r := newRenderCtx(b)
t := r.createTag(map[string]interface{}{
"scale": 0.84,
"scale-set": true,
"foreground": "#808080", // blue-ish URL color
})
bindClicker(t, func(_ *gtk.TextView, ev *gdk.Event) {
switch ev := gdk.EventMotionNewFromEvent(ev); ev.Type() {
case gdk.EVENT_PROXIMITY_IN:
log.Println("Proximity in")
case gdk.EVENT_PROXIMITY_OUT:
log.Println("Proximity out")
}
})
b.InsertWithTag(b.GetEndIter(), " (edited)", t)
}
func RenderTextBuffer(b *gtk.TextBuffer, content text.Rich) {
r := newRenderCtx(b)
b.SetText(content.Content)
// Sort so that all starting points are sorted incrementally.
sort.Slice(content.Segments, func(i, j int) bool {
i, _ = content.Segments[i].Bounds()
j, _ = content.Segments[j].Bounds()
return i < j
})
for _, segment := range content.Segments {
start, end := segment.Bounds()
switch segment := segment.(type) {
case text.Attributor:
r.tagAttr(start, end, segment.Attribute())
case text.Colorer:
color := fmt.Sprintf("#%06X", segment.Color())
r.applyProps(start, end, map[string]interface{}{
"foreground": color,
})
case text.Codeblocker:
r.applyProps(start, end, map[string]interface{}{
"family": "Monospace",
})
}
}
}
type renderCtx struct {
b *gtk.TextBuffer
t *gtk.TextTagTable
}
func newRenderCtx(b *gtk.TextBuffer) *renderCtx {
t, _ := b.GetTagTable()
return &renderCtx{b, t}
}
type OnClicker func(tv *gtk.TextView, ev *gdk.Event)
func bindClicker(v primitives.Connector, fn OnClicker) {
v.Connect("event", func(_ *gtk.TextTag, tv *gtk.TextView, ev *gdk.Event) {
evButton := gdk.EventButtonNewFromEvent(ev)
if evButton.Type() != gdk.EVENT_BUTTON_RELEASE || evButton.Button() != gdk.BUTTON_PRIMARY {
return
}
fn(tv, ev)
})
}
func (r *renderCtx) applyHyperlink(start, end int, url string) {
t := r.createTag(map[string]interface{}{
"underline": pango.UNDERLINE_SINGLE,
"foreground": "#3F7CE0", // blue-ish URL color
})
bindClicker(t, func(*gtk.TextView, *gdk.Event) {
if err := open.Start(url); err != nil {
log.Error(errors.Wrap(err, "Failed to open image URL"))
}
})
}
func (r *renderCtx) applyProps(start, end int, props map[string]interface{}) {
tag := r.createTag(props)
r.applyTag(start, end, tag)
}
func (r *renderCtx) applyTag(start, end int, tag *gtk.TextTag) {
istart, iend := r.iters(start, end)
r.b.ApplyTag(tag, istart, iend)
}
func (r *renderCtx) createTag(props map[string]interface{}) *gtk.TextTag {
t, _ := gtk.TextTagNew("")
r.t.Add(t)
if props != nil {
for k, v := range props {
t.SetProperty(k, v)
}
}
return t
}
func (r *renderCtx) iters(start, end int) (is, ie *gtk.TextIter) {
return r.b.GetIterAtOffset(start), r.b.GetIterAtOffset(end)
}
func (r *renderCtx) tagAttr(start, end int, attr text.Attribute) {
var props = tagAttrMap(attr)
if props == nil {
return
}
r.applyTag(start, end, r.createTag(props))
}
func tagAttrMap(attr text.Attribute) map[string]interface{} {
if attr == 0 {
return nil
}
var props = make(map[string]interface{}, 1)
if attr.Has(text.AttrBold) {
props["weight"] = pango.WEIGHT_BOLD
}
if attr.Has(text.AttrItalics) {
props["style"] = pango.STYLE_ITALIC
}
if attr.Has(text.AttrUnderline) {
props["underline"] = pango.UNDERLINE_SINGLE
}
if attr.Has(text.AttrStrikethrough) {
props["strikethrough"] = true
}
if attr.Has(text.AttrSpoiler) {
props["foreground"] = "#808080"
}
if attr.Has(text.AttrMonospace) {
props["family"] = "Monospace"
}
return props
}

View File

@ -1,55 +0,0 @@
package parser
import (
"strings"
"testing"
"github.com/diamondburned/cchat-mock/segments"
"github.com/diamondburned/cchat/text"
)
func TestRenderMarkup(t *testing.T) {
content := text.Rich{Content: "astolfo is the best trap"}
content.Segments = []text.Segment{
segments.NewColored(content.Content, 0x55CDFC),
}
expect := `<span color="#55CDFC">` + content.Content + "</span>"
if text := RenderMarkup(content); text != expect {
t.Fatal("Unexpected text:", text)
}
}
func TestRenderMarkupPartial(t *testing.T) {
content := text.Rich{Content: "random placeholder text go brrr"}
content.Segments = []text.Segment{
// This is absolutely jankery that should not work at all, but we'll try
// it anyway.
coloredSegment{0, 4, 0x55CDFC},
coloredSegment{2, 6, 0xFFFFFF}, // naive parsing, so spans close unexpectedly.
coloredSegment{4, 6, 0xF7A8B8},
}
const expect = "" +
`<span color="#55CDFC">ra<span color="#FFFFFF">nd</span>` +
`<span color="#F7A8B8">om</span></span>`
if text := RenderMarkup(content); !strings.HasPrefix(text, expect) {
t.Fatal("Unexpected text:", text)
}
}
type coloredSegment struct {
start int
end int
color uint32
}
var _ text.Colorer = (*coloredSegment)(nil)
func (c coloredSegment) Bounds() (start, end int) {
return c.start, c.end
}
func (c coloredSegment) Color() uint32 {
return c.color
}