diff options
-rw-r--r-- | commands/commands.go | 5 | ||||
-rw-r--r-- | commands/commands_test.go | 52 | ||||
-rw-r--r-- | lib/state/templates.go | 120 | ||||
-rw-r--r-- | models/templates.go | 1 | ||||
-rw-r--r-- | widgets/account.go | 6 | ||||
-rw-r--r-- | widgets/compose.go | 11 | ||||
-rw-r--r-- | widgets/dirlist.go | 6 | ||||
-rw-r--r-- | widgets/dirtree.go | 4 | ||||
-rw-r--r-- | widgets/msglist.go | 10 | ||||
-rw-r--r-- | widgets/status.go | 5 |
10 files changed, 147 insertions, 73 deletions
diff --git a/commands/commands.go b/commands/commands.go index 7dfd183b..59f87321 100644 --- a/commands/commands.go +++ b/commands/commands.go @@ -98,8 +98,7 @@ func templateData( msg, _ = acct.SelectedMessage() } - var data state.TemplateData - + data := state.NewDataSetter() data.SetAccount(cfg) data.SetFolder(folder) data.SetInfo(msg, 0, false) @@ -109,7 +108,7 @@ func templateData( }) } - return &data + return data.Data() } func (cmds *Commands) ExecuteCommand( diff --git a/commands/commands_test.go b/commands/commands_test.go index a424d16d..dccb8572 100644 --- a/commands/commands_test.go +++ b/commands/commands_test.go @@ -3,8 +3,10 @@ package commands import ( "reflect" "testing" + "time" - "git.sr.ht/~rjarry/aerc/lib/state" + "git.sr.ht/~rjarry/aerc/models" + "github.com/emersion/go-message/mail" ) func TestExecuteCommand_expand(t *testing.T) { @@ -40,7 +42,7 @@ func TestExecuteCommand_expand(t *testing.T) { }, } - data := state.TemplateData{} + var data dummyData for i, test := range tests { got, err := expand(&data, test.args) @@ -52,3 +54,49 @@ func TestExecuteCommand_expand(t *testing.T) { } } } + +// only for validation +type dummyData struct{} + +var ( + addr1 = mail.Address{Name: "John Foo", Address: "foo@bar.org"} + addr2 = mail.Address{Name: "John Bar", Address: "bar@foo.org"} +) + +func (d *dummyData) Account() string { return "work" } +func (d *dummyData) Folder() string { return "INBOX" } +func (d *dummyData) To() []*mail.Address { return []*mail.Address{&addr1} } +func (d *dummyData) Cc() []*mail.Address { return nil } +func (d *dummyData) Bcc() []*mail.Address { return nil } +func (d *dummyData) From() []*mail.Address { return []*mail.Address{&addr2} } +func (d *dummyData) Peer() []*mail.Address { return d.From() } +func (d *dummyData) ReplyTo() []*mail.Address { return nil } +func (d *dummyData) Date() time.Time { return time.Now() } +func (d *dummyData) DateAutoFormat(time.Time) string { return "" } +func (d *dummyData) Header(string) string { return "" } +func (d *dummyData) ThreadPrefix() string { return "└─>" } +func (d *dummyData) Subject() string { return "Re: [PATCH] hey" } +func (d *dummyData) SubjectBase() string { return "[PATCH] hey" } +func (d *dummyData) Number() int { return 0 } +func (d *dummyData) Labels() []string { return nil } +func (d *dummyData) Flags() []string { return nil } +func (d *dummyData) MessageId() string { return "123456789@foo.org" } +func (d *dummyData) Size() int { return 420 } +func (d *dummyData) OriginalText() string { return "Blah blah blah" } +func (d *dummyData) OriginalDate() time.Time { return time.Now() } +func (d *dummyData) OriginalFrom() []*mail.Address { return d.From() } +func (d *dummyData) OriginalMIMEType() string { return "text/plain" } +func (d *dummyData) OriginalHeader(string) string { return "" } +func (d *dummyData) Recent(...string) int { return 1 } +func (d *dummyData) Unread(...string) int { return 3 } +func (d *dummyData) Exists(...string) int { return 14 } +func (d *dummyData) RUE(...string) string { return "1/3/14" } +func (d *dummyData) Connected() bool { return false } +func (d *dummyData) ConnectionInfo() string { return "" } +func (d *dummyData) ContentInfo() string { return "" } +func (d *dummyData) StatusInfo() string { return "" } +func (d *dummyData) TrayInfo() string { return "" } +func (d *dummyData) PendingKeys() string { return "" } +func (d *dummyData) Role() string { return "inbox" } +func (d *dummyData) Style(string, string) string { return "" } +func (d *dummyData) StyleSwitch(string, ...models.Case) string { return "" } diff --git a/lib/state/templates.go b/lib/state/templates.go index e93d0bb0..b0daa2cc 100644 --- a/lib/state/templates.go +++ b/lib/state/templates.go @@ -12,7 +12,19 @@ import ( "github.com/emersion/go-message/mail" ) -type TemplateData struct { +type DataSetter interface { + Data() models.TemplateData + SetHeaders(*mail.Header, *models.OriginalMail) + SetInfo(*models.MessageInfo, int, bool) + SetThreading(string, bool) + SetAccount(*config.AccountConfig) + SetFolder(*models.Directory) + SetRUE([]string, func(string) (int, int, int)) + SetState(s *AccountState) + SetPendingKeys([]config.KeyStroke) +} + +type templateData struct { // only available when composing/replying/forwarding headers *mail.Header // only available when replying with a quote @@ -37,25 +49,35 @@ type TemplateData struct { pendingKeys []config.KeyStroke } +func NewDataSetter() DataSetter { + return &templateData{} +} + +// Data returns the template data +func (d *templateData) Data() models.TemplateData { + return d +} + // only used for compose/reply/forward -func (d *TemplateData) SetHeaders(h *mail.Header, o *models.OriginalMail) { +func (d *templateData) SetHeaders(h *mail.Header, o *models.OriginalMail) { d.headers = h d.parent = o } // only used for message list templates -func (d *TemplateData) SetInfo(info *models.MessageInfo, num int, marked bool) { +func (d *templateData) SetInfo(info *models.MessageInfo, num int, marked bool, +) { d.info = info d.msgNum = num d.marked = marked } -func (d *TemplateData) SetThreading(prefix string, same bool) { +func (d *templateData) SetThreading(prefix string, same bool) { d.threadPrefix = prefix d.threadSameSubject = same } -func (d *TemplateData) SetAccount(acct *config.AccountConfig) { +func (d *templateData) SetAccount(acct *config.AccountConfig) { d.account = acct d.myAddresses = make(map[string]bool) if acct != nil { @@ -66,49 +88,51 @@ func (d *TemplateData) SetAccount(acct *config.AccountConfig) { } } -func (d *TemplateData) SetFolder(folder *models.Directory) { +func (d *templateData) SetFolder(folder *models.Directory) { d.folder = folder } -func (d *TemplateData) SetRUE(folders []string, cb func(string) (int, int, int)) { +func (d *templateData) SetRUE(folders []string, + cb func(string) (int, int, int), +) { d.folders = folders d.getRUEcount = cb } -func (d *TemplateData) SetState(state *AccountState) { +func (d *templateData) SetState(state *AccountState) { d.state = state } -func (d *TemplateData) SetPendingKeys(keys []config.KeyStroke) { +func (d *templateData) SetPendingKeys(keys []config.KeyStroke) { d.pendingKeys = keys } -func (d *TemplateData) Account() string { +func (d *templateData) Account() string { if d.account != nil { return d.account.Name } return "" } -func (d *TemplateData) Folder() string { +func (d *templateData) Folder() string { if d.folder != nil { return d.folder.Name } return "" } -func (d *TemplateData) Role() string { +func (d *templateData) Role() string { if d.folder != nil { return string(d.folder.Role) } return "" } -func (d *TemplateData) ui() *config.UIConfig { +func (d *templateData) ui() *config.UIConfig { return config.Ui.ForAccount(d.Account()).ForFolder(d.Folder()) } -func (d *TemplateData) To() []*mail.Address { +func (d *templateData) To() []*mail.Address { var to []*mail.Address switch { case d.info != nil && d.info.Envelope != nil: @@ -119,7 +143,7 @@ func (d *TemplateData) To() []*mail.Address { return to } -func (d *TemplateData) Cc() []*mail.Address { +func (d *templateData) Cc() []*mail.Address { var cc []*mail.Address switch { case d.info != nil && d.info.Envelope != nil: @@ -130,7 +154,7 @@ func (d *TemplateData) Cc() []*mail.Address { return cc } -func (d *TemplateData) Bcc() []*mail.Address { +func (d *templateData) Bcc() []*mail.Address { var bcc []*mail.Address switch { case d.info != nil && d.info.Envelope != nil: @@ -141,7 +165,7 @@ func (d *TemplateData) Bcc() []*mail.Address { return bcc } -func (d *TemplateData) From() []*mail.Address { +func (d *templateData) From() []*mail.Address { var from []*mail.Address switch { case d.info != nil && d.info.Envelope != nil: @@ -152,7 +176,7 @@ func (d *TemplateData) From() []*mail.Address { return from } -func (d *TemplateData) Peer() []*mail.Address { +func (d *templateData) Peer() []*mail.Address { var from, to []*mail.Address switch { case d.info != nil && d.info.Envelope != nil: @@ -170,7 +194,7 @@ func (d *TemplateData) Peer() []*mail.Address { return from } -func (d *TemplateData) ReplyTo() []*mail.Address { +func (d *templateData) ReplyTo() []*mail.Address { var replyTo []*mail.Address switch { case d.info != nil && d.info.Envelope != nil: @@ -181,7 +205,7 @@ func (d *TemplateData) ReplyTo() []*mail.Address { return replyTo } -func (d *TemplateData) Date() time.Time { +func (d *templateData) Date() time.Time { var date time.Time switch { case d.info != nil && d.info.Envelope != nil: @@ -194,7 +218,7 @@ func (d *TemplateData) Date() time.Time { return date } -func (d *TemplateData) DateAutoFormat(date time.Time) string { +func (d *templateData) DateAutoFormat(date time.Time) string { if date.IsZero() { return "" } @@ -218,7 +242,7 @@ func (d *TemplateData) DateAutoFormat(date time.Time) string { return date.Format(fmt) } -func (d *TemplateData) Header(name string) string { +func (d *templateData) Header(name string) string { var h *mail.Header switch { case d.headers != nil: @@ -235,11 +259,11 @@ func (d *TemplateData) Header(name string) string { return text } -func (d *TemplateData) ThreadPrefix() string { +func (d *templateData) ThreadPrefix() string { return d.threadPrefix } -func (d *TemplateData) Subject() string { +func (d *templateData) Subject() string { var subject string switch { case d.info != nil && d.info.Envelope != nil: @@ -253,7 +277,7 @@ func (d *TemplateData) Subject() string { return subject } -func (d *TemplateData) SubjectBase() string { +func (d *templateData) SubjectBase() string { var subject string switch { case d.info != nil && d.info.Envelope != nil: @@ -265,18 +289,18 @@ func (d *TemplateData) SubjectBase() string { return base } -func (d *TemplateData) Number() int { +func (d *templateData) Number() int { return d.msgNum } -func (d *TemplateData) Labels() []string { +func (d *templateData) Labels() []string { if d.info == nil { return nil } return d.info.Labels } -func (d *TemplateData) Flags() []string { +func (d *templateData) Flags() []string { var flags []string if d.info == nil { return flags @@ -312,35 +336,35 @@ func (d *TemplateData) Flags() []string { return flags } -func (d *TemplateData) MessageId() string { +func (d *templateData) MessageId() string { if d.info == nil || d.info.Envelope == nil { return "" } return d.info.Envelope.MessageId } -func (d *TemplateData) Size() int { +func (d *templateData) Size() int { if d.info == nil || d.info.Envelope == nil { return 0 } return int(d.info.Size) } -func (d *TemplateData) OriginalText() string { +func (d *templateData) OriginalText() string { if d.parent == nil { return "" } return d.parent.Text } -func (d *TemplateData) OriginalDate() time.Time { +func (d *templateData) OriginalDate() time.Time { if d.parent == nil { return time.Time{} } return d.parent.Date } -func (d *TemplateData) OriginalFrom() []*mail.Address { +func (d *templateData) OriginalFrom() []*mail.Address { if d.parent == nil || d.parent.RFC822Headers == nil { return nil } @@ -348,14 +372,14 @@ func (d *TemplateData) OriginalFrom() []*mail.Address { return from } -func (d *TemplateData) OriginalMIMEType() string { +func (d *templateData) OriginalMIMEType() string { if d.parent == nil { return "" } return d.parent.MIMEType } -func (d *TemplateData) OriginalHeader(name string) string { +func (d *templateData) OriginalHeader(name string) string { if d.parent == nil || d.parent.RFC822Headers == nil { return "" } @@ -366,7 +390,7 @@ func (d *TemplateData) OriginalHeader(name string) string { return text } -func (d *TemplateData) rue(folders ...string) (int, int, int) { +func (d *templateData) rue(folders ...string) (int, int, int) { var recent, unread, exists int if d.getRUEcount != nil { if len(folders) == 0 { @@ -382,22 +406,22 @@ func (d *TemplateData) rue(folders ...string) (int, int, int) { return recent, unread, exists } -func (d *TemplateData) Recent(folders ...string) int { +func (d *templateData) Recent(folders ...string) int { r, _, _ := d.rue(folders...) return r } -func (d *TemplateData) Unread(folders ...string) int { +func (d *templateData) Unread(folders ...string) int { _, u, _ := d.rue(folders...) return u } -func (d *TemplateData) Exists(folders ...string) int { +func (d *templateData) Exists(folders ...string) int { _, _, e := d.rue(folders...) return e } -func (d *TemplateData) RUE(folders ...string) string { +func (d *templateData) RUE(folders ...string) string { r, u, e := d.rue(folders...) switch { case r > 0: @@ -410,14 +434,14 @@ func (d *TemplateData) RUE(folders ...string) string { return "" } -func (d *TemplateData) Connected() bool { +func (d *templateData) Connected() bool { if d.state != nil { return d.state.Connected } return false } -func (d *TemplateData) ConnectionInfo() string { +func (d *templateData) ConnectionInfo() string { switch { case d.state == nil: return "" @@ -430,7 +454,7 @@ func (d *TemplateData) ConnectionInfo() string { } } -func (d *TemplateData) ContentInfo() string { +func (d *templateData) ContentInfo() string { if d.state == nil { return "" } @@ -447,7 +471,7 @@ func (d *TemplateData) ContentInfo() string { return strings.Join(content, config.Statusline.Separator) } -func (d *TemplateData) StatusInfo() string { +func (d *templateData) StatusInfo() string { stat := d.ConnectionInfo() if content := d.ContentInfo(); content != "" { stat += config.Statusline.Separator + content @@ -455,7 +479,7 @@ func (d *TemplateData) StatusInfo() string { return stat } -func (d *TemplateData) TrayInfo() string { +func (d *templateData) TrayInfo() string { if d.state == nil { return "" } @@ -473,17 +497,17 @@ func (d *TemplateData) TrayInfo() string { return strings.Join(tray, config.Statusline.Separator) } -func (d *TemplateData) PendingKeys() string { +func (d *templateData) PendingKeys() string { return config.FormatKeyStrokes(d.pendingKeys) } -func (d *TemplateData) Style(content, name string) string { +func (d *templateData) Style(content, name string) string { cfg := config.Ui.ForAccount(d.Account()) style := cfg.GetUserStyle(name) return parse.ApplyStyle(style, content) } -func (d *TemplateData) StyleSwitch(content string, cases ...models.Case) string { +func (d *templateData) StyleSwitch(content string, cases ...models.Case) string { for _, c := range cases { if c.Matches(content) { cfg := config.Ui.ForAccount(d.Account()) diff --git a/models/templates.go b/models/templates.go index cebad5ea..f6d79c36 100644 --- a/models/templates.go +++ b/models/templates.go @@ -26,6 +26,7 @@ type TemplateData interface { Labels() []string Flags() []string MessageId() string + Role() string Size() int OriginalText() string OriginalDate() time.Time diff --git a/widgets/account.go b/widgets/account.go index e28804de..d76333d2 100644 --- a/widgets/account.go +++ b/widgets/account.go @@ -605,17 +605,17 @@ func (acct *AccountView) Vsplit(n int) error { // setTitle executes the title template and sets the tab title func (acct *AccountView) setTitle() { - var data state.TemplateData - if acct.tab == nil { return } + + data := state.NewDataSetter() data.SetAccount(acct.acct) data.SetFolder(acct.Directories().SelectedDirectory()) data.SetRUE(acct.dirlist.List(), acct.dirlist.GetRUECount) var buf bytes.Buffer - err := templates.Render(acct.uiConf.TabTitleAccount, &buf, &data) + err := templates.Render(acct.uiConf.TabTitleAccount, &buf, data.Data()) if err != nil { acct.PushError(err) return diff --git a/widgets/compose.go b/widgets/compose.go index 4735782b..ea6a50a4 100644 --- a/widgets/compose.go +++ b/widgets/compose.go @@ -96,11 +96,11 @@ func NewComposer( completer: nil, } - var data state.TemplateData + data := state.NewDataSetter() data.SetAccount(acct.acct) data.SetFolder(acct.Directories().SelectedDirectory()) data.SetHeaders(h, orig) - if err := c.AddTemplate(template, &data); err != nil { + if err := c.AddTemplate(template, data.Data()); err != nil { return nil, err } c.AddSignature() @@ -1588,8 +1588,6 @@ func (c *Composer) setTitle() { return } - var data state.TemplateData - header := c.header.Copy() // Get subject direct from the textinput subject, ok := c.editors["subject"] @@ -1599,12 +1597,15 @@ func (c *Composer) setTitle() { if header.Get("subject") == "" { header.SetSubject("New Email") } + + data := state.NewDataSetter() data.SetAccount(c.acctConfig) data.SetFolder(c.acct.Directories().SelectedDirectory()) data.SetHeaders(&header, c.parent) var buf bytes.Buffer - err := templates.Render(c.acct.UiConfig().TabTitleComposer, &buf, &data) + err := templates.Render(c.acct.UiConfig().TabTitleComposer, &buf, + data.Data()) if err != nil { c.acct.PushError(err) return diff --git a/widgets/dirlist.go b/widgets/dirlist.go index 25d1e7ff..8112ebd2 100644 --- a/widgets/dirlist.go +++ b/widgets/dirlist.go @@ -238,8 +238,8 @@ func (dirlist *DirectoryList) Draw(ctx *ui.Context) { } listCtx := ctx.Subcontext(0, 0, textWidth, ctx.Height()) - var data state.TemplateData + data := state.NewDataSetter() data.SetAccount(dirlist.acctConf) for i, name := range dirlist.dirs { @@ -254,7 +254,7 @@ func (dirlist *DirectoryList) Draw(ctx *ui.Context) { data.SetFolder(dirlist.Directory(name)) data.SetRUE([]string{name}, dirlist.GetRUECount) left, right, style := dirlist.renderDir( - name, uiConfig, &data, + name, uiConfig, data.Data(), name == dirlist.selecting, listCtx.Width(), ) listCtx.Printf(0, row, style, "%s %s", left, right) @@ -267,7 +267,7 @@ func (dirlist *DirectoryList) Draw(ctx *ui.Context) { } func (dirlist *DirectoryList) renderDir( - path string, conf *config.UIConfig, data *state.TemplateData, + path string, conf *config.UIConfig, data models.TemplateData, selected bool, width int, ) (string, string, tcell.Style) { var left, right string diff --git a/widgets/dirtree.go b/widgets/dirtree.go index 98b25850..9a33e6c3 100644 --- a/widgets/dirtree.go +++ b/widgets/dirtree.go @@ -98,8 +98,8 @@ func (dt *DirectoryTree) Draw(ctx *ui.Context) { } treeCtx := ctx.Subcontext(0, 0, textWidth, ctx.Height()) - var data state.TemplateData + data := state.NewDataSetter() data.SetAccount(dt.acctConf) n = 0 @@ -124,7 +124,7 @@ func (dt *DirectoryTree) Draw(ctx *ui.Context) { data.SetRUE([]string{path}, dt.GetRUECount) left, right, style := dt.renderDir( - path, uiConfig, &data, + path, uiConfig, data.Data(), i == dt.listIdx, treeCtx.Width(), ) diff --git a/widgets/msglist.go b/widgets/msglist.go index 1fc33279..df005431 100644 --- a/widgets/msglist.go +++ b/widgets/msglist.go @@ -91,8 +91,8 @@ func (ml *MessageList) Draw(ctx *ui.Context) { } var needsHeaders []uint32 - var data state.TemplateData + data := state.NewDataSetter() data.SetAccount(acct.acct) data.SetFolder(acct.Directories().SelectedDirectory()) @@ -171,7 +171,7 @@ func (ml *MessageList) Draw(ctx *ui.Context) { lastSubject = baseSubject prevThread = thread - if addMessage(store, thread.Uid, &table, &data, uiConfig) { + if addMessage(store, thread.Uid, &table, data, uiConfig) { break threadLoop } } @@ -183,7 +183,7 @@ func (ml *MessageList) Draw(ctx *ui.Context) { continue } uid := iter.Value().(uint32) - if addMessage(store, uid, &table, &data, uiConfig) { + if addMessage(store, uid, &table, data, uiConfig) { break } } @@ -216,7 +216,7 @@ func (ml *MessageList) Draw(ctx *ui.Context) { func addMessage( store *lib.MessageStore, uid uint32, - table *ui.Table, data *state.TemplateData, + table *ui.Table, data state.DataSetter, uiConfig *config.UIConfig, ) bool { msg := store.Messages[uid] @@ -258,7 +258,7 @@ func addMessage( for c, col := range table.Columns { var buf bytes.Buffer - err := col.Def.Template.Execute(&buf, data) + err := col.Def.Template.Execute(&buf, data.Data()) if err != nil { log.Errorf("<%s> %s", msg.Envelope.MessageId, err) cells[c] = err.Error() diff --git a/widgets/status.go b/widgets/status.go index 9f2544c7..6157dd10 100644 --- a/widgets/status.go +++ b/widgets/status.go @@ -49,7 +49,7 @@ func (status *StatusLine) Draw(ctx *ui.Context) { style := status.uiConfig().GetStyle(config.STYLE_STATUSLINE_ERROR) ctx.Printf(0, 0, style, "%s", msg) case status.aerc != nil && status.acct != nil: - var data state.TemplateData + data := state.NewDataSetter() data.SetPendingKeys(status.aerc.pendingKeys) data.SetState(&status.acct.state) data.SetAccount(status.acct.acct) @@ -66,7 +66,8 @@ func (status *StatusLine) Draw(ctx *ui.Context) { var buf bytes.Buffer cells := make([]string, len(table.Columns)) for c, col := range table.Columns { - err := templates.Render(col.Def.Template, &buf, &data) + err := templates.Render(col.Def.Template, &buf, + data.Data()) if err != nil { log.Errorf("%s", err) cells[c] = err.Error() |