From 1d95fea9f359a2ce13cb8d1f7cc87a06cd97b0ac Mon Sep 17 00:00:00 2001
From: Casper Weiss Bang <master@thecdk.net>
Date: Tue, 29 Oct 2019 23:31:58 +0100
Subject: [PATCH] Revised some styling elements

- Colored block codes in a whole block
- Colored quotes
- Fixed a few bugs
---
 cmdConfig.go     |  4 +--
 cmdDownload.go   |  5 +--
 cmdUploadFile.go |  7 ++--
 colors.go        | 87 +++++++++++++++++++++++++-------------------
 defaultConfig.go | 93 +++++++++++++++++++++++++-----------------------
 go.mod           | 12 -------
 go.sum           | 23 ------------
 kbtui.toml       | 93 +++++++++++++++++++++++++-----------------------
 main.go          | 63 ++++++++++++++++++++------------
 types.go         |  2 ++
 10 files changed, 200 insertions(+), 189 deletions(-)
 delete mode 100644 go.mod
 delete mode 100644 go.sum

diff --git a/cmdConfig.go b/cmdConfig.go
index 5d7355b..a32373d 100644
--- a/cmdConfig.go
+++ b/cmdConfig.go
@@ -31,7 +31,7 @@ func cmdConfig(cmd []string) {
 				printError(err.Error())
 				return
 			}
-			printInfoF("Config file loaded: $TEXT", config.Colors.Message.Attachment.stylize(config.filepath))
+			printInfoF("Config file loaded: $TEXT", config.Colors.Feed.File.stylize(config.filepath))
 			return
 		}
 	case len(cmd) > 2:
@@ -41,7 +41,7 @@ func cmdConfig(cmd []string) {
 				printError(err.Error())
 				return
 			}
-			printInfoF("Config file loaded: $TEXT", config.Colors.Message.Attachment.stylize(config.filepath))
+			printInfoF("Config file loaded: $TEXT", config.Colors.Feed.File.stylize(config.filepath))
 			return
 		}
 	}
diff --git a/cmdDownload.go b/cmdDownload.go
index c78df27..0c1b54b 100644
--- a/cmdDownload.go
+++ b/cmdDownload.go
@@ -48,9 +48,10 @@ func cmdDownloadFile(cmd []string) {
 
 	_, err = chat.Download(messageID, fmt.Sprintf("%s/%s", config.Basics.DownloadPath, fileName))
 	channelName := config.Colors.Message.LinkKeybase.stylize(channel.Name)
+	fileNameStylizied := config.Colors.Feed.File.stylize(fileName)
 	if err != nil {
-		printErrorF(fmt.Sprintf("There was an error downloading %s from $TEXT", fileName), channelName)
+		printErrorF("There was an error downloading $TEXT from $TEXT", fileNameStylizied, channelName)
 	} else {
-		printInfoF(fmt.Sprintf("Downloaded %s from $TEXT", fileName), channelName)
+		printInfoF("Downloaded $TEXT from $TEXT", fileNameStylizied, channelName)
 	}
 }
diff --git a/cmdUploadFile.go b/cmdUploadFile.go
index 07d05a8..df91686 100644
--- a/cmdUploadFile.go
+++ b/cmdUploadFile.go
@@ -40,10 +40,11 @@ func cmdUploadFile(cmd []string) {
 	}
 	chat := k.NewChat(channel)
 	_, err := chat.Upload(fileName, filePath)
-	channelName := config.Colors.Message.LinkKeybase.stylize(channel.Name).string()
+	channelName := config.Colors.Message.LinkKeybase.stylize(channel.Name)
+	fileNameStylized := config.Colors.Feed.File.stylize(filePath)
 	if err != nil {
-		printError(fmt.Sprintf("There was an error uploading %s to %s\n%+v", filePath, channelName, err))
+		printError(fmt.Sprintf("There was an error uploading %s to %s\n%+v", filePath, channel.Name, err))
 	} else {
-		printInfo(fmt.Sprintf("Uploaded %s to %s", filePath, channelName))
+		printInfoF("Uploaded $TEXT to $TEXT", fileNameStylized, channelName)
 	}
 }
diff --git a/colors.go b/colors.go
index 4782966..c5400a2 100644
--- a/colors.go
+++ b/colors.go
@@ -79,6 +79,7 @@ func (s Style) withBackground(color int) Style {
 	s.Background = colorFromInt(color)
 	return s
 }
+
 func (s Style) withBold() Style {
 	s.Bold = true
 	return s
@@ -107,30 +108,31 @@ func (s Style) toANSI() string {
 	if config.Basics.Colorless {
 		return ""
 	}
-	output := "\x1b[0m\x1b[0"
+	styleSlice := []string{"0"}
+
 	if colorFromString(s.Foreground) != normal {
-		output += fmt.Sprintf(";%d", 30+colorFromString(s.Foreground))
+		styleSlice = append(styleSlice, fmt.Sprintf("%d", 30+colorFromString(s.Foreground)))
 	}
 	if colorFromString(s.Background) != normal {
-		output += fmt.Sprintf(";%d", 40+colorFromString(s.Background))
+		styleSlice = append(styleSlice, fmt.Sprintf("%d", 40+colorFromString(s.Background)))
 	}
 	if s.Bold {
-		output += ";1"
+		styleSlice = append(styleSlice, "1")
 	}
 	if s.Italic {
-		output += ";3"
+		styleSlice = append(styleSlice, "3")
 	}
 	if s.Underline {
-		output += ";4"
+		styleSlice = append(styleSlice, "4")
 	}
 	if s.Inverse {
-		output += ";7"
+		styleSlice = append(styleSlice, "7")
 	}
 	if s.Strikethrough {
-		output += ";9"
+		styleSlice = append(styleSlice, "9")
 	}
 
-	return output + "m"
+	return "\x1b[" + strings.Join(styleSlice, ";") + "m"
 }
 
 // End Colors
@@ -142,6 +144,12 @@ type StyledString struct {
 	style   Style
 }
 
+func (ss StyledString) withStyle(style Style) StyledString {
+	return StyledString{ss.message, style}
+}
+
+// TODO change StyledString to have styles at start-end indexes.
+
 // TODO handle all formatting types
 func (s Style) sprintf(base string, parts ...StyledString) StyledString {
 	text := s.stylize(removeFormatting(base))
@@ -158,51 +166,58 @@ func (s Style) sprintf(base string, parts ...StyledString) StyledString {
 func (s Style) stylize(msg string) StyledString {
 	return StyledString{msg, s}
 }
-func (t StyledString) stringFollowedByStyle(style Style) string {
-	return t.style.toANSI() + t.message + style.toANSI()
+func (ss StyledString) stringFollowedByStyle(style Style) string {
+	return ss.style.toANSI() + ss.message + style.toANSI()
 }
-func (t StyledString) string() string {
-	return t.stringFollowedByStyle(basicStyle)
+func (ss StyledString) string() string {
+	return ss.stringFollowedByStyle(basicStyle)
 }
 
-func (t StyledString) replace(match string, value StyledString) StyledString {
-	return t.replaceN(match, value, -1)
+func (ss StyledString) replace(match string, value StyledString) StyledString {
+	return ss.replaceN(match, value, -1)
 }
-func (t StyledString) replaceN(match string, value StyledString, n int) StyledString {
-	t.message = strings.Replace(t.message, match, value.stringFollowedByStyle(t.style), n)
-	return t
+func (ss StyledString) replaceN(match string, value StyledString, n int) StyledString {
+	ss.message = strings.Replace(ss.message, match, value.stringFollowedByStyle(ss.style), n)
+	return ss
 }
-func (t StyledString) replaceString(match string, value string) StyledString {
-	t.message = strings.Replace(t.message, match, value, -1)
-	return t
+func (ss StyledString) replaceString(match string, value string) StyledString {
+	ss.message = strings.Replace(ss.message, match, value, -1)
+	return ss
 }
 
 // Overrides current formatting
-func (t StyledString) colorRegex(match string, style Style) StyledString {
-	re := regexp.MustCompile("(" + match + ")")
-	locations := re.FindAllStringIndex(t.message, -1)
+func (ss StyledString) colorRegex(match string, style Style) StyledString {
+	return ss.regexReplaceFunc(match, func(subString string) string {
+		return style.stylize(removeFormatting(subString)).stringFollowedByStyle(ss.style)
+	})
+}
+
+// Replacer function takes the current match as input and should return how the match should be preseneted instead
+func (ss StyledString) regexReplaceFunc(match string, replacer func(string) string) StyledString {
+	re := regexp.MustCompile(match)
+	locations := re.FindAllStringIndex(ss.message, -1)
 	var newMessage string
 	var prevIndex int
 	for _, loc := range locations {
-		cleanSubstring := style.stylize(removeFormatting(string(t.message[loc[0]:loc[1]])))
-		newMessage += t.message[prevIndex:loc[0]]
-		newMessage += cleanSubstring.stringFollowedByStyle(t.style)
+		newSubstring := replacer(ss.message[loc[0]:loc[1]])
+		newMessage += ss.message[prevIndex:loc[0]]
+		newMessage += newSubstring
 		prevIndex = loc[1]
 	}
 	// Append any string after the final match
-	newMessage += t.message[prevIndex:len(t.message)]
-	t.message = newMessage
-	return t
+	newMessage += ss.message[prevIndex:len(ss.message)]
+	ss.message = newMessage
+	return ss
 }
 
 // Appends the other stylize at the end, but retains same style
-func (t StyledString) append(other StyledString) StyledString {
-	t.message = t.message + other.stringFollowedByStyle(t.style)
-	return t
+func (ss StyledString) append(other StyledString) StyledString {
+	ss.message = ss.message + other.stringFollowedByStyle(ss.style)
+	return ss
 }
-func (t StyledString) appendString(other string) StyledString {
-	t.message += other
-	return t
+func (ss StyledString) appendString(other string) StyledString {
+	ss.message += other
+	return ss
 }
 
 // Begin Formatting
diff --git a/defaultConfig.go b/defaultConfig.go
index 5859e69..0471703 100644
--- a/defaultConfig.go
+++ b/defaultConfig.go
@@ -24,49 +24,54 @@ time_format = "15:04"
 
 
 [colors]
-	 [colors.channels]
-		  [colors.channels.basic]
-		  foreground = "normal"
-		  [colors.channels.header]
-		  foreground = "magenta"
-		  bold = true
-		  [colors.channels.unread]
-		  foreground = "green"
-		  italic = true
+    [colors.channels]
+        [colors.channels.basic]
+        foreground = "normal"
+        [colors.channels.header]
+        foreground = "magenta"
+        bold = true
+        [colors.channels.unread]
+        foreground = "green"
+        italic = true
 
-	 [colors.message]
-		  [colors.message.body]
-		  foreground = "normal"
-		  [colors.message.header]
-		  foreground = "grey"
-		  [colors.message.mention]
-		  foreground = "green"
-		  italic = true
-		  bold = true
-		  [colors.message.id]
-		  foreground = "yellow"
-		  [colors.message.time]
-		  foreground = "magenta"
-		  [colors.message.sender_default]
-		  foreground = "cyan"
-		  bold = true
-		  [colors.message.sender_device]
-		  foreground = "cyan"
-		  [colors.message.attachment]
-		  foreground = "red"
-		  [colors.message.link_url]
-		  foreground = "yellow"
-		  [colors.message.link_keybase]
-		  foreground = "yellow"
-		  [colors.message.reaction]
-		  foreground = "magenta"
-		  bold = true
-		  [colors.message.code]
-		  foreground = "cyan"
-		  background = "grey"
-	 [colors.feed]
-		  [colors.feed.basic]
-		  foreground = "grey"
-		  [colors.feed.error]
-		  foreground = "red"
+    [colors.message]
+        [colors.message.body]
+        foreground = "normal"
+        [colors.message.header]
+        foreground = "grey"
+        bold = true
+        [colors.message.mention]
+        foreground = "green"
+        italic = true
+        bold = true
+        [colors.message.id]
+        foreground = "yellow"
+        [colors.message.time]
+        foreground = "magenta"
+        [colors.message.sender_default]
+        foreground = "cyan"
+        bold = true
+        [colors.message.sender_device]
+        foreground = "cyan"
+        [colors.message.attachment]
+        foreground = "red"
+        [colors.message.link_url]
+        foreground = "yellow"
+        [colors.message.link_keybase]
+        foreground = "yellow"
+        [colors.message.reaction]
+        foreground = "magenta"
+        bold = true
+        [colors.message.quote]
+        foreground = "green"
+        [colors.message.code]
+        foreground = "green"
+        background = "grey"
+    [colors.feed]
+        [colors.feed.basic]
+        foreground = "grey"
+        [colors.feed.error]
+        foreground = "red"
+        [colors.feed.file]
+        foreground = "yellow"
 `
diff --git a/go.mod b/go.mod
deleted file mode 100644
index 8be19cf..0000000
--- a/go.mod
+++ /dev/null
@@ -1,12 +0,0 @@
-module github.com/Rudi9719/kbtui
-
-go 1.12
-
-require (
-	github.com/awesome-gocui/gocui v0.6.0
-	github.com/magefile/mage v1.9.0
-	github.com/mattn/go-runewidth v0.0.5 // indirect
-	github.com/pelletier/go-toml v1.6.0
-	gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
-	samhofi.us/x/keybase v0.0.0-20191023034410-b00e56e8dd3c
-)
diff --git a/go.sum b/go.sum
deleted file mode 100644
index 097bc30..0000000
--- a/go.sum
+++ /dev/null
@@ -1,23 +0,0 @@
-github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
-github.com/awesome-gocui/gocui v0.6.0 h1:hhDJiQC12tEsJNJ+iZBBVaSSLFYo9llFuYpQlL5JZVI=
-github.com/awesome-gocui/gocui v0.6.0/go.mod h1:1QikxFaPhe2frKeKvEwZEIGia3haiOxOUXKinrv17mA=
-github.com/awesome-gocui/termbox-go v0.0.0-20190427202837-c0aef3d18bcc h1:wGNpKcHU8Aadr9yOzsT3GEsFLS7HQu8HxQIomnekqf0=
-github.com/awesome-gocui/termbox-go v0.0.0-20190427202837-c0aef3d18bcc/go.mod h1:tOy3o5Nf1bA17mnK4W41gD7PS3u4Cv0P0pqFcoWMy8s=
-github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
-github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
-github.com/magefile/mage v1.9.0 h1:t3AU2wNwehMCW97vuqQLtw6puppWXHO+O2MHo5a50XE=
-github.com/magefile/mage v1.9.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
-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.5 h1:jrGtp51JOKTWgvLFzfG6OtZOJcK2sEnzc/U+zw7TtbA=
-github.com/mattn/go-runewidth v0.0.5/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
-github.com/pelletier/go-toml v1.5.0 h1:5BakdOZdtKJ1FFk6QdL8iSGrMWsXgchNJcrnarjbmJQ=
-github.com/pelletier/go-toml v1.5.0/go.mod h1:5N711Q9dKgbdkxHL+MEfF31hpT7l0S0s/t2kKREewys=
-github.com/pelletier/go-toml v1.6.0 h1:aetoXYr0Tv7xRU/V4B4IZJ2QcbtMUFoNb3ORp7TzIK4=
-github.com/pelletier/go-toml v1.6.0/go.mod h1:5N711Q9dKgbdkxHL+MEfF31hpT7l0S0s/t2kKREewys=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-samhofi.us/x/keybase v0.0.0-20191023034410-b00e56e8dd3c h1:qIKOKqYnRCx+O2IOz3a/lplrD0p1e3n/VoGOdrTGrVo=
-samhofi.us/x/keybase v0.0.0-20191023034410-b00e56e8dd3c/go.mod h1:fcva80IUFyWcHtV4bBSzgKg07K6Rvuvi3GtGCLNGkyE=
diff --git a/kbtui.toml b/kbtui.toml
index a72723f..fa98691 100644
--- a/kbtui.toml
+++ b/kbtui.toml
@@ -21,48 +21,53 @@ time_format = "15:04"
 
 
 [colors]
-	 [colors.channels]
-		  [colors.channels.basic]
-		  foreground = "normal"
-		  [colors.channels.header]
-		  foreground = "magenta"
-		  bold = true
-		  [colors.channels.unread]
-		  foreground = "green"
-		  italic = true
+    [colors.channels]
+        [colors.channels.basic]
+        foreground = "normal"
+        [colors.channels.header]
+        foreground = "magenta"
+        bold = true
+        [colors.channels.unread]
+        foreground = "green"
+        italic = true
 
-	 [colors.message]
-		  [colors.message.body]
-		  foreground = "normal"
-		  [colors.message.header]
-		  foreground = "grey"
-		  [colors.message.mention]
-		  foreground = "green"
-		  italic = true
-		  bold = true
-		  [colors.message.id]
-		  foreground = "yellow"
-		  [colors.message.time]
-		  foreground = "magenta"
-		  [colors.message.sender_default]
-		  foreground = "cyan"
-		  bold = true
-		  [colors.message.sender_device]
-		  foreground = "cyan"
-		  [colors.message.attachment]
-		  foreground = "red"
-		  [colors.message.link_url]
-		  foreground = "yellow"
-		  [colors.message.link_keybase]
-		  foreground = "yellow"
-		  [colors.message.reaction]
-		  foreground = "magenta"
-		  bold = true
-		  [colors.message.code]
-		  foreground = "cyan"
-		  background = "grey"
-	 [colors.feed]
-		  [colors.feed.basic]
-		  foreground = "grey"
-		  [colors.feed.error]
-		  foreground = "red"
+    [colors.message]
+        [colors.message.body]
+        foreground = "normal"
+        [colors.message.header]
+        foreground = "grey"
+        bold = true
+        [colors.message.mention]
+        foreground = "green"
+        italic = true
+        bold = true
+        [colors.message.id]
+        foreground = "yellow"
+        [colors.message.time]
+        foreground = "magenta"
+        [colors.message.sender_default]
+        foreground = "cyan"
+        bold = true
+        [colors.message.sender_device]
+        foreground = "cyan"
+        [colors.message.attachment]
+        foreground = "red"
+        [colors.message.link_url]
+        foreground = "yellow"
+        [colors.message.link_keybase]
+        foreground = "yellow"
+        [colors.message.reaction]
+        foreground = "magenta"
+        bold = true
+        [colors.message.quote]
+        foreground = "green"
+        [colors.message.code]
+        foreground = "green"
+        background = "grey"
+    [colors.feed]
+        [colors.feed.basic]
+        foreground = "grey"
+        [colors.feed.error]
+        foreground = "red"
+        [colors.feed.file]
+        foreground = "yellow"
diff --git a/main.go b/main.go
index cdf1acb..67f2a06 100644
--- a/main.go
+++ b/main.go
@@ -9,6 +9,7 @@ import (
 
 	"github.com/awesome-gocui/gocui"
 	"samhofi.us/x/keybase"
+	"unicode/utf8"
 )
 
 var (
@@ -333,6 +334,9 @@ func printErrorF(message string, parts ...StyledString) {
 func printInfo(message string) {
 	printInfoF(message)
 }
+func printInfoStyledString(message StyledString) {
+	printInfoF("$TEXT", message)
+}
 
 // this removes formatting
 func printInfoF(message string, parts ...StyledString) {
@@ -343,12 +347,12 @@ func printToView(viewName string, message string) {
 		updatingView, err := g.View(viewName)
 		if err != nil {
 			return err
-		} else {
-			if config.Basics.UnicodeEmojis {
-				message = emojiUnicodeConvert(message)
-			}
-			fmt.Fprintf(updatingView, "%s\n", message)
 		}
+
+		if config.Basics.UnicodeEmojis {
+			message = emojiUnicodeConvert(message)
+		}
+		fmt.Fprintf(updatingView, "%s\n", message)
 		return nil
 	})
 }
@@ -459,21 +463,34 @@ func populateList() {
 
 // Formatting
 func formatMessageBody(body string) StyledString {
-	output := config.Colors.Message.Body.stylize(body)
+	message := config.Colors.Message.Body.stylize(body)
 
-	output = colorReplaceMentionMe(output)
-	output = output.colorRegex(`_[^_]*_`, config.Colors.Message.Body.withItalic())
-	output = output.colorRegex(`~[^~]*~`, config.Colors.Message.Body.withStrikethrough())
-	output = output.colorRegex(`@[\w_]*(\.[\w_]+)*`, config.Colors.Message.LinkKeybase)
+	message = colorReplaceMentionMe(message)
+	message = message.colorRegex(`_[^_]*_`, config.Colors.Message.Body.withItalic())
+	message = message.colorRegex(`~[^~]*~`, config.Colors.Message.Body.withStrikethrough())
+	message = message.colorRegex(`@[\w_]*([\.#][\w_]+)*`, config.Colors.Message.LinkKeybase)
 	// TODO change how bold, italic etc works, so it uses boldOn boldOff ([1m and [22m)
-	output = output.colorRegex(`\*[^\*]*\*`, config.Colors.Message.Body.withBold())
-	output = output.replaceString("```", "\n<code>\n")
-	// TODO make background color cover whole line
-	output = output.colorRegex("<code>(.*\n)*<code>", config.Colors.Message.Code)
-	output = output.colorRegex("`[^`]*`", config.Colors.Message.Code)
+	message = message.colorRegex(`\*[^\*]*\*`, config.Colors.Message.Body.withBold())
+	message = message.colorRegex(">.*$", config.Colors.Message.Quote)
+	message = message.regexReplaceFunc("```(.*\n)*```", func(match string) string {
+		maxWidth, _ := g.Size()
+		output := "\n"
+		match = strings.Replace(strings.Replace(match, "```", "<code>", -1), "\t", "  ", -1)
+		lines := strings.Split(match, "\n")
+		for _, line := range lines {
+			maxLineLength := maxWidth/2 + maxWidth/3 - 2
+			spaces := maxLineLength - utf8.RuneCountInString(line)
+			for i := 1; spaces < 0; i++ {
+				spaces = i*maxLineLength - utf8.RuneCountInString(line)
+			}
+			output += line + strings.Repeat(" ", spaces) + "\n"
+		}
+		return config.Colors.Message.Code.stylize(output).stringFollowedByStyle(message.style)
+	})
+	message = message.colorRegex("`[^`]*`", config.Colors.Message.Code)
 	// mention URL
-	output = output.colorRegex(`(https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*))`, config.Colors.Message.LinkURL)
-	return output
+	message = message.colorRegex(`(https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*))`, config.Colors.Message.LinkURL)
+	return message
 }
 
 // TODO use this more
@@ -497,7 +514,7 @@ func cleanChannelName(c string) string {
 	return strings.Replace(newChannelName, fmt.Sprintf(",%s", k.Username), "", 1)
 }
 
-func formatMessage(api keybase.ChatAPI, formatString string) string {
+func formatMessage(api keybase.ChatAPI, formatString string) StyledString {
 	ret := config.Colors.Message.Header.stylize("")
 	msgType := api.Msg.Content.Type
 	switch msgType {
@@ -527,14 +544,14 @@ func formatMessage(api keybase.ChatAPI, formatString string) string {
 		ret = ret.replace("$DATE", date)
 		ret = ret.replace("$TEAM", channelName)
 	}
-	return ret.string()
+	return ret
 }
 func formatOutput(api keybase.ChatAPI) string {
 	format := config.Formatting.OutputFormat
 	if stream {
 		format = config.Formatting.OutputStreamFormat
 	}
-	return formatMessage(api, format)
+	return formatMessage(api, format).string()
 }
 
 // End formatting
@@ -560,7 +577,7 @@ func handleMessage(api keybase.ChatAPI) {
 						if m.Text == k.Username {
 							// We are in a team
 							if topicName != channel.TopicName {
-								printInfo(formatMessage(api, config.Formatting.OutputMentionFormat))
+								printInfoStyledString(formatMessage(api, config.Formatting.OutputMentionFormat))
 								fmt.Print("\a")
 							}
 
@@ -569,7 +586,7 @@ func handleMessage(api keybase.ChatAPI) {
 					}
 				} else {
 					if msgSender != channel.Name {
-						printInfo(formatMessage(api, config.Formatting.PMFormat))
+						printInfoStyledString(formatMessage(api, config.Formatting.PMFormat))
 						fmt.Print("\a")
 					}
 
@@ -587,7 +604,7 @@ func handleMessage(api keybase.ChatAPI) {
 			if api.Msg.Channel.MembersType == keybase.TEAM {
 				printToView("Chat", formatOutput(api))
 			} else {
-				printToView("Chat", formatMessage(api, config.Formatting.PMFormat))
+				printToView("Chat", formatMessage(api, config.Formatting.PMFormat).string())
 			}
 		}
 	} else {
diff --git a/types.go b/types.go
index 9ce3de3..1d68216 100644
--- a/types.go
+++ b/types.go
@@ -82,6 +82,7 @@ type Message struct {
 	LinkURL       Style `toml:"link_url"`
 	LinkKeybase   Style `toml:"link_keybase"`
 	Reaction      Style `toml:"reaction"`
+	Quote         Style `toml:"quote"`
 	Code          Style `toml:"code"`
 }
 
@@ -89,4 +90,5 @@ type Message struct {
 type Feed struct {
 	Basic Style `toml:"basic"`
 	Error Style `toml:"error"`
+	File  Style `toml:"file"`
 }