From b0c4ef81e9a1c7d9376685875d795ad3b5d8db01 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sun, 7 Apr 2019 18:21:38 +0300 Subject: [PATCH] More changes to do #91 --- ui/command-processor.go | 13 ++-- ui/commands.go | 15 +++++ ui/message-view.go | 40 +++++++----- ui/messages/base.go | 51 +++++++-------- ui/messages/expandedtextmessage.go | 5 -- ui/messages/htmlmessage.go | 99 +++++++++++++++++++++++------- ui/messages/imagemessage.go | 7 --- ui/messages/message.go | 4 +- ui/messages/parser/htmlparser.go | 3 + ui/messages/textbase.go | 2 - ui/messages/textmessage.go | 5 -- 11 files changed, 158 insertions(+), 86 deletions(-) diff --git a/ui/command-processor.go b/ui/command-processor.go index 77a7b3d..7a4c2c1 100644 --- a/ui/command-processor.go +++ b/ui/command-processor.go @@ -76,12 +76,12 @@ func NewCommandProcessor(parent *MainView) *CommandProcessor { Gomuks: parent.gmx, }, aliases: map[string]*Alias{ - "part": {"leave"}, - "send": {"sendevent"}, - "msend": {"msendevent"}, - "state": {"setstate"}, - "mstate":{"msetstate"}, - "rb": {"rainbow"}, + "part": {"leave"}, + "send": {"sendevent"}, + "msend": {"msendevent"}, + "state": {"setstate"}, + "mstate": {"msetstate"}, + "rb": {"rainbow"}, }, commands: map[string]CommandHandler{ "unknown-command": cmdUnknownCommand, @@ -102,6 +102,7 @@ func NewCommandProcessor(parent *MainView) *CommandProcessor { "msetstate": cmdMSetState, "rainbow": cmdRainbow, "invite": cmdInvite, + "hprof": cmdHeapProfile, }, } } diff --git a/ui/commands.go b/ui/commands.go index a8b1faa..a652518 100644 --- a/ui/commands.go +++ b/ui/commands.go @@ -19,6 +19,9 @@ package ui import ( "encoding/json" "fmt" + "os" + "runtime" + "runtime/pprof" "strings" "unicode" @@ -69,6 +72,18 @@ var rainbow = GradientTable{ {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. func cmdRainbow(cmd *Command) { text := strings.Join(cmd.Args, " ") diff --git a/ui/message-view.go b/ui/message-view.go index f2c7260..20831f1 100644 --- a/ui/message-view.go +++ b/ui/message-view.go @@ -75,6 +75,7 @@ func NewMessageView(parent *RoomView) *MessageView { textBuffer: make([]tstring.TString, 0), metaBuffer: make([]ifc.MessageMeta, 0), + width: 80, widestSender: 5, prevWidth: -1, prevHeight: -1, @@ -159,8 +160,8 @@ func (view *MessageView) appendBuffer(message messages.UIMessage) { } } - view.textBuffer = append(view.textBuffer, message.Buffer()...) - for range message.Buffer() { + for i := 0; i < message.Height(); i++ { + view.textBuffer = append(view.textBuffer, nil) view.metaBuffer = append(view.metaBuffer, message) } view.prevMsgCount++ @@ -200,10 +201,15 @@ func (view *MessageView) replaceBuffer(original messages.UIMessage, new messages end++ } - view.textBuffer = append(append(view.textBuffer[0:start], new.Buffer()...), view.textBuffer[end:]...) - if len(new.Buffer()) != end-start { + if new.Height() == 0 { + 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] - for range new.Buffer() { + for i := 0; i < new.Height(); i++ { metaBuffer = append(metaBuffer, new) } view.metaBuffer = append(metaBuffer, view.metaBuffer[end:]...) @@ -504,16 +510,22 @@ func (view *MessageView) Draw(screen mauview.Screen) { meta.SenderColor()) } prevMeta = meta - htmlMessage, ok := meta.(*messages.HTMLMessage) - if ok { - htmlMessage.Draw(mauview.NewProxyScreen(screen, 0, line, view.width, htmlMessage.Height())) - if ok { - line += htmlMessage.Height() - continue - } - } } - 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) + } } } diff --git a/ui/messages/base.go b/ui/messages/base.go index ba1902d..d045e42 100644 --- a/ui/messages/base.go +++ b/ui/messages/base.go @@ -21,9 +21,9 @@ import ( "time" "maunium.net/go/mautrix" + "maunium.net/go/mauview" "maunium.net/go/tcell" - "maunium.net/go/gomuks/config" "maunium.net/go/gomuks/interface" "maunium.net/go/gomuks/ui/messages/tstring" "maunium.net/go/gomuks/ui/widget" @@ -34,33 +34,30 @@ func init() { } type BaseMessage struct { - MsgID string - MsgType mautrix.MessageType - MsgSenderID string - MsgSender string - MsgSenderColor tcell.Color - MsgTimestamp time.Time - MsgState ifc.MessageState - MsgIsHighlight bool - MsgIsService bool - buffer []tstring.TString - plainBuffer []tstring.TString - prevBufferWidth int - prevPrefs config.UserPreferences + MsgID string + MsgType mautrix.MessageType + MsgSenderID string + MsgSender string + MsgSenderColor tcell.Color + MsgTimestamp time.Time + MsgState ifc.MessageState + MsgIsHighlight bool + MsgIsService bool + buffer []tstring.TString + plainBuffer []tstring.TString } func newBaseMessage(id, sender, displayname string, msgtype mautrix.MessageType, timestamp time.Time) BaseMessage { return BaseMessage{ - MsgSenderID: sender, - MsgSender: displayname, - MsgTimestamp: timestamp, - MsgSenderColor: widget.GetHashColor(sender), - MsgType: msgtype, - MsgID: id, - prevBufferWidth: 0, - MsgState: ifc.MessageStateDefault, - MsgIsHighlight: false, - MsgIsService: false, + MsgSenderID: sender, + MsgSender: displayname, + MsgTimestamp: timestamp, + MsgSenderColor: widget.GetHashColor(sender), + MsgType: msgtype, + MsgID: id, + MsgState: ifc.MessageStateDefault, + MsgIsHighlight: false, + MsgIsService: false, } } @@ -227,3 +224,9 @@ func (msg *BaseMessage) IsService() bool { func (msg *BaseMessage) SetIsService(isService bool) { msg.MsgIsService = isService } + +func (msg *BaseMessage) Draw(screen mauview.Screen) { + for y, line := range msg.buffer { + line.Draw(screen, 0, y) + } +} diff --git a/ui/messages/expandedtextmessage.go b/ui/messages/expandedtextmessage.go index d889771..044613f 100644 --- a/ui/messages/expandedtextmessage.go +++ b/ui/messages/expandedtextmessage.go @@ -58,8 +58,3 @@ func (msg *ExpandedTextMessage) PlainText() string { func (msg *ExpandedTextMessage) CalculateBuffer(prefs config.UserPreferences, width int) { 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) -} diff --git a/ui/messages/htmlmessage.go b/ui/messages/htmlmessage.go index de4b30c..33e4d87 100644 --- a/ui/messages/htmlmessage.go +++ b/ui/messages/htmlmessage.go @@ -17,6 +17,8 @@ package messages import ( + "fmt" + "strings" "time" "github.com/mattn/go-runewidth" @@ -57,9 +59,13 @@ func (hw *HTMLMessage) OnPasteEvent(event mauview.PasteEvent) bool { } 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) + //debug.Print(hw.Root.String()) } func (hw *HTMLMessage) Height() int { @@ -109,8 +115,8 @@ func (he *HTMLEntity) Draw(screen mauview.Screen) { } if len(he.Children) > 0 { proxyScreen := &mauview.ProxyScreen{Parent: screen, OffsetX: he.Indent, Width: width - he.Indent} - for _, entity := range he.Children { - if entity.Block { + for i, entity := range he.Children { + if i != 0 && entity.startX == 0 { proxyScreen.OffsetY++ } proxyScreen.Height = entity.height @@ -120,38 +126,87 @@ func (he *HTMLEntity) Draw(screen mauview.Screen) { } } -func (he *HTMLEntity) calculateBuffer(width, startX int, bare bool) int { +func (he *HTMLEntity) String() string { + var buf strings.Builder + buf.WriteString("&HTMLEntity{\n") + _, _ = fmt.Fprintf(&buf, ` Tag="%s", Style=%d, Block=%t, Indent=%d, startX=%d, height=%d,\n`, + he.Tag, he.Style, he.Block, he.Indent, he.startX, he.height) + _, _ = 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 { - childStartX := 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 *HTMLEntity) 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 for _, entity := range he.Children { + if entity.Block || childStartX == 0 || he.height == 0 { + he.height++ + } childStartX = entity.calculateBuffer(width-he.Indent, childStartX, bare) he.height += entity.height - 1 } - } - if len(he.Text) > 0 && width != he.prevWidth { - he.prevWidth = width - he.buffer = make([]string, 0, 1) - text := he.Text - if !he.Block { - he.startX = startX - } else { - startX = 0 + 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 { - extract := runewidth.Truncate(text, width-startX, "") - extract = trim(extract, text, bare) - he.buffer = append(he.buffer, extract) + 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):] - startX = 0 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 runewidth.StringWidth(extract) + return textStartX + runewidth.StringWidth(extract) } + textStartX = 0 } } return 0 @@ -164,12 +219,13 @@ func (he *HTMLEntity) calculateBuffer(width, startX int, bare bool) int { spacePattern = regexp.MustCompile(`\s+`) )*/ -func trim(extract, full string, bare bool) string { +func trim(extract, full string, bare bool) (string, bool) { if len(extract) == len(full) { - return extract + return extract, true } if spaces := spacePattern.FindStringIndex(full[len(extract):]); spaces != nil && spaces[0] == 0 { extract = full[:len(extract)+spaces[1]] + //return extract, true } regex := boundaryPattern if bare { @@ -180,8 +236,9 @@ func trim(extract, full string, bare bool) string { if match := matches[len(matches)-1]; len(match) >= 2 { if until := match[1]; until < len(extract) { extract = extract[:until] + return extract, true } } } - return extract + return extract, len(extract) > 0 && extract[len(extract)-1] == ' ' } diff --git a/ui/messages/imagemessage.go b/ui/messages/imagemessage.go index cad76a4..968f29e 100644 --- a/ui/messages/imagemessage.go +++ b/ui/messages/imagemessage.go @@ -112,11 +112,4 @@ func (msg *ImageMessage) CalculateBuffer(prefs config.UserPreferences, width int } 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) } diff --git a/ui/messages/message.go b/ui/messages/message.go index e1888e6..db93879 100644 --- a/ui/messages/message.go +++ b/ui/messages/message.go @@ -19,7 +19,7 @@ package messages import ( "maunium.net/go/gomuks/config" "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. @@ -27,7 +27,7 @@ type UIMessage interface { ifc.Message CalculateBuffer(preferences config.UserPreferences, width int) - Buffer() []tstring.TString + Draw(screen mauview.Screen) Height() int PlainText() string diff --git a/ui/messages/parser/htmlparser.go b/ui/messages/parser/htmlparser.go index 3d1548b..9a9c2d1 100644 --- a/ui/messages/parser/htmlparser.go +++ b/ui/messages/parser/htmlparser.go @@ -266,6 +266,9 @@ func (parser *htmlParser) singleNodeToEntity(node *html.Node, stripLinebreak boo case html.ElementNode: return parser.tagNodeToEntity(node, stripLinebreak) case html.DocumentNode: + if node.FirstChild.Data == "html" && node.FirstChild.NextSibling == nil { + return parser.singleNodeToEntity(node.FirstChild, stripLinebreak) + } return &messages.HTMLEntity{ Tag: "html", Children: parser.nodeToEntities(node.FirstChild, stripLinebreak), diff --git a/ui/messages/textbase.go b/ui/messages/textbase.go index 01e7b5c..321d998 100644 --- a/ui/messages/textbase.go +++ b/ui/messages/textbase.go @@ -92,6 +92,4 @@ func (msg *BaseMessage) calculateBufferWithText(prefs config.UserPreferences, te str = str[len(extract):] } } - msg.prevBufferWidth = width - msg.prevPrefs = prefs } diff --git a/ui/messages/textmessage.go b/ui/messages/textmessage.go index 8ce9482..6364659 100644 --- a/ui/messages/textmessage.go +++ b/ui/messages/textmessage.go @@ -90,8 +90,3 @@ func (msg *TextMessage) PlainText() string { func (msg *TextMessage) CalculateBuffer(prefs config.UserPreferences, width int) { 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) -}