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 }