aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--interface/ui.go1
-rw-r--r--ui/message-view.go2
-rw-r--r--ui/messages/base.go7
-rw-r--r--ui/messages/expandedtextmessage.go8
-rw-r--r--ui/messages/html/blockquote.go32
-rw-r--r--ui/messages/html/break.go12
-rw-r--r--ui/messages/html/codeblock.go7
-rw-r--r--ui/messages/html/entity.go11
-rw-r--r--ui/messages/html/horizontalline.go56
-rw-r--r--ui/messages/html/list.go33
-rw-r--r--ui/messages/html/parser.go10
-rw-r--r--ui/messages/htmlmessage.go8
-rw-r--r--ui/messages/imagemessage.go13
-rw-r--r--ui/messages/message.go2
-rw-r--r--ui/messages/parser.go26
-rw-r--r--ui/messages/textmessage.go15
-rw-r--r--ui/messages/tstring/string.go15
-rw-r--r--ui/room-view.go10
18 files changed, 245 insertions, 23 deletions
diff --git a/interface/ui.go b/interface/ui.go
index 12aabbe..781f803 100644
--- a/interface/ui.go
+++ b/interface/ui.go
@@ -61,6 +61,7 @@ type RoomView interface {
UpdateUserList()
ParseEvent(evt *mautrix.Event) Message
+ GetEvent(eventID string) Message
AddMessage(message Message)
AddServiceMessage(message string)
}
diff --git a/ui/message-view.go b/ui/message-view.go
index 924eb7a..2211503 100644
--- a/ui/message-view.go
+++ b/ui/message-view.go
@@ -257,7 +257,7 @@ func (view *MessageView) handleMessageClick(message messages.UIMessage) bool {
case *messages.ImageMessage:
open.Open(message.Path())
case messages.UIMessage:
- debug.Print("Message clicked:", message.NotificationContent())
+ debug.Print("Message clicked:", message)
}
return false
}
diff --git a/ui/messages/base.go b/ui/messages/base.go
index c377ccb..c3ee1f0 100644
--- a/ui/messages/base.go
+++ b/ui/messages/base.go
@@ -44,7 +44,6 @@ type BaseMessage struct {
MsgSource json.RawMessage
ReplyTo UIMessage
buffer []tstring.TString
- plainBuffer []tstring.TString
}
func newBaseMessage(event *mautrix.Event, displayname string) BaseMessage {
@@ -259,6 +258,12 @@ func (msg *BaseMessage) Draw(screen mauview.Screen) {
}
}
+func (msg *BaseMessage) clone() BaseMessage {
+ clone := *msg
+ clone.buffer = nil
+ return clone
+}
+
func (msg *BaseMessage) CalculateReplyBuffer(preferences config.UserPreferences, width int) {
if msg.ReplyTo == nil {
return
diff --git a/ui/messages/expandedtextmessage.go b/ui/messages/expandedtextmessage.go
index 6ed96fc..cf71ba1 100644
--- a/ui/messages/expandedtextmessage.go
+++ b/ui/messages/expandedtextmessage.go
@@ -55,6 +55,14 @@ func NewDateChangeMessage(text string) UIMessage {
}
}
+
+func (msg *ExpandedTextMessage) Clone() UIMessage {
+ return &ExpandedTextMessage{
+ BaseMessage: msg.BaseMessage.clone(),
+ MsgText: msg.MsgText.Clone(),
+ }
+}
+
func (msg *ExpandedTextMessage) GenerateText() tstring.TString {
return msg.MsgText
}
diff --git a/ui/messages/html/blockquote.go b/ui/messages/html/blockquote.go
index 30ce52e..17799a2 100644
--- a/ui/messages/html/blockquote.go
+++ b/ui/messages/html/blockquote.go
@@ -18,6 +18,7 @@ package html
import (
"fmt"
+ "strings"
"maunium.net/go/mauview"
)
@@ -37,6 +38,10 @@ func NewBlockquoteEntity(children []Entity) *BlockquoteEntity {
}}
}
+func (be *BlockquoteEntity) Clone() Entity {
+ return &BlockquoteEntity{BaseEntity: be.BaseEntity.Clone().(*BaseEntity)}
+}
+
func (be *BlockquoteEntity) Draw(screen mauview.Screen) {
be.BaseEntity.Draw(screen)
for y := 0; y < be.height; y++ {
@@ -44,6 +49,33 @@ func (be *BlockquoteEntity) Draw(screen mauview.Screen) {
}
}
+func (be *BlockquoteEntity) PlainText() string {
+ if len(be.Children) == 0 {
+ return ""
+ }
+ var buf strings.Builder
+ newlined := false
+ for i, child := range be.Children {
+ if i != 0 && child.IsBlock() && !newlined {
+ buf.WriteRune('\n')
+ }
+ newlined = false
+ for i, row := range strings.Split(child.PlainText(), "\n") {
+ if i != 0 {
+ buf.WriteRune('\n')
+ }
+ buf.WriteRune('>')
+ buf.WriteRune(' ')
+ buf.WriteString(row)
+ }
+ if child.IsBlock() {
+ buf.WriteRune('\n')
+ newlined = true
+ }
+ }
+ return strings.TrimSpace(buf.String())
+}
+
func (be *BlockquoteEntity) String() string {
return fmt.Sprintf("&html.BlockquoteEntity{%s},\n", be.BaseEntity)
}
diff --git a/ui/messages/html/break.go b/ui/messages/html/break.go
index d400f00..ea67ead 100644
--- a/ui/messages/html/break.go
+++ b/ui/messages/html/break.go
@@ -26,3 +26,15 @@ func NewBreakEntity() *BreakEntity {
Block: true,
}}
}
+
+func (be *BreakEntity) Clone() Entity {
+ return NewBreakEntity()
+}
+
+func (be *BreakEntity) PlainText() string {
+ return "\n"
+}
+
+func (be *BreakEntity) String() string {
+ return "&html.BreakEntity{},\n"
+}
diff --git a/ui/messages/html/codeblock.go b/ui/messages/html/codeblock.go
index ec6181d..4a0766c 100644
--- a/ui/messages/html/codeblock.go
+++ b/ui/messages/html/codeblock.go
@@ -37,6 +37,13 @@ func NewCodeBlockEntity(children []Entity, background tcell.Style) *CodeBlockEnt
}
}
+func (ce *CodeBlockEntity) Clone() Entity {
+ return &CodeBlockEntity{
+ BaseEntity: ce.BaseEntity.Clone().(*BaseEntity),
+ Background: ce.Background,
+ }
+}
+
func (ce *CodeBlockEntity) Draw(screen mauview.Screen) {
screen.Fill(' ', ce.Background)
ce.BaseEntity.Draw(screen)
diff --git a/ui/messages/html/entity.go b/ui/messages/html/entity.go
index 2ce37a8..be58d61 100644
--- a/ui/messages/html/entity.go
+++ b/ui/messages/html/entity.go
@@ -165,17 +165,20 @@ func (he *BaseEntity) PlainText() string {
buf.WriteString(he.Text)
newlined := false
for _, child := range he.Children {
- if child.IsBlock() && !newlined {
+ text := child.PlainText()
+ if !strings.HasPrefix(text, "\n") && child.IsBlock() && !newlined {
buf.WriteRune('\n')
}
newlined = false
- buf.WriteString(child.PlainText())
+ buf.WriteString(text)
if child.IsBlock() {
- buf.WriteRune('\n')
+ if !strings.HasSuffix(text, "\n") {
+ buf.WriteRune('\n')
+ }
newlined = true
}
}
- return buf.String()
+ return strings.TrimSpace(buf.String())
}
// Draw draws this entity onto the given mauview Screen.
diff --git a/ui/messages/html/horizontalline.go b/ui/messages/html/horizontalline.go
new file mode 100644
index 0000000..32761aa
--- /dev/null
+++ b/ui/messages/html/horizontalline.go
@@ -0,0 +1,56 @@
+// 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 html
+
+import (
+ "strings"
+
+ "maunium.net/go/mauview"
+)
+
+type HorizontalLineEntity struct {
+ *BaseEntity
+}
+
+const HorizontalLineChar = '━'
+
+func NewHorizontalLineEntity() *HorizontalLineEntity {
+ return &HorizontalLineEntity{&BaseEntity{
+ Tag: "hr",
+ Block: true,
+ DefaultHeight: 1,
+ }}
+}
+
+func (he *HorizontalLineEntity) Clone() Entity {
+ return NewHorizontalLineEntity()
+}
+
+func (he *HorizontalLineEntity) Draw(screen mauview.Screen) {
+ width, _ := screen.Size()
+ for x := 0; x < width; x++ {
+ screen.SetContent(x, 0, HorizontalLineChar, nil, he.Style)
+ }
+}
+
+func (he *HorizontalLineEntity) PlainText() string {
+ return strings.Repeat(string(HorizontalLineChar), 5)
+}
+
+func (he *HorizontalLineEntity) String() string {
+ return "&html.HorizontalLineEntity{},\n"
+}
diff --git a/ui/messages/html/list.go b/ui/messages/html/list.go
index 9f45b92..c611e66 100644
--- a/ui/messages/html/list.go
+++ b/ui/messages/html/list.go
@@ -56,6 +56,14 @@ func NewListEntity(ordered bool, start int, children []Entity) *ListEntity {
return entity
}
+func (le *ListEntity) Clone() Entity {
+ return &ListEntity{
+ BaseEntity: le.BaseEntity.Clone().(*BaseEntity),
+ Ordered: le.Ordered,
+ Start: le.Start,
+ }
+}
+
func (le *ListEntity) Draw(screen mauview.Screen) {
width, _ := screen.Size()
@@ -75,6 +83,31 @@ func (le *ListEntity) Draw(screen mauview.Screen) {
}
}
+func (le *ListEntity) PlainText() string {
+ if len(le.Children) == 0 {
+ return ""
+ }
+ var buf strings.Builder
+ for i, child := range le.Children {
+ indent := strings.Repeat(" ", le.Indent)
+ if le.Ordered {
+ number := le.Start + i
+ _, _ = fmt.Fprintf(&buf, "%d. %s", number, strings.Repeat(" ", le.Indent-2-digits(number)))
+ } else {
+ buf.WriteString("● ")
+ }
+ for j, row := range strings.Split(child.PlainText(), "\n") {
+ if j != 0 {
+ buf.WriteRune('\n')
+ buf.WriteString(indent)
+ }
+ buf.WriteString(row)
+ }
+ buf.WriteRune('\n')
+ }
+ return strings.TrimSpace(buf.String())
+}
+
func (le *ListEntity) String() string {
return fmt.Sprintf("&html.ListEntity{Ordered=%t, Start=%d, Base=%s},\n", le.Ordered, le.Start, le.BaseEntity)
}
diff --git a/ui/messages/html/parser.go b/ui/messages/html/parser.go
index 0bdf483..9e08ab7 100644
--- a/ui/messages/html/parser.go
+++ b/ui/messages/html/parser.go
@@ -111,7 +111,7 @@ func (parser *htmlParser) basicFormatToEntity(node *html.Node) Entity {
entity.AdjustStyle(AdjustStyleBold)
case "i", "em":
entity.AdjustStyle(AdjustStyleItalic)
- case "s", "del":
+ case "s", "del", "strike":
entity.AdjustStyle(AdjustStyleStrikethrough)
case "u", "ins":
entity.AdjustStyle(AdjustStyleUnderline)
@@ -237,7 +237,7 @@ func (parser *htmlParser) syntaxHighlight(text, language string) Entity {
children := make([]Entity, len(tokens))
for i, token := range tokens {
if token.Value == "\n" {
- children[i] = &BaseEntity{Block: true, Tag: "br"}
+ children[i] = NewBreakEntity()
} else {
children[i] = &BaseEntity{
Tag: token.Type.String(),
@@ -282,7 +282,7 @@ func (parser *htmlParser) tagNodeToEntity(node *html.Node) Entity {
return parser.headerToEntity(node)
case "br":
return NewBreakEntity()
- case "b", "strong", "i", "em", "s", "del", "u", "ins", "font":
+ case "b", "strong", "i", "em", "s", "strike", "del", "u", "ins", "font":
return parser.basicFormatToEntity(node)
case "a":
return parser.linkToEntity(node)
@@ -290,6 +290,10 @@ func (parser *htmlParser) tagNodeToEntity(node *html.Node) Entity {
return parser.imageToEntity(node)
case "pre":
return parser.codeblockToEntity(node)
+ case "hr":
+ return NewHorizontalLineEntity()
+ case "mx-reply":
+ return nil
default:
return &BaseEntity{
Tag: node.Data,
diff --git a/ui/messages/htmlmessage.go b/ui/messages/htmlmessage.go
index 577a33d..9ee1eab 100644
--- a/ui/messages/htmlmessage.go
+++ b/ui/messages/htmlmessage.go
@@ -40,6 +40,14 @@ func NewHTMLMessage(event *mautrix.Event, displayname string, root html.Entity)
}
}
+func (hw *HTMLMessage) Clone() UIMessage {
+ return &HTMLMessage{
+ BaseMessage: hw.BaseMessage.clone(),
+ Root: hw.Root.Clone(),
+ FocusedBg: hw.FocusedBg,
+ }
+}
+
func (hw *HTMLMessage) Draw(screen mauview.Screen) {
screen = hw.DrawReply(screen)
if hw.focused {
diff --git a/ui/messages/imagemessage.go b/ui/messages/imagemessage.go
index 9bebc11..01a6500 100644
--- a/ui/messages/imagemessage.go
+++ b/ui/messages/imagemessage.go
@@ -53,6 +53,19 @@ func NewImageMessage(matrix ifc.MatrixContainer, event *mautrix.Event, displayna
}
}
+func (msg *ImageMessage) Clone() UIMessage {
+ data := make([]byte, len(msg.data))
+ copy(data, msg.data)
+ return &ImageMessage{
+ BaseMessage: msg.BaseMessage.clone(),
+ Body: msg.Body,
+ Homeserver: msg.Homeserver,
+ FileID: msg.FileID,
+ data: data,
+ matrix: msg.matrix,
+ }
+}
+
func (msg *ImageMessage) RegisterMatrix(matrix ifc.MatrixContainer) {
msg.matrix = matrix
diff --git a/ui/messages/message.go b/ui/messages/message.go
index 44aaec1..c990368 100644
--- a/ui/messages/message.go
+++ b/ui/messages/message.go
@@ -43,6 +43,8 @@ type UIMessage interface {
Height() int
PlainText() string
+ Clone() UIMessage
+
RealSender() string
RegisterMatrix(matrix ifc.MatrixContainer)
}
diff --git a/ui/messages/parser.go b/ui/messages/parser.go
index 097c9d0..75e010a 100644
--- a/ui/messages/parser.go
+++ b/ui/messages/parser.go
@@ -31,7 +31,18 @@ import (
"maunium.net/go/gomuks/ui/widget"
)
-func ParseEvent(matrix ifc.MatrixContainer, room *rooms.Room, evt *mautrix.Event) UIMessage {
+func getCachedEvent(mainView ifc.MainView, roomID, eventID string) UIMessage {
+ if roomView := mainView.GetRoom(roomID); roomView != nil {
+ if replyToIfcMsg := roomView.GetEvent(eventID); replyToIfcMsg != nil {
+ if replyToMsg, ok := replyToIfcMsg.(UIMessage); ok && replyToMsg != nil {
+ return replyToMsg
+ }
+ }
+ }
+ return nil
+}
+
+func ParseEvent(matrix ifc.MatrixContainer, mainView ifc.MainView, room *rooms.Room, evt *mautrix.Event) UIMessage {
msg := directParseEvent(matrix, room, evt)
if msg == nil {
return nil
@@ -41,10 +52,14 @@ func ParseEvent(matrix ifc.MatrixContainer, room *rooms.Room, evt *mautrix.Event
if len(evt.Content.RelatesTo.InReplyTo.RoomID) > 0 {
replyToRoom = matrix.GetRoom(evt.Content.RelatesTo.InReplyTo.RoomID)
}
- replyToEvt, _ := matrix.GetEvent(replyToRoom, evt.Content.GetReplyTo())
- if replyToEvt != nil {
- replyToMsg := directParseEvent(matrix, replyToRoom, replyToEvt)
- if replyToMsg != nil {
+
+ if replyToMsg := getCachedEvent(mainView, replyToRoom.ID, evt.Content.GetReplyTo()); replyToMsg != nil {
+ debug.Print("Cloning cached UIMessage", replyToMsg)
+ replyToMsg = replyToMsg.Clone()
+ replyToMsg.SetReplyTo(nil)
+ msg.SetReplyTo(replyToMsg)
+ } else if replyToEvt, _ := matrix.GetEvent(replyToRoom, evt.Content.GetReplyTo()); replyToEvt != nil {
+ if replyToMsg := directParseEvent(matrix, replyToRoom, replyToEvt); replyToMsg != nil {
msg.SetReplyTo(replyToMsg)
} else {
// TODO add unrenderable reply header
@@ -68,6 +83,7 @@ func directParseEvent(matrix ifc.MatrixContainer, room *rooms.Room, evt *mautrix
case mautrix.StateMember:
return ParseMembershipEvent(room, evt)
}
+
return nil
}
diff --git a/ui/messages/textmessage.go b/ui/messages/textmessage.go
index 355a90e..f8c4573 100644
--- a/ui/messages/textmessage.go
+++ b/ui/messages/textmessage.go
@@ -43,15 +43,22 @@ func NewTextMessage(event *mautrix.Event, displayname string, text string) UIMes
func NewServiceMessage(text string) UIMessage {
return &TextMessage{
BaseMessage: BaseMessage{
- MsgSenderID: "*",
- MsgSender: "*",
- MsgTimestamp: time.Now(),
- MsgIsService: true,
+ MsgSenderID: "*",
+ MsgSender: "*",
+ MsgTimestamp: time.Now(),
+ MsgIsService: true,
},
MsgText: text,
}
}
+func (msg *TextMessage) Clone() UIMessage {
+ return &TextMessage{
+ BaseMessage: msg.BaseMessage.clone(),
+ MsgText: msg.MsgText,
+ }
+}
+
func (msg *TextMessage) getCache() tstring.TString {
if msg.cache == nil {
switch msg.MsgType {
diff --git a/ui/messages/tstring/string.go b/ui/messages/tstring/string.go
index bd6798d..48a6507 100644
--- a/ui/messages/tstring/string.go
+++ b/ui/messages/tstring/string.go
@@ -21,6 +21,7 @@ import (
"unicode"
"github.com/mattn/go-runewidth"
+
"maunium.net/go/mauview"
"maunium.net/go/tcell"
@@ -29,11 +30,11 @@ import (
type TString []Cell
func NewBlankTString() TString {
- return make([]Cell, 0)
+ return make(TString, 0)
}
func NewTString(str string) TString {
- newStr := make([]Cell, len(str))
+ newStr := make(TString, len(str))
for i, char := range str {
newStr[i] = NewCell(char)
}
@@ -41,7 +42,7 @@ func NewTString(str string) TString {
}
func NewColorTString(str string, color tcell.Color) TString {
- newStr := make([]Cell, len(str))
+ newStr := make(TString, len(str))
for i, char := range str {
newStr[i] = NewColorCell(char, color)
}
@@ -49,7 +50,7 @@ func NewColorTString(str string, color tcell.Color) TString {
}
func NewStyleTString(str string, style tcell.Style) TString {
- newStr := make([]Cell, len(str))
+ newStr := make(TString, len(str))
for i, char := range str {
newStr[i] = NewStyleCell(char, style)
}
@@ -74,6 +75,12 @@ func Join(strings []TString, separator string) TString {
return out
}
+func (str TString) Clone() TString {
+ newStr := make(TString, len(str))
+ copy(newStr, str)
+ return newStr
+}
+
func (str TString) AppendTString(dataList ...TString) TString {
newStr := str
for _, data := range dataList {
diff --git a/ui/room-view.go b/ui/room-view.go
index 4402eaa..2bb3e74 100644
--- a/ui/room-view.go
+++ b/ui/room-view.go
@@ -445,5 +445,13 @@ func (view *RoomView) AddMessage(message ifc.Message) {
}
func (view *RoomView) ParseEvent(evt *mautrix.Event) ifc.Message {
- return messages.ParseEvent(view.parent.matrix, view.Room, evt)
+ return messages.ParseEvent(view.parent.matrix, view.parent, view.Room, evt)
+}
+
+func (view *RoomView) GetEvent(eventID string) ifc.Message {
+ message, ok := view.content.messageIDs[eventID]
+ if !ok {
+ return nil
+ }
+ return message
}