Merge branch 'deep-message-rendering'
This commit is contained in:
		
							
								
								
									
										1
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								go.mod
									
									
									
									
									
								
							@@ -3,6 +3,7 @@ module maunium.net/go/gomuks
 | 
				
			|||||||
go 1.12
 | 
					go 1.12
 | 
				
			||||||
 | 
					
 | 
				
			||||||
require (
 | 
					require (
 | 
				
			||||||
 | 
						github.com/alecthomas/chroma v0.6.3
 | 
				
			||||||
	github.com/disintegration/imaging v1.6.0
 | 
						github.com/disintegration/imaging v1.6.0
 | 
				
			||||||
	github.com/kyokomi/emoji v2.1.0+incompatible
 | 
						github.com/kyokomi/emoji v2.1.0+incompatible
 | 
				
			||||||
	github.com/lithammer/fuzzysearch v1.0.2
 | 
						github.com/lithammer/fuzzysearch v1.0.2
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										17
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								go.sum
									
									
									
									
									
								
							@@ -1,5 +1,16 @@
 | 
				
			|||||||
 | 
					github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38/go.mod h1:r7bzyVFMNntcxPZXK3/+KdruV1H5KSlyVY0gc+NgInI=
 | 
				
			||||||
 | 
					github.com/alecthomas/chroma v0.6.3 h1:8H1D0yddf0mvgvO4JDBKnzLd9ERmzzAijBxnZXGV/FA=
 | 
				
			||||||
 | 
					github.com/alecthomas/chroma v0.6.3/go.mod h1:quT2EpvJNqkuPi6DmBHB+E33FXBgBBPzyH5++Dn1LPc=
 | 
				
			||||||
 | 
					github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721/go.mod h1:QO9JBoKquHd+jz9nshCh40fOfO+JzsoXy8qTHF68zU0=
 | 
				
			||||||
 | 
					github.com/alecthomas/kong v0.1.15/go.mod h1:0m2VYms8rH0qbCqVB2gvGHk74bqLIq0HXjCs5bNbNQU=
 | 
				
			||||||
 | 
					github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ=
 | 
				
			||||||
 | 
					github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 h1:y5HC9v93H5EPKqaS1UYVg1uYah5Xf51mBfIoWehClUQ=
 | 
				
			||||||
 | 
					github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964/go.mod h1:Xd9hchkHSWYkEqJwUGisez3G1QY8Ryz0sdWrLPMGjLk=
 | 
				
			||||||
 | 
					github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
				
			||||||
github.com/disintegration/imaging v1.6.0 h1:nVPXRUUQ36Z7MNf0O77UzgnOb1mkMMor7lmJMJXc/mA=
 | 
					github.com/disintegration/imaging v1.6.0 h1:nVPXRUUQ36Z7MNf0O77UzgnOb1mkMMor7lmJMJXc/mA=
 | 
				
			||||||
github.com/disintegration/imaging v1.6.0/go.mod h1:xuIt+sRxDFrHS0drzXUlCJthkJ8k7lkkUojDSR247MQ=
 | 
					github.com/disintegration/imaging v1.6.0/go.mod h1:xuIt+sRxDFrHS0drzXUlCJthkJ8k7lkkUojDSR247MQ=
 | 
				
			||||||
 | 
					github.com/dlclark/regexp2 v1.1.6 h1:CqB4MjHw0MFCDj+PHHjiESmHX+N7t0tJzKvC6M97BRg=
 | 
				
			||||||
 | 
					github.com/dlclark/regexp2 v1.1.6/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
 | 
				
			||||||
github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
 | 
					github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
 | 
				
			||||||
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
 | 
					github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
 | 
				
			||||||
github.com/kyokomi/emoji v2.1.0+incompatible h1:+DYU2RgpI6OHG4oQkM5KlqD3Wd3UPEsX8jamTo1Mp6o=
 | 
					github.com/kyokomi/emoji v2.1.0+incompatible h1:+DYU2RgpI6OHG4oQkM5KlqD3Wd3UPEsX8jamTo1Mp6o=
 | 
				
			||||||
@@ -8,16 +19,21 @@ github.com/lithammer/fuzzysearch v1.0.2 h1:AjCE2iwc5y+8K+h2nXVc0Pmrpjvu+JVqMgiZ0
 | 
				
			|||||||
github.com/lithammer/fuzzysearch v1.0.2/go.mod h1:bvAJyokfCQ7Vknrd4Kgc+izmMrPj5CiBAu2t6rK1Kak=
 | 
					github.com/lithammer/fuzzysearch v1.0.2/go.mod h1:bvAJyokfCQ7Vknrd4Kgc+izmMrPj5CiBAu2t6rK1Kak=
 | 
				
			||||||
github.com/lucasb-eyer/go-colorful v1.0.1 h1:nKJRBvZWPzvkwB4sY8A3U4zgqLf2Y9c02yzPsbXu/5c=
 | 
					github.com/lucasb-eyer/go-colorful v1.0.1 h1:nKJRBvZWPzvkwB4sY8A3U4zgqLf2Y9c02yzPsbXu/5c=
 | 
				
			||||||
github.com/lucasb-eyer/go-colorful v1.0.1/go.mod h1:tLy1nWSoU0DGtxQyNRrUmb6PUiB7usbds6gd97XTXwA=
 | 
					github.com/lucasb-eyer/go-colorful v1.0.1/go.mod h1:tLy1nWSoU0DGtxQyNRrUmb6PUiB7usbds6gd97XTXwA=
 | 
				
			||||||
 | 
					github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
 | 
				
			||||||
 | 
					github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
 | 
				
			||||||
github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
 | 
					github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
 | 
				
			||||||
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
 | 
					github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
 | 
				
			||||||
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d h1:VhgPp6v9qf9Agr/56bj7Y/xa04UccTW04VP0Qed4vnQ=
 | 
					github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d h1:VhgPp6v9qf9Agr/56bj7Y/xa04UccTW04VP0Qed4vnQ=
 | 
				
			||||||
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d/go.mod h1:YUTz3bUH2ZwIWBy3CJBeOBEugqcmXREj14T+iG/4k4U=
 | 
					github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d/go.mod h1:YUTz3bUH2ZwIWBy3CJBeOBEugqcmXREj14T+iG/4k4U=
 | 
				
			||||||
 | 
					github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 | 
				
			||||||
github.com/rivo/uniseg v0.0.0-20190313204849-f699dde9c340 h1:nOZbL5f2xmBAHWYrrHbHV1xatzZirN++oOQ3g83Ypgs=
 | 
					github.com/rivo/uniseg v0.0.0-20190313204849-f699dde9c340 h1:nOZbL5f2xmBAHWYrrHbHV1xatzZirN++oOQ3g83Ypgs=
 | 
				
			||||||
github.com/rivo/uniseg v0.0.0-20190313204849-f699dde9c340/go.mod h1:SOLvOL4ybwgLJ6TYoX/rtaJ8EGOulH4XU7E9/TLrTCE=
 | 
					github.com/rivo/uniseg v0.0.0-20190313204849-f699dde9c340/go.mod h1:SOLvOL4ybwgLJ6TYoX/rtaJ8EGOulH4XU7E9/TLrTCE=
 | 
				
			||||||
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
 | 
					github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
 | 
				
			||||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 | 
					github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 | 
				
			||||||
 | 
					github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
 | 
				
			||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
 | 
					github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
 | 
				
			||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
 | 
					github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
 | 
				
			||||||
 | 
					github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 | 
				
			||||||
github.com/zyedidia/clipboard v0.0.0-20180718195219-bd31d747117d h1:Lhqt2eo+rgM8aswvM7nTtAMVm8ARPWzkE9n6eZDOccY=
 | 
					github.com/zyedidia/clipboard v0.0.0-20180718195219-bd31d747117d h1:Lhqt2eo+rgM8aswvM7nTtAMVm8ARPWzkE9n6eZDOccY=
 | 
				
			||||||
github.com/zyedidia/clipboard v0.0.0-20180718195219-bd31d747117d/go.mod h1:WDk3p8GiZV9+xFWlSo8qreeoLhW6Ik692rqXk+cNeRY=
 | 
					github.com/zyedidia/clipboard v0.0.0-20180718195219-bd31d747117d/go.mod h1:WDk3p8GiZV9+xFWlSo8qreeoLhW6Ik692rqXk+cNeRY=
 | 
				
			||||||
go.etcd.io/bbolt v1.3.2 h1:Z/90sZLPOeCy2PwprqkFa25PdkusRzaj9P8zm/KNyvk=
 | 
					go.etcd.io/bbolt v1.3.2 h1:Z/90sZLPOeCy2PwprqkFa25PdkusRzaj9P8zm/KNyvk=
 | 
				
			||||||
@@ -32,6 +48,7 @@ golang.org/x/net v0.0.0-20190110200230-915654e7eabc/go.mod h1:mL1N/T3taQHkDXs73r
 | 
				
			|||||||
golang.org/x/net v0.0.0-20190326090315-15845e8f865b/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 | 
					golang.org/x/net v0.0.0-20190326090315-15845e8f865b/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 | 
				
			||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
 | 
					golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
 | 
				
			||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 | 
					golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 | 
				
			||||||
 | 
					golang.org/x/sys v0.0.0-20181128092732-4ed8d59d0b35/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
					golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20190322080309-f49334f85ddc/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20190322080309-f49334f85ddc/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -76,12 +76,12 @@ func NewCommandProcessor(parent *MainView) *CommandProcessor {
 | 
				
			|||||||
			Gomuks:   parent.gmx,
 | 
								Gomuks:   parent.gmx,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		aliases: map[string]*Alias{
 | 
							aliases: map[string]*Alias{
 | 
				
			||||||
			"part":  {"leave"},
 | 
								"part":   {"leave"},
 | 
				
			||||||
			"send":  {"sendevent"},
 | 
								"send":   {"sendevent"},
 | 
				
			||||||
			"msend": {"msendevent"},
 | 
								"msend":  {"msendevent"},
 | 
				
			||||||
			"state": {"setstate"},
 | 
								"state":  {"setstate"},
 | 
				
			||||||
			"mstate":{"msetstate"},
 | 
								"mstate": {"msetstate"},
 | 
				
			||||||
			"rb":    {"rainbow"},
 | 
								"rb":     {"rainbow"},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		commands: map[string]CommandHandler{
 | 
							commands: map[string]CommandHandler{
 | 
				
			||||||
			"unknown-command": cmdUnknownCommand,
 | 
								"unknown-command": cmdUnknownCommand,
 | 
				
			||||||
@@ -102,6 +102,7 @@ func NewCommandProcessor(parent *MainView) *CommandProcessor {
 | 
				
			|||||||
			"msetstate":       cmdMSetState,
 | 
								"msetstate":       cmdMSetState,
 | 
				
			||||||
			"rainbow":         cmdRainbow,
 | 
								"rainbow":         cmdRainbow,
 | 
				
			||||||
			"invite":          cmdInvite,
 | 
								"invite":          cmdInvite,
 | 
				
			||||||
 | 
								"hprof":           cmdHeapProfile,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,6 +19,9 @@ package ui
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"encoding/json"
 | 
						"encoding/json"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"runtime"
 | 
				
			||||||
 | 
						"runtime/pprof"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"unicode"
 | 
						"unicode"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -69,6 +72,18 @@ var rainbow = GradientTable{
 | 
				
			|||||||
	{colorful.LinearRgb(1, 0, 0.5), 1},
 | 
						{colorful.LinearRgb(1, 0, 0.5), 1},
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func cmdHeapProfile(cmd *Command) {
 | 
				
			||||||
 | 
						runtime.GC()
 | 
				
			||||||
 | 
						memProfile, err := os.Create("gomuks.prof")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							debug.Print(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer memProfile.Close()
 | 
				
			||||||
 | 
						if err := pprof.WriteHeapProfile(memProfile); err != nil {
 | 
				
			||||||
 | 
							debug.Print(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TODO this command definitely belongs in a plugin once we have a plugin system.
 | 
					// TODO this command definitely belongs in a plugin once we have a plugin system.
 | 
				
			||||||
func cmdRainbow(cmd *Command) {
 | 
					func cmdRainbow(cmd *Command) {
 | 
				
			||||||
	text := strings.Join(cmd.Args, " ")
 | 
						text := strings.Join(cmd.Args, " ")
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -75,6 +75,7 @@ func NewMessageView(parent *RoomView) *MessageView {
 | 
				
			|||||||
		textBuffer: make([]tstring.TString, 0),
 | 
							textBuffer: make([]tstring.TString, 0),
 | 
				
			||||||
		metaBuffer: make([]ifc.MessageMeta, 0),
 | 
							metaBuffer: make([]ifc.MessageMeta, 0),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							width:        80,
 | 
				
			||||||
		widestSender: 5,
 | 
							widestSender: 5,
 | 
				
			||||||
		prevWidth:    -1,
 | 
							prevWidth:    -1,
 | 
				
			||||||
		prevHeight:   -1,
 | 
							prevHeight:   -1,
 | 
				
			||||||
@@ -159,8 +160,8 @@ func (view *MessageView) appendBuffer(message messages.UIMessage) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	view.textBuffer = append(view.textBuffer, message.Buffer()...)
 | 
						for i := 0; i < message.Height(); i++ {
 | 
				
			||||||
	for range message.Buffer() {
 | 
							view.textBuffer = append(view.textBuffer, nil)
 | 
				
			||||||
		view.metaBuffer = append(view.metaBuffer, message)
 | 
							view.metaBuffer = append(view.metaBuffer, message)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	view.prevMsgCount++
 | 
						view.prevMsgCount++
 | 
				
			||||||
@@ -200,10 +201,15 @@ func (view *MessageView) replaceBuffer(original messages.UIMessage, new messages
 | 
				
			|||||||
		end++
 | 
							end++
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	view.textBuffer = append(append(view.textBuffer[0:start], new.Buffer()...), view.textBuffer[end:]...)
 | 
						if new.Height() == 0 {
 | 
				
			||||||
	if len(new.Buffer()) != end-start {
 | 
							new.CalculateBuffer(view.prevPrefs, view.prevWidth)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						textBuf := make([]tstring.TString, new.Height())
 | 
				
			||||||
 | 
						view.textBuffer = append(append(view.textBuffer[0:start], textBuf...), view.textBuffer[end:]...)
 | 
				
			||||||
 | 
						if new.Height() != end-start {
 | 
				
			||||||
		metaBuffer := view.metaBuffer[0:start]
 | 
							metaBuffer := view.metaBuffer[0:start]
 | 
				
			||||||
		for range new.Buffer() {
 | 
							for i := 0; i < new.Height(); i++ {
 | 
				
			||||||
			metaBuffer = append(metaBuffer, new)
 | 
								metaBuffer = append(metaBuffer, new)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		view.metaBuffer = append(metaBuffer, view.metaBuffer[end:]...)
 | 
							view.metaBuffer = append(metaBuffer, view.metaBuffer[end:]...)
 | 
				
			||||||
@@ -497,15 +503,30 @@ func (view *MessageView) Draw(screen mauview.Screen) {
 | 
				
			|||||||
			if len(meta.FormatTime()) > 0 {
 | 
								if len(meta.FormatTime()) > 0 {
 | 
				
			||||||
				widget.WriteLineSimpleColor(screen, meta.FormatTime(), 0, line, meta.TimestampColor())
 | 
									widget.WriteLineSimpleColor(screen, meta.FormatTime(), 0, line, meta.TimestampColor())
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			if !bareMode && (prevMeta == nil || meta.Sender() != prevMeta.Sender()) {
 | 
								// TODO hiding senders might not be that nice after all, maybe an option? (disabled for now)
 | 
				
			||||||
				widget.WriteLineColor(
 | 
								//if !bareMode && (prevMeta == nil || meta.Sender() != prevMeta.Sender()) {
 | 
				
			||||||
					screen, mauview.AlignRight, meta.Sender(),
 | 
								widget.WriteLineColor(
 | 
				
			||||||
					usernameX, line, view.widestSender,
 | 
									screen, mauview.AlignRight, meta.Sender(),
 | 
				
			||||||
					meta.SenderColor())
 | 
									usernameX, line, view.widestSender,
 | 
				
			||||||
			}
 | 
									meta.SenderColor())
 | 
				
			||||||
 | 
								//}
 | 
				
			||||||
			prevMeta = meta
 | 
								prevMeta = meta
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		text.Draw(screen, messageX, line)
 | 
							message, ok := meta.(messages.UIMessage)
 | 
				
			||||||
 | 
							if ok {
 | 
				
			||||||
 | 
								for i := index - 1; i >= 0 && view.metaBuffer[i] == meta; i-- {
 | 
				
			||||||
 | 
									line--
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								message.Draw(mauview.NewProxyScreen(screen, messageX, line, view.width-messageX, message.Height()))
 | 
				
			||||||
 | 
								if !bareMode {
 | 
				
			||||||
 | 
									for i := line; i < line+message.Height(); i++ {
 | 
				
			||||||
 | 
										screen.SetContent(separatorX, i, borderChar, nil, borderStyle)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								line += message.Height() - 1
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								text.Draw(screen, messageX, line)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,9 +21,9 @@ import (
 | 
				
			|||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"maunium.net/go/mautrix"
 | 
						"maunium.net/go/mautrix"
 | 
				
			||||||
 | 
						"maunium.net/go/mauview"
 | 
				
			||||||
	"maunium.net/go/tcell"
 | 
						"maunium.net/go/tcell"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"maunium.net/go/gomuks/config"
 | 
					 | 
				
			||||||
	"maunium.net/go/gomuks/interface"
 | 
						"maunium.net/go/gomuks/interface"
 | 
				
			||||||
	"maunium.net/go/gomuks/ui/messages/tstring"
 | 
						"maunium.net/go/gomuks/ui/messages/tstring"
 | 
				
			||||||
	"maunium.net/go/gomuks/ui/widget"
 | 
						"maunium.net/go/gomuks/ui/widget"
 | 
				
			||||||
@@ -34,33 +34,30 @@ func init() {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type BaseMessage struct {
 | 
					type BaseMessage struct {
 | 
				
			||||||
	MsgID           string
 | 
						MsgID          string
 | 
				
			||||||
	MsgType         mautrix.MessageType
 | 
						MsgType        mautrix.MessageType
 | 
				
			||||||
	MsgSenderID     string
 | 
						MsgSenderID    string
 | 
				
			||||||
	MsgSender       string
 | 
						MsgSender      string
 | 
				
			||||||
	MsgSenderColor  tcell.Color
 | 
						MsgSenderColor tcell.Color
 | 
				
			||||||
	MsgTimestamp    time.Time
 | 
						MsgTimestamp   time.Time
 | 
				
			||||||
	MsgState        ifc.MessageState
 | 
						MsgState       ifc.MessageState
 | 
				
			||||||
	MsgIsHighlight  bool
 | 
						MsgIsHighlight bool
 | 
				
			||||||
	MsgIsService    bool
 | 
						MsgIsService   bool
 | 
				
			||||||
	buffer          []tstring.TString
 | 
						buffer         []tstring.TString
 | 
				
			||||||
	plainBuffer     []tstring.TString
 | 
						plainBuffer    []tstring.TString
 | 
				
			||||||
	prevBufferWidth int
 | 
					 | 
				
			||||||
	prevPrefs       config.UserPreferences
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func newBaseMessage(id, sender, displayname string, msgtype mautrix.MessageType, timestamp time.Time) BaseMessage {
 | 
					func newBaseMessage(id, sender, displayname string, msgtype mautrix.MessageType, timestamp time.Time) BaseMessage {
 | 
				
			||||||
	return BaseMessage{
 | 
						return BaseMessage{
 | 
				
			||||||
		MsgSenderID:     sender,
 | 
							MsgSenderID:    sender,
 | 
				
			||||||
		MsgSender:       displayname,
 | 
							MsgSender:      displayname,
 | 
				
			||||||
		MsgTimestamp:    timestamp,
 | 
							MsgTimestamp:   timestamp,
 | 
				
			||||||
		MsgSenderColor:  widget.GetHashColor(sender),
 | 
							MsgSenderColor: widget.GetHashColor(sender),
 | 
				
			||||||
		MsgType:         msgtype,
 | 
							MsgType:        msgtype,
 | 
				
			||||||
		MsgID:           id,
 | 
							MsgID:          id,
 | 
				
			||||||
		prevBufferWidth: 0,
 | 
							MsgState:       ifc.MessageStateDefault,
 | 
				
			||||||
		MsgState:        ifc.MessageStateDefault,
 | 
							MsgIsHighlight: false,
 | 
				
			||||||
		MsgIsHighlight:  false,
 | 
							MsgIsService:   false,
 | 
				
			||||||
		MsgIsService:    false,
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -227,3 +224,9 @@ func (msg *BaseMessage) IsService() bool {
 | 
				
			|||||||
func (msg *BaseMessage) SetIsService(isService bool) {
 | 
					func (msg *BaseMessage) SetIsService(isService bool) {
 | 
				
			||||||
	msg.MsgIsService = isService
 | 
						msg.MsgIsService = isService
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (msg *BaseMessage) Draw(screen mauview.Screen) {
 | 
				
			||||||
 | 
						for y, line := range msg.buffer {
 | 
				
			||||||
 | 
							line.Draw(screen, 0, y)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -58,8 +58,3 @@ func (msg *ExpandedTextMessage) PlainText() string {
 | 
				
			|||||||
func (msg *ExpandedTextMessage) CalculateBuffer(prefs config.UserPreferences, width int) {
 | 
					func (msg *ExpandedTextMessage) CalculateBuffer(prefs config.UserPreferences, width int) {
 | 
				
			||||||
	msg.calculateBufferWithText(prefs, msg.MsgText, width)
 | 
						msg.calculateBufferWithText(prefs, msg.MsgText, width)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
// RecalculateBuffer calculates the buffer again with the previously provided width.
 | 
					 | 
				
			||||||
func (msg *ExpandedTextMessage) RecalculateBuffer() {
 | 
					 | 
				
			||||||
	msg.CalculateBuffer(msg.prevPrefs, msg.prevBufferWidth)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										411
									
								
								ui/messages/htmlmessage.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										411
									
								
								ui/messages/htmlmessage.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,411 @@
 | 
				
			|||||||
 | 
					// gomuks - A terminal Matrix client written in Go.
 | 
				
			||||||
 | 
					// Copyright (C) 2019 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 messages
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"math"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/mattn/go-runewidth"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"maunium.net/go/mautrix"
 | 
				
			||||||
 | 
						"maunium.net/go/mauview"
 | 
				
			||||||
 | 
						"maunium.net/go/tcell"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"maunium.net/go/gomuks/config"
 | 
				
			||||||
 | 
						"maunium.net/go/gomuks/ui/widget"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type HTMLMessage struct {
 | 
				
			||||||
 | 
						BaseMessage
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Root HTMLEntity
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewHTMLMessage(id, sender, displayname string, msgtype mautrix.MessageType, root HTMLEntity, timestamp time.Time) UIMessage {
 | 
				
			||||||
 | 
						return &HTMLMessage{
 | 
				
			||||||
 | 
							BaseMessage: newBaseMessage(id, sender, displayname, msgtype, timestamp),
 | 
				
			||||||
 | 
							Root:        root,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					func (hw *HTMLMessage) Draw(screen mauview.Screen) {
 | 
				
			||||||
 | 
						hw.Root.Draw(screen)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (hw *HTMLMessage) OnKeyEvent(event mauview.KeyEvent) bool {
 | 
				
			||||||
 | 
						return false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (hw *HTMLMessage) OnMouseEvent(event mauview.MouseEvent) bool {
 | 
				
			||||||
 | 
						return false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (hw *HTMLMessage) OnPasteEvent(event mauview.PasteEvent) bool {
 | 
				
			||||||
 | 
						return false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (hw *HTMLMessage) CalculateBuffer(preferences config.UserPreferences, width int) {
 | 
				
			||||||
 | 
						if width <= 0 {
 | 
				
			||||||
 | 
							panic("Negative width in CalculateBuffer")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// TODO account for bare messages in initial startX
 | 
				
			||||||
 | 
						startX := 0
 | 
				
			||||||
 | 
						hw.Root.calculateBuffer(width, startX, preferences.BareMessageView)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (hw *HTMLMessage) Height() int {
 | 
				
			||||||
 | 
						return hw.Root.Height()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (hw *HTMLMessage) PlainText() string {
 | 
				
			||||||
 | 
						return hw.Root.PlainText()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (hw *HTMLMessage) NotificationContent() string {
 | 
				
			||||||
 | 
						return hw.Root.PlainText()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type AdjustStyleFunc func(tcell.Style) tcell.Style
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type HTMLEntity interface {
 | 
				
			||||||
 | 
						AdjustStyle(AdjustStyleFunc) HTMLEntity
 | 
				
			||||||
 | 
						Draw(screen mauview.Screen)
 | 
				
			||||||
 | 
						IsBlock() bool
 | 
				
			||||||
 | 
						GetTag() string
 | 
				
			||||||
 | 
						PlainText() string
 | 
				
			||||||
 | 
						String() string
 | 
				
			||||||
 | 
						Height() int
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						calculateBuffer(width, startX int, bare bool) int
 | 
				
			||||||
 | 
						getStartX() int
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type BlockquoteEntity struct {
 | 
				
			||||||
 | 
						*BaseHTMLEntity
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewBlockquoteEntity(children []HTMLEntity) *BlockquoteEntity {
 | 
				
			||||||
 | 
						return &BlockquoteEntity{&BaseHTMLEntity{
 | 
				
			||||||
 | 
							Tag:      "blockquote",
 | 
				
			||||||
 | 
							Children: children,
 | 
				
			||||||
 | 
							Block:    true,
 | 
				
			||||||
 | 
							Indent:   2,
 | 
				
			||||||
 | 
						}}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (be *BlockquoteEntity) Draw(screen mauview.Screen) {
 | 
				
			||||||
 | 
						be.BaseHTMLEntity.Draw(screen)
 | 
				
			||||||
 | 
						for y := 0; y < be.height; y++ {
 | 
				
			||||||
 | 
							screen.SetContent(0, y, '>', nil, be.Style)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (be *BlockquoteEntity) String() string {
 | 
				
			||||||
 | 
						return fmt.Sprintf("&BlockquoteEntity{%s},\n", be.BaseHTMLEntity)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ListEntity struct {
 | 
				
			||||||
 | 
						*BaseHTMLEntity
 | 
				
			||||||
 | 
						Ordered bool
 | 
				
			||||||
 | 
						Start   int
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func digits(num int) int {
 | 
				
			||||||
 | 
						if num <= 0 {
 | 
				
			||||||
 | 
							return 0
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return int(math.Floor(math.Log10(float64(num))) + 1)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewListEntity(ordered bool, start int, children []HTMLEntity) *ListEntity {
 | 
				
			||||||
 | 
						entity := &ListEntity{
 | 
				
			||||||
 | 
							BaseHTMLEntity: &BaseHTMLEntity{
 | 
				
			||||||
 | 
								Tag:      "ul",
 | 
				
			||||||
 | 
								Children: children,
 | 
				
			||||||
 | 
								Block:    true,
 | 
				
			||||||
 | 
								Indent:   2,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							Ordered: ordered,
 | 
				
			||||||
 | 
							Start:   start,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if ordered {
 | 
				
			||||||
 | 
							entity.Tag = "ol"
 | 
				
			||||||
 | 
							entity.Indent += digits(start + len(children) - 1)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return entity
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (le *ListEntity) Draw(screen mauview.Screen) {
 | 
				
			||||||
 | 
						width, _ := screen.Size()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						proxyScreen := &mauview.ProxyScreen{Parent: screen, OffsetX: le.Indent, Width: width - le.Indent}
 | 
				
			||||||
 | 
						for i, entity := range le.Children {
 | 
				
			||||||
 | 
							proxyScreen.Height = entity.Height()
 | 
				
			||||||
 | 
							if le.Ordered {
 | 
				
			||||||
 | 
								number := le.Start + i
 | 
				
			||||||
 | 
								line := fmt.Sprintf("%d. %s", number, strings.Repeat(" ", le.Indent-2-digits(number)))
 | 
				
			||||||
 | 
								widget.WriteLine(screen, mauview.AlignLeft, line, 0, proxyScreen.OffsetY, le.Indent, le.Style)
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								screen.SetContent(0, proxyScreen.OffsetY, '●', nil, le.Style)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							entity.Draw(proxyScreen)
 | 
				
			||||||
 | 
							proxyScreen.OffsetY += entity.Height()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (le *ListEntity) String() string {
 | 
				
			||||||
 | 
						return fmt.Sprintf("&ListEntity{Ordered=%t, Start=%d, Base=%s},\n", le.Ordered, le.Start, le.BaseHTMLEntity)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type BreakEntity struct {
 | 
				
			||||||
 | 
						*BaseHTMLEntity
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewBreakEntity() *BreakEntity {
 | 
				
			||||||
 | 
						return &BreakEntity{&BaseHTMLEntity{
 | 
				
			||||||
 | 
							Tag:   "br",
 | 
				
			||||||
 | 
							Block: true,
 | 
				
			||||||
 | 
						}}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type BaseHTMLEntity struct {
 | 
				
			||||||
 | 
						// Permanent variables
 | 
				
			||||||
 | 
						Tag      string
 | 
				
			||||||
 | 
						Text     string
 | 
				
			||||||
 | 
						Style    tcell.Style
 | 
				
			||||||
 | 
						Children []HTMLEntity
 | 
				
			||||||
 | 
						Block    bool
 | 
				
			||||||
 | 
						Indent   int
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						DefaultHeight int
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Non-permanent variables (calculated buffer data)
 | 
				
			||||||
 | 
						buffer    []string
 | 
				
			||||||
 | 
						prevWidth int
 | 
				
			||||||
 | 
						startX    int
 | 
				
			||||||
 | 
						height    int
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewHTMLTextEntity(text string) *BaseHTMLEntity {
 | 
				
			||||||
 | 
						return &BaseHTMLEntity{
 | 
				
			||||||
 | 
							Tag:  "text",
 | 
				
			||||||
 | 
							Text: text,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewHTMLEntity(tag string, children []HTMLEntity, block bool) *BaseHTMLEntity {
 | 
				
			||||||
 | 
						return &BaseHTMLEntity{
 | 
				
			||||||
 | 
							Tag:      tag,
 | 
				
			||||||
 | 
							Children: children,
 | 
				
			||||||
 | 
							Block:    block,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (he *BaseHTMLEntity) AdjustStyle(fn AdjustStyleFunc) HTMLEntity {
 | 
				
			||||||
 | 
						for _, child := range he.Children {
 | 
				
			||||||
 | 
							child.AdjustStyle(fn)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						he.Style = fn(he.Style)
 | 
				
			||||||
 | 
						return he
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (he *BaseHTMLEntity) IsBlock() bool {
 | 
				
			||||||
 | 
						return he.Block
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (he *BaseHTMLEntity) GetTag() string {
 | 
				
			||||||
 | 
						return he.Tag
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (he *BaseHTMLEntity) Height() int {
 | 
				
			||||||
 | 
						return he.height
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (he *BaseHTMLEntity) getStartX() int {
 | 
				
			||||||
 | 
						return he.startX
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (he *BaseHTMLEntity) String() string {
 | 
				
			||||||
 | 
						var buf strings.Builder
 | 
				
			||||||
 | 
						buf.WriteString("&BaseHTMLEntity{\n")
 | 
				
			||||||
 | 
						_, _ = fmt.Fprintf(&buf, `    Tag="%s", Style=%d, Block=%t, Indent=%d, startX=%d, height=%d,`,
 | 
				
			||||||
 | 
							he.Tag, he.Style, he.Block, he.Indent, he.startX, he.height)
 | 
				
			||||||
 | 
						buf.WriteRune('\n')
 | 
				
			||||||
 | 
						_, _ = fmt.Fprintf(&buf, `    Buffer=["%s"]`, strings.Join(he.buffer, "\", \""))
 | 
				
			||||||
 | 
						if len(he.Text) > 0 {
 | 
				
			||||||
 | 
							buf.WriteString(",\n")
 | 
				
			||||||
 | 
							_, _ = fmt.Fprintf(&buf, `    Text="%s"`, he.Text)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if len(he.Children) > 0 {
 | 
				
			||||||
 | 
							buf.WriteString(",\n")
 | 
				
			||||||
 | 
							buf.WriteString("    Children={")
 | 
				
			||||||
 | 
							for _, child := range he.Children {
 | 
				
			||||||
 | 
								buf.WriteString("\n        ")
 | 
				
			||||||
 | 
								buf.WriteString(strings.Join(strings.Split(strings.TrimRight(child.String(), "\n"), "\n"), "\n        "))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							buf.WriteString("\n    },")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						buf.WriteString("\n},\n")
 | 
				
			||||||
 | 
						return buf.String()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (he *BaseHTMLEntity) PlainText() string {
 | 
				
			||||||
 | 
						if len(he.Children) == 0 {
 | 
				
			||||||
 | 
							return he.Text
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						var buf strings.Builder
 | 
				
			||||||
 | 
						buf.WriteString(he.Text)
 | 
				
			||||||
 | 
						newlined := false
 | 
				
			||||||
 | 
						for _, child := range he.Children {
 | 
				
			||||||
 | 
							if child.IsBlock() && !newlined {
 | 
				
			||||||
 | 
								buf.WriteRune('\n')
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							newlined = false
 | 
				
			||||||
 | 
							buf.WriteString(child.PlainText())
 | 
				
			||||||
 | 
							if child.IsBlock() {
 | 
				
			||||||
 | 
								buf.WriteRune('\n')
 | 
				
			||||||
 | 
								newlined = true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return buf.String()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (he *BaseHTMLEntity) Draw(screen mauview.Screen) {
 | 
				
			||||||
 | 
						width, _ := screen.Size()
 | 
				
			||||||
 | 
						if len(he.buffer) > 0 {
 | 
				
			||||||
 | 
							x := he.startX
 | 
				
			||||||
 | 
							for y, line := range he.buffer {
 | 
				
			||||||
 | 
								widget.WriteLine(screen, mauview.AlignLeft, line, x, y, width, he.Style)
 | 
				
			||||||
 | 
								x = 0
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if len(he.Children) > 0 {
 | 
				
			||||||
 | 
							prevBreak := false
 | 
				
			||||||
 | 
							proxyScreen := &mauview.ProxyScreen{Parent: screen, OffsetX: he.Indent, Width: width - he.Indent}
 | 
				
			||||||
 | 
							for i, entity := range he.Children {
 | 
				
			||||||
 | 
								if i != 0 && entity.getStartX() == 0 {
 | 
				
			||||||
 | 
									proxyScreen.OffsetY++
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								proxyScreen.Height = entity.Height()
 | 
				
			||||||
 | 
								entity.Draw(proxyScreen)
 | 
				
			||||||
 | 
								proxyScreen.OffsetY += entity.Height() - 1
 | 
				
			||||||
 | 
								_, isBreak := entity.(*BreakEntity)
 | 
				
			||||||
 | 
								if prevBreak && isBreak {
 | 
				
			||||||
 | 
									proxyScreen.OffsetY++
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								prevBreak = isBreak
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (he *BaseHTMLEntity) calculateBuffer(width, startX int, bare bool) int {
 | 
				
			||||||
 | 
						he.startX = startX
 | 
				
			||||||
 | 
						if he.Block {
 | 
				
			||||||
 | 
							he.startX = 0
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						he.height = 0
 | 
				
			||||||
 | 
						if len(he.Children) > 0 {
 | 
				
			||||||
 | 
							childStartX := he.startX
 | 
				
			||||||
 | 
							prevBreak := false
 | 
				
			||||||
 | 
							for _, entity := range he.Children {
 | 
				
			||||||
 | 
								if entity.IsBlock() || childStartX == 0 || he.height == 0 {
 | 
				
			||||||
 | 
									he.height++
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								childStartX = entity.calculateBuffer(width-he.Indent, childStartX, bare)
 | 
				
			||||||
 | 
								he.height += entity.Height() - 1
 | 
				
			||||||
 | 
								_, isBreak := entity.(*BreakEntity)
 | 
				
			||||||
 | 
								if prevBreak && isBreak {
 | 
				
			||||||
 | 
									he.height++
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								prevBreak = isBreak
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if len(he.Text) == 0 && !he.Block {
 | 
				
			||||||
 | 
								return childStartX
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if len(he.Text) > 0 {
 | 
				
			||||||
 | 
							he.prevWidth = width
 | 
				
			||||||
 | 
							if he.buffer == nil {
 | 
				
			||||||
 | 
								he.buffer = []string{}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							bufPtr := 0
 | 
				
			||||||
 | 
							text := he.Text
 | 
				
			||||||
 | 
							textStartX := he.startX
 | 
				
			||||||
 | 
							for {
 | 
				
			||||||
 | 
								// TODO add option no wrap and character wrap options
 | 
				
			||||||
 | 
								extract := runewidth.Truncate(text, width-textStartX, "")
 | 
				
			||||||
 | 
								extract, wordWrapped := trim(extract, text, bare)
 | 
				
			||||||
 | 
								if !wordWrapped && textStartX > 0 {
 | 
				
			||||||
 | 
									if bufPtr < len(he.buffer) {
 | 
				
			||||||
 | 
										he.buffer[bufPtr] = ""
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										he.buffer = append(he.buffer, "")
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									bufPtr++
 | 
				
			||||||
 | 
									textStartX = 0
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if bufPtr < len(he.buffer) {
 | 
				
			||||||
 | 
									he.buffer[bufPtr] = extract
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									he.buffer = append(he.buffer, extract)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								bufPtr++
 | 
				
			||||||
 | 
								text = text[len(extract):]
 | 
				
			||||||
 | 
								if len(text) == 0 {
 | 
				
			||||||
 | 
									he.buffer = he.buffer[:bufPtr]
 | 
				
			||||||
 | 
									he.height += len(he.buffer)
 | 
				
			||||||
 | 
									// This entity is over, return the startX for the next entity
 | 
				
			||||||
 | 
									if he.Block {
 | 
				
			||||||
 | 
										// ...except if it's a block entity
 | 
				
			||||||
 | 
										return 0
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									return textStartX + runewidth.StringWidth(extract)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								textStartX = 0
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if len(he.Text) == 0 && len(he.Children) == 0 {
 | 
				
			||||||
 | 
							he.height = he.DefaultHeight
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return he.startX
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func trim(extract, full string, bare bool) (string, bool) {
 | 
				
			||||||
 | 
						if len(extract) == len(full) {
 | 
				
			||||||
 | 
							return extract, true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if spaces := spacePattern.FindStringIndex(full[len(extract):]); spaces != nil && spaces[0] == 0 {
 | 
				
			||||||
 | 
							extract = full[:len(extract)+spaces[1]]
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						regex := boundaryPattern
 | 
				
			||||||
 | 
						if bare {
 | 
				
			||||||
 | 
							regex = bareBoundaryPattern
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						matches := regex.FindAllStringIndex(extract, -1)
 | 
				
			||||||
 | 
						if len(matches) > 0 {
 | 
				
			||||||
 | 
							if match := matches[len(matches)-1]; len(match) >= 2 {
 | 
				
			||||||
 | 
								if until := match[1]; until < len(extract) {
 | 
				
			||||||
 | 
									extract = extract[:until]
 | 
				
			||||||
 | 
									return extract, true
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return extract, len(extract) > 0 && extract[len(extract)-1] == ' '
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -112,11 +112,4 @@ func (msg *ImageMessage) CalculateBuffer(prefs config.UserPreferences, width int
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	msg.buffer = image.Render()
 | 
						msg.buffer = image.Render()
 | 
				
			||||||
	msg.prevBufferWidth = width
 | 
					 | 
				
			||||||
	msg.prevPrefs = prefs
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// RecalculateBuffer calculates the buffer again with the previously provided width.
 | 
					 | 
				
			||||||
func (msg *ImageMessage) RecalculateBuffer() {
 | 
					 | 
				
			||||||
	msg.CalculateBuffer(msg.prevPrefs, msg.prevBufferWidth)
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,7 +19,7 @@ package messages
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"maunium.net/go/gomuks/config"
 | 
						"maunium.net/go/gomuks/config"
 | 
				
			||||||
	"maunium.net/go/gomuks/interface"
 | 
						"maunium.net/go/gomuks/interface"
 | 
				
			||||||
	"maunium.net/go/gomuks/ui/messages/tstring"
 | 
						"maunium.net/go/mauview"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// UIMessage is a wrapper for the content and metadata of a Matrix message intended to be displayed.
 | 
					// UIMessage is a wrapper for the content and metadata of a Matrix message intended to be displayed.
 | 
				
			||||||
@@ -27,8 +27,7 @@ type UIMessage interface {
 | 
				
			|||||||
	ifc.Message
 | 
						ifc.Message
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	CalculateBuffer(preferences config.UserPreferences, width int)
 | 
						CalculateBuffer(preferences config.UserPreferences, width int)
 | 
				
			||||||
	RecalculateBuffer()
 | 
						Draw(screen mauview.Screen)
 | 
				
			||||||
	Buffer() []tstring.TString
 | 
					 | 
				
			||||||
	Height() int
 | 
						Height() int
 | 
				
			||||||
	PlainText() string
 | 
						PlainText() string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,12 +17,13 @@
 | 
				
			|||||||
package parser
 | 
					package parser
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"math"
 | 
					 | 
				
			||||||
	"regexp"
 | 
						"regexp"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/alecthomas/chroma"
 | 
				
			||||||
 | 
						"github.com/alecthomas/chroma/lexers"
 | 
				
			||||||
 | 
						"github.com/alecthomas/chroma/styles"
 | 
				
			||||||
	"github.com/lucasb-eyer/go-colorful"
 | 
						"github.com/lucasb-eyer/go-colorful"
 | 
				
			||||||
	"golang.org/x/net/html"
 | 
						"golang.org/x/net/html"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -30,7 +31,7 @@ import (
 | 
				
			|||||||
	"maunium.net/go/tcell"
 | 
						"maunium.net/go/tcell"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"maunium.net/go/gomuks/matrix/rooms"
 | 
						"maunium.net/go/gomuks/matrix/rooms"
 | 
				
			||||||
	"maunium.net/go/gomuks/ui/messages/tstring"
 | 
						"maunium.net/go/gomuks/ui/messages"
 | 
				
			||||||
	"maunium.net/go/gomuks/ui/widget"
 | 
						"maunium.net/go/gomuks/ui/widget"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -40,11 +41,6 @@ type htmlParser struct {
 | 
				
			|||||||
	room *rooms.Room
 | 
						room *rooms.Room
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type taggedTString struct {
 | 
					 | 
				
			||||||
	tstring.TString
 | 
					 | 
				
			||||||
	tag string
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func AdjustStyleBold(style tcell.Style) tcell.Style {
 | 
					func AdjustStyleBold(style tcell.Style) tcell.Style {
 | 
				
			||||||
	return style.Bold(true)
 | 
						return style.Bold(true)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -82,65 +78,53 @@ func (parser *htmlParser) getAttribute(node *html.Node, attribute string) string
 | 
				
			|||||||
	return ""
 | 
						return ""
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func digits(num int) int {
 | 
					func (parser *htmlParser) listToEntity(node *html.Node, stripLinebreak bool) messages.HTMLEntity {
 | 
				
			||||||
	if num <= 0 {
 | 
						children := parser.nodeToEntities(node.FirstChild, stripLinebreak)
 | 
				
			||||||
		return 0
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return int(math.Floor(math.Log10(float64(num))) + 1)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (parser *htmlParser) listToTString(node *html.Node, stripLinebreak bool) tstring.TString {
 | 
					 | 
				
			||||||
	ordered := node.Data == "ol"
 | 
						ordered := node.Data == "ol"
 | 
				
			||||||
	taggedChildren := parser.nodeToTaggedTStrings(node.FirstChild, stripLinebreak)
 | 
						start := 1
 | 
				
			||||||
	counter := 1
 | 
					 | 
				
			||||||
	indentLength := 0
 | 
					 | 
				
			||||||
	if ordered {
 | 
						if ordered {
 | 
				
			||||||
		start := parser.getAttribute(node, "start")
 | 
							if startRaw := parser.getAttribute(node, "start"); len(startRaw) > 0 {
 | 
				
			||||||
		if len(start) > 0 {
 | 
								var err error
 | 
				
			||||||
			counter, _ = strconv.Atoi(start)
 | 
								start, err = strconv.Atoi(startRaw)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									start = 1
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					 | 
				
			||||||
		longestIndex := (counter - 1) + len(taggedChildren)
 | 
					 | 
				
			||||||
		indentLength = digits(longestIndex)
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	indent := strings.Repeat(" ", indentLength+2)
 | 
						listItems := children[:0]
 | 
				
			||||||
	var children []tstring.TString
 | 
						for _, child := range children {
 | 
				
			||||||
	for _, child := range taggedChildren {
 | 
							if child.GetTag() == "li" {
 | 
				
			||||||
		if child.tag != "li" {
 | 
								listItems = append(listItems, child)
 | 
				
			||||||
			continue
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		var prefix string
 | 
					 | 
				
			||||||
		if ordered {
 | 
					 | 
				
			||||||
			indexPadding := indentLength - digits(counter)
 | 
					 | 
				
			||||||
			prefix = fmt.Sprintf("%d. %s", counter, strings.Repeat(" ", indexPadding))
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			prefix = "● "
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		str := child.TString.Prepend(prefix)
 | 
					 | 
				
			||||||
		counter++
 | 
					 | 
				
			||||||
		parts := str.Split('\n')
 | 
					 | 
				
			||||||
		for i, part := range parts[1:] {
 | 
					 | 
				
			||||||
			parts[i+1] = part.Prepend(indent)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		str = tstring.Join(parts, "\n")
 | 
					 | 
				
			||||||
		children = append(children, str)
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return tstring.Join(children, "\n")
 | 
						return messages.NewListEntity(ordered, start, listItems)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (parser *htmlParser) basicFormatToTString(node *html.Node, stripLinebreak bool) tstring.TString {
 | 
					func (parser *htmlParser) basicFormatToEntity(node *html.Node, stripLinebreak bool) messages.HTMLEntity {
 | 
				
			||||||
	str := parser.nodeToTagAwareTString(node.FirstChild, stripLinebreak)
 | 
						entity := &messages.BaseHTMLEntity{
 | 
				
			||||||
 | 
							Tag:      node.Data,
 | 
				
			||||||
 | 
							Children: parser.nodeToEntities(node.FirstChild, stripLinebreak),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	switch node.Data {
 | 
						switch node.Data {
 | 
				
			||||||
	case "b", "strong":
 | 
						case "b", "strong":
 | 
				
			||||||
		str.AdjustStyleFull(AdjustStyleBold)
 | 
							entity.AdjustStyle(AdjustStyleBold)
 | 
				
			||||||
	case "i", "em":
 | 
						case "i", "em":
 | 
				
			||||||
		str.AdjustStyleFull(AdjustStyleItalic)
 | 
							entity.AdjustStyle(AdjustStyleItalic)
 | 
				
			||||||
	case "s", "del":
 | 
						case "s", "del":
 | 
				
			||||||
		str.AdjustStyleFull(AdjustStyleStrikethrough)
 | 
							entity.AdjustStyle(AdjustStyleStrikethrough)
 | 
				
			||||||
	case "u", "ins":
 | 
						case "u", "ins":
 | 
				
			||||||
		str.AdjustStyleFull(AdjustStyleUnderline)
 | 
							entity.AdjustStyle(AdjustStyleUnderline)
 | 
				
			||||||
 | 
						case "font":
 | 
				
			||||||
 | 
							fgColor, ok := parser.parseColor(node, "data-mx-color", "color")
 | 
				
			||||||
 | 
							if ok {
 | 
				
			||||||
 | 
								entity.AdjustStyle(AdjustStyleTextColor(fgColor))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							bgColor, ok := parser.parseColor(node, "data-mx-bg-color", "background-color")
 | 
				
			||||||
 | 
							if ok {
 | 
				
			||||||
 | 
								entity.AdjustStyle(AdjustStyleBackgroundColor(bgColor))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return str
 | 
						return entity
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (parser *htmlParser) parseColor(node *html.Node, mainName, altName string) (color tcell.Color, ok bool) {
 | 
					func (parser *htmlParser) parseColor(node *html.Node, mainName, altName string) (color tcell.Color, ok bool) {
 | 
				
			||||||
@@ -165,103 +149,200 @@ func (parser *htmlParser) parseColor(node *html.Node, mainName, altName string)
 | 
				
			|||||||
	return tcell.NewRGBColor(int32(r), int32(g), int32(b)), true
 | 
						return tcell.NewRGBColor(int32(r), int32(g), int32(b)), true
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (parser *htmlParser) fontToTString(node *html.Node, stripLinebreak bool) tstring.TString {
 | 
					func (parser *htmlParser) headerToEntity(node *html.Node, stripLinebreak bool) messages.HTMLEntity {
 | 
				
			||||||
	str := parser.nodeToTagAwareTString(node.FirstChild, stripLinebreak)
 | 
					 | 
				
			||||||
	fgColor, ok := parser.parseColor(node, "data-mx-color", "color")
 | 
					 | 
				
			||||||
	if ok {
 | 
					 | 
				
			||||||
		str.AdjustStyleFull(AdjustStyleTextColor(fgColor))
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	bgColor, ok := parser.parseColor(node, "data-mx-bg-color", "background-color")
 | 
					 | 
				
			||||||
	if ok {
 | 
					 | 
				
			||||||
		str.AdjustStyleFull(AdjustStyleBackgroundColor(bgColor))
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return str
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (parser *htmlParser) headerToTString(node *html.Node, stripLinebreak bool) tstring.TString {
 | 
					 | 
				
			||||||
	children := parser.nodeToTStrings(node.FirstChild, stripLinebreak)
 | 
					 | 
				
			||||||
	length := int(node.Data[1] - '0')
 | 
						length := int(node.Data[1] - '0')
 | 
				
			||||||
	prefix := strings.Repeat("#", length) + " "
 | 
						prefix := strings.Repeat("#", length) + " "
 | 
				
			||||||
	return tstring.Join(children, "").Prepend(prefix)
 | 
						return (&messages.BaseHTMLEntity{
 | 
				
			||||||
 | 
							Tag:      node.Data,
 | 
				
			||||||
 | 
							Text:     prefix,
 | 
				
			||||||
 | 
							Children: parser.nodeToEntities(node.FirstChild, stripLinebreak),
 | 
				
			||||||
 | 
						}).AdjustStyle(AdjustStyleBold)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (parser *htmlParser) blockquoteToTString(node *html.Node, stripLinebreak bool) tstring.TString {
 | 
					func (parser *htmlParser) blockquoteToEntity(node *html.Node, stripLinebreak bool) messages.HTMLEntity {
 | 
				
			||||||
	str := parser.nodeToTagAwareTString(node.FirstChild, stripLinebreak)
 | 
						return messages.NewBlockquoteEntity(parser.nodeToEntities(node.FirstChild, stripLinebreak))
 | 
				
			||||||
	childrenArr := str.TrimSpace().Split('\n')
 | 
					}
 | 
				
			||||||
	for index, child := range childrenArr {
 | 
					
 | 
				
			||||||
		childrenArr[index] = child.Prepend("> ")
 | 
					func (parser *htmlParser) linkToEntity(node *html.Node, stripLinebreak bool) messages.HTMLEntity {
 | 
				
			||||||
 | 
						entity := &messages.BaseHTMLEntity{
 | 
				
			||||||
 | 
							Tag:      "a",
 | 
				
			||||||
 | 
							Children: parser.nodeToEntities(node.FirstChild, stripLinebreak),
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return tstring.Join(childrenArr, "\n")
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (parser *htmlParser) linkToTString(node *html.Node, stripLinebreak bool) tstring.TString {
 | 
					 | 
				
			||||||
	str := parser.nodeToTagAwareTString(node.FirstChild, stripLinebreak)
 | 
					 | 
				
			||||||
	href := parser.getAttribute(node, "href")
 | 
						href := parser.getAttribute(node, "href")
 | 
				
			||||||
	if len(href) == 0 {
 | 
						if len(href) == 0 {
 | 
				
			||||||
		return str
 | 
							return entity
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	match := matrixToURL.FindStringSubmatch(href)
 | 
						match := matrixToURL.FindStringSubmatch(href)
 | 
				
			||||||
	if len(match) == 2 {
 | 
						if len(match) == 2 {
 | 
				
			||||||
 | 
							entity.Children = nil
 | 
				
			||||||
		pillTarget := match[1]
 | 
							pillTarget := match[1]
 | 
				
			||||||
 | 
							entity.Text = pillTarget
 | 
				
			||||||
		if pillTarget[0] == '@' {
 | 
							if pillTarget[0] == '@' {
 | 
				
			||||||
			if member := parser.room.GetMember(pillTarget); member != nil {
 | 
								if member := parser.room.GetMember(pillTarget); member != nil {
 | 
				
			||||||
				return tstring.NewColorTString(member.Displayname, widget.GetHashColor(pillTarget))
 | 
									entity.Text = member.Displayname
 | 
				
			||||||
 | 
									entity.Style = entity.Style.Foreground(widget.GetHashColor(pillTarget))
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return tstring.NewTString(pillTarget)
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return str.Append(fmt.Sprintf(" (%s)", href))
 | 
						// TODO add click action and underline on hover for links
 | 
				
			||||||
 | 
						return entity
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (parser *htmlParser) tagToTString(node *html.Node, stripLinebreak bool) tstring.TString {
 | 
					func (parser *htmlParser) imageToEntity(node *html.Node) messages.HTMLEntity {
 | 
				
			||||||
 | 
						alt := parser.getAttribute(node, "alt")
 | 
				
			||||||
 | 
						if len(alt) == 0 {
 | 
				
			||||||
 | 
							alt = parser.getAttribute(node, "title")
 | 
				
			||||||
 | 
							if len(alt) == 0 {
 | 
				
			||||||
 | 
								alt = "[inline image]"
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						entity := &messages.BaseHTMLEntity{
 | 
				
			||||||
 | 
							Tag:  "img",
 | 
				
			||||||
 | 
							Text: alt,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// TODO add click action and underline on hover for inline images
 | 
				
			||||||
 | 
						return entity
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func colourToColor(colour chroma.Colour) tcell.Color {
 | 
				
			||||||
 | 
						if !colour.IsSet() {
 | 
				
			||||||
 | 
							return tcell.ColorDefault
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return tcell.NewRGBColor(int32(colour.Red()), int32(colour.Green()), int32(colour.Blue()))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func styleEntryToStyle(se chroma.StyleEntry) tcell.Style {
 | 
				
			||||||
 | 
						return tcell.StyleDefault.
 | 
				
			||||||
 | 
							Bold(se.Bold == chroma.Yes).
 | 
				
			||||||
 | 
							Italic(se.Italic == chroma.Yes).
 | 
				
			||||||
 | 
							Underline(se.Underline == chroma.Yes).
 | 
				
			||||||
 | 
							Foreground(colourToColor(se.Colour)).
 | 
				
			||||||
 | 
							Background(colourToColor(se.Background))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (parser *htmlParser) syntaxHighlight(text, language string) messages.HTMLEntity {
 | 
				
			||||||
 | 
						lexer := lexers.Get(language)
 | 
				
			||||||
 | 
						if lexer == nil {
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						iter, err := lexer.Tokenise(nil, text)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						style := styles.SolarizedDark
 | 
				
			||||||
 | 
						tokens := iter.Tokens()
 | 
				
			||||||
 | 
						children := make([]messages.HTMLEntity, len(tokens))
 | 
				
			||||||
 | 
						for i, token := range tokens {
 | 
				
			||||||
 | 
							if token.Value == "\n" {
 | 
				
			||||||
 | 
								children[i] = &messages.BaseHTMLEntity{Block: true, Tag: "br"}
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								children[i] = &messages.BaseHTMLEntity{
 | 
				
			||||||
 | 
									Tag:   token.Type.String(),
 | 
				
			||||||
 | 
									Text:  token.Value,
 | 
				
			||||||
 | 
									Style: styleEntryToStyle(style.Get(token.Type)),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									DefaultHeight: 1,
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return &messages.BaseHTMLEntity{
 | 
				
			||||||
 | 
							Tag:      "pre",
 | 
				
			||||||
 | 
							Block:    true,
 | 
				
			||||||
 | 
							Children: children,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (parser *htmlParser) codeblockToEntity(node *html.Node) messages.HTMLEntity {
 | 
				
			||||||
 | 
						entity := &messages.BaseHTMLEntity{
 | 
				
			||||||
 | 
							Tag:   "pre",
 | 
				
			||||||
 | 
							Block: true,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// TODO allow disabling syntax highlighting
 | 
				
			||||||
 | 
						if node.FirstChild.Type == html.ElementNode && node.FirstChild.Data == "code" {
 | 
				
			||||||
 | 
							text := (&messages.BaseHTMLEntity{
 | 
				
			||||||
 | 
								Children: parser.nodeToEntities(node.FirstChild.FirstChild, false),
 | 
				
			||||||
 | 
							}).PlainText()
 | 
				
			||||||
 | 
							attr := parser.getAttribute(node.FirstChild, "class")
 | 
				
			||||||
 | 
							var lang string
 | 
				
			||||||
 | 
							for _, class := range strings.Split(attr, " ") {
 | 
				
			||||||
 | 
								if strings.HasPrefix(class, "language-") {
 | 
				
			||||||
 | 
									lang = class[len("language-"):]
 | 
				
			||||||
 | 
									break
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if len(lang) != 0 {
 | 
				
			||||||
 | 
								if parsed := parser.syntaxHighlight(text, lang); parsed != nil {
 | 
				
			||||||
 | 
									return parsed
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						entity.Children = parser.nodeToEntities(node.FirstChild, false)
 | 
				
			||||||
 | 
						return entity
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (parser *htmlParser) tagNodeToEntity(node *html.Node, stripLinebreak bool) messages.HTMLEntity {
 | 
				
			||||||
	switch node.Data {
 | 
						switch node.Data {
 | 
				
			||||||
	case "blockquote":
 | 
						case "blockquote":
 | 
				
			||||||
		return parser.blockquoteToTString(node, stripLinebreak)
 | 
							return parser.blockquoteToEntity(node, stripLinebreak)
 | 
				
			||||||
	case "ol", "ul":
 | 
						case "ol", "ul":
 | 
				
			||||||
		return parser.listToTString(node, stripLinebreak)
 | 
							return parser.listToEntity(node, stripLinebreak)
 | 
				
			||||||
	case "h1", "h2", "h3", "h4", "h5", "h6":
 | 
						case "h1", "h2", "h3", "h4", "h5", "h6":
 | 
				
			||||||
		return parser.headerToTString(node, stripLinebreak)
 | 
							return parser.headerToEntity(node, stripLinebreak)
 | 
				
			||||||
	case "br":
 | 
						case "br":
 | 
				
			||||||
		return tstring.NewTString("\n")
 | 
							return messages.NewBreakEntity()
 | 
				
			||||||
	case "b", "strong", "i", "em", "s", "del", "u", "ins":
 | 
						case "b", "strong", "i", "em", "s", "del", "u", "ins", "font":
 | 
				
			||||||
		return parser.basicFormatToTString(node, stripLinebreak)
 | 
							return parser.basicFormatToEntity(node, stripLinebreak)
 | 
				
			||||||
	case "font":
 | 
					 | 
				
			||||||
		return parser.fontToTString(node, stripLinebreak)
 | 
					 | 
				
			||||||
	case "a":
 | 
						case "a":
 | 
				
			||||||
		return parser.linkToTString(node, stripLinebreak)
 | 
							return parser.linkToEntity(node, stripLinebreak)
 | 
				
			||||||
	case "p":
 | 
						case "img":
 | 
				
			||||||
		return parser.nodeToTagAwareTString(node.FirstChild, stripLinebreak).Append("\n")
 | 
							return parser.imageToEntity(node)
 | 
				
			||||||
	case "pre":
 | 
						case "pre":
 | 
				
			||||||
		return parser.nodeToTString(node.FirstChild, false)
 | 
							return parser.codeblockToEntity(node)
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		return parser.nodeToTagAwareTString(node.FirstChild, stripLinebreak)
 | 
							return &messages.BaseHTMLEntity{
 | 
				
			||||||
 | 
								Tag:      node.Data,
 | 
				
			||||||
 | 
								Children: parser.nodeToEntities(node.FirstChild, stripLinebreak),
 | 
				
			||||||
 | 
								Block:    parser.isBlockTag(node.Data),
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (parser *htmlParser) singleNodeToTString(node *html.Node, stripLinebreak bool) taggedTString {
 | 
					func (parser *htmlParser) singleNodeToEntity(node *html.Node, stripLinebreak bool) messages.HTMLEntity {
 | 
				
			||||||
	switch node.Type {
 | 
						switch node.Type {
 | 
				
			||||||
	case html.TextNode:
 | 
						case html.TextNode:
 | 
				
			||||||
		if stripLinebreak {
 | 
							if stripLinebreak {
 | 
				
			||||||
			node.Data = strings.Replace(node.Data, "\n", "", -1)
 | 
								node.Data = strings.Replace(node.Data, "\n", "", -1)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return taggedTString{tstring.NewTString(node.Data), "text"}
 | 
							return &messages.BaseHTMLEntity{
 | 
				
			||||||
 | 
								Tag:  "text",
 | 
				
			||||||
 | 
								Text: node.Data,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	case html.ElementNode:
 | 
						case html.ElementNode:
 | 
				
			||||||
		return taggedTString{parser.tagToTString(node, stripLinebreak), node.Data}
 | 
							return parser.tagNodeToEntity(node, stripLinebreak)
 | 
				
			||||||
	case html.DocumentNode:
 | 
						case html.DocumentNode:
 | 
				
			||||||
		return taggedTString{parser.nodeToTagAwareTString(node.FirstChild, stripLinebreak), "html"}
 | 
							if node.FirstChild.Data == "html" && node.FirstChild.NextSibling == nil {
 | 
				
			||||||
 | 
								return parser.singleNodeToEntity(node.FirstChild, stripLinebreak)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return &messages.BaseHTMLEntity{
 | 
				
			||||||
 | 
								Tag:      "html",
 | 
				
			||||||
 | 
								Children: parser.nodeToEntities(node.FirstChild, stripLinebreak),
 | 
				
			||||||
 | 
								Block:    true,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		return taggedTString{tstring.NewBlankTString(), "unknown"}
 | 
							return nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (parser *htmlParser) nodeToTaggedTStrings(node *html.Node, stripLinebreak bool) (strs []taggedTString) {
 | 
					func (parser *htmlParser) nodeToEntities(node *html.Node, stripLinebreak bool) (entities []messages.HTMLEntity) {
 | 
				
			||||||
	for ; node != nil; node = node.NextSibling {
 | 
						for ; node != nil; node = node.NextSibling {
 | 
				
			||||||
		strs = append(strs, parser.singleNodeToTString(node, stripLinebreak))
 | 
							if entity := parser.singleNodeToEntity(node, stripLinebreak); entity != nil {
 | 
				
			||||||
 | 
								entities = append(entities, entity)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return
 | 
						return
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var BlockTags = []string{"p", "h1", "h2", "h3", "h4", "h5", "h6", "ol", "ul", "pre", "blockquote", "div", "hr", "table"}
 | 
					var BlockTags = []string{"p", "h1", "h2", "h3", "h4", "h5", "h6", "ol", "ul", "li", "pre", "blockquote", "div", "hr", "table"}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (parser *htmlParser) isBlockTag(tag string) bool {
 | 
					func (parser *htmlParser) isBlockTag(tag string) bool {
 | 
				
			||||||
	for _, blockTag := range BlockTags {
 | 
						for _, blockTag := range BlockTags {
 | 
				
			||||||
@@ -272,51 +353,31 @@ func (parser *htmlParser) isBlockTag(tag string) bool {
 | 
				
			|||||||
	return false
 | 
						return false
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (parser *htmlParser) nodeToTagAwareTString(node *html.Node, stripLinebreak bool) tstring.TString {
 | 
					func (parser *htmlParser) Parse(htmlData string) messages.HTMLEntity {
 | 
				
			||||||
	strs := parser.nodeToTaggedTStrings(node, stripLinebreak)
 | 
					 | 
				
			||||||
	output := tstring.NewBlankTString()
 | 
					 | 
				
			||||||
	for _, str := range strs {
 | 
					 | 
				
			||||||
		tstr := str.TString
 | 
					 | 
				
			||||||
		if parser.isBlockTag(str.tag) {
 | 
					 | 
				
			||||||
			tstr = tstr.Prepend("\n").Append("\n")
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		output = output.AppendTString(tstr)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return output.TrimSpace()
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (parser *htmlParser) nodeToTStrings(node *html.Node, stripLinebreak bool) (strs []tstring.TString) {
 | 
					 | 
				
			||||||
	for ; node != nil; node = node.NextSibling {
 | 
					 | 
				
			||||||
		strs = append(strs, parser.singleNodeToTString(node, stripLinebreak).TString)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (parser *htmlParser) nodeToTString(node *html.Node, stripLinebreak bool) tstring.TString {
 | 
					 | 
				
			||||||
	return tstring.Join(parser.nodeToTStrings(node, stripLinebreak), "")
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (parser *htmlParser) Parse(htmlData string) tstring.TString {
 | 
					 | 
				
			||||||
	node, _ := html.Parse(strings.NewReader(htmlData))
 | 
						node, _ := html.Parse(strings.NewReader(htmlData))
 | 
				
			||||||
	return parser.nodeToTagAwareTString(node, true)
 | 
						return parser.singleNodeToEntity(node, true)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ParseHTMLMessage parses a HTML-formatted Matrix event into a UIMessage.
 | 
					// ParseHTMLMessage parses a HTML-formatted Matrix event into a UIMessage.
 | 
				
			||||||
func ParseHTMLMessage(room *rooms.Room, evt *mautrix.Event, senderDisplayname string) tstring.TString {
 | 
					func ParseHTMLMessage(room *rooms.Room, evt *mautrix.Event, senderDisplayname string) messages.HTMLEntity {
 | 
				
			||||||
	htmlData := evt.Content.FormattedBody
 | 
						htmlData := evt.Content.FormattedBody
 | 
				
			||||||
	htmlData = strings.Replace(htmlData, "\t", "    ", -1)
 | 
						htmlData = strings.Replace(htmlData, "\t", "    ", -1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	parser := htmlParser{room}
 | 
						parser := htmlParser{room}
 | 
				
			||||||
	str := parser.Parse(htmlData)
 | 
						root := parser.Parse(htmlData)
 | 
				
			||||||
 | 
						root.(*messages.BaseHTMLEntity).Block = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if evt.Content.MsgType == mautrix.MsgEmote {
 | 
						if evt.Content.MsgType == mautrix.MsgEmote {
 | 
				
			||||||
		str = tstring.Join([]tstring.TString{
 | 
							root = &messages.BaseHTMLEntity{
 | 
				
			||||||
			tstring.NewTString("* "),
 | 
								Tag: "emote",
 | 
				
			||||||
			tstring.NewColorTString(senderDisplayname, widget.GetHashColor(evt.Sender)),
 | 
								Children: []messages.HTMLEntity{
 | 
				
			||||||
			tstring.NewTString(" "),
 | 
									messages.NewHTMLTextEntity("* "),
 | 
				
			||||||
			str,
 | 
									messages.NewHTMLTextEntity("* ").AdjustStyle(AdjustStyleTextColor(widget.GetHashColor(evt.Sender))),
 | 
				
			||||||
		}, "")
 | 
									messages.NewHTMLTextEntity(" "),
 | 
				
			||||||
 | 
									root,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return str
 | 
						return root
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -113,7 +113,7 @@ func ParseMessage(matrix ifc.MatrixContainer, room *rooms.Room, evt *mautrix.Eve
 | 
				
			|||||||
				replyToEvt.Content.FormattedBody = html.EscapeString(replyToEvt.Content.Body)
 | 
									replyToEvt.Content.FormattedBody = html.EscapeString(replyToEvt.Content.Body)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			evt.Content.FormattedBody = fmt.Sprintf(
 | 
								evt.Content.FormattedBody = fmt.Sprintf(
 | 
				
			||||||
				"In reply to <a href='https://matrix.to/#/%[1]s'>%[1]s</a><blockquote>%[2]s</blockquote><br/>%[3]s",
 | 
									"In reply to <a href='https://matrix.to/#/%[1]s'>%[1]s</a><blockquote>%[2]s</blockquote><br/><br/>%[3]s",
 | 
				
			||||||
				replyToEvt.Sender, replyToEvt.Content.FormattedBody, evt.Content.FormattedBody)
 | 
									replyToEvt.Sender, replyToEvt.Content.FormattedBody, evt.Content.FormattedBody)
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			evt.Content.FormattedBody = fmt.Sprintf(
 | 
								evt.Content.FormattedBody = fmt.Sprintf(
 | 
				
			||||||
@@ -125,8 +125,7 @@ func ParseMessage(matrix ifc.MatrixContainer, room *rooms.Room, evt *mautrix.Eve
 | 
				
			|||||||
	switch evt.Content.MsgType {
 | 
						switch evt.Content.MsgType {
 | 
				
			||||||
	case "m.text", "m.notice", "m.emote":
 | 
						case "m.text", "m.notice", "m.emote":
 | 
				
			||||||
		if evt.Content.Format == mautrix.FormatHTML {
 | 
							if evt.Content.Format == mautrix.FormatHTML {
 | 
				
			||||||
			text := ParseHTMLMessage(room, evt, displayname)
 | 
								return messages.NewHTMLMessage(evt.ID, evt.Sender, displayname, evt.Content.MsgType, ParseHTMLMessage(room, evt, displayname), ts)
 | 
				
			||||||
			return messages.NewExpandedTextMessage(evt.ID, evt.Sender, displayname, evt.Content.MsgType, text, ts)
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		evt.Content.Body = strings.Replace(evt.Content.Body, "\t", "    ", -1)
 | 
							evt.Content.Body = strings.Replace(evt.Content.Body, "\t", "    ", -1)
 | 
				
			||||||
		return messages.NewTextMessage(evt.ID, evt.Sender, displayname, evt.Content.MsgType, evt.Content.Body, ts)
 | 
							return messages.NewTextMessage(evt.ID, evt.Sender, displayname, evt.Content.MsgType, evt.Content.Body, ts)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -92,6 +92,4 @@ func (msg *BaseMessage) calculateBufferWithText(prefs config.UserPreferences, te
 | 
				
			|||||||
			str = str[len(extract):]
 | 
								str = str[len(extract):]
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	msg.prevBufferWidth = width
 | 
					 | 
				
			||||||
	msg.prevPrefs = prefs
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -90,8 +90,3 @@ func (msg *TextMessage) PlainText() string {
 | 
				
			|||||||
func (msg *TextMessage) CalculateBuffer(prefs config.UserPreferences, width int) {
 | 
					func (msg *TextMessage) CalculateBuffer(prefs config.UserPreferences, width int) {
 | 
				
			||||||
	msg.calculateBufferWithText(prefs, msg.getCache(), width)
 | 
						msg.calculateBufferWithText(prefs, msg.getCache(), width)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
// RecalculateBuffer calculates the buffer again with the previously provided width.
 | 
					 | 
				
			||||||
func (msg *TextMessage) RecalculateBuffer() {
 | 
					 | 
				
			||||||
	msg.CalculateBuffer(msg.prevPrefs, msg.prevBufferWidth)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -74,8 +74,8 @@ func (ui *GomuksUI) NewLoginView() mauview.Component {
 | 
				
			|||||||
	view.username.SetText(ui.gmx.Config().UserID)
 | 
						view.username.SetText(ui.gmx.Config().UserID)
 | 
				
			||||||
	view.password.SetMaskCharacter('*')
 | 
						view.password.SetMaskCharacter('*')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	view.quitButton.SetOnClick(ui.gmx.Stop).SetBackgroundColor(tcell.ColorBlue)
 | 
						view.quitButton.SetOnClick(ui.gmx.Stop).SetBackgroundColor(tcell.ColorDarkCyan)
 | 
				
			||||||
	view.loginButton.SetOnClick(view.Login).SetBackgroundColor(tcell.ColorBlue)
 | 
						view.loginButton.SetOnClick(view.Login).SetBackgroundColor(tcell.ColorDarkCyan)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	view.SetColumns([]int{1, 10, 1, 9, 1, 9, 1, 10, 1})
 | 
						view.SetColumns([]int{1, 10, 1, 9, 1, 9, 1, 10, 1})
 | 
				
			||||||
	view.SetRows([]int{1, 1, 1, 1, 1, 1, 1, 1, 1, 1})
 | 
						view.SetRows([]int{1, 1, 1, 1, 1, 1, 1, 1, 1, 1})
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user