More changes to do #91

This commit is contained in:
Tulir Asokan 2019-04-07 18:21:38 +03:00
parent 21b81ccb27
commit b0c4ef81e9
11 changed files with 158 additions and 86 deletions

View File

@ -80,7 +80,7 @@ func NewCommandProcessor(parent *MainView) *CommandProcessor {
"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{
@ -102,6 +102,7 @@ func NewCommandProcessor(parent *MainView) *CommandProcessor {
"msetstate": cmdMSetState, "msetstate": cmdMSetState,
"rainbow": cmdRainbow, "rainbow": cmdRainbow,
"invite": cmdInvite, "invite": cmdInvite,
"hprof": cmdHeapProfile,
}, },
} }
} }

View File

@ -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, " ")

View File

@ -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:]...)
@ -504,16 +510,22 @@ func (view *MessageView) Draw(screen mauview.Screen) {
meta.SenderColor()) meta.SenderColor())
} }
prevMeta = meta 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
}
}
} }
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) text.Draw(screen, messageX, line)
} }
}
} }

View File

@ -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"
@ -45,8 +45,6 @@ type BaseMessage struct {
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 {
@ -57,7 +55,6 @@ func newBaseMessage(id, sender, displayname string, msgtype mautrix.MessageType,
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)
}
}

View File

@ -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)
}

View File

@ -17,6 +17,8 @@
package messages package messages
import ( import (
"fmt"
"strings"
"time" "time"
"github.com/mattn/go-runewidth" "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) { 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 // TODO account for bare messages in initial startX
startX := 0 startX := 0
hw.Root.calculateBuffer(width, startX, preferences.BareMessageView) hw.Root.calculateBuffer(width, startX, preferences.BareMessageView)
//debug.Print(hw.Root.String())
} }
func (hw *HTMLMessage) Height() int { func (hw *HTMLMessage) Height() int {
@ -109,8 +115,8 @@ func (he *HTMLEntity) Draw(screen mauview.Screen) {
} }
if len(he.Children) > 0 { if len(he.Children) > 0 {
proxyScreen := &mauview.ProxyScreen{Parent: screen, OffsetX: he.Indent, Width: width - he.Indent} proxyScreen := &mauview.ProxyScreen{Parent: screen, OffsetX: he.Indent, Width: width - he.Indent}
for _, entity := range he.Children { for i, entity := range he.Children {
if entity.Block { if i != 0 && entity.startX == 0 {
proxyScreen.OffsetY++ proxyScreen.OffsetY++
} }
proxyScreen.Height = entity.height 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 { 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 { for _, entity := range he.Children {
if entity.Block || childStartX == 0 || he.height == 0 {
he.height++
}
childStartX = entity.calculateBuffer(width-he.Indent, childStartX, bare) childStartX = entity.calculateBuffer(width-he.Indent, childStartX, bare)
he.height += entity.height - 1 he.height += entity.height - 1
} }
if len(he.Text) == 0 && !he.Block {
return childStartX
} }
if len(he.Text) > 0 && width != he.prevWidth { }
if len(he.Text) > 0 {
he.prevWidth = width he.prevWidth = width
he.buffer = make([]string, 0, 1) if he.buffer == nil {
text := he.Text he.buffer = []string{}
if !he.Block {
he.startX = startX
} else {
startX = 0
} }
bufPtr := 0
text := he.Text
textStartX := he.startX
for { for {
extract := runewidth.Truncate(text, width-startX, "") extract := runewidth.Truncate(text, width-textStartX, "")
extract = trim(extract, text, bare) 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) he.buffer = append(he.buffer, extract)
}
bufPtr++
text = text[len(extract):] text = text[len(extract):]
startX = 0
if len(text) == 0 { if len(text) == 0 {
he.buffer = he.buffer[:bufPtr]
he.height += len(he.buffer) he.height += len(he.buffer)
// This entity is over, return the startX for the next entity // This entity is over, return the startX for the next entity
if he.Block { if he.Block {
// ...except if it's a block entity // ...except if it's a block entity
return 0 return 0
} }
return runewidth.StringWidth(extract) return textStartX + runewidth.StringWidth(extract)
} }
textStartX = 0
} }
} }
return 0 return 0
@ -164,12 +219,13 @@ func (he *HTMLEntity) calculateBuffer(width, startX int, bare bool) int {
spacePattern = regexp.MustCompile(`\s+`) 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) { if len(extract) == len(full) {
return extract return extract, true
} }
if spaces := spacePattern.FindStringIndex(full[len(extract):]); spaces != nil && spaces[0] == 0 { if spaces := spacePattern.FindStringIndex(full[len(extract):]); spaces != nil && spaces[0] == 0 {
extract = full[:len(extract)+spaces[1]] extract = full[:len(extract)+spaces[1]]
//return extract, true
} }
regex := boundaryPattern regex := boundaryPattern
if bare { if bare {
@ -180,8 +236,9 @@ func trim(extract, full string, bare bool) string {
if match := matches[len(matches)-1]; len(match) >= 2 { if match := matches[len(matches)-1]; len(match) >= 2 {
if until := match[1]; until < len(extract) { if until := match[1]; until < len(extract) {
extract = extract[:until] extract = extract[:until]
return extract, true
} }
} }
} }
return extract return extract, len(extract) > 0 && extract[len(extract)-1] == ' '
} }

View File

@ -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)
} }

View File

@ -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,7 +27,7 @@ type UIMessage interface {
ifc.Message ifc.Message
CalculateBuffer(preferences config.UserPreferences, width int) CalculateBuffer(preferences config.UserPreferences, width int)
Buffer() []tstring.TString Draw(screen mauview.Screen)
Height() int Height() int
PlainText() string PlainText() string

View File

@ -266,6 +266,9 @@ func (parser *htmlParser) singleNodeToEntity(node *html.Node, stripLinebreak boo
case html.ElementNode: case html.ElementNode:
return parser.tagNodeToEntity(node, stripLinebreak) return parser.tagNodeToEntity(node, stripLinebreak)
case html.DocumentNode: case html.DocumentNode:
if node.FirstChild.Data == "html" && node.FirstChild.NextSibling == nil {
return parser.singleNodeToEntity(node.FirstChild, stripLinebreak)
}
return &messages.HTMLEntity{ return &messages.HTMLEntity{
Tag: "html", Tag: "html",
Children: parser.nodeToEntities(node.FirstChild, stripLinebreak), Children: parser.nodeToEntities(node.FirstChild, stripLinebreak),

View File

@ -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
} }

View File

@ -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)
}