aboutsummaryrefslogtreecommitdiff
path: root/telegram
diff options
context:
space:
mode:
authorBohdan Horbeshko <bodqhrohro@gmail.com>2022-03-12 04:27:15 +0300
committerBohdan Horbeshko <bodqhrohro@gmail.com>2022-03-12 04:27:15 +0300
commitc6556eb6b86d4267bb35b7a27c34a0046668e3b9 (patch)
treedce73e18ffa5c46c03f877d8700f72bf1551cfdf /telegram
parentae8152ad87b32c827fa681285717b7b6ae3176fd (diff)
Clasp directives to the following span as required by XEP-0393
Diffstat (limited to 'telegram')
-rw-r--r--telegram/formatter/formatter.go51
-rw-r--r--telegram/formatter/formatter_test.go80
2 files changed, 130 insertions, 1 deletions
diff --git a/telegram/formatter/formatter.go b/telegram/formatter/formatter.go
index eca514a..d6541cb 100644
--- a/telegram/formatter/formatter.go
+++ b/telegram/formatter/formatter.go
@@ -2,6 +2,7 @@ package formatter
import (
"sort"
+ "unicode"
log "github.com/sirupsen/logrus"
"github.com/zelenin/go-tdlib/client"
@@ -118,6 +119,54 @@ func MergeAdjacentEntities(entities []*client.TextEntity) []*client.TextEntity {
return mergedEntities
}
+// ClaspDirectives to the following span as required by XEP-0393
+func ClaspDirectives(text string, entities []*client.TextEntity) []*client.TextEntity {
+ alignedEntities := make([]*client.TextEntity, len(entities))
+ copy(alignedEntities, entities)
+
+ // transform the source text into a form with uniform runes and code points,
+ // by duplicating the Basic Multilingual Plane
+ doubledRunes := make([]rune, 0, len(text)*2)
+
+ for _, cp := range text {
+ if cp > 0x0000ffff {
+ doubledRunes = append(doubledRunes, cp, cp)
+ } else {
+ doubledRunes = append(doubledRunes, cp)
+ }
+ }
+ for i, entity := range alignedEntities {
+ var dirty bool
+ endOffset := entity.Offset + entity.Length
+
+ if unicode.IsSpace(doubledRunes[entity.Offset]) {
+ for j, r := range doubledRunes[entity.Offset+1:endOffset] {
+ if !unicode.IsSpace(r) {
+ dirty = true
+ entity.Offset += int32(j+1)
+ entity.Length -= int32(j+1)
+ break
+ }
+ }
+ }
+ if unicode.IsSpace(doubledRunes[endOffset-1]) {
+ for j := endOffset-2; j >= entity.Offset; j-- {
+ if !unicode.IsSpace(doubledRunes[j]) {
+ dirty = true
+ entity.Length = j+1-entity.Offset
+ break
+ }
+ }
+ }
+
+ if dirty {
+ alignedEntities[i] = entity
+ }
+ }
+
+ return alignedEntities
+}
+
func markupBraces(entity *client.TextEntity, lbrace, rbrace []rune) (*Insertion, *Insertion) {
return &Insertion{
Offset: entity.Offset,
@@ -191,7 +240,7 @@ func Format(
return sourceText
}
- mergedEntities := SortEntities(MergeAdjacentEntities(SortEntities(entities)))
+ mergedEntities := SortEntities(ClaspDirectives(sourceText, MergeAdjacentEntities(SortEntities(entities))))
startStack := make(InsertionStack, 0, len(sourceText))
endStack := make(InsertionStack, 0, len(sourceText))
diff --git a/telegram/formatter/formatter_test.go b/telegram/formatter/formatter_test.go
index c988b10..e4bdd23 100644
--- a/telegram/formatter/formatter_test.go
+++ b/telegram/formatter/formatter_test.go
@@ -392,3 +392,83 @@ func TestFormattingXEP0393Strikethrough(t *testing.T) {
t.Errorf("Wrong strikethrough formatting: %v", markup)
}
}
+
+func TestClaspLeft(t *testing.T) {
+ text := "a b c"
+ entities := []*client.TextEntity{
+ &client.TextEntity{
+ Offset: 1,
+ Length: 2,
+ },
+ }
+ entities = ClaspDirectives(text, entities)
+ if !(len(entities) == 1 &&
+ entities[0].Offset == 2 && entities[0].Length == 1) {
+ t.Errorf("Wrong claspleft: %#v", entities)
+ }
+}
+
+func TestClaspBoth(t *testing.T) {
+ text := "a b c"
+ entities := []*client.TextEntity{
+ &client.TextEntity{
+ Offset: 1,
+ Length: 3,
+ },
+ }
+ entities = ClaspDirectives(text, entities)
+ if !(len(entities) == 1 &&
+ entities[0].Offset == 2 && entities[0].Length == 1) {
+ t.Errorf("Wrong claspboth: %#v", entities)
+ }
+}
+
+func TestClaspNotNeeded(t *testing.T) {
+ text := " abc "
+ entities := []*client.TextEntity{
+ &client.TextEntity{
+ Offset: 1,
+ Length: 3,
+ },
+ }
+ entities = ClaspDirectives(text, entities)
+ if !(len(entities) == 1 &&
+ entities[0].Offset == 1 && entities[0].Length == 3) {
+ t.Errorf("Wrong claspnotneeded: %#v", entities)
+ }
+}
+
+func TestClaspNested(t *testing.T) {
+ text := "a b c"
+ entities := []*client.TextEntity{
+ &client.TextEntity{
+ Offset: 1,
+ Length: 3,
+ },
+ &client.TextEntity{
+ Offset: 2,
+ Length: 2,
+ },
+ }
+ entities = ClaspDirectives(text, entities)
+ if !(len(entities) == 2 &&
+ entities[0].Offset == 2 && entities[0].Length == 1 &&
+ entities[1].Offset == 2 && entities[1].Length == 1) {
+ t.Errorf("Wrong claspnested: %#v", entities)
+ }
+}
+
+func TestClaspEmoji(t *testing.T) {
+ text := "a 🐖 c"
+ entities := []*client.TextEntity{
+ &client.TextEntity{
+ Offset: 1,
+ Length: 4,
+ },
+ }
+ entities = ClaspDirectives(text, entities)
+ if !(len(entities) == 1 &&
+ entities[0].Offset == 2 && entities[0].Length == 2) {
+ t.Errorf("Wrong claspemoji: %#v", entities)
+ }
+}