diff options
author | Robin Jarry <robin@jarry.cc> | 2023-03-11 20:42:55 +0100 |
---|---|---|
committer | Robin Jarry <robin@jarry.cc> | 2023-03-31 21:02:23 +0200 |
commit | 2f46f64b0b0b93e99b4754a566c84a08d4563078 (patch) | |
tree | a91261379109115c6116e1106ec3e37b62eba048 /config/style.go | |
parent | 47675e80850d981b19c6fb231fbebaf5674f3682 (diff) | |
download | aerc-2f46f64b0b0b93e99b4754a566c84a08d4563078.tar.gz |
styleset: allow dynamic msglist styling
Add support for dynamic msglist*.$HEADER,$VALUE.$ATTR = $VALUE where
$VALUE can be either a fixed string or a regular expression. This is
intended as a replacement of contextual ui sections based on subject
values.
Implements: https://todo.sr.ht/~rjarry/aerc/18
Signed-off-by: Robin Jarry <robin@jarry.cc>
Tested-by: Bence Ferdinandy <bence@ferdinandy.com>
Diffstat (limited to 'config/style.go')
-rw-r--r-- | config/style.go | 112 |
1 files changed, 87 insertions, 25 deletions
diff --git a/config/style.go b/config/style.go index c8134585..79bd69d1 100644 --- a/config/style.go +++ b/config/style.go @@ -9,6 +9,7 @@ import ( "strconv" "strings" + "github.com/emersion/go-message/mail" "github.com/gdamore/tcell/v2" "github.com/go-ini/ini" "github.com/mitchellh/go-homedir" @@ -107,6 +108,9 @@ type Style struct { Reverse bool Italic bool Dim bool + header string // only for msglist + pattern string // only for msglist + re *regexp.Regexp // only for msglist } func (s Style) Get() tcell.Style { @@ -251,40 +255,64 @@ func (s Style) composeWith(styles []*Style) Style { return newStyle } +type StyleConf struct { + base Style + dynamic []Style +} + type StyleSet struct { - objects map[StyleObject]*Style - selected map[StyleObject]*Style + objects map[StyleObject]*StyleConf + selected map[StyleObject]*StyleConf user map[string]*Style path string } func NewStyleSet() StyleSet { ss := StyleSet{ - objects: make(map[StyleObject]*Style), - selected: make(map[StyleObject]*Style), + objects: make(map[StyleObject]*StyleConf), + selected: make(map[StyleObject]*StyleConf), user: make(map[string]*Style), } for _, so := range StyleNames { - ss.objects[so] = new(Style) - ss.selected[so] = new(Style) + ss.objects[so] = new(StyleConf) + ss.selected[so] = new(StyleConf) } - return ss } func (ss StyleSet) reset() { for _, so := range StyleNames { - ss.objects[so].Reset() - ss.selected[so].Reset() + ss.objects[so].base.Reset() + for _, d := range ss.objects[so].dynamic { + d.Reset() + } + ss.selected[so].base.Reset() + for _, d := range ss.selected[so].dynamic { + d.Reset() + } } } -func (ss StyleSet) Get(so StyleObject) tcell.Style { - return ss.objects[so].Get() +func (c *StyleConf) getStyle(h *mail.Header) *Style { + if h == nil { + return &c.base + } + for _, s := range c.dynamic { + val, _ := h.Text(s.header) + if s.re.MatchString(val) { + s = c.base.composeWith([]*Style{&s}) + return &s + } + } + return &c.base +} + +func (ss StyleSet) Get(so StyleObject, h *mail.Header) tcell.Style { + return ss.objects[so].getStyle(h).Get() } -func (ss StyleSet) Selected(so StyleObject) tcell.Style { - return ss.selected[so].Get() +func (ss StyleSet) Selected(so StyleObject, h *mail.Header) tcell.Style { + return ss.selected[so].getStyle(h).Get() } func (ss StyleSet) UserStyle(name string) tcell.Style { @@ -294,23 +322,25 @@ func (ss StyleSet) UserStyle(name string) tcell.Style { return tcell.StyleDefault } -func (ss StyleSet) Compose(so StyleObject, sos []StyleObject) tcell.Style { - base := *ss.objects[so] +func (ss StyleSet) Compose( + so StyleObject, sos []StyleObject, h *mail.Header, +) tcell.Style { + base := *ss.objects[so].getStyle(h) styles := make([]*Style, len(sos)) for i, so := range sos { - styles[i] = ss.objects[so] + styles[i] = ss.objects[so].getStyle(h) } return base.composeWith(styles).Get() } -func (ss StyleSet) ComposeSelected(so StyleObject, - sos []StyleObject, +func (ss StyleSet) ComposeSelected( + so StyleObject, sos []StyleObject, h *mail.Header, ) tcell.Style { - base := *ss.selected[so] + base := *ss.selected[so].getStyle(h) styles := make([]*Style, len(sos)) for i, so := range sos { - styles[i] = ss.selected[so] + styles[i] = ss.selected[so].getStyle(h) } return base.composeWith(styles).Get() @@ -386,17 +416,18 @@ func (ss *StyleSet) ParseStyleSet(file *ini.File) error { return nil } -var styleObjRe = regexp.MustCompile(`^([\w\*\?]+)(\.selected)?\.(\w+)$`) +var styleObjRe = regexp.MustCompile(`^([\w\*\?]+)(?:\.([\w-]+),(.+?))?(\.selected)?\.(\w+)$`) func (ss *StyleSet) parseKey(key *ini.Key, selected bool) error { groups := styleObjRe.FindStringSubmatch(key.Name()) if groups == nil { return errors.New("invalid style syntax: " + key.Name()) } - if groups[2] == ".selected" && !selected { + if groups[4] == ".selected" && !selected { return nil } - obj, attr := groups[1], groups[3] + obj, attr := groups[1], groups[5] + header, pattern := groups[2], groups[3] objRe, err := fnmatchToRegex(obj) if err != nil { @@ -408,12 +439,12 @@ func (ss *StyleSet) parseKey(key *ini.Key, selected bool) error { continue } if !selected { - err = ss.objects[so].Set(attr, key.Value()) + err = ss.objects[so].update(header, pattern, attr, key.Value()) if err != nil { return err } } - err = ss.selected[so].Set(attr, key.Value()) + err = ss.selected[so].update(header, pattern, attr, key.Value()) if err != nil { return err } @@ -425,6 +456,37 @@ func (ss *StyleSet) parseKey(key *ini.Key, selected bool) error { return nil } +func (c *StyleConf) update(header, pattern, attr, val string) error { + if header == "" || pattern == "" { + return (&c.base).Set(attr, val) + } + for i := range c.dynamic { + s := &c.dynamic[i] + if s.header == header && s.pattern == pattern { + return s.Set(attr, val) + } + } + if strings.HasPrefix(pattern, "~") { + pattern = pattern[1:] + } else { + pattern = "^" + regexp.QuoteMeta(pattern) + "$" + } + re, err := regexp.Compile(pattern) + if err != nil { + return err + } + var s Style + err = (&s).Set(attr, val) + if err != nil { + return err + } + s.header = header + s.pattern = pattern + s.re = re + c.dynamic = append(c.dynamic, s) + return nil +} + func (ss *StyleSet) LoadStyleSet(stylesetName string, stylesetDirs []string) error { filepath, err := findStyleSet(stylesetName, stylesetDirs) if err != nil { |