aw

package module
v0.29.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 15, 2021 License: MIT Imports: 21 Imported by: 192

README

AwGo Logo

AwGo — A Go library for Alfred workflows

Build Status Go Report Card Coverage Status Go Reference

Full-featured library to build lightning-fast workflows in a jiffy.

Features

Installation & usage

Install AwGo with:

go get -u github.com/deanishe/awgo

Typically, you'd call your program's main entry point via Workflow.Run(). This way, the library will rescue any panic, log the stack trace and show an error message to the user in Alfred.

program.go:

package main

// Package is called aw
import "github.com/deanishe/awgo"

// Workflow is the main API
var wf *aw.Workflow

func init() {
    // Create a new Workflow using default settings.
    // Critical settings are provided by Alfred via environment variables,
    // so this *will* die in flames if not run in an Alfred-like environment.
    wf = aw.New()
}

// Your workflow starts here
func run() {
    // Add a "Script Filter" result
    wf.NewItem("First result!")
    // Send results to Alfred
    wf.SendFeedback()
}

func main() {
    // Wrap your entry point with Run() to catch and log panics and
    // show an error in Alfred instead of silently dying
    wf.Run(run)
}

In the Script Filter's Script box (Language = /bin/bash with input as argv):

./program "$1"

Documentation

Read the docs on pkg.go.dev.

Check out the example workflows (docs), which show how to use AwGo. Use one as a template to get your own workflow up and running quickly.

Requirements

The library (and therefore the unit tests) rely on being run in a minimally Alfred-like environment, as they pull configuration options from the environment variables set by Alfred.

This means that if you want to run AwGo-based code outside Alfred, e.g. in your shell, you must set at least the following environment variables to meaningful values, or the library will panic:

  • alfred_workflow_bundleid
  • alfred_workflow_cache
  • alfred_workflow_data

And if you're using the update API, also:

  • alfred_workflow_version
  • alfred_version (not needed for Alfred 4+)

Development

To create a sufficiently Alfred-like environment, you can source the env.sh script in the project root or run unit tests via the run-tests.sh script (which also sets up an appropriate environment before calling go test).

Licensing & thanks

This library is released under the MIT licence. It was built with neovim and GoLand by JetBrains.

The icon is based on the Go Gopher by Renee French.

Documentation

Overview

Package aw is a "plug-and-play" workflow development library/framework for Alfred 3 & 4 (https://www.alfredapp.com/). It requires Go 1.13 or later.

It provides everything you need to create a polished and blazing-fast Alfred frontend for your project.

Features

As of AwGo 0.26, all applicable features of Alfred 4.1 are supported.

The main features are:

  • Full support for Alfred 3 & 4
  • Bi-directional interface to workflow's configuration
  • Fluent API for generating Script Filter JSON
  • Fuzzy filtering
  • Simple, powerful API for caching/saving workflow data
  • Keychain API to securely store (and sync) sensitive data
  • API to call Alfred's AppleScript methods from Go code
  • Helpers to easily run scripts and script code
  • Workflow update API with built-in support for GitHub & Gitea
  • Pre-configured logging for easier debugging, with a rotating log file
  • Catches panics, logs stack trace and shows user an error message
  • "Magic" queries/actions for simplified development and user support
  • Some default icons based on macOS system icons

Usage

AwGo is an opinionated framework that expects to be used in a certain way in order to eliminate boilerplate. It *will* panic if not run in a valid, minimally Alfred-like environment. At a minimum the following environment variables should be set to meaningful values:

// Absolutely required. No ifs or buts.
alfred_workflow_bundleid

// Cache & data dirs can be set to anything, but for best
// results, point them at the same directories as Alfred uses
// Alfred 3:  ~/Library/Caches/com.runningwithcrayons.Alfred-3/Workflow Data/<bundle ID>/
// Alfred 4+: ~/Library/Caches/com.runningwithcrayons.Alfred/Workflow Data/<bundle ID>/
alfred_workflow_cache

// Alfred 3:  ~/Library/Application Support/Alfred 3/Workflow Data/<bundle ID>/
// Alfred 4+: ~/Library/Application Support/Alfred/Workflow Data/<bundle ID>/
alfred_workflow_data

// If you're using the Updater API, a semantic-ish workflow version
// must be set otherwise the Updater will panic
alfred_workflow_version

// If you're using the Alfred API and running Alfred 3, you need to
// set `alfred_version` as AwGo defaults to calling Alfred 4+
alfred_version=3

NOTE: AwGo is currently in development. The API *will* change and should not be considered stable until v1.0. Until then, be sure to pin a version using go modules or similar.

Be sure to also check out the _examples/ subdirectory, which contains some simple, but complete, workflows that demonstrate the features of AwGo and useful workflow idioms.

Typically, you'd call your program's main entry point via Workflow.Run(). This way, the library will rescue any panic, log the stack trace and show an error message to the user in Alfred.

// script_filter.go

package main

// Import name is "aw"
import "github.com/deanishe/awgo"

// aw.Workflow is the main API
var wf *aw.Workflow

func init() {
	// Create a new *Workflow using default configuration
	// (workflow settings are read from the environment variables
	// set by Alfred)
	wf = aw.New()
}

func main() {
	// Wrap your entry point with Run() to catch and log panics and
	// show an error in Alfred instead of silently dying
	wf.Run(run)
}

func run() {
	// Create a new item
	wf.NewItem("Hello World!")
	// And send the results to Alfred
	wf.SendFeedback()
}

In the Script box (Language = "/bin/bash"):

./script_filter

Script Filters

To generate results for Alfred to show in a Script Filter, use the feedback API of Workflow:

// Create new items
NewItem()
NewFileItem()
NewWarningItem()

// Sorting/filtering results
Filter()

// Send feedback to Alfred
SendFeedback()

// Warning/error calls that drop all other Items on the floor
// and send feedback immediately
Warn()
WarnEmpty()
Fatal()      // exits program
Fatalf()     // exits program
FatalError() // exits program

You can set workflow variables (via feedback) with Workflow.Var, Item.Var and Modifier.Var.

See Workflow.SendFeedback for more documentation.

Run Script actions

Alfred requires a different JSON format if you wish to set workflow variables.

Use the ArgVars (named for its equivalent element in Alfred) struct to generate output from Run Script actions.

Be sure to set TextErrors to true to prevent Workflow from generating Alfred JSON if it catches a panic:

wf.Configure(aw.TextErrors(true))

See ArgVars for more information.

Configuration

New() creates a *Workflow using the default values and workflow settings read from environment variables set by Alfred.

You can change defaults by passing one or more Options to New(). If you do not want to use Alfred's environment variables, or they aren't set (i.e. you're not running the code in Alfred), use NewFromEnv() with a custom Env implementation.

A Workflow can be re-configured later using its Configure() method.

See the documentation for Option for more information on configuring a Workflow.

Updates

AwGo can check for and install new versions of your workflow. Subpackage update provides an implementation of the Updater interface and sources to load updates from GitHub or Gitea releases, or from the URL of an Alfred `metadata.json` file.

See subpackage update and _examples/update.

Fuzzy filtering

AwGo can filter Script Filter feedback using a Sublime Text-like fuzzy matching algorithm.

Workflow.Filter() sorts feedback Items against the provided query, removing those that do not match.

See _examples/fuzzy for a basic demonstration, and _examples/bookmarks for a demonstration of implementing fuzzy.Sortable on your own structs and customising the fuzzy sort settings.

Fuzzy matching is done by package https://godoc.org/go.deanishe.net/fuzzy

Logging

AwGo automatically configures the default log package to write to STDERR (Alfred's debugger) and a log file in the workflow's cache directory.

The log file is necessary because background processes aren't connected to Alfred, so their output is only visible in the log. It is rotated when it exceeds 1 MiB in size. One previous log is kept.

AwGo detects when Alfred's debugger is open (Workflow.Debug() returns true) and in this case prepends filename:linenumber: to log messages.

Workflow settings

The Config struct (which is included in Workflow as Workflow.Config) provides an interface to the workflow's settings from the Workflow Environment Variables panel (see https://www.alfredapp.com/help/workflows/advanced/variables/#environment).

Alfred exports these settings as environment variables, and you can read them ad-hoc with the Config.Get*() methods, and save values back to Alfred/info.plist with Config.Set().

Using Config.To() and Config.From(), you can "bind" your own structs to the settings in Alfred:

// Options will be populated from workflow/environment variables
type Options struct {
	Server   string `env:"HOSTNAME"`
	Port     int    // use default: PORT
	User     string `env:"USERNAME"`
	Password string `env:"-"` // ignore
}

cfg := NewConfig()
opts := &Options{}

// Populate Options from the corresponding environment variables.
if err := cfg.To(opts); err != nil {
	// handle error
}

// Save Options back to Alfred/info.plist.
if err := cfg.From(opts); err != nil {
	// handle error
}

See the documentation for Config.To and Config.From for more information, and _examples/settings for a demo workflow based on the API.

Alfred actions

The Alfred struct provides methods for the rest of Alfred's AppleScript API. Amongst other things, you can use it to tell Alfred to open, to search for a query, to browse/action files & directories, or to run External Triggers.

See documentation of the Alfred struct for more information.

Storing data

AwGo provides a basic, but useful, API for loading and saving data. In addition to reading/writing bytes and marshalling/unmarshalling to/from JSON, the API can auto-refresh expired cache data.

See Cache and Session for the API documentation.

Workflow has three caches tied to different directories:

Workflow.Data     // Cache pointing to workflow's data directory
Workflow.Cache    // Cache pointing to workflow's cache directory
Workflow.Session  // Session pointing to cache directory tied to session ID

These all share (almost) the same API. The difference is in when the data go away.

Data saved with Session are deleted after the user closes Alfred or starts using a different workflow. The Cache directory is in a system cache directory, so may be deleted by the system or "system maintenance" tools.

The Data directory lives with Alfred's application data and would not normally be deleted.

Scripts and background jobs

Subpackage util provides several functions for running script files and snippets of AppleScript/JavaScript code. See util for documentation and examples.

AwGo offers a simple API to start/stop background processes via Workflow's RunInBackground(), IsRunning() and Kill() methods. This is useful for running checks for updates and other jobs that hit the network or take a significant amount of time to complete, allowing you to keep your Script Filters extremely responsive.

See _examples/update and _examples/workflows for demonstrations of this API.

Index

Examples

Constants

View Source
const (
	// Workflow info assigned in Alfred Preferences
	EnvVarName     = "alfred_workflow_name"     // Name of workflow
	EnvVarBundleID = "alfred_workflow_bundleid" // Bundle ID
	EnvVarVersion  = "alfred_workflow_version"  // Workflow version

	EnvVarUID = "alfred_workflow_uid" // Random UID assigned by Alfred

	// Workflow storage directories
	EnvVarCacheDir = "alfred_workflow_cache" // For temporary data
	EnvVarDataDir  = "alfred_workflow_data"  // For permanent data

	// Set to 1 when Alfred's debugger is open
	EnvVarDebug = "alfred_debug"

	// Theme info. Colours are in rgba format, e.g. "rgba(255,255,255,1.0)"
	EnvVarTheme            = "alfred_theme"                      // ID of user's selected theme
	EnvVarThemeBG          = "alfred_theme_background"           // Background colour
	EnvVarThemeSelectionBG = "alfred_theme_selection_background" // BG colour of selected item

	// Alfred info
	EnvVarAlfredVersion = "alfred_version"       // Alfred's version number
	EnvVarAlfredBuild   = "alfred_version_build" // Alfred's build number
	EnvVarPreferences   = "alfred_preferences"   // Path to "Alfred.alfredpreferences" file
	// Machine-specific hash. Machine preferences are stored in
	// Alfred.alfredpreferences/local/<hash>
	EnvVarLocalhash = "alfred_preferences_localhash"
)

Environment variables containing workflow and Alfred info.

Read the values with os.Getenv(EnvVarName) or via Config:

// Returns a string
Config.Get(EnvVarName)
// Parse string into a bool
Config.GetBool(EnvVarDebug)
View Source
const (
	ModCmd   string = "cmd"   // Alternate action for ⌘↩
	ModAlt   string = "alt"   // Alternate action for ⌥↩
	ModOpt   string = "alt"   // Synonym for ModAlt
	ModCtrl  string = "ctrl"  // Alternate action for ^↩
	ModShift string = "shift" // Alternate action for ⇧↩
	ModFn    string = "fn"    // Alternate action for fn↩
)

Valid modifier keys pressed by the user to run an alternate item action in Script Filters (in combination with ↩). Passed to Item.NewModifier().

Alfred 3 only permits one modifier at a time, but in Alfred 4+ you can combine them arbitrarily.

View Source
const (
	TypeFile = "file" // values are paths
	TypeURL  = "url"  // values are URLs
	TypeText = "text" // values are just text
)

Types understood by Alfred's `action` API call and item field. Added in Alfred 4.5.

View Source
const (
	DefaultLogPrefix   = "\U0001F37A"    // Beer mug
	DefaultMaxLogSize  = 1048576         // 1 MiB
	DefaultMaxResults  = 0               // No limit, i.e. send all results to Alfred
	DefaultSessionName = "AW_SESSION_ID" // Workflow variable session ID is stored in
	DefaultMagicPrefix = "workflow:"     // Prefix to call "magic" actions
)

Default Workflow settings. Can be changed with the corresponding Options.

See the Options and Workflow documentation for more information.

View Source
const AwGoVersion = "0.27.1"

AwGoVersion is the semantic version number of this library.

Variables

View Source
var (
	// Workflow's own icon
	IconWorkflow = &Icon{"icon.png", IconTypeImage}

	// System icons
	IconAccount   = &Icon{Value: sysIcons + "Accounts.icns"}
	IconBurn      = &Icon{Value: sysIcons + "BurningIcon.icns"}
	IconClock     = &Icon{Value: sysIcons + "Clock.icns"}
	IconColor     = &Icon{Value: sysIcons + "ProfileBackgroundColor.icns"}
	IconColour    = &Icon{Value: sysIcons + "ProfileBackgroundColor.icns"}
	IconEject     = &Icon{Value: sysIcons + "EjectMediaIcon.icns"}
	IconError     = &Icon{Value: sysIcons + "AlertStopIcon.icns"}
	IconFavorite  = &Icon{Value: sysIcons + "ToolbarFavoritesIcon.icns"}
	IconFavourite = &Icon{Value: sysIcons + "ToolbarFavoritesIcon.icns"}
	IconGroup     = &Icon{Value: sysIcons + "GroupIcon.icns"}
	IconHelp      = &Icon{Value: sysIcons + "HelpIcon.icns"}
	IconHome      = &Icon{Value: sysIcons + "HomeFolderIcon.icns"}
	IconInfo      = &Icon{Value: sysIcons + "ToolbarInfo.icns"}
	IconNetwork   = &Icon{Value: sysIcons + "GenericNetworkIcon.icns"}
	IconNote      = &Icon{Value: sysIcons + "AlertNoteIcon.icns"}
	IconSettings  = &Icon{Value: sysIcons + "ToolbarAdvanced.icns"}
	IconSwirl     = &Icon{Value: sysIcons + "ErasingIcon.icns"}
	IconSwitch    = &Icon{Value: sysIcons + "General.icns"}
	IconSync      = &Icon{Value: sysIcons + "Sync.icns"}
	IconTrash     = &Icon{Value: sysIcons + "TrashIcon.icns"}
	IconUser      = &Icon{Value: sysIcons + "UserIcon.icns"}
	IconWarning   = &Icon{Value: sysIcons + "AlertCautionIcon.icns"}
	IconWeb       = &Icon{Value: sysIcons + "BookmarkIcon.icns"}
)

Ready-to-use icons based on macOS system icons. These icons are all found in

/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources

The icons are the same as found in the Alfred-Workflow library for Python. Preview them here: http://www.deanishe.net/alfred-workflow/guide/icons.html#list-of-icons

Functions

func IsJobExists added in v0.15.0

func IsJobExists(err error) bool

IsJobExists returns true if error is of type or wraps ErrJobExists.

func NewSessionID

func NewSessionID() string

NewSessionID returns a pseudo-random string based on the current UNIX time in nanoseconds.

Types

type Alfred added in v0.15.0

type Alfred struct {
	Env
	// contains filtered or unexported fields
}

Alfred wraps Alfred's AppleScript API, allowing you to open Alfred in various modes or call External Triggers.

a := NewAlfred()

// Open Alfred
if err := a.Search(""); err != nil {
	// handle error
}

// Browse /Applications
if err := a.Browse("/Applications"); err != nil {
	// handle error
}

func NewAlfred added in v0.15.0

func NewAlfred(env ...Env) *Alfred

NewAlfred creates a new Alfred from the environment.

It accepts one optional Env argument. If an Env is passed, Alfred is initialised from that instead of the system environment.

func (*Alfred) Action added in v0.15.0

func (a *Alfred) Action(value ...string) error

Action tells Alfred to show Universal Actions for value(s). This calls Alfred.ActionAsType with an empty type.

func (*Alfred) ActionAsType added in v0.29.0

func (a *Alfred) ActionAsType(typ string, value ...string) error

ActionAsType tells Alfred to show Universal Actions for value(s). Type typ may be one of "file", "url" or "text", or an empty string to tell Alfred to guess the type.

Added in Alfred 4.5

func (*Alfred) Browse added in v0.15.0

func (a *Alfred) Browse(path string) error

Browse tells Alfred to open path in navigation mode.

func (*Alfred) ReloadWorkflow added in v0.25.0

func (a *Alfred) ReloadWorkflow(bundleID ...string) error

ReloadWorkflow tells Alfred to reload a workflow from disk.

It accepts one optional bundleID argument, which is the bundle ID of the workflow to reload. If not specified, it defaults to the current workflow's.

func (*Alfred) RunTrigger added in v0.15.0

func (a *Alfred) RunTrigger(name, query string, bundleID ...string) error

RunTrigger runs an External Trigger in the given workflow. Query may be empty.

It accepts one optional bundleID argument, which is the bundle ID of the workflow whose trigger should be run. If not specified, it defaults to the current workflow's.

func (*Alfred) Search added in v0.15.0

func (a *Alfred) Search(query string) error

Search runs Alfred with the given query. Use an empty query to just open Alfred.

func (*Alfred) SetTheme added in v0.15.0

func (a *Alfred) SetTheme(name string) error

SetTheme tells Alfred to use the specified theme.

type ArgVars

type ArgVars struct {
	// contains filtered or unexported fields
}

ArgVars lets you set workflow variables from Run Script actions. It emits the arg and variables you set in the format required by Alfred.

Use ArgVars.Send() to pass variables to downstream workflow elements.

Example
// Set workflow variables from Alfred's Run Script Action
av := NewArgVars()
av.Arg("baz")        // Set output (i.e. next action's {query}) to "baz"
av.Var("foo", "bar") // Set workflow variable "foo" to "bar"
if err := av.Send(); err != nil {
	panic(err)
}
Output:

{"alfredworkflow":{"arg":"baz","variables":{"foo":"bar"}}}

func NewArgVars

func NewArgVars() *ArgVars

NewArgVars returns an initialised ArgVars object.

func (*ArgVars) Arg

func (a *ArgVars) Arg(s ...string) *ArgVars

Arg sets the arg(s)/query to be passed to the next workflow action. Multiple values are allowed in Alfred 4.1 and later.

func (*ArgVars) MarshalJSON

func (a *ArgVars) MarshalJSON() ([]byte, error)

MarshalJSON serialises ArgVars to JSON. You probably don't need to call this: use ArgVars.Send() instead.

func (*ArgVars) Send added in v0.10.1

func (a *ArgVars) Send() error

Send outputs arg and variables to Alfred by printing a response to STDOUT.

func (*ArgVars) String

func (a *ArgVars) String() (string, error)

String returns a string representation.

If any variables are set, JSON is returned. Otherwise, a plain string is returned.

func (*ArgVars) Var

func (a *ArgVars) Var(k, v string) *ArgVars

Var sets the value of a workflow variable.

func (*ArgVars) Vars

func (a *ArgVars) Vars() map[string]string

Vars returns ArgVars' variables. NOTE: This function only returns variables you have set with ArgVars.Var() for export to Alfred during this run. To read variables from the environment, use Workflow.Config.

type Cache

type Cache struct {
	Dir string // Directory to save data in
}

Cache implements a simple store/load API, saving data to specified directory.

There are two APIs, one for storing/loading bytes and one for marshalling and storing/loading and unmarshalling JSON.

Each API has basic Store/Load functions plus a LoadOrStore function which loads cached data if these exist and aren't too old, or retrieves new data via the provided function, then caches and returns these.

The `name` parameter passed to Load*/Store* methods is used as the filename for the on-disk cache, so make sure it's filesystem-safe, and consider adding an appropriate extension to the name, e.g. use "name.txt" (or "name.json" with LoadOrStoreJSON).

Example
var (
	// Cache "key" (filename) and the value to store
	name  = "LastOpened"
	value = time.Now()
)

// Create a temporary directory for Cache to use
dir, err := ioutil.TempDir("", "awgo-demo")
if err != nil {
	panic(err)
}
defer os.RemoveAll(dir)

// Create a new cache
c := NewCache(dir)

// Cache doesn't exist yet
fmt.Println(c.Exists(name)) // -> false

// The API uses bytes
data, _ := value.MarshalText()

if err := c.Store(name, data); err != nil {
	panic(err)
}

// Cache now exists
fmt.Println(c.Exists(name)) // -> true

// Load data from cache
data, err = c.Load(name)
if err != nil {
	panic(err)
}

v2 := time.Time{}
_ = v2.UnmarshalText(data)

// Values are equal
fmt.Println(value.Equal(v2)) // -> true
Output:

false
true
true

func NewCache

func NewCache(dir string) *Cache

NewCache creates a new Cache using given directory. Directory is created if it doesn't exist. Panics if directory can't be created.

func (Cache) Age

func (c Cache) Age(name string) (time.Duration, error)

Age returns the age of the data cached at name.

func (Cache) Exists

func (c Cache) Exists(name string) bool

Exists returns true if the named cache exists.

func (Cache) Expired

func (c Cache) Expired(name string, maxAge time.Duration) bool

Expired returns true if the named cache does not exist or is older than maxAge.

func (Cache) Load

func (c Cache) Load(name string) ([]byte, error)

Load reads data saved under given name.

func (Cache) LoadJSON

func (c Cache) LoadJSON(name string, v interface{}) error

LoadJSON unmarshals named cache into v.

func (Cache) LoadOrStore

func (c Cache) LoadOrStore(name string, maxAge time.Duration, reload func() ([]byte, error)) ([]byte, error)

LoadOrStore loads data from cache if they exist and are newer than maxAge. If data do not exist or are older than maxAge, the reload function is called, and the returned data are saved to the cache and also returned.

If maxAge is 0, any cached data are always returned.

Example

LoadOrStore loads data from cache if they're fresh enough, otherwise it calls the reload function for new data (which it caches).

var (
	name        = "Expiring"
	data        = []byte("test")
	maxAge      = time.Millisecond * 1000
	start       = time.Now()
	reloadCount int
)

// Create a temporary directory for Cache to use
dir, err := ioutil.TempDir("", "awgo-demo")
if err != nil {
	panic(err)
}
defer os.RemoveAll(dir)

// Create a new cache
c := NewCache(dir)

// Called by LoadOrStore when cache is empty or has expired
reload := func() ([]byte, error) {
	// Log call count
	reloadCount++
	fmt.Printf("reload #%d\n", reloadCount)

	return data, nil
}

// Cache is empty
fmt.Println(c.Exists(name)) // -> false

out, err := c.LoadOrStore(name, maxAge, reload)
if err != nil {
	panic(err)
}

// Reload was called and cache exists
fmt.Println(c.Exists(name)) // -> true

// Value is the same
fmt.Println(string(out) == string(data)) // -> true

// Load again, this time from cache, not reload
out, err = c.LoadOrStore(name, maxAge, reload)
if err != nil {
	panic(err)
}

// Value is the same
fmt.Println(string(out) == string(data)) // -> true

// Wait for cache to expire, then try again
time.Sleep(time.Millisecond + maxAge - time.Since(start))

// reload is called again
out, err = c.LoadOrStore(name, maxAge, reload)
if err != nil {
	panic(err)
}

// Value is the same
fmt.Println(string(out) == string(data)) // -> true
Output:

false
reload #1
true
true
true
reload #2
true

func (Cache) LoadOrStoreJSON

func (c Cache) LoadOrStoreJSON(name string, maxAge time.Duration, reload func() (interface{}, error), v interface{}) error

LoadOrStoreJSON loads JSON-serialised data from cache if they exist and are newer than maxAge. If the data do not exist or are older than maxAge, the reload function is called, and the data it returns are marshalled to JSON & cached, and also unmarshalled into v.

If maxAge is 0, any cached data are loaded regardless of age.

Example

LoadOrStoreJSON marshals JSON to/from the cache.

var (
	name   = "Host"
	maxAge = time.Second * 5
)

// Create a temporary directory for Cache to use
dir, err := ioutil.TempDir("", "awgo-demo")
if err != nil {
	panic(err)
}
defer os.RemoveAll(dir)

// struct to cache
type host struct {
	Hostname string
	Port     int
}

// Called by LoadOrStoreJSON. Returns default host.
// Normally, this function would do something that takes some time, like
// fetch data from the web or an application.
reload := func() (interface{}, error) {
	fmt.Println("reload")

	return &host{
		Hostname: "localhost",
		Port:     6000,
	}, nil
}

// Create a new cache
c := NewCache(dir)

// Cache is empty
fmt.Println(c.Exists(name)) // -> false

// Populate new host from cache/reload
h := &host{}
if err := c.LoadOrStoreJSON(name, maxAge, reload, h); err != nil {
	panic(err)
}

fmt.Println(h.Hostname)
fmt.Println(h.Port)
Output:

false
reload
localhost
6000

func (Cache) Store

func (c Cache) Store(name string, data []byte) error

Store saves data under the given name. If data is nil, the cache is deleted.

func (Cache) StoreJSON

func (c Cache) StoreJSON(name string, v interface{}) error

StoreJSON serialises v to JSON and saves it to the cache. If v is nil, the cache is deleted.

type Config added in v0.15.0

type Config struct {
	Env
	// contains filtered or unexported fields
}

Config loads workflow settings from Alfred's environment variables.

The Get* methods read a variable from the environment, converting it to the desired type, and the Set() method saves a variable to info.plist.

NOTE: Because calling Alfred via AppleScript is very slow (~0.2s/call), Config users a "Doer" API for setting variables, whereby calls are collected and all executed at once when Config.Do() is called:

cfg := NewConfig()
if err := cfg.Set("key1", "value1").Set("key2", "value2").Do(); err != nil {
    // handle error
}

Finally, you can use Config.To() to populate a struct from environment variables, and Config.From() to read a struct's fields and save them to info.plist.

func NewConfig added in v0.15.0

func NewConfig(e ...Env) *Config

NewConfig creates a new Config from the environment.

It accepts one optional Env argument. If an Env is passed, Config is initialised from that instead of the system environment.

func (*Config) Do added in v0.15.0

func (cfg *Config) Do() error

Do calls Alfred and runs the accumulated actions.

Returns an error if there are no commands to run, or if the call to Alfred fails. Succeed or fail, any accumulated scripts and errors are cleared when Do() is called.

func (*Config) From added in v0.15.0

func (cfg *Config) From(v interface{}, opt ...env.DumpOption) error

From saves the fields of (tagged) struct v to the workflow's settings in Alfred. All supported and unignored fields are saved by default. The behaviour can be customised by passing in options from deanishe/go-env, such as env.IgnoreZeroValues to omit any fields set to zero values.

https://godoc.org/go.deanishe.net/env#DumpOption

func (*Config) Get added in v0.15.0

func (cfg *Config) Get(key string, fallback ...string) string

Get returns the value for envvar "key". It accepts one optional "fallback" argument. If no envvar is set, returns fallback or an empty string.

If a variable is set, but empty, its value is used.

Example

Basic usage of Config.Get. Returns an empty string if variable is unset.

// Set some test variables
_ = os.Setenv("TEST_NAME", "Bob Smith")
_ = os.Setenv("TEST_ADDRESS", "7, Dreary Lane")

// New Config from environment
cfg := NewConfig()

fmt.Println(cfg.Get("TEST_NAME"))
fmt.Println(cfg.Get("TEST_ADDRESS"))
fmt.Println(cfg.Get("TEST_NONEXISTENT")) // unset variable

// GetString is a synonym
fmt.Println(cfg.GetString("TEST_NAME"))
Output:

Bob Smith
7, Dreary Lane

Bob Smith
Example (Fallback)

The fallback value is returned if the variable is unset.

// Set some test variables
_ = os.Setenv("TEST_NAME", "Bob Smith")
_ = os.Setenv("TEST_ADDRESS", "7, Dreary Lane")
_ = os.Setenv("TEST_EMAIL", "")

// New Config from environment
cfg := NewConfig()

fmt.Println(cfg.Get("TEST_NAME", "default name"))       // fallback ignored
fmt.Println(cfg.Get("TEST_ADDRESS", "default address")) // fallback ignored
fmt.Println(cfg.Get("TEST_EMAIL", "test@example.com"))  // fallback ignored (var is empty, not unset)
fmt.Println(cfg.Get("TEST_NONEXISTENT", "hi there!"))   // unset variable
Output:

Bob Smith
7, Dreary Lane

hi there!

func (*Config) GetBool added in v0.15.0

func (cfg *Config) GetBool(key string, fallback ...bool) bool

GetBool returns the value for envvar "key" as a boolean. It accepts one optional "fallback" argument. If no envvar is set, returns fallback or false.

Values are parsed with strconv.ParseBool().

Example

Strings are parsed using strconv.ParseBool().

// Set some test variables
_ = os.Setenv("LIKE_PEAS", "t")
_ = os.Setenv("LIKE_CARROTS", "true")
_ = os.Setenv("LIKE_BEANS", "1")
_ = os.Setenv("LIKE_LIVER", "f")
_ = os.Setenv("LIKE_TOMATOES", "0")
_ = os.Setenv("LIKE_BVB", "false")
_ = os.Setenv("LIKE_BAYERN", "FALSE")

// New Config from environment
cfg := NewConfig()

// strconv.ParseBool() supports many formats
fmt.Println(cfg.GetBool("LIKE_PEAS"))
fmt.Println(cfg.GetBool("LIKE_CARROTS"))
fmt.Println(cfg.GetBool("LIKE_BEANS"))
fmt.Println(cfg.GetBool("LIKE_LIVER"))
fmt.Println(cfg.GetBool("LIKE_TOMATOES"))
fmt.Println(cfg.GetBool("LIKE_BVB"))
fmt.Println(cfg.GetBool("LIKE_BAYERN"))

// Fallback
fmt.Println(cfg.GetBool("LIKE_BEER", true))
Output:

true
true
true
false
false
false
false
true

func (*Config) GetDuration added in v0.15.0

func (cfg *Config) GetDuration(key string, fallback ...time.Duration) time.Duration

GetDuration returns the value for envvar "key" as a time.Duration. It accepts one optional "fallback" argument. If no envvar is set, returns fallback or 0.

Values are parsed with time.ParseDuration().

Example

Durations are parsed using time.ParseDuration.

// Set some test variables
_ = os.Setenv("DURATION_NAP", "20m")
_ = os.Setenv("DURATION_EGG", "5m")
_ = os.Setenv("DURATION_BIG_EGG", "")
_ = os.Setenv("DURATION_MATCH", "1.5h")

// New Config from environment
cfg := NewConfig()

// returns time.Duration
fmt.Println(cfg.GetDuration("DURATION_NAP"))
fmt.Println(cfg.GetDuration("DURATION_EGG") * 2)
// fallback with unset variable
fmt.Println(cfg.GetDuration("DURATION_POWERNAP", time.Minute*45))
// or an empty one
fmt.Println(cfg.GetDuration("DURATION_BIG_EGG", time.Minute*10))
fmt.Println(cfg.GetDuration("DURATION_MATCH").Minutes())
Output:

20m0s
10m0s
45m0s
10m0s
90

func (*Config) GetFloat added in v0.15.0

func (cfg *Config) GetFloat(key string, fallback ...float64) float64

GetFloat returns the value for envvar "key" as a float. It accepts one optional "fallback" argument. If no envvar is set, returns fallback or 0.0.

Values are parsed with strconv.ParseFloat().

Example

Strings are parsed to floats using strconv.ParseFloat().

// Set some test variables
_ = os.Setenv("TOTAL_SCORE", "172.3")
_ = os.Setenv("AVERAGE_SCORE", "7.54")

// New Config from environment
cfg := NewConfig()

fmt.Printf("%0.2f\n", cfg.GetFloat("TOTAL_SCORE"))
fmt.Printf("%0.1f\n", cfg.GetFloat("AVERAGE_SCORE"))
fmt.Println(cfg.GetFloat("NON_EXISTENT_SCORE", 120.5))
Output:

172.30
7.5
120.5

func (*Config) GetInt added in v0.15.0

func (cfg *Config) GetInt(key string, fallback ...int) int

GetInt returns the value for envvar "key" as an int. It accepts one optional "fallback" argument. If no envvar is set, returns fallback or 0.

Values are parsed with strconv.ParseInt(). If strconv.ParseInt() fails, tries to parse the number with strconv.ParseFloat() and truncate it to an int.

Example

Getting int values with and without fallbacks.

// Set some test variables
_ = os.Setenv("PORT", "3000")
_ = os.Setenv("PING_INTERVAL", "")

// New Config from environment
cfg := NewConfig()

fmt.Println(cfg.GetInt("PORT"))
fmt.Println(cfg.GetInt("PORT", 5000))        // fallback is ignored
fmt.Println(cfg.GetInt("PING_INTERVAL"))     // returns zero value
fmt.Println(cfg.GetInt("PING_INTERVAL", 60)) // returns fallback
Output:

3000
3000
0
60

func (*Config) GetString added in v0.15.0

func (cfg *Config) GetString(key string, fallback ...string) string

GetString is a synonym for Get.

func (*Config) Set added in v0.15.0

func (cfg *Config) Set(key, value string, export bool, bundleID ...string) *Config

Set saves a workflow variable to info.plist.

It accepts one optional bundleID argument, which is the bundle ID of the workflow whose configuration should be changed. If not specified, it defaults to the current workflow's.

func (*Config) To added in v0.15.0

func (cfg *Config) To(v interface{}) error

To populates (tagged) struct v with values from the environment.

Example

Populate a struct from workflow/environment variables. See EnvVarForField for information on how fields are mapped to environment variables if no variable name is specified using an `env:"name"` tag.

// Set some test values
_ = os.Setenv("USERNAME", "dave")
_ = os.Setenv("API_KEY", "hunter2")
_ = os.Setenv("INTERVAL", "5m")
_ = os.Setenv("FORCE", "1")

// Program settings to load from env
type Settings struct {
	Username       string
	APIKey         string
	UpdateInterval time.Duration `env:"INTERVAL"`
	Force          bool
}

var (
	s   = &Settings{}
	cfg = NewConfig()
)

// Populate Settings from workflow/environment variables
if err := cfg.To(s); err != nil {
	panic(err)
}

fmt.Println(s.Username)
fmt.Println(s.APIKey)
fmt.Println(s.UpdateInterval)
fmt.Println(s.Force)
Output:

dave
hunter2
5m0s
true

func (*Config) Unset added in v0.15.0

func (cfg *Config) Unset(key string, bundleID ...string) *Config

Unset removes a workflow variable from info.plist.

It accepts one optional bundleID argument, which is the bundle ID of the workflow whose configuration should be changed. If not specified, it defaults to the current workflow's.

type Env added in v0.15.0

type Env interface {
	// Lookup retrieves the value of the variable named by key.
	//
	// It follows the same semantics as os.LookupEnv(). If a variable
	// is unset, the boolean will be false. If a variable is set, the
	// boolean will be true, but the variable may still be an empty
	// string.
	Lookup(key string) (string, bool)
}

Env is the data source for configuration lookups.

Pass a custom implementation to NewFromEnv() to provide a custom source for the required workflow configuration settings.

As an absolute minimum, the following variables must be set:

alfred_workflow_bundleid
alfred_workflow_cache
alfred_workflow_data

See EnvVar* consts for all variables set by Alfred.

type ErrJobExists added in v0.15.0

type ErrJobExists struct {
	Name string // Name of the job
	Pid  int    // PID of the running job
}

ErrJobExists is the error returned by RunInBackground if a job with the given name is already running.

func (ErrJobExists) Error added in v0.15.0

func (err ErrJobExists) Error() string

Error implements error interface.

func (ErrJobExists) Is added in v0.25.0

func (err ErrJobExists) Is(target error) bool

Is returns true if target is of type ErrJobExists.

type Feedback

type Feedback struct {
	Items  []*Item // The results to be sent to Alfred.
	NoUIDs bool    // If true, suppress Item UIDs.
	// contains filtered or unexported fields
}

Feedback represents the results for an Alfred Script Filter.

Normally, you won't use this struct directly, but via the Workflow methods NewItem(), SendFeedback(), etc. It is important to use the constructor functions for Feedback, Item and Modifier structs so they are properly initialised and bound to their parent.

func NewFeedback

func NewFeedback() *Feedback

NewFeedback creates a new, initialised Feedback struct.

func (*Feedback) Clear

func (fb *Feedback) Clear()

Clear removes any items.

func (*Feedback) Filter

func (fb *Feedback) Filter(query string, opts ...fuzzy.Option) []*fuzzy.Result

Filter fuzzy-sorts Items against query and deletes Items that don't match. It returns a slice of Result structs, which contain the results of the fuzzy sorting.

func (*Feedback) IsEmpty

func (fb *Feedback) IsEmpty() bool

IsEmpty returns true if Feedback contains no items.

func (*Feedback) Keywords added in v0.15.0

func (fb *Feedback) Keywords(i int) string

Keywords implements fuzzy.Sortable.

Returns the match or title field for Item i.

func (*Feedback) Len

func (fb *Feedback) Len() int

Len implements sort.Interface.

func (*Feedback) Less

func (fb *Feedback) Less(i, j int) bool

Less implements sort.Interface.

func (*Feedback) MarshalJSON

func (fb *Feedback) MarshalJSON() ([]byte, error)

MarshalJSON serializes Feedback to Alfred's JSON format. You shouldn't need to call this: use Send() instead.

func (*Feedback) NewItem

func (fb *Feedback) NewItem(title string) *Item

NewItem adds a new Item and returns a pointer to it.

The Item inherits any workflow variables set on the Feedback parent at time of creation.

func (*Feedback) Rerun

func (fb *Feedback) Rerun(secs float64) *Feedback

Rerun tells Alfred to re-run the Script Filter after `secs` seconds.

func (*Feedback) Send

func (fb *Feedback) Send() error

Send generates JSON from this struct and sends it to Alfred (by writing the JSON to STDOUT).

You shouldn't need to call this directly: use SendFeedback() instead.

func (*Feedback) Sort

func (fb *Feedback) Sort(query string, opts ...fuzzy.Option) []*fuzzy.Result

Sort sorts Items against query. Uses a fuzzy.Sorter with the specified options.

func (*Feedback) Swap

func (fb *Feedback) Swap(i, j int)

Swap implements sort.Interface.

func (*Feedback) Var

func (fb *Feedback) Var(k, v string) *Feedback

Var sets an Alfred variable for subsequent workflow elements.

func (*Feedback) Vars

func (fb *Feedback) Vars() map[string]string

Vars returns the Feedback's workflow variables.

type Icon

type Icon struct {
	Value string   `json:"path"`           // Path or UTI
	Type  IconType `json:"type,omitempty"` // "fileicon", "filetype" or ""
}

Icon represents the icon for an Item.

Alfred can show icons based on image files, UTIs (e.g. "public.folder") or can use the icon of a specific file (e.g. "/Applications/Safari.app" to use Safari's icon).

Type = "" (the default) will treat Value as the path to an image file. Alfred supports (at least) PNG, ICNS, JPG, GIF.

Type = IconTypeFileIcon will treat Value as the path to a file or directory and use that file's icon, e.g:

icon := &aw.Icon{"/Applications/Mail.app", aw.IconTypeFileIcon}

will display Mail.app's icon.

Type = IconTypeFileType will treat Value as a UTI, such as "public.movie" or "com.microsoft.word.doc". UTIs are useful when you don't have a local path to point to.

You can find out the UTI of a filetype by dragging one of the files to a File Filter's File Types list in Alfred, or in a shell with:

mdls -name kMDItemContentType -raw /path/to/the/file

This will only work on Spotlight-indexed files.

type IconType added in v0.13.1

type IconType string

IconType specifies the type of an aw.Icon struct. It can be an image file, the icon of a file, e.g. an application's icon, or the icon for a UTI.

const (
	// Indicates that Icon.Value is the path to an image file that should
	// be used as the Item's icon.
	IconTypeImage IconType = ""
	// Indicates that Icon.Value points to an object whose icon should be show
	// in Alfred, e.g. combine with "/Applications/Safari.app" to show Safari's icon.
	IconTypeFileIcon IconType = "fileicon"
	// Indicates that Icon.Value is a UTI, e.g. "public.folder",
	// which will give you the icon for a folder.
	IconTypeFileType IconType = "filetype"
)

Valid icon types.

type Item

type Item struct {
	// contains filtered or unexported fields
}

Item is a single Alfred Script Filter result. Together with Feedback & Modifier, Item generates Script Filter feedback for Alfred.

Create Items via NewItem(), so they are bound to their parent Feedback.

func (*Item) Action added in v0.29.0

func (it *Item) Action(value ...string) *Item

Action sets the value(s) to be passed to Alfred's Universal Actions if the user actions this item. Alfred will auto-detect the type of the value(s).

Added in Alfred 4.5.

func (*Item) ActionForType added in v0.29.0

func (it *Item) ActionForType(typ string, value ...string) *Item

ActionForType sets the value(s) to be passed to Alfred's Universal Actions if the user actions this item. Type may be one of "file", "url" or "text".

Added in Alfred 4.5.

func (*Item) Alt added in v0.16.0

func (it *Item) Alt() *Modifier

Alt returns an initialised Modifier bound to this Item and the ALT/OPT (⌥) key.

func (*Item) Arg

func (it *Item) Arg(s ...string) *Item

Arg sets Item's arg, the value(s) passed as {query} to the next workflow action. Multiple values are allowed in Alfred 4.1 and later.

func (*Item) Autocomplete

func (it *Item) Autocomplete(s string) *Item

Autocomplete sets what Alfred's query expands to when the user TABs result. (or hits RETURN on a result where valid is false)

func (*Item) Cmd added in v0.16.0

func (it *Item) Cmd() *Modifier

Cmd returns an initialised Modifier bound to this Item and the CMD (⌘) key.

func (*Item) Copytext

func (it *Item) Copytext(s string) *Item

Copytext is what CMD+C should copy instead of Arg (the default).

func (*Item) Ctrl added in v0.16.0

func (it *Item) Ctrl() *Modifier

Ctrl returns an initialised Modifier bound to this Item and the CTRL (^) key.

func (*Item) Fn added in v0.16.0

func (it *Item) Fn() *Modifier

Fn returns an initialised Modifier bound to this Item and the fn key.

func (*Item) Icon

func (it *Item) Icon(icon *Icon) *Item

Icon sets the icon for the Item. Can point to an image file, a filepath of a file whose icon should be used, or a UTI.

See the documentation for Icon for more details.

func (*Item) IsFile

func (it *Item) IsFile(b bool) *Item

IsFile tells Alfred that this Item is a file, i.e. Arg is a path and Alfred's File Actions should be made available.

func (*Item) Largetype

func (it *Item) Largetype(s string) *Item

Largetype is what is shown in Alfred's Large Text window on CMD+L instead of Arg (the default).

func (*Item) MarshalJSON

func (it *Item) MarshalJSON() ([]byte, error)

MarshalJSON serializes Item to Alfred's JSON format. You shouldn't need to call this directly: use SendFeedback() instead.

func (*Item) Match added in v0.10.1

func (it *Item) Match(s string) *Item

Match sets Item's match field for filtering. If present, this field is preferred over the item's title for fuzzy sorting via Feedback, and by Alfred's "Alfred filters results" feature.

func (*Item) NewModifier

func (it *Item) NewModifier(key ...string) *Modifier

NewModifier returns an initialised Modifier bound to this Item. It also populates the Modifier with any workflow variables set in the Item.

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 (*Item) Opt added in v0.16.0

func (it *Item) Opt() *Modifier

Opt is a synonym for Alt().

func (*Item) Quicklook added in v0.13.1

func (it *Item) Quicklook(s string) *Item

Quicklook is a path or URL shown in a macOS Quicklook window on SHIFT or CMD+Y.

func (*Item) SetModifier

func (it *Item) SetModifier(m *Modifier)

SetModifier sets a Modifier for a modifier key.

func (*Item) Shift added in v0.16.0

func (it *Item) Shift() *Modifier

Shift returns an initialised Modifier bound to this Item and the SHIFT (⇧) key.

func (*Item) Subtitle

func (it *Item) Subtitle(s string) *Item

Subtitle sets the subtitle of the item in Alfred's results.

func (*Item) Title

func (it *Item) Title(s string) *Item

Title sets the title of the item in Alfred's results.

func (*Item) UID

func (it *Item) UID(s string) *Item

UID sets Item's unique ID, which is used by Alfred to remember your choices. Use a blank string to force results to appear in the order you add them.

You can also use the SuppressUIDs() Option to (temporarily) suppress output of UIDs.

func (*Item) Valid

func (it *Item) Valid(b bool) *Item

Valid tells Alfred whether the result is "actionable", i.e. ENTER will pass Arg to subsequent action.

func (*Item) Var

func (it *Item) Var(k, v string) *Item

Var sets an Alfred variable for subsequent workflow elements.

func (*Item) Vars

func (it *Item) Vars() map[string]string

Vars returns the Item's workflow variables.

type MagicAction

type MagicAction interface {
	// Keyword is what the user must enter to run the action after
	// AwGo has recognised the magic prefix. So if the prefix is
	// "workflow:" (the default), a user must enter the query
	// "workflow:<keyword>" to execute this action.
	Keyword() string

	// Description is shown when a user has entered "magic" mode, but
	// the query does not yet match a keyword.
	Description() string

	// RunText is sent to Alfred and written to the log file &
	// debugger when the action is run.
	RunText() string

	// Run is called when the Magic Action is triggered.
	Run() error
}

MagicAction is a command that is called directly by AwGo (i.e. your workflow code is not run) if its keyword is passed in a user query.

To use Magic Actions, it's imperative that your workflow call Workflow.Args().

Calls to Workflow.Args() check the workflow's arguments (os.Args[1:]) for the magic prefix ("workflow:" by default), and hijack control of the workflow if found.

If an exact keyword match is found (e.g. "workflow:log"), the corresponding action is executed, and the workflow exits.

If no exact match is found, AwGo runs a Script Filter for the user to select an action. Hitting TAB or RETURN on an item will run it.

Magic Actions are mainly aimed at making debugging and supporting users easier (via the built-in actions), but they also provide a simple way to integrate your own commands that don't need a "real" UI.

For example, setting an Updater on Workflow adds an "update" command that checks for & installs a new version of the workflow.

Defaults

There are several built-in magic actions, which are registered by default:

<prefix>log         Open workflow's log file in the default app (usually
                    Console).
<prefix>data        Open workflow's data directory in the default app
                    (usually Finder).
<prefix>cache       Open workflow's data directory in the default app
                    (usually Finder).
<prefix>deldata     Delete everything in the workflow's data directory.
<prefix>delcache    Delete everything in the workflow's cache directory.
<prefix>reset       Delete everything in the workflow's data and cache directories.
<prefix>help        Open help URL in default browser.
                    Only registered if you have set a HelpURL.
<prefix>update      Check for updates and install a newer version of the
                    workflow if available.
                    Only registered if you have configured an Updater.

Custom Actions

To add custom magicActions, you must register them with your Workflow *before* you call Workflow.Args()

To do this, configure Workflow with the AddMagic option.

type Modifier

type Modifier struct {
	// The modifier key, e.g. "cmd", "alt".
	// With Alfred 4+, modifiers can be combined, e.g. "cmd+alt", "ctrl+shift+cmd"
	Key string
	// contains filtered or unexported fields
}

Modifier encapsulates alterations to Item when a modifier key is held when the user actions the item.

Create new Modifiers via Item.NewModifier(). This binds the Modifier to the Item, initializes Modifier's map and inherits Item's workflow variables. Variables are inherited at creation time, so any Item variables you set after creating the Modifier are not inherited.

func (*Modifier) Arg

func (m *Modifier) Arg(s ...string) *Modifier

Arg sets the arg for the Modifier. Multiple values are allowed in Alfred 4.1 and later.

func (*Modifier) Icon added in v0.13.1

func (m *Modifier) Icon(i *Icon) *Modifier

Icon sets an icon for the Modifier.

func (*Modifier) MarshalJSON

func (m *Modifier) MarshalJSON() ([]byte, error)

MarshalJSON serializes Item to Alfred 3's JSON format. You shouldn't need to call this directly: use SendFeedback() instead.

func (*Modifier) Subtitle

func (m *Modifier) Subtitle(s string) *Modifier

Subtitle sets the subtitle for the Modifier.

func (*Modifier) Valid

func (m *Modifier) Valid(v bool) *Modifier

Valid sets the valid status for the Modifier.

func (*Modifier) Var

func (m *Modifier) Var(k, v string) *Modifier

Var sets a variable for the Modifier.

func (*Modifier) Vars

func (m *Modifier) Vars() map[string]string

Vars returns all Modifier variables.

type Option

type Option func(wf *Workflow) Option

Option is a configuration option for Workflow. Pass one or more Options to New() or Workflow.Configure().

An Option returns its inverse (i.e. an Option that restores the previous value).

You can apply Options at any time, so you can, e.g. suppress UIDs if you need to for items to be in a particular order.

func AddMagic

func AddMagic(actions ...MagicAction) Option

AddMagic registers Magic Actions with the Workflow. Magic Actions connect special keywords/queries to callback functions. See the MagicAction interface for more information.

func HelpURL

func HelpURL(url string) Option

HelpURL sets link shown in debugger & log if Run() catches a panic ("Get help at http://…"). Set this to the URL of an issue tracker/forum thread where users can ask for help.

func LogPrefix

func LogPrefix(prefix string) Option

LogPrefix is the printed to debugger at the start of each run. Its purpose is to ensure that the first real log message is shown on its own line. It is only sent to Alfred's debugger, not the log file.

Default: Beer Mug (\U0001F37A)

func MagicPrefix

func MagicPrefix(prefix string) Option

MagicPrefix sets the prefix for "magic" commands. If a user enters this prefix, AwGo takes control of the workflow and shows a list of matching magic commands to the user.

Default: workflow:

func MaxLogSize

func MaxLogSize(bytes int) Option

MaxLogSize sets the size (in bytes) when workflow log is rotated. Default: 1 MiB

func MaxResults

func MaxResults(num int) Option

MaxResults is the maximum number of results to send to Alfred. 0 means send all results. Default: 0

func RemoveMagic

func RemoveMagic(actions ...MagicAction) Option

RemoveMagic unregisters Magic Actions with Workflow. Magic Actions connect special keywords/queries to callback functions. See the MagicAction interface for more information.

func SessionName added in v0.15.0

func SessionName(name string) Option

SessionName changes the name of the variable used to store the session ID.

This is useful if you have multiple Script Filters chained together that you don't want to use the same cache.

func SortOptions

func SortOptions(opts ...fuzzy.Option) Option

SortOptions sets the fuzzy sorting options for Workflow.Filter(). See fuzzy and fuzzy.Option for info on (configuring) the sorting algorithm.

_examples/fuzzy contains an example workflow using fuzzy sort.

func SuppressUIDs added in v0.13.1

func SuppressUIDs(on bool) Option

SuppressUIDs prevents UIDs from being set on feedback Items.

This turns off Alfred's knowledge, i.e. prevents Alfred from applying its own sort, so items will be shown in the order you add them.

Useful if you need to force a particular item to the top/bottom.

This setting only applies to Items created *after* it has been set.

func TextErrors

func TextErrors(on bool) Option

TextErrors tells Workflow to print errors as text, not JSON. Messages are still sent to STDOUT. Set to true if error should be captured by Alfred, e.g. if output goes to a Notification.

func Update

func Update(updater Updater) Option

Update sets the updater for the Workflow. Panics if a version number isn't set (in Alfred Preferences).

See Updater interface and subpackage update for more documentation.

type Session

type Session struct {
	SessionID string
	// contains filtered or unexported fields
}

Session is a Cache that is tied to the `sessionID` value passed to NewSession().

All cached data are stored under the sessionID. NewSessionID() creates a pseudo-random string based on the current UNIX time (in nanoseconds). The Workflow struct persists this value as a session ID as long as the user is using the current workflow via the `AW_SESSION_ID` top-level workflow variable.

As soon as Alfred closes or the user calls another workflow, this variable is lost and the data are "hidden". Session.Clear(false) must be called to actually remove the data from the cache directory, which Workflow.Run() does.

In contrast to the Cache API, Session methods lack an explicit `maxAge` parameter. It is always `0`, i.e. cached data are always loaded regardless of age as long as the session is valid.

TODO: Embed Cache rather than wrapping it?

func NewSession

func NewSession(dir, sessionID string) *Session

NewSession creates and initialises a Session.

func (Session) Clear

func (s Session) Clear(current bool) error

Clear removes session-scoped cache data. If current is true, it also removes data cached for the current session.

func (Session) Exists

func (s Session) Exists(name string) bool

Exists returns true if the named cache exists.

func (Session) Load

func (s Session) Load(name string) ([]byte, error)

Load reads data saved under given name.

func (Session) LoadJSON

func (s Session) LoadJSON(name string, v interface{}) error

LoadJSON unmarshals a cache into v.

func (Session) LoadOrStore

func (s Session) LoadOrStore(name string, reload func() ([]byte, error)) ([]byte, error)

LoadOrStore loads data from cache if they exist. If data do not exist, reload is called, and the resulting data are cached & returned.

func (Session) LoadOrStoreJSON

func (s Session) LoadOrStoreJSON(name string, reload func() (interface{}, error), v interface{}) error

LoadOrStoreJSON loads JSON-serialised data from cache if they exist. If the data do not exist, reload is called, and the resulting interface{} is cached and returned.

func (Session) Store

func (s Session) Store(name string, data []byte) error

Store saves data under the given name. If len(data) is 0, the file is deleted.

func (Session) StoreJSON

func (s Session) StoreJSON(name string, v interface{}) error

StoreJSON serialises v to JSON and saves it to the cache. If v is nil, the cache is deleted.

type Updater

type Updater interface {
	UpdateAvailable() bool // Return true if a newer version is available
	CheckDue() bool        // Return true if a check for a newer version is due
	CheckForUpdate() error // Retrieve available releases, e.g. from a URL
	Install() error        // Install the latest version
}

Updater can check for and download & install newer versions of the workflow. There is a concrete implementation and documentation in subpackage update.

type Workflow

type Workflow struct {
	sync.WaitGroup
	// Interface to workflow's settings.
	// Reads workflow variables by type and saves new values to info.plist.
	Config *Config

	// Call Alfred's AppleScript functions.
	Alfred *Alfred

	// Cache is a Cache pointing to the workflow's cache directory.
	Cache *Cache
	// Data is a Cache pointing to the workflow's data directory.
	Data *Cache
	// Session is a cache that stores session-scoped data. These data
	// persist until the user closes Alfred or runs a different workflow.
	Session *Session

	// Access macOS Keychain. Passwords are saved using the workflow's
	// bundle ID as the service name. Passwords are synced between
	// devices if you have iCloud Keychain turned on.
	Keychain *keychain.Keychain

	// The response that will be sent to Alfred. Workflow provides
	// convenience wrapper methods, so you don't normally have to
	// interact with this directly.
	Feedback *Feedback

	// Updater fetches updates for the workflow.
	Updater Updater
	// contains filtered or unexported fields
}

Workflow provides a consolidated API for building Script Filters.

As a rule, you should create a Workflow in init or main and call your main entry-point via Workflow.Run(), which catches panics, and logs & shows the error in Alfred.

Script Filter

To generate feedback for a Script Filter, use Workflow.NewItem() to create new Items and Workflow.SendFeedback() to send the results to Alfred.

Run Script

Use the TextErrors option, so any rescued panics are printed as text, not as JSON.

Use ArgVars to set workflow variables, not Workflow/Feedback.

See the _examples/ subdirectory for some full examples of workflows.

func New

func New(opts ...Option) *Workflow

New creates and initialises a new Workflow, passing any Options to Workflow.Configure().

For available options, see the documentation for the Option type and the following functions.

IMPORTANT: In order to be able to initialise the Workflow correctly, New must be run within a valid Alfred environment; specifically *at least* the following environment variables must be set:

alfred_workflow_bundleid
alfred_workflow_cache
alfred_workflow_data

If you aren't running from Alfred, or would like to specify a custom environment, use NewFromEnv().

Example

New initialises a Workflow with the default settings. Name, bundle ID, version etc. are read from the environment variables set by Alfred.

wf := New()
// Name is read from environment
fmt.Println(wf.Name())
// BundleID is read from environment
fmt.Println(wf.BundleID())
// Version is from info.plist
fmt.Println(wf.Version())
Output:

AwGo
net.deanishe.awgo
1.2.0
Example (WithOptions)

Pass one or more Options to New() to configure the created Workflow.

wf := New(HelpURL("http://www.example.com"), MaxResults(200))
fmt.Println(wf.helpURL)
fmt.Println(wf.maxResults)
Output:

http://www.example.com
200

func NewFromEnv added in v0.15.0

func NewFromEnv(env Env, opts ...Option) *Workflow

NewFromEnv creates a new Workflows from the specified Env. If env is nil, the system environment is used.

func (*Workflow) Args

func (wf *Workflow) Args() []string

Args returns command-line arguments passed to the program. It intercepts "magic args" and runs the corresponding actions, terminating the workflow. See MagicAction for full documentation.

func (*Workflow) BundleID

func (wf *Workflow) BundleID() string

BundleID returns the workflow's bundle ID. This library will not work without a bundle ID, which is set in the workflow's main setup sheet in Alfred Preferences.

func (*Workflow) CacheDir

func (wf *Workflow) CacheDir() string

CacheDir returns the path to the workflow's cache directory.

func (*Workflow) CheckForUpdate

func (wf *Workflow) CheckForUpdate() error

CheckForUpdate retrieves and caches the list of available releases.

func (*Workflow) ClearCache

func (wf *Workflow) ClearCache() error

ClearCache deletes all files from the workflow's cache directory.

func (*Workflow) ClearData

func (wf *Workflow) ClearData() error

ClearData deletes all files from the workflow's data directory.

func (*Workflow) Configure

func (wf *Workflow) Configure(opts ...Option) (previous Option)

Configure applies one or more Options to Workflow. The returned Option reverts all Options passed to Configure.

Example

Change Workflow's configuration after creation, then revert it.

wf := New()
// Default settings (false and 0)
fmt.Println(wf.textErrors)
fmt.Println(wf.maxResults)
// Turn text errors on, set max results and save Option to revert
// to previous configuration
previous := wf.Configure(TextErrors(true), MaxResults(200))
fmt.Println(wf.textErrors)
fmt.Println(wf.maxResults)
// Revert to previous configuration
wf.Configure(previous)
fmt.Println(wf.textErrors)
fmt.Println(wf.maxResults)
Output:

false
0
true
200
false
0

func (*Workflow) DataDir

func (wf *Workflow) DataDir() string

DataDir returns the path to the workflow's data directory.

func (*Workflow) Debug

func (wf *Workflow) Debug() bool

Debug returns true if Alfred's debugger is open.

func (*Workflow) Dir

func (wf *Workflow) Dir() string

Dir returns the path to the workflow's root directory.

func (*Workflow) Fatal

func (wf *Workflow) Fatal(msg string)

Fatal displays an error message in Alfred, then calls log.Fatal(), terminating the workflow.

func (*Workflow) FatalError

func (wf *Workflow) FatalError(err error)

FatalError displays an error message in Alfred, then calls log.Fatal(), terminating the workflow.

func (*Workflow) Fatalf

func (wf *Workflow) Fatalf(format string, args ...interface{})

Fatalf displays an error message in Alfred, then calls log.Fatal(), terminating the workflow.

func (*Workflow) Filter

func (wf *Workflow) Filter(query string) []*fuzzy.Result

Filter fuzzy-sorts feedback Items against query and deletes Items that don't match.

func (*Workflow) InstallUpdate

func (wf *Workflow) InstallUpdate() error

InstallUpdate downloads and installs the latest version of the workflow.

func (*Workflow) IsEmpty

func (wf *Workflow) IsEmpty() bool

IsEmpty returns true if Workflow contains no items.

func (*Workflow) IsRunning added in v0.15.0

func (wf *Workflow) IsRunning(jobName string) bool

IsRunning returns true if a job with name jobName is currently running.

func (*Workflow) Kill added in v0.15.0

func (wf *Workflow) Kill(jobName string) error

Kill stops a background job.

func (*Workflow) LogFile

func (wf *Workflow) LogFile() string

LogFile returns the path to the workflow's log file.

func (*Workflow) Name

func (wf *Workflow) Name() string

Name returns the workflow's name as specified in the workflow's main setup sheet in Alfred Preferences.

func (*Workflow) NewFileItem

func (wf *Workflow) NewFileItem(path string) *Item

NewFileItem adds and returns a new Item pre-populated from path. Title and Autocomplete are the base name of the file, Subtitle is the path to the file (using "~" for $HOME), Valid is true, UID and Arg are set to path, Type is "file", and Icon is the icon of the file at path.

func (*Workflow) NewItem

func (wf *Workflow) NewItem(title string) *Item

NewItem adds and returns a new feedback Item. See Feedback.NewItem() for more information.

Example

The normal way to create a new Item, but not the normal way to use it.

Typically, when you're done adding Items, you call SendFeedback() to send the results to Alfred.

wf := New()
// Create a new item via the Workflow object, which will
// track the Item and send it to Alfred when you call
// Workflow.SendFeedback()
//
// Title is the only required value.
it := wf.NewItem("First Result").
	Subtitle("Some details here")

// Just to see what it looks like...
data, _ := json.Marshal(it)
fmt.Println(string(data))
Output:

{"title":"First Result","subtitle":"Some details here","valid":false}

func (*Workflow) NewWarningItem

func (wf *Workflow) NewWarningItem(title, subtitle string) *Item

NewWarningItem adds and returns a new Feedback Item with the system warning icon (exclamation mark on yellow triangle).

func (*Workflow) OpenCache

func (wf *Workflow) OpenCache() error

OpenCache opens the workflow's cache directory in the default application (usually Finder).

func (*Workflow) OpenData

func (wf *Workflow) OpenData() error

OpenData opens the workflow's data directory in the default application (usually Finder).

func (*Workflow) OpenHelp added in v0.13.1

func (wf *Workflow) OpenHelp() error

OpenHelp opens the workflow's help URL (if set) in the default browser.

func (*Workflow) OpenLog

func (wf *Workflow) OpenLog() error

OpenLog opens the workflow's logfile in the default application (usually Console.app).

func (*Workflow) Rerun

func (wf *Workflow) Rerun(secs float64) *Workflow

Rerun tells Alfred to re-run the Script Filter after `secs` seconds.

func (*Workflow) Reset

func (wf *Workflow) Reset() error

Reset deletes all workflow data (cache and data directories).

func (*Workflow) Run

func (wf *Workflow) Run(fn func())

Run runs your workflow function, catching any errors. If the workflow panics, Run rescues and displays an error message in Alfred.

func (*Workflow) RunInBackground added in v0.15.0

func (wf *Workflow) RunInBackground(jobName string, cmd *exec.Cmd) error

RunInBackground executes cmd in the background. It returns an ErrJobExists error if a job of the same name is already running.

func (*Workflow) SendFeedback

func (wf *Workflow) SendFeedback() *Workflow

SendFeedback sends Script Filter results to Alfred.

Results are output as JSON to STDOUT. As you can output results only once, subsequent calls to sending methods are logged and ignored.

The sending methods are:

SendFeedback()
Fatal()
Fatalf()
FatalError()
Warn()
WarnEmpty()  // only sends if there are no items

func (*Workflow) SessionID

func (wf *Workflow) SessionID() string

SessionID returns the session ID for this run of the workflow. This is used internally for session-scoped caching.

The session ID is persisted as a workflow variable. It and the session persist as long as the user is using the workflow in Alfred. That means that the session expires as soon as Alfred closes or the user runs a different workflow.

func (*Workflow) UpdateAvailable

func (wf *Workflow) UpdateAvailable() bool

UpdateAvailable returns true if a newer version is available to install.

func (*Workflow) UpdateCheckDue

func (wf *Workflow) UpdateCheckDue() bool

UpdateCheckDue returns true if an update is available.

func (*Workflow) Var

func (wf *Workflow) Var(k, v string) *Workflow

Var sets the value of workflow variable k on Workflow.Feedback to v. See Feedback.Var() for more information.

func (*Workflow) Vars

func (wf *Workflow) Vars() map[string]string

Vars returns the workflow variables set on Workflow.Feedback. See Feedback.Vars() for more information.

func (*Workflow) Version

func (wf *Workflow) Version() string

Version returns the workflow's version set in the workflow's configuration sheet in Alfred Preferences.

func (*Workflow) Warn

func (wf *Workflow) Warn(title, subtitle string) *Workflow

Warn displays a warning message in Alfred immediately. Unlike FatalError()/Fatal(), this does not terminate the workflow, but you can't send any more results to Alfred.

Example
wf := New()
// Add some items
wf.NewItem("Item One").
	Subtitle("Subtitle one")
wf.NewItem("Item Two").
	Subtitle("Subtitle two")

// Delete existing items, add a warning, then
// immediately send feedback
wf.Warn("Bad Items", "Those items are boring")
Output:

{
  "variables": {
    "AW_SESSION_ID": "test-session-id"
  },
  "items": [
    {
      "title": "Bad Items",
      "subtitle": "Those items are boring",
      "valid": false,
      "icon": {
        "path": "/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/AlertCautionIcon.icns"
      }
    }
  ]
}

func (*Workflow) WarnEmpty

func (wf *Workflow) WarnEmpty(title, subtitle string)

WarnEmpty adds a warning item to feedback if there are no other items.

Directories

Path Synopsis
Package examples contains trivial, but complete, Alfred workflows demonstrating features of AwGo and/or useful workflow idioms.
Package examples contains trivial, but complete, Alfred workflows demonstrating features of AwGo and/or useful workflow idioms.
fuzzy
Workflow fuzzy is a basic demonstration of AwGo's fuzzy filtering.
Workflow fuzzy is a basic demonstration of AwGo's fuzzy filtering.
reading-list
Workflow reading-list is a more advanced example of fuzzy filtering.
Workflow reading-list is a more advanced example of fuzzy filtering.
settings
Workflow settings demonstrates binding a struct to Alfred's settings.
Workflow settings demonstrates binding a struct to Alfred's settings.
update
Workflow update is an example of how to use AwGo's update API.
Workflow update is an example of how to use AwGo's update API.
workflows
Workflow workflows retrieves and filters GitHub repos tagged with "alfred-workflow".
Workflow workflows retrieves and filters GitHub repos tagged with "alfred-workflow".
Package keychain implements a simple interface to the macOS Keychain.
Package keychain implements a simple interface to the macOS Keychain.
Package update implements an API for fetching workflow updates from remote servers.
Package update implements an API for fetching workflow updates from remote servers.
Package util contains general helper functions for workflow (library) authors.
Package util contains general helper functions for workflow (library) authors.
build
Package build provides helpers for developing and building workflows.
Package build provides helpers for developing and building workflows.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL