aboutsummaryrefslogtreecommitdiffstats
path: root/lib/state/renderer.go
diff options
context:
space:
mode:
authorRobin Jarry <robin@jarry.cc>2023-02-05 23:23:02 +0100
committerRobin Jarry <robin@jarry.cc>2023-02-20 14:48:42 +0100
commit6af06c9dfec03e923589d34187ba8358e3423d5c (patch)
tree3722f17464ca651ebd12d7d6d55a0e97ae72c8ec /lib/state/renderer.go
parent34db5942bd7b642107002b75de9d5d5c7fe90e4c (diff)
downloadaerc-6af06c9dfec03e923589d34187ba8358e3423d5c.tar.gz
statusline: move files to lib/state
These modules will not handle statusline rendering after next commit. Move them in lib/state to make next commit easier to review. Signed-off-by: Robin Jarry <robin@jarry.cc> Acked-by: Tim Culverhouse <tim@timculverhouse.com>
Diffstat (limited to 'lib/state/renderer.go')
-rw-r--r--lib/state/renderer.go205
1 files changed, 205 insertions, 0 deletions
diff --git a/lib/state/renderer.go b/lib/state/renderer.go
new file mode 100644
index 00000000..13e593fe
--- /dev/null
+++ b/lib/state/renderer.go
@@ -0,0 +1,205 @@
+package state
+
+import (
+ "errors"
+ "fmt"
+ "os"
+ "strings"
+ "unicode"
+
+ "git.sr.ht/~rjarry/aerc/config"
+ "github.com/mattn/go-runewidth"
+)
+
+type renderParams struct {
+ width int
+ sep string
+ acct *accountState
+ fldr *folderState
+}
+
+type renderFunc func(r renderParams) string
+
+func newRenderer() renderFunc {
+ var texter Texter
+ switch strings.ToLower(config.Statusline.DisplayMode) {
+ case "icon":
+ texter = &icon{}
+ default:
+ texter = &text{}
+ }
+
+ return renderer(texter)
+}
+
+func renderer(texter Texter) renderFunc {
+ var leftFmt, rightFmt string
+ if idx := strings.Index(config.Statusline.RenderFormat, "%>"); idx < 0 {
+ leftFmt = config.Statusline.RenderFormat
+ } else {
+ leftFmt = config.Statusline.RenderFormat[:idx]
+ rightFmt = strings.Replace(config.Statusline.RenderFormat[idx:], "%>", "", 1)
+ }
+
+ return func(r renderParams) string {
+ lfmtStr, largs, err := parseStatuslineFormat(leftFmt, texter, r)
+ if err != nil {
+ return err.Error()
+ }
+ rfmtStr, rargs, err := parseStatuslineFormat(rightFmt, texter, r)
+ if err != nil {
+ return err.Error()
+ }
+ leftText, rightText := fmt.Sprintf(lfmtStr, largs...), fmt.Sprintf(rfmtStr, rargs...)
+ return runewidth.FillRight(leftText, r.width-len(rightText)-1) + rightText
+ }
+}
+
+func connectionInfo(acct *accountState, texter Texter) (conn string) {
+ if acct.ConnActivity != "" {
+ conn += acct.ConnActivity
+ } else {
+ if acct.Connected {
+ conn += texter.Connected()
+ } else {
+ conn += texter.Disconnected()
+ }
+ }
+ return
+}
+
+func contentInfo(acct *accountState, fldr *folderState, texter Texter) []string {
+ var status []string
+ if fldr.FilterActivity != "" {
+ status = append(status, fldr.FilterActivity)
+ } else if fldr.Filter != "" {
+ status = append(status, texter.FormatFilter(fldr.Filter))
+ }
+ if fldr.Search != "" {
+ status = append(status, texter.FormatSearch(fldr.Search))
+ }
+ return status
+}
+
+func trayInfo(acct *accountState, fldr *folderState, texter Texter) []string {
+ var tray []string
+ if fldr.Sorting {
+ tray = append(tray, texter.Sorting())
+ }
+ if fldr.Threading {
+ tray = append(tray, texter.Threading())
+ }
+ if acct.Passthrough {
+ tray = append(tray, texter.Passthrough())
+ }
+ return tray
+}
+
+func parseStatuslineFormat(format string, texter Texter, r renderParams) (string, []interface{}, error) {
+ retval := make([]byte, 0, len(format))
+ var args []interface{}
+ mute := false
+
+ var c rune
+ for i, ni := 0, 0; i < len(format); {
+ ni = strings.IndexByte(format[i:], '%')
+ if ni < 0 {
+ ni = len(format)
+ retval = append(retval, []byte(format[i:ni])...)
+ break
+ }
+ ni += i + 1
+ // Check for fmt flags
+ if ni == len(format) {
+ goto handle_end_error
+ }
+ c = rune(format[ni])
+ if c == '+' || c == '-' || c == '#' || c == ' ' || c == '0' {
+ ni++
+ }
+
+ // Check for precision and width
+ if ni == len(format) {
+ goto handle_end_error
+ }
+ c = rune(format[ni])
+ for unicode.IsDigit(c) {
+ ni++
+ c = rune(format[ni])
+ }
+ if c == '.' {
+ ni++
+ c = rune(format[ni])
+ for unicode.IsDigit(c) {
+ ni++
+ c = rune(format[ni])
+ }
+ }
+
+ retval = append(retval, []byte(format[i:ni])...)
+ // Get final format verb
+ if ni == len(format) {
+ goto handle_end_error
+ }
+ c = rune(format[ni])
+ switch c {
+ case '%':
+ retval = append(retval, '%')
+ case 'a':
+ retval = append(retval, 's')
+ args = append(args, r.acct.Name)
+ case 'c':
+ retval = append(retval, 's')
+ args = append(args, connectionInfo(r.acct, texter))
+ case 'd':
+ retval = append(retval, 's')
+ args = append(args, r.fldr.Name)
+ case 'm':
+ mute = true
+ case 'S':
+ var status []string
+ if conn := connectionInfo(r.acct, texter); conn != "" {
+ status = append(status, conn)
+ }
+
+ if r.acct.Connected {
+ status = append(status, contentInfo(r.acct, r.fldr, texter)...)
+ }
+ retval = append(retval, 's')
+ args = append(args, strings.Join(status, r.sep))
+ case 'T':
+ var tray []string
+ if r.acct.Connected {
+ tray = trayInfo(r.acct, r.fldr, texter)
+ }
+ retval = append(retval, 's')
+ args = append(args, strings.Join(tray, r.sep))
+ case 'p':
+ path, err := os.Getwd()
+ if err == nil {
+ home, _ := os.UserHomeDir()
+ if strings.HasPrefix(path, home) {
+ path = strings.Replace(path, home, "~", 1)
+ }
+ retval = append(retval, 's')
+ args = append(args, path)
+ }
+ default:
+ // Just ignore it and print as is
+ // so %k in index format becomes %%k to Printf
+ retval = append(retval, '%')
+ retval = append(retval, byte(c))
+ }
+ i = ni + 1
+ }
+
+ if mute {
+ return "", nil, nil
+ }
+
+ return string(retval), args, nil
+
+handle_end_error:
+ return "", nil,
+ errors.New("reached end of string while parsing statusline format")
+}