aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSrivathsan Murali <sri@vathsan.com>2020-01-23 13:56:48 +0100
committerDrew DeVault <sir@cmpwn.com>2020-01-24 10:50:21 -0500
commitb2fa5a16f52741a6f7f6e5f33561457d702dc31d (patch)
tree3c44cd8f100e0e8c156ad2bbe17ba46fb68a2e01
parentaa967682bcdbeaa11b3e79d66b1d68b129dd4161 (diff)
downloadaerc-b2fa5a16f52741a6f7f6e5f33561457d702dc31d.tar.gz
Contextual UI Configuration
+ Adds parsing of contextual ui sections to aerc config. + Add GetUiConfig method for AercConfig that is used to get the specialized UI config. + Add UiConfig method to AccountView to get specialized UI Config. + Modifies Aerc codebase to use specialized UIConfig instead. + Adds documentation for Contextual UI Configuration
-rw-r--r--config/config.go110
-rw-r--r--doc/aerc-config.5.scd40
-rw-r--r--go.mod1
-rw-r--r--go.sum2
-rw-r--r--widgets/account.go23
-rw-r--r--widgets/msglist.go12
-rw-r--r--widgets/msgviewer.go2
7 files changed, 170 insertions, 20 deletions
diff --git a/config/config.go b/config/config.go
index fe548ffa..0b46014e 100644
--- a/config/config.go
+++ b/config/config.go
@@ -16,6 +16,7 @@ import (
"github.com/gdamore/tcell"
"github.com/go-ini/ini"
+ "github.com/imdario/mergo"
"github.com/kyoh86/xdg"
"git.sr.ht/~sircmpwn/aerc/lib/templates"
@@ -46,6 +47,18 @@ type UIConfig struct {
}
const (
+ UI_CONTEXT_FOLDER = iota
+ UI_CONTEXT_ACCOUNT
+ UI_CONTEXT_SUBJECT
+)
+
+type UIConfigContext struct {
+ ContextType int
+ Regex *regexp.Regexp
+ UiConfig UIConfig
+}
+
+const (
FILTER_MIMETYPE = iota
FILTER_HEADER
)
@@ -112,16 +125,17 @@ type TemplateConfig struct {
}
type AercConfig struct {
- Bindings BindingConfig
- Compose ComposeConfig
- Ini *ini.File `ini:"-"`
- Accounts []AccountConfig `ini:"-"`
- Filters []FilterConfig `ini:"-"`
- Viewer ViewerConfig `ini:"-"`
- Triggers TriggersConfig `ini:"-"`
- Ui UIConfig
- General GeneralConfig
- Templates TemplateConfig
+ Bindings BindingConfig
+ Compose ComposeConfig
+ Ini *ini.File `ini:"-"`
+ Accounts []AccountConfig `ini:"-"`
+ Filters []FilterConfig `ini:"-"`
+ Viewer ViewerConfig `ini:"-"`
+ Triggers TriggersConfig `ini:"-"`
+ Ui UIConfig
+ ContextualUis []UIConfigContext
+ General GeneralConfig
+ Templates TemplateConfig
}
// Input: TimestampFormat
@@ -314,6 +328,55 @@ func (config *AercConfig) LoadConfig(file *ini.File) error {
return err
}
}
+ for _, sectionName := range file.SectionStrings() {
+ if !strings.Contains(sectionName, "ui:") {
+ continue
+ }
+
+ uiSection, err := file.GetSection(sectionName)
+ if err != nil {
+ return err
+ }
+ uiSubConfig := UIConfig{}
+ if err := uiSection.MapTo(&uiSubConfig); err != nil {
+ return err
+ }
+ contextualUi :=
+ UIConfigContext{
+ UiConfig: uiSubConfig,
+ }
+
+ var index int
+ if strings.Contains(sectionName, "~") {
+ index = strings.Index(sectionName, "~")
+ regex := string(sectionName[index+1:])
+ contextualUi.Regex, err = regexp.Compile(regex)
+ if err != nil {
+ return err
+ }
+ } else if strings.Contains(sectionName, "=") {
+ index = strings.Index(sectionName, "=")
+ value := string(sectionName[index+1:])
+ contextualUi.Regex, err = regexp.Compile(regexp.QuoteMeta(value))
+ if err != nil {
+ return err
+ }
+ } else {
+ return fmt.Errorf("Invalid Ui Context regex in %s", sectionName)
+ }
+
+ switch sectionName[3:index] {
+ case "account":
+ contextualUi.ContextType = UI_CONTEXT_ACCOUNT
+ case "folder":
+ contextualUi.ContextType = UI_CONTEXT_FOLDER
+ case "subject":
+ contextualUi.ContextType = UI_CONTEXT_SUBJECT
+ default:
+ return fmt.Errorf("Unknown Contextual Ui Section: %s", sectionName)
+ }
+ config.ContextualUis = append(config.ContextualUis, contextualUi)
+ }
if triggers, err := file.GetSection("triggers"); err == nil {
if err := triggers.MapTo(&config.Triggers); err != nil {
return err
@@ -395,6 +458,8 @@ func LoadConfigFromFile(root *string, sharedir string) (*AercConfig, error) {
CompletionPopovers: true,
},
+ ContextualUis: []UIConfigContext{},
+
Viewer: ViewerConfig{
Pager: "less -R",
Alternatives: []string{"text/plain", "text/html"},
@@ -536,3 +601,28 @@ func parseLayout(layout string) [][]string {
}
return l
}
+
+func (config *AercConfig) mergeContextualUi(baseUi *UIConfig, contextType int, s string) {
+ for _, contextualUi := range config.ContextualUis {
+ if contextualUi.ContextType != contextType {
+ continue
+ }
+
+ if !contextualUi.Regex.Match([]byte(s)) {
+ continue
+ }
+
+ mergo.MergeWithOverwrite(baseUi, contextualUi.UiConfig)
+ return
+ }
+}
+
+func (config *AercConfig) GetUiConfig(params map[int]string) UIConfig {
+ baseUi := config.Ui
+
+ for k, v := range params {
+ config.mergeContextualUi(&baseUi, k, v)
+ }
+
+ return baseUi
+}
diff --git a/doc/aerc-config.5.scd b/doc/aerc-config.5.scd
index 791a39db..c747c618 100644
--- a/doc/aerc-config.5.scd
+++ b/doc/aerc-config.5.scd
@@ -168,6 +168,46 @@ These options are configured in the *[ui]* section of aerc.conf.
Default: 250ms
+## Contextual UI Configuration
+
+The UI configuration can be specialized for accounts, specific mail
+directories and message subjects. The specializations are added using
+contextual config sections based on the context.
+
+The contextual UI configuration is merged to the base UiConfig in the
+following order:
+*Base UIConfig > Account Context > Folder Context > Subject Context.*
+
+*[ui:account=<AccountName>]*
+ Adds account specific configuration with the account name.
+
+*[ui:folder=<FolderName>]*
+ Add folder specific configuration with the folder name.
+
+*[ui:folder~<Regex>]*
+ Add folder specific configuration for folders whose names match the regular
+ expression.
+
+*[ui:subject~<Regex>]*
+ Add specialized ui configuration for messages that match a given regular
+ expression.
+
+Example:
+```
+[ui:account=Work]
+sidebar-width=...
+
+[ui:folder=Sent]
+index-format=...
+
+[ui:folder~Archive/\d+/.*]
+index-format=...
+
+[ui:subject~^\[PATCH]
+index-format=...
+```
+
+
## VIEWER
These options are configured in the *[viewer]* section of aerc.conf.
diff --git a/go.mod b/go.mod
index 824185e9..c7839fd9 100644
--- a/go.mod
+++ b/go.mod
@@ -19,6 +19,7 @@ require (
github.com/golang/protobuf v1.3.2 // indirect
github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf
github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c // indirect
+ github.com/imdario/mergo v0.3.8
github.com/kyoh86/xdg v1.0.0
github.com/mattn/go-isatty v0.0.8
github.com/mattn/go-runewidth v0.0.4
diff --git a/go.sum b/go.sum
index 119d317b..01024b4a 100644
--- a/go.sum
+++ b/go.sum
@@ -52,6 +52,8 @@ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGa
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c h1:7lF+Vz0LqiRidnzC1Oq86fpX1q/iEv2KJdrCtttYjT4=
github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
+github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ=
+github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/kyoh86/xdg v1.0.0 h1:TD1layQ0epNApNwGRblnQnT3S/2UH/gCQN1cmXWotvE=
diff --git a/widgets/account.go b/widgets/account.go
index 404a9eaf..66320a3c 100644
--- a/widgets/account.go
+++ b/widgets/account.go
@@ -31,13 +31,24 @@ type AccountView struct {
worker *types.Worker
}
+func (acct *AccountView) UiConfig() config.UIConfig {
+ return acct.conf.GetUiConfig(map[int]string{
+ config.UI_CONTEXT_ACCOUNT: acct.AccountConfig().Name,
+ config.UI_CONTEXT_FOLDER: acct.Directories().Selected(),
+ })
+}
+
func NewAccountView(aerc *Aerc, conf *config.AercConfig, acct *config.AccountConfig,
logger *log.Logger, host TabHost) *AccountView {
+ acctUiConf := conf.GetUiConfig(map[int]string{
+ config.UI_CONTEXT_ACCOUNT: acct.Name,
+ })
+
grid := ui.NewGrid().Rows([]ui.GridSpec{
{ui.SIZE_WEIGHT, 1},
}).Columns([]ui.GridSpec{
- {ui.SIZE_EXACT, conf.Ui.SidebarWidth},
+ {ui.SIZE_EXACT, acctUiConf.SidebarWidth},
{ui.SIZE_WEIGHT, 1},
})
@@ -54,8 +65,8 @@ func NewAccountView(aerc *Aerc, conf *config.AercConfig, acct *config.AccountCon
}
}
- dirlist := NewDirectoryList(acct, &conf.Ui, logger, worker)
- if conf.Ui.SidebarWidth > 0 {
+ dirlist := NewDirectoryList(acct, &acctUiConf, logger, worker)
+ if acctUiConf.SidebarWidth > 0 {
grid.AddChild(ui.NewBordered(dirlist, ui.BORDER_RIGHT))
}
@@ -236,7 +247,7 @@ func (acct *AccountView) onMessage(msg types.WorkerMessage) {
acct.conf.Triggers.ExecNewEmail(acct.acct,
acct.conf, msg)
}, func() {
- if acct.conf.Ui.NewMessageBell {
+ if acct.UiConfig().NewMessageBell {
acct.host.Beep()
}
})
@@ -272,10 +283,10 @@ func (acct *AccountView) onMessage(msg types.WorkerMessage) {
}
func (acct *AccountView) getSortCriteria() []*types.SortCriterion {
- if len(acct.conf.Ui.Sort) == 0 {
+ if len(acct.UiConfig().Sort) == 0 {
return nil
}
- criteria, err := sort.GetSortCriteria(acct.conf.Ui.Sort)
+ criteria, err := sort.GetSortCriteria(acct.UiConfig().Sort)
if err != nil {
acct.aerc.PushError(" ui.sort: " + err.Error())
return nil
diff --git a/widgets/msglist.go b/widgets/msglist.go
index 243c5dbc..24a99404 100644
--- a/widgets/msglist.go
+++ b/widgets/msglist.go
@@ -106,10 +106,16 @@ func (ml *MessageList) Draw(ctx *ui.Context) {
}
ctx.Fill(0, row, ctx.Width(), 1, ' ', style)
+ uiConfig := ml.conf.GetUiConfig(map[int]string{
+ config.UI_CONTEXT_ACCOUNT: ml.aerc.SelectedAccount().AccountConfig().Name,
+ config.UI_CONTEXT_FOLDER: ml.aerc.SelectedAccount().Directories().Selected(),
+ config.UI_CONTEXT_SUBJECT: msg.Envelope.Subject,
+ })
+
fmtStr, args, err := format.ParseMessageFormat(
ml.aerc.SelectedAccount().acct.From,
- ml.conf.Ui.IndexFormat,
- ml.conf.Ui.TimestampFormat, "", i, msg, store.IsMarked(uid))
+ uiConfig.IndexFormat,
+ uiConfig.TimestampFormat, "", i, msg, store.IsMarked(uid))
if err != nil {
ctx.Printf(0, row, style, "%v", err)
} else {
@@ -265,7 +271,7 @@ func (ml *MessageList) Scroll() {
}
func (ml *MessageList) drawEmptyMessage(ctx *ui.Context) {
- msg := ml.conf.Ui.EmptyMessage
+ msg := ml.aerc.SelectedAccount().UiConfig().EmptyMessage
ctx.Printf((ctx.Width()/2)-(len(msg)/2), 0,
tcell.StyleDefault, "%s", msg)
}
diff --git a/widgets/msgviewer.go b/widgets/msgviewer.go
index 0bfd2d85..93d3d89e 100644
--- a/widgets/msgviewer.go
+++ b/widgets/msgviewer.go
@@ -63,7 +63,7 @@ func NewMessageViewer(acct *AccountView, conf *config.AercConfig,
func(header string) ui.Drawable {
return &HeaderView{
Name: header,
- Value: fmtHeader(msg, header, conf.Ui.TimestampFormat),
+ Value: fmtHeader(msg, header, acct.UiConfig().TimestampFormat),
}
},
)