github.com/ChicK00o/awgo@v0.29.4/alfred.go (about)

     1  // Copyright (c) 2018 Dean Jackson <deanishe@deanishe.net>
     2  // MIT Licence - http://opensource.org/licenses/MIT
     3  
     4  package aw
     5  
     6  import (
     7  	"fmt"
     8  	"os"
     9  	"path/filepath"
    10  	"strings"
    11  
    12  	"github.com/ChicK00o/awgo/util"
    13  )
    14  
    15  // JXA scripts to call Alfred.
    16  const (
    17  	scriptSearch = "Application(%s).search(%s);"
    18  	scriptAction = "Application(%s).action(%s);"
    19  	// support "asType" option added in Alfred 4.5
    20  	scriptActionType = "Application(%s).action(%s, %s);"
    21  	scriptBrowse     = "Application(%s).browse(%s);"
    22  	scriptSetTheme   = "Application(%s).setTheme(%s);"
    23  	scriptTrigger    = "Application(%s).runTrigger(%s, %s);"
    24  	scriptSetConfig  = "Application(%s).setConfiguration(%s, %s);"
    25  	scriptRmConfig   = "Application(%s).removeConfiguration(%s, %s);"
    26  	scriptReload     = "Application(%s).reloadWorkflow(%s);"
    27  )
    28  
    29  /*
    30  Alfred wraps Alfred's AppleScript API, allowing you to open Alfred in
    31  various modes or call External Triggers.
    32  
    33  	a := NewAlfred()
    34  
    35  	// Open Alfred
    36  	if err := a.Search(""); err != nil {
    37  		// handle error
    38  	}
    39  
    40  	// Browse /Applications
    41  	if err := a.Browse("/Applications"); err != nil {
    42  		// handle error
    43  	}
    44  */
    45  type Alfred struct {
    46  	Env
    47  	// For testing. Set to true to save JXA script to lastScript
    48  	// instead of running it.
    49  	noRunScripts bool
    50  	lastScript   string
    51  }
    52  
    53  // NewAlfred creates a new Alfred from the environment.
    54  //
    55  // It accepts one optional Env argument. If an Env is passed, Alfred
    56  // is initialised from that instead of the system environment.
    57  func NewAlfred(env ...Env) *Alfred {
    58  	var e Env = sysEnv{}
    59  	if len(env) > 0 {
    60  		e = env[0]
    61  	}
    62  
    63  	return &Alfred{Env: e}
    64  }
    65  
    66  // Search runs Alfred with the given query. Use an empty query to just open Alfred.
    67  func (a *Alfred) Search(query string) error {
    68  	return a.runScript(scriptSearch, query)
    69  }
    70  
    71  // Browse tells Alfred to open path in navigation mode.
    72  func (a *Alfred) Browse(path string) error {
    73  	var err error
    74  
    75  	if path, err = filepath.Abs(path); err != nil {
    76  		return err
    77  	}
    78  
    79  	return a.runScript(scriptBrowse, path)
    80  }
    81  
    82  // SetTheme tells Alfred to use the specified theme.
    83  func (a *Alfred) SetTheme(name string) error {
    84  	return a.runScript(scriptSetTheme, name)
    85  }
    86  
    87  // Action tells Alfred to show Universal Actions for value(s). This calls Alfred.ActionAsType
    88  // with an empty type.
    89  func (a *Alfred) Action(value ...string) error { return a.ActionAsType("", value...) }
    90  
    91  // ActionAsType tells Alfred to show Universal Actions for value(s). Type typ
    92  // may be one of "file", "url" or "text", or an empty string to tell Alfred
    93  // to guess the type.
    94  //
    95  // Added in Alfred 4.5
    96  func (a *Alfred) ActionAsType(typ string, value ...string) error {
    97  	if len(value) == 0 {
    98  		return nil
    99  	}
   100  
   101  	if typ == TypeFile {
   102  		for i, s := range value {
   103  			p, err := filepath.Abs(s)
   104  			if err != nil {
   105  				return fmt.Errorf("make absolute path %q: %w", s, err)
   106  			}
   107  			value[i] = p
   108  		}
   109  	}
   110  
   111  	switch typ {
   112  	case "":
   113  		return a.runScript(scriptAction, value)
   114  	case "file", "url", "text":
   115  		opts := map[string]interface{}{
   116  			"asType": typ,
   117  		}
   118  		return a.runScript(scriptActionType, value, opts)
   119  	default:
   120  		return fmt.Errorf("unknown type: %s", typ)
   121  	}
   122  }
   123  
   124  // RunTrigger runs an External Trigger in the given workflow. Query may be empty.
   125  //
   126  // It accepts one optional bundleID argument, which is the bundle ID of the
   127  // workflow whose trigger should be run.
   128  // If not specified, it defaults to the current workflow's.
   129  func (a *Alfred) RunTrigger(name, query string, bundleID ...string) error {
   130  	bid, _ := a.Lookup(EnvVarBundleID)
   131  	if len(bundleID) > 0 {
   132  		bid = bundleID[0]
   133  	}
   134  
   135  	opts := map[string]interface{}{
   136  		"inWorkflow": bid,
   137  	}
   138  
   139  	if query != "" {
   140  		opts["withArgument"] = query
   141  	}
   142  
   143  	return a.runScript(scriptTrigger, name, opts)
   144  }
   145  
   146  // ReloadWorkflow tells Alfred to reload a workflow from disk.
   147  //
   148  // It accepts one optional bundleID argument, which is the bundle ID of the
   149  // workflow to reload. If not specified, it defaults to the current workflow's.
   150  func (a *Alfred) ReloadWorkflow(bundleID ...string) error {
   151  	bid, _ := a.Lookup(EnvVarBundleID)
   152  	if len(bundleID) > 0 {
   153  		bid = bundleID[0]
   154  	}
   155  
   156  	return a.runScript(scriptReload, bid)
   157  }
   158  
   159  func (a *Alfred) runScript(script string, arg ...interface{}) error {
   160  	quoted := []interface{}{util.QuoteJS(scriptAppName())}
   161  	for _, v := range arg {
   162  		quoted = append(quoted, util.QuoteJS(v))
   163  	}
   164  	script = fmt.Sprintf(script, quoted...)
   165  
   166  	if a.noRunScripts {
   167  		a.lastScript = script
   168  		return nil
   169  	}
   170  
   171  	_, err := util.RunJS(script)
   172  	return err
   173  }
   174  
   175  // Name of JXA Application for running Alfred
   176  func scriptAppName() string {
   177  	// Alfred 3
   178  	if strings.HasPrefix(os.Getenv(EnvVarAlfredVersion), "3") {
   179  		return "Alfred 3"
   180  	}
   181  	// Alfred 4+
   182  	return "com.runningwithcrayons.Alfred"
   183  }