123 lines
3.2 KiB
Go
123 lines
3.2 KiB
Go
// mautrix-signal - A Matrix-Signal puppeting bridge.
|
|
// Copyright (C) 2023 Tulir Asokan
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Affero General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Affero General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
package signalfmt
|
|
|
|
import (
|
|
"fmt"
|
|
"sort"
|
|
|
|
"google.golang.org/protobuf/proto"
|
|
|
|
signalpb "go.mau.fi/mautrix-signal/pkg/signalmeow/protobuf"
|
|
)
|
|
|
|
type BodyRange struct {
|
|
Start int
|
|
Length int
|
|
Value BodyRangeValue
|
|
}
|
|
|
|
type BodyRangeList []BodyRange
|
|
|
|
var _ sort.Interface = BodyRangeList(nil)
|
|
|
|
func (b BodyRangeList) Len() int {
|
|
return len(b)
|
|
}
|
|
|
|
func (b BodyRangeList) Less(i, j int) bool {
|
|
return b[i].Start < b[j].Start || b[i].Length > b[j].Length
|
|
}
|
|
|
|
func (b BodyRangeList) Swap(i, j int) {
|
|
b[i], b[j] = b[j], b[i]
|
|
}
|
|
|
|
func (b BodyRange) String() string {
|
|
return fmt.Sprintf("%d:%d:%v", b.Start, b.Length, b.Value)
|
|
}
|
|
|
|
// End returns the end index of the range.
|
|
func (b BodyRange) End() int {
|
|
return b.Start + b.Length
|
|
}
|
|
|
|
// Offset changes the start of the range without affecting the length.
|
|
func (b BodyRange) Offset(offset int) *BodyRange {
|
|
b.Start += offset
|
|
return &b
|
|
}
|
|
|
|
// TruncateStart changes the length of the range, so it starts at the given index and ends at the same index as before.
|
|
func (b BodyRange) TruncateStart(startAt int) *BodyRange {
|
|
if b.Start < startAt {
|
|
b.Length -= startAt - b.Start
|
|
b.Start = startAt
|
|
}
|
|
return &b
|
|
}
|
|
|
|
// TruncateEnd changes the length of the range, so it ends at or before the given index and starts at the same index as before.
|
|
func (b BodyRange) TruncateEnd(maxEnd int) *BodyRange {
|
|
if b.End() > maxEnd {
|
|
b.Length = maxEnd - b.Start
|
|
}
|
|
return &b
|
|
}
|
|
|
|
func (b BodyRange) Proto() *signalpb.BodyRange {
|
|
return &signalpb.BodyRange{
|
|
Start: proto.Uint32(uint32(b.Start)),
|
|
Length: proto.Uint32(uint32(b.Length)),
|
|
AssociatedValue: b.Value.Proto(),
|
|
}
|
|
}
|
|
|
|
// LinkedRangeTree is a linked tree of formatting entities.
|
|
//
|
|
// It's meant to parse a list of Signal body ranges into nodes that either overlap completely or not at all,
|
|
// which enables more natural conversion to HTML.
|
|
type LinkedRangeTree struct {
|
|
Node *BodyRange
|
|
Sibling *LinkedRangeTree
|
|
Child *LinkedRangeTree
|
|
}
|
|
|
|
func ptrAdd(to **LinkedRangeTree, r *BodyRange) {
|
|
if *to == nil {
|
|
*to = &LinkedRangeTree{}
|
|
}
|
|
(*to).Add(r)
|
|
}
|
|
|
|
// Add adds the given formatting entity to this tree.
|
|
func (lrt *LinkedRangeTree) Add(r *BodyRange) {
|
|
if lrt.Node == nil {
|
|
lrt.Node = r
|
|
return
|
|
}
|
|
lrtEnd := lrt.Node.End()
|
|
if r.Start >= lrtEnd {
|
|
ptrAdd(&lrt.Sibling, r.Offset(-lrtEnd))
|
|
return
|
|
}
|
|
if r.End() > lrtEnd {
|
|
ptrAdd(&lrt.Sibling, r.TruncateStart(lrtEnd).Offset(-lrtEnd))
|
|
}
|
|
ptrAdd(&lrt.Child, r.TruncateEnd(lrtEnd).Offset(-lrt.Node.Start))
|
|
}
|