Skip to content

Commit

Permalink
Add support for multiple modifiers (Alfred 4+)
Browse files Browse the repository at this point in the history
  • Loading branch information
deanishe committed May 30, 2019
1 parent 2369d84 commit 978d1ab
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 17 deletions.
54 changes: 42 additions & 12 deletions feedback.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,18 @@ import (
"fmt"
"log"
"os"
"sort"
"strings"

"github.com/deanishe/awgo/fuzzy"
)

// ModKey is a modifier key pressed by the user to run an alternate
// item action in Alfred (in combination with ↩).
// item action in Alfred (in combination with ↩). It is passed
// to Item.NewModifier().
//
// It is passed to Item.NewModifier(). ModKeys cannot be combined:
// Alfred only permits one modifier at a time.
// Alfred 3 only permits one modifier at a time, but on Alfred 4+
// you can combine them arbitrarily.
type ModKey string

// Valid modifier keys used to specify alternate actions in Script Filters.
Expand Down Expand Up @@ -153,12 +156,15 @@ func (it *Item) Var(k, v string) *Item {

// NewModifier returns an initialised Modifier bound to this Item.
// It also populates the Modifier with any workflow variables set in the Item.
func (it *Item) NewModifier(key ModKey) *Modifier {
m, err := newModifier(key)
if err != nil {
panic(err)
}

//
// You must specify at least one modifier key. Alfred 3 only supports
// a single modifier, but Alfred 4+ allow them to be arbitrarily combined.
// Any invalid modifier keys are ignored. If you specify an unusable set of
// modifiers (i.e. they evaluate to ""), although a Modifier is returned,
// it is not retained by Item and will not be sent to Alfred. An error message
// is also logged.
func (it *Item) NewModifier(key ...ModKey) *Modifier {
m := newModifier(key...)
// Add Item variables to Modifier
if it.vars != nil {
for k, v := range it.vars {
Expand All @@ -172,6 +178,10 @@ func (it *Item) NewModifier(key ModKey) *Modifier {

// SetModifier sets a Modifier for a modifier key.
func (it *Item) SetModifier(m *Modifier) {
if m.Key == "" {
log.Printf("[ERROR] modifier has no key: %#v", m)
return
}
if it.mods == nil {
it.mods = map[ModKey]*Modifier{}
}
Expand Down Expand Up @@ -270,7 +280,8 @@ type itemText struct {
// Variables are inherited at creation time, so any Item variables you set
// after creating the Modifier are not inherited.
type Modifier struct {
// The modifier key. May be any of ValidModifiers.
// The modifier key, e.g. "cmd", "alt".
// With Alfred 4+, modifiers can be combined, e.g. "cmd+alt", "ctrl+shift+cmd"
Key ModKey
arg *string
subtitle *string
Expand All @@ -280,8 +291,27 @@ type Modifier struct {
}

// newModifier creates a Modifier, validating key.
func newModifier(key ModKey) (*Modifier, error) {
return &Modifier{Key: key, vars: map[string]string{}}, nil
func newModifier(key ...ModKey) *Modifier {
l := []string{}
for _, k := range key {
s := strings.TrimSpace(strings.ToLower(string(k)))
if s == "opt" {
s = "alt"
}
if s == "" {
continue
}
if s != "alt" && s != "cmd" && s != "ctrl" &&
s != "fn" && s != "opt" && s != "shift" {

log.Printf("[warning] ignored invalid modifier %q", k)
continue
}
l = append(l, s)
}
sort.Strings(l)
s := strings.Join(l, "+")
return &Modifier{Key: ModKey(s), vars: map[string]string{}}
}

// Arg sets the arg for the Modifier.
Expand Down
75 changes: 75 additions & 0 deletions feedback_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,81 @@ func TestModifiersInheritVars(t *testing.T) {
}
}

func TestEmptyModifiersIgnored(t *testing.T) {
t.Parallel()

fb := NewFeedback()

tests := []struct {
keys []ModKey
ok bool
}{
{[]ModKey{}, false},
{[]ModKey{""}, false},
{[]ModKey{"", ""}, false},
{[]ModKey{"rick flair"}, false},
{[]ModKey{"andre the giant", ""}, false},
{[]ModKey{"ultimate warrior", "cmd"}, true},
{[]ModKey{"ctrl", "", "giant haystacks"}, true},
}

for _, td := range tests {
it := fb.NewItem("title")
v := len(it.mods)
if v != 0 {
t.Fatalf("Unexpected modifiers: %+v", it.mods)
}
_ = it.NewModifier(td.keys...)
v = len(it.mods)
if td.ok {
if v != 1 {
t.Errorf("Good mod %+v not accepted", td.keys)
}
} else {
if v != 0 {
t.Errorf("Bad mod %+v accepted", td.keys)
}
}
}
}

func TestMultipleModifiers(t *testing.T) {
t.Parallel()

fb := NewFeedback()
it := fb.NewItem("title")

tests := []struct {
keys []ModKey
x string
}{
{[]ModKey{"cmd"}, "cmd"},
{[]ModKey{"alt"}, "alt"},
{[]ModKey{"opt"}, "alt"},
{[]ModKey{"fn"}, "fn"},
{[]ModKey{"shift"}, "shift"},
{[]ModKey{"alt", "cmd"}, "alt+cmd"},
{[]ModKey{"cmd", "alt"}, "alt+cmd"},
{[]ModKey{"cmd", "opt"}, "alt+cmd"},
{[]ModKey{"cmd", "opt", "ctrl"}, "alt+cmd+ctrl"},
{[]ModKey{"cmd", "opt", "shift"}, "alt+cmd+shift"},
// invalid keys ignored
{[]ModKey{}, ""},
{[]ModKey{""}, ""},
{[]ModKey{"shift", "cmd", ""}, "cmd+shift"},
{[]ModKey{"shift", "ctrl", "hulk hogan"}, "ctrl+shift"},
{[]ModKey{"shift", "undertaker", "cmd", ""}, "cmd+shift"},
}

for _, td := range tests {
m := it.NewModifier(td.keys...)
v := string(m.Key)
if v != td.x {
t.Errorf("Bad Modifier for %#v. Expected=%q, Got=%q", td.keys, td.x, v)
}
}
}

// TestFeedbackRerun verifies that rerun is properly set.
func TestFeedbackRerun(t *testing.T) {
t.Parallel()
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ go 1.12

require (
github.com/pkg/errors v0.8.1
golang.org/x/text v0.3.0
golang.org/x/text v0.3.2
)
5 changes: 3 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
2 changes: 1 addition & 1 deletion run-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ export alfred_workflow_data="${testdir}/data"
export alfred_workflow_cache="${testdir}/cache"

[[ $#@ -eq 0 ]] && {
pkgs=(go list ./...)
pkgs=(./...)
} || {
pkgs=($@)
}
Expand Down
2 changes: 1 addition & 1 deletion workflow.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import (
)

// AwGoVersion is the semantic version number of this library.
const AwGoVersion = "0.17.0"
const AwGoVersion = "0.18.0"

// Default Workflow settings. Can be changed with the corresponding Options.
//
Expand Down

0 comments on commit 978d1ab

Please sign in to comment.