aboutsummaryrefslogtreecommitdiffstats
path: root/config
diff options
context:
space:
mode:
authorRobin Jarry <robin@jarry.cc>2022-11-13 18:40:11 +0100
committerRobin Jarry <robin@jarry.cc>2022-11-16 16:11:56 +0100
commitb9e5346d6a136244212c913c80a2661d69de692c (patch)
tree94ca6b4b8406f47f743edb795c3591572dda4ed6 /config
parent46e027bde57204a2383193bd35cabcd0a5dfaaf8 (diff)
downloadaerc-b9e5346d6a136244212c913c80a2661d69de692c.tar.gz
config: move [ui] parsing in separate file
The config.go file is getting too big. Move the aerc.conf [ui] section parsing logic into a dedicated ui.go file. Add a defaultUiConfig() function to also get the default configuration values in a separate file. Extract fragmented bits of code in AercConfig.parseConfig() in a new AercConfig.parseUi() function defined in ui.go. Change parseUiConfig() into a UIConfig.parse() method. Change some methods receiver args to pointers to avoid unnecessary copies. No functional change. Signed-off-by: Robin Jarry <robin@jarry.cc> Acked-by: Moritz Poldrack <moritz@poldrack.dev>
Diffstat (limited to 'config')
-rw-r--r--config/config.go321
-rw-r--r--config/ui.go340
2 files changed, 345 insertions, 316 deletions
diff --git a/config/config.go b/config/config.go
index 7a880b6d..91fd99a2 100644
--- a/config/config.go
+++ b/config/config.go
@@ -8,13 +8,10 @@ import (
"path"
"regexp"
"strings"
- "time"
"unicode"
- "github.com/gdamore/tcell/v2"
"github.com/go-ini/ini"
"github.com/google/shlex"
- "github.com/imdario/mergo"
"github.com/kyoh86/xdg"
"github.com/mitchellh/go-homedir"
@@ -28,75 +25,6 @@ type GeneralConfig struct {
UnsafeAccountsConf bool `ini:"unsafe-accounts-conf"`
}
-type UIConfig struct {
- AutoMarkRead bool `ini:"auto-mark-read"`
- IndexFormat string `ini:"index-format"`
- TimestampFormat string `ini:"timestamp-format"`
- ThisDayTimeFormat string `ini:"this-day-time-format"`
- ThisWeekTimeFormat string `ini:"this-week-time-format"`
- ThisYearTimeFormat string `ini:"this-year-time-format"`
- MessageViewTimestampFormat string `ini:"message-view-timestamp-format"`
- MessageViewThisDayTimeFormat string `ini:"message-view-this-day-time-format"`
- MessageViewThisWeekTimeFormat string `ini:"message-view-this-week-time-format"`
- MessageViewThisYearTimeFormat string `ini:"message-view-this-year-time-format"`
- ShowHeaders []string `delim:","`
- RenderAccountTabs string `ini:"render-account-tabs"`
- PinnedTabMarker string `ini:"pinned-tab-marker"`
- SidebarWidth int `ini:"sidebar-width"`
- PreviewHeight int `ini:"preview-height"`
- EmptyMessage string `ini:"empty-message"`
- EmptyDirlist string `ini:"empty-dirlist"`
- MouseEnabled bool `ini:"mouse-enabled"`
- ThreadingEnabled bool `ini:"threading-enabled"`
- ForceClientThreads bool `ini:"force-client-threads"`
- ClientThreadsDelay time.Duration `ini:"client-threads-delay"`
- FuzzyComplete bool `ini:"fuzzy-complete"`
- NewMessageBell bool `ini:"new-message-bell"`
- Spinner string `ini:"spinner"`
- SpinnerDelimiter string `ini:"spinner-delimiter"`
- IconUnencrypted string `ini:"icon-unencrypted"`
- IconEncrypted string `ini:"icon-encrypted"`
- IconSigned string `ini:"icon-signed"`
- IconSignedEncrypted string `ini:"icon-signed-encrypted"`
- IconUnknown string `ini:"icon-unknown"`
- IconInvalid string `ini:"icon-invalid"`
- DirListFormat string `ini:"dirlist-format"`
- DirListDelay time.Duration `ini:"dirlist-delay"`
- DirListTree bool `ini:"dirlist-tree"`
- DirListCollapse int `ini:"dirlist-collapse"`
- Sort []string `delim:" "`
- NextMessageOnDelete bool `ini:"next-message-on-delete"`
- CompletionDelay time.Duration `ini:"completion-delay"`
- CompletionMinChars int `ini:"completion-min-chars"`
- CompletionPopovers bool `ini:"completion-popovers"`
- StyleSetDirs []string `ini:"stylesets-dirs" delim:":"`
- StyleSetName string `ini:"styleset-name"`
- style StyleSet
- // customize border appearance
- BorderCharVertical rune `ini:"-"`
- BorderCharHorizontal rune `ini:"-"`
-
- ReverseOrder bool `ini:"reverse-msglist-order"`
- ReverseThreadOrder bool `ini:"reverse-thread-order"`
- SortThreadSiblings bool `ini:"sort-thread-siblings"`
-}
-
-type ContextType int
-
-const (
- UI_CONTEXT_FOLDER ContextType = iota
- UI_CONTEXT_ACCOUNT
- UI_CONTEXT_SUBJECT
- BIND_CONTEXT_ACCOUNT
- BIND_CONTEXT_FOLDER
-)
-
-type UIConfigContext struct {
- ContextType ContextType
- Regex *regexp.Regexp
- UiConfig UIConfig
-}
-
const (
FILTER_MIMETYPE = iota
FILTER_HEADER
@@ -326,61 +254,6 @@ func (config *AercConfig) LoadConfig(file *ini.File) error {
}
}
- if ui, err := file.GetSection("ui"); err == nil {
- if err := parseUiConfig(ui, &config.Ui); err != nil {
- 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 := parseUiConfig(uiSection, &uiSubConfig); err != nil {
- return err
- }
- contextualUi := UIConfigContext{
- UiConfig: uiSubConfig,
- }
-
- var index int
- switch {
- case strings.Contains(sectionName, "~"):
- index = strings.Index(sectionName, "~")
- regex := string(sectionName[index+1:])
- contextualUi.Regex, err = regexp.Compile(regex)
- if err != nil {
- return err
- }
- case 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
- }
- default:
- 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
@@ -396,11 +269,8 @@ func (config *AercConfig) LoadConfig(file *ini.File) error {
}
}
- // append default paths to template-dirs and styleset-dirs
+ // append default paths to template-dirs
for _, dir := range SearchDirs {
- config.Ui.StyleSetDirs = append(
- config.Ui.StyleSetDirs, path.Join(dir, "stylesets"),
- )
config.Templates.TemplateDirs = append(
config.Templates.TemplateDirs, path.Join(dir, "templates"),
)
@@ -418,82 +288,6 @@ func (config *AercConfig) LoadConfig(file *ini.File) error {
if err := templates.CheckTemplate(t.Forwards, t.TemplateDirs); err != nil {
return err
}
- if err := config.Ui.loadStyleSet(
- config.Ui.StyleSetDirs); err != nil {
- return err
- }
-
- for idx, contextualUi := range config.ContextualUis {
- if contextualUi.UiConfig.StyleSetName == "" &&
- len(contextualUi.UiConfig.StyleSetDirs) == 0 {
- continue // no need to do anything if nothing is overridden
- }
- // fill in the missing part from the base
- if contextualUi.UiConfig.StyleSetName == "" {
- config.ContextualUis[idx].UiConfig.StyleSetName = config.Ui.StyleSetName
- } else if len(contextualUi.UiConfig.StyleSetDirs) == 0 {
- config.ContextualUis[idx].UiConfig.StyleSetDirs = config.Ui.StyleSetDirs
- }
- // since at least one of them has changed, load the styleset
- if err := config.ContextualUis[idx].UiConfig.loadStyleSet(
- config.ContextualUis[idx].UiConfig.StyleSetDirs); err != nil {
- return err
- }
- }
-
- return nil
-}
-
-func parseUiConfig(section *ini.Section, config *UIConfig) error {
- if err := section.MapTo(config); err != nil {
- return err
- }
-
- if key, err := section.GetKey("border-char-vertical"); err == nil {
- chars := []rune(key.String())
- if len(chars) != 1 {
- return fmt.Errorf("%v must be one and only one character", key)
- }
- config.BorderCharVertical = chars[0]
- }
- if key, err := section.GetKey("border-char-horizontal"); err == nil {
- chars := []rune(key.String())
- if len(chars) != 1 {
- return fmt.Errorf("%v must be one and only one character", key)
- }
- config.BorderCharHorizontal = chars[0]
- }
-
- // Values with type=time.Duration must be explicitly set. If these
- // values are given a default in the struct passed to ui.MapTo, which
- // they are, a zero-value in the config won't overwrite the default.
- if key, err := section.GetKey("dirlist-delay"); err == nil {
- dur, err := key.Duration()
- if err != nil {
- return err
- }
- config.DirListDelay = dur
- }
- if key, err := section.GetKey("completion-delay"); err == nil {
- dur, err := key.Duration()
- if err != nil {
- return err
- }
- config.CompletionDelay = dur
- }
-
- if config.MessageViewTimestampFormat == "" {
- config.MessageViewTimestampFormat = config.TimestampFormat
- }
- if config.MessageViewThisDayTimeFormat == "" {
- config.MessageViewThisDayTimeFormat = config.TimestampFormat
- }
- if config.MessageViewThisWeekTimeFormat == "" {
- config.MessageViewThisWeekTimeFormat = config.TimestampFormat
- }
- if config.MessageViewThisDayTimeFormat == "" {
- config.MessageViewThisDayTimeFormat = config.TimestampFormat
- }
return nil
}
@@ -547,47 +341,7 @@ func LoadConfigFromFile(root *string, accts []string) (*AercConfig, error) {
UnsafeAccountsConf: false,
},
- Ui: UIConfig{
- AutoMarkRead: true,
- IndexFormat: "%-20.20D %-17.17n %Z %s",
- TimestampFormat: "2006-01-02 03:04 PM",
- ThisDayTimeFormat: "",
- ThisWeekTimeFormat: "",
- ThisYearTimeFormat: "",
- ShowHeaders: []string{
- "From", "To", "Cc", "Bcc", "Subject", "Date",
- },
- RenderAccountTabs: "auto",
- PinnedTabMarker: "`",
- SidebarWidth: 20,
- PreviewHeight: 12,
- EmptyMessage: "(no messages)",
- EmptyDirlist: "(no folders)",
- MouseEnabled: false,
- ClientThreadsDelay: 50 * time.Millisecond,
- NewMessageBell: true,
- FuzzyComplete: false,
- Spinner: "[..] , [..] , [..] , [..] , [..], [..] , [..] , [..] ",
- SpinnerDelimiter: ",",
- IconUnencrypted: "",
- IconSigned: "[s]",
- IconEncrypted: "[e]",
- IconSignedEncrypted: "",
- IconUnknown: "[s?]",
- IconInvalid: "[s!]",
- DirListFormat: "%n %>r",
- DirListDelay: 200 * time.Millisecond,
- NextMessageOnDelete: true,
- CompletionDelay: 250 * time.Millisecond,
- CompletionMinChars: 1,
- CompletionPopovers: true,
- StyleSetDirs: []string{},
- StyleSetName: "default",
- // border defaults
- BorderCharVertical: ' ',
- BorderCharHorizontal: ' ',
- },
-
+ Ui: defaultUiConfig(),
ContextualUis: []UIConfigContext{},
Viewer: ViewerConfig{
@@ -630,6 +384,9 @@ func LoadConfigFromFile(root *string, accts []string) (*AercConfig, error) {
if err = config.LoadConfig(file); err != nil {
return nil, err
}
+ if err := config.parseUi(file); err != nil {
+ return nil, err
+ }
if ui, err := file.GetSection("general"); err == nil {
if err := ui.MapTo(&config.General); err != nil {
@@ -641,7 +398,6 @@ func LoadConfigFromFile(root *string, accts []string) (*AercConfig, error) {
}
logging.Debugf("aerc.conf: [general] %#v", config.General)
- logging.Debugf("aerc.conf: [ui] %#v", config.Ui)
logging.Debugf("aerc.conf: [statusline] %#v", config.Statusline)
logging.Debugf("aerc.conf: [viewer] %#v", config.Viewer)
logging.Debugf("aerc.conf: [compose] %#v", config.Compose)
@@ -669,73 +425,6 @@ func parseLayout(layout string) [][]string {
return l
}
-func (ui *UIConfig) loadStyleSet(styleSetDirs []string) error {
- ui.style = NewStyleSet()
- err := ui.style.LoadStyleSet(ui.StyleSetName, styleSetDirs)
- if err != nil {
- return fmt.Errorf("Unable to load default styleset: %w", err)
- }
-
- return nil
-}
-
-func (config AercConfig) mergeContextualUi(baseUi UIConfig,
- contextType ContextType, s string,
-) UIConfig {
- for _, contextualUi := range config.ContextualUis {
- if contextualUi.ContextType != contextType {
- continue
- }
-
- if !contextualUi.Regex.Match([]byte(s)) {
- continue
- }
-
- err := mergo.Merge(&baseUi, contextualUi.UiConfig, mergo.WithOverride)
- if err != nil {
- logging.Warnf("merge ui failed: %v", err)
- }
- if contextualUi.UiConfig.StyleSetName != "" {
- baseUi.style = contextualUi.UiConfig.style
- }
- return baseUi
- }
-
- return baseUi
-}
-
-func (config AercConfig) GetUiConfig(params map[ContextType]string) *UIConfig {
- baseUi := config.Ui
-
- for k, v := range params {
- baseUi = config.mergeContextualUi(baseUi, k, v)
- }
-
- return &baseUi
-}
-
-func (config *AercConfig) GetContextualUIConfigs() []UIConfigContext {
- return config.ContextualUis
-}
-
-func (uiConfig UIConfig) GetStyle(so StyleObject) tcell.Style {
- return uiConfig.style.Get(so)
-}
-
-func (uiConfig UIConfig) GetStyleSelected(so StyleObject) tcell.Style {
- return uiConfig.style.Selected(so)
-}
-
-func (uiConfig UIConfig) GetComposedStyle(base StyleObject,
- styles []StyleObject,
-) tcell.Style {
- return uiConfig.style.Compose(base, styles)
-}
-
-func (uiConfig UIConfig) GetComposedStyleSelected(base StyleObject, styles []StyleObject) tcell.Style {
- return uiConfig.style.ComposeSelected(base, styles)
-}
-
func contains(list []string, v string) bool {
for _, item := range list {
if item == v {
diff --git a/config/ui.go b/config/ui.go
new file mode 100644
index 00000000..16a296df
--- /dev/null
+++ b/config/ui.go
@@ -0,0 +1,340 @@
+package config
+
+import (
+ "fmt"
+ "path"
+ "regexp"
+ "strings"
+ "time"
+
+ "git.sr.ht/~rjarry/aerc/logging"
+ "github.com/gdamore/tcell/v2"
+ "github.com/go-ini/ini"
+ "github.com/imdario/mergo"
+)
+
+type UIConfig struct {
+ AutoMarkRead bool `ini:"auto-mark-read"`
+ IndexFormat string `ini:"index-format"`
+ TimestampFormat string `ini:"timestamp-format"`
+ ThisDayTimeFormat string `ini:"this-day-time-format"`
+ ThisWeekTimeFormat string `ini:"this-week-time-format"`
+ ThisYearTimeFormat string `ini:"this-year-time-format"`
+ MessageViewTimestampFormat string `ini:"message-view-timestamp-format"`
+ MessageViewThisDayTimeFormat string `ini:"message-view-this-day-time-format"`
+ MessageViewThisWeekTimeFormat string `ini:"message-view-this-week-time-format"`
+ MessageViewThisYearTimeFormat string `ini:"message-view-this-year-time-format"`
+ ShowHeaders []string `delim:","`
+ RenderAccountTabs string `ini:"render-account-tabs"`
+ PinnedTabMarker string `ini:"pinned-tab-marker"`
+ SidebarWidth int `ini:"sidebar-width"`
+ PreviewHeight int `ini:"preview-height"`
+ EmptyMessage string `ini:"empty-message"`
+ EmptyDirlist string `ini:"empty-dirlist"`
+ MouseEnabled bool `ini:"mouse-enabled"`
+ ThreadingEnabled bool `ini:"threading-enabled"`
+ ForceClientThreads bool `ini:"force-client-threads"`
+ ClientThreadsDelay time.Duration `ini:"client-threads-delay"`
+ FuzzyComplete bool `ini:"fuzzy-complete"`
+ NewMessageBell bool `ini:"new-message-bell"`
+ Spinner string `ini:"spinner"`
+ SpinnerDelimiter string `ini:"spinner-delimiter"`
+ IconUnencrypted string `ini:"icon-unencrypted"`
+ IconEncrypted string `ini:"icon-encrypted"`
+ IconSigned string `ini:"icon-signed"`
+ IconSignedEncrypted string `ini:"icon-signed-encrypted"`
+ IconUnknown string `ini:"icon-unknown"`
+ IconInvalid string `ini:"icon-invalid"`
+ DirListFormat string `ini:"dirlist-format"`
+ DirListDelay time.Duration `ini:"dirlist-delay"`
+ DirListTree bool `ini:"dirlist-tree"`
+ DirListCollapse int `ini:"dirlist-collapse"`
+ Sort []string `delim:" "`
+ NextMessageOnDelete bool `ini:"next-message-on-delete"`
+ CompletionDelay time.Duration `ini:"completion-delay"`
+ CompletionMinChars int `ini:"completion-min-chars"`
+ CompletionPopovers bool `ini:"completion-popovers"`
+ StyleSetDirs []string `ini:"stylesets-dirs" delim:":"`
+ StyleSetName string `ini:"styleset-name"`
+ style StyleSet
+ // customize border appearance
+ BorderCharVertical rune `ini:"-"`
+ BorderCharHorizontal rune `ini:"-"`
+
+ ReverseOrder bool `ini:"reverse-msglist-order"`
+ ReverseThreadOrder bool `ini:"reverse-thread-order"`
+ SortThreadSiblings bool `ini:"sort-thread-siblings"`
+}
+
+type ContextType int
+
+const (
+ UI_CONTEXT_FOLDER ContextType = iota
+ UI_CONTEXT_ACCOUNT
+ UI_CONTEXT_SUBJECT
+ BIND_CONTEXT_ACCOUNT
+ BIND_CONTEXT_FOLDER
+)
+
+type UIConfigContext struct {
+ ContextType ContextType
+ Regex *regexp.Regexp
+ UiConfig UIConfig
+}
+
+func defaultUiConfig() UIConfig {
+ return UIConfig{
+ AutoMarkRead: true,
+ IndexFormat: "%-20.20D %-17.17n %Z %s",
+ TimestampFormat: "2006-01-02 03:04 PM",
+ ThisDayTimeFormat: "",
+ ThisWeekTimeFormat: "",
+ ThisYearTimeFormat: "",
+ ShowHeaders: []string{
+ "From", "To", "Cc", "Bcc", "Subject", "Date",
+ },
+ RenderAccountTabs: "auto",
+ PinnedTabMarker: "`",
+ SidebarWidth: 20,
+ PreviewHeight: 12,
+ EmptyMessage: "(no messages)",
+ EmptyDirlist: "(no folders)",
+ MouseEnabled: false,
+ ClientThreadsDelay: 50 * time.Millisecond,
+ NewMessageBell: true,
+ FuzzyComplete: false,
+ Spinner: "[..] , [..] , [..] , [..] , [..], [..] , [..] , [..] ",
+ SpinnerDelimiter: ",",
+ IconUnencrypted: "",
+ IconSigned: "[s]",
+ IconEncrypted: "[e]",
+ IconSignedEncrypted: "",
+ IconUnknown: "[s?]",
+ IconInvalid: "[s!]",
+ DirListFormat: "%n %>r",
+ DirListDelay: 200 * time.Millisecond,
+ NextMessageOnDelete: true,
+ CompletionDelay: 250 * time.Millisecond,
+ CompletionMinChars: 1,
+ CompletionPopovers: true,
+ StyleSetDirs: []string{},
+ StyleSetName: "default",
+ // border defaults
+ BorderCharVertical: ' ',
+ BorderCharHorizontal: ' ',
+ }
+}
+
+func (config *AercConfig) parseUi(file *ini.File) error {
+ if ui, err := file.GetSection("ui"); err == nil {
+ if err := config.Ui.parse(ui); err != nil {
+ 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 := uiSubConfig.parse(uiSection); err != nil {
+ return err
+ }
+ contextualUi := UIConfigContext{
+ UiConfig: uiSubConfig,
+ }
+
+ var index int
+ switch {
+ case strings.Contains(sectionName, "~"):
+ index = strings.Index(sectionName, "~")
+ regex := string(sectionName[index+1:])
+ contextualUi.Regex, err = regexp.Compile(regex)
+ if err != nil {
+ return err
+ }
+ case 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
+ }
+ default:
+ 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)
+ }
+
+ // append default paths to styleset-dirs
+ for _, dir := range SearchDirs {
+ config.Ui.StyleSetDirs = append(
+ config.Ui.StyleSetDirs, path.Join(dir, "stylesets"),
+ )
+ }
+
+ if err := config.Ui.loadStyleSet(config.Ui.StyleSetDirs); err != nil {
+ return err
+ }
+
+ for idx, contextualUi := range config.ContextualUis {
+ if contextualUi.UiConfig.StyleSetName == "" &&
+ len(contextualUi.UiConfig.StyleSetDirs) == 0 {
+ continue // no need to do anything if nothing is overridden
+ }
+ // fill in the missing part from the base
+ if contextualUi.UiConfig.StyleSetName == "" {
+ config.ContextualUis[idx].UiConfig.StyleSetName = config.Ui.StyleSetName
+ } else if len(contextualUi.UiConfig.StyleSetDirs) == 0 {
+ config.ContextualUis[idx].UiConfig.StyleSetDirs = config.Ui.StyleSetDirs
+ }
+ // since at least one of them has changed, load the styleset
+ if err := config.ContextualUis[idx].UiConfig.loadStyleSet(
+ config.ContextualUis[idx].UiConfig.StyleSetDirs); err != nil {
+ return err
+ }
+ }
+
+ logging.Debugf("aerc.conf: [ui] %#v", config.Ui)
+
+ return nil
+}
+
+func (config *UIConfig) parse(section *ini.Section) error {
+ if err := section.MapTo(config); err != nil {
+ return err
+ }
+
+ if key, err := section.GetKey("border-char-vertical"); err == nil {
+ chars := []rune(key.String())
+ if len(chars) != 1 {
+ return fmt.Errorf("%v must be one and only one character", key)
+ }
+ config.BorderCharVertical = chars[0]
+ }
+ if key, err := section.GetKey("border-char-horizontal"); err == nil {
+ chars := []rune(key.String())
+ if len(chars) != 1 {
+ return fmt.Errorf("%v must be one and only one character", key)
+ }
+ config.BorderCharHorizontal = chars[0]
+ }
+
+ // Values with type=time.Duration must be explicitly set. If these
+ // values are given a default in the struct passed to ui.MapTo, which
+ // they are, a zero-value in the config won't overwrite the default.
+ if key, err := section.GetKey("dirlist-delay"); err == nil {
+ dur, err := key.Duration()
+ if err != nil {
+ return err
+ }
+ config.DirListDelay = dur
+ }
+ if key, err := section.GetKey("completion-delay"); err == nil {
+ dur, err := key.Duration()
+ if err != nil {
+ return err
+ }
+ config.CompletionDelay = dur
+ }
+
+ if config.MessageViewTimestampFormat == "" {
+ config.MessageViewTimestampFormat = config.TimestampFormat
+ }
+ if config.MessageViewThisDayTimeFormat == "" {
+ config.MessageViewThisDayTimeFormat = config.TimestampFormat
+ }
+ if config.MessageViewThisWeekTimeFormat == "" {
+ config.MessageViewThisWeekTimeFormat = config.TimestampFormat
+ }
+ if config.MessageViewThisDayTimeFormat == "" {
+ config.MessageViewThisDayTimeFormat = config.TimestampFormat
+ }
+
+ return nil
+}
+
+func (ui *UIConfig) loadStyleSet(styleSetDirs []string) error {
+ ui.style = NewStyleSet()
+ err := ui.style.LoadStyleSet(ui.StyleSetName, styleSetDirs)
+ if err != nil {
+ return fmt.Errorf("Unable to load default styleset: %w", err)
+ }
+
+ return nil
+}
+
+func (config *AercConfig) mergeContextualUi(baseUi UIConfig,
+ contextType ContextType, s string,
+) UIConfig {
+ for _, contextualUi := range config.ContextualUis {
+ if contextualUi.ContextType != contextType {
+ continue
+ }
+
+ if !contextualUi.Regex.Match([]byte(s)) {
+ continue
+ }
+
+ err := mergo.Merge(&baseUi, contextualUi.UiConfig, mergo.WithOverride)
+ if err != nil {
+ logging.Warnf("merge ui failed: %v", err)
+ }
+ if contextualUi.UiConfig.StyleSetName != "" {
+ baseUi.style = contextualUi.UiConfig.style
+ }
+ return baseUi
+ }
+
+ return baseUi
+}
+
+func (uiConfig *UIConfig) GetStyle(so StyleObject) tcell.Style {
+ return uiConfig.style.Get(so)
+}
+
+func (uiConfig *UIConfig) GetStyleSelected(so StyleObject) tcell.Style {
+ return uiConfig.style.Selected(so)
+}
+
+func (uiConfig *UIConfig) GetComposedStyle(base StyleObject,
+ styles []StyleObject,
+) tcell.Style {
+ return uiConfig.style.Compose(base, styles)
+}
+
+func (uiConfig *UIConfig) GetComposedStyleSelected(
+ base StyleObject, styles []StyleObject,
+) tcell.Style {
+ return uiConfig.style.ComposeSelected(base, styles)
+}
+
+func (config *AercConfig) GetUiConfig(params map[ContextType]string) *UIConfig {
+ baseUi := config.Ui
+
+ for k, v := range params {
+ baseUi = config.mergeContextualUi(baseUi, k, v)
+ }
+
+ return &baseUi
+}
+
+func (config *AercConfig) GetContextualUIConfigs() []UIConfigContext {
+ return config.ContextualUis
+}