~sircmpwn/aerc

This thread contains a patchset. You're looking at the original emails, but you may wish to use the patch review UI. Review patch
2 2

[PATCH v2] Contextual UI Configuration

Details
Message ID
<20200123125648.318392-1-sri@vathsan.com>
DKIM signature
missing
Download raw message
Patch: +170 -20
+ 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
---
Finaly got time to work on v2 of this patch, made changes according
to Drew's feedback from the last version.
Also added subject context to have specific index-format for patches.

Happy new year folks :D

 config/config.go      | 110 ++++++++++++++++++++++++++++++++++++++----
 doc/aerc-config.5.scd |  40 +++++++++++++++
 go.mod                |   1 +
 go.sum                |   2 +
 widgets/account.go    |  23 ++++++---
 widgets/msglist.go    |  12 +++--
 widgets/msgviewer.go  |   2 +-
 7 files changed, 170 insertions(+), 20 deletions(-)

diff --git a/config/config.go b/config/config.go
index e5f739592685..0f7771dd2c58 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"
@@ -45,6 +46,18 @@ type UIConfig struct {
	CompletionPopovers  bool          `ini:"completion-popovers"`
}

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 5dac4ac71d05..13c76b35e71c 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 824185e94345..c7839fd92a3c 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 119d317b7529..01024b4a3c23 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 404a9eaf9144..66320a3cc998 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 243c5dbcfb3c..24a994040133 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 0bfd2d85090f..93d3d89eb077 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),
			}
		},
	)
-- 
2.25.0
Details
Message ID
<C03CSEHYSPFC.QY5GEG838IVV@jupiter.local>
In-Reply-To
<20200123125648.318392-1-sri@vathsan.com> (view parent)
DKIM signature
missing
Download raw message
On Thu Jan 23, 2020 at 13:56, Srivathsan Murali wrote:
> + 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
> ---
> Finaly got time to work on v2 of this patch, made changes according
> to Drew's feedback from the last version.
> Also added subject context to have specific index-format for patches.
>
> Happy new year folks :D

Tried this out, working great for me so far! Code LGTM as well.
Details
Message ID
<C0452RS04D5S.52I8MI9J4VLH@homura>
In-Reply-To
<20200123125648.318392-1-sri@vathsan.com> (view parent)
DKIM signature
missing
Download raw message
This is awesome! Great work. Thanks!

To git.sr.ht:~sircmpwn/aerc
   aa96768..b2fa5a1  master -> master
Reply to thread Export thread (mbox)