def run(wf, argv): """Run ``searchio user`` sub-command.""" args = docopt(usage(wf), argv) ctx = Context(wf) query = wf.decode(args.get('<query>') or '').strip() ICON_BACK = ctx.icon('back') # log.debug('args=%r', args) f = util.FileFinder([ctx.searches_dir], ['json']) searches = [Search.from_file(p) for p in f] searches.sort(key=attrgetter('title')) if query: searches = wf.filter(query, searches, key=attrgetter('title')) else: it = wf.add_item( u'Configuration', 'Back to configuration', arg='back', valid=True, icon=ICON_BACK) it.setvar('action', 'back') log.debug('%d search(es)', len(searches)) if args.get('--text') or util.textmode(): # Display for terminal print(_text_help, file=sys.stderr) table = util.Table([u'ID', u'Title']) for s in searches: table.add_row((s.uid, s.title)) print(table) print() else: # Display for Alfred for s in searches: it = wf.add_item( s.title, 'Reveal configuration in Finder', autocomplete=s.title, arg=s.uid, valid=True, icon=s.icon, ) it.setvar('search', s.uid) it.setvar('action', 'reveal') m = it.add_modifier('cmd', u'Delete search') m.setvar('action', 'delete') wf.send_feedback()
def run(wf, argv): """Run ``searchio search`` sub-command.""" args = docopt(usage(wf), argv) ctx = Context(wf) query = wf.decode(args.get('<query>') or '').strip() uid = wf.decode(args.get('<search>') or '').strip() if not uid or not query: raise RuntimeError('<search> and <query> are required') start = time() p = ctx.search(uid) if not os.path.exists(p): raise ValueError('Unknown search "{}" ({!r})'.format(uid, p)) search = engines.Search.from_file(p) results = cached_search(ctx, search, query) log.debug('[search/%s] %d result(s) in %0.3fs', uid, len(results), time() - start) # --------------------------------------------------------- # Text results if args.get('--text') or util.textmode(): print() msg = u'{:d} result(s) for "{:s}"'.format(len(results), query) print(msg, file=sys.stderr) print('=' * len(msg)) print() table = util.Table([u'Suggestion', u'URL']) for r in results: table.add_row(r[:2]) # print('{0}\t{1}'.format(term, url)) print(table) print() # --------------------------------------------------------- # Alfred results else: for r in results: wf.add_item( r.term, u'Search {} for "{}"'.format(r.source, r.term), arg=r.url, autocomplete=r.term + u' ', valid=True, icon=search.icon, ) wf.send_feedback()
def do_get_url(wf, args): """Check clipboard and browsers for a URL.""" ctx = Context(wf) ICON_CLIPBOARD = ctx.icon('clipboard') items = [] url = clipboard_url() if url: items.append( dict(title='Clipboard', subtitle=url, autocomplete=url, icon=ICON_CLIPBOARD)) # Browsers cmd = ['/usr/bin/osascript', '-l', 'JavaScript', '-e', JXA_RUNNING] + \ BROWSERS.keys() output = subprocess.check_output(cmd) running = json.loads(output) for name in BROWSERS: app, script = BROWSERS[name] if not running[name]: log.debug('"%s" not running', name) continue log.debug('fetching current tab from "%s" ...', name) script = script % dict(appname=name) cmd = ['/usr/bin/osascript', '-l', 'JavaScript', '-e', script] output = subprocess.check_output(cmd) data = json.loads(output) if not data: log.debug('no URL for "%s"', name) continue title = data['title'] or 'Untitled' url = data['url'] items.append( dict(title=title, subtitle=url, autocomplete=url, icon=app, icontype='fileicon')) if not items: wf.add_item('No Clipboard or Browser URL', 'Enter a URL or copy one to clipboard.', icon=ICON_WARNING) for item in items: wf.add_item(**item) wf.send_feedback()
def do_import_search(wf, url): """Parse URL for OpenSearch config.""" ctx = Context(wf) # ICON_IMPORT = ctx.icon('import') ICONS_PROGRESS = [ ctx.icon('progress-1'), ctx.icon('progress-2'), ctx.icon('progress-3'), ctx.icon('progress-4'), ] data = wf.cached_data('import', None, max_age=0, session=True) if data: error = data['error'] search = data['search'] # Clear cache data wf.cache_data('import', None, session=True) wf.cache_data('import-status', None, session=True) if error: wf.add_item(error, icon=ICON_ERROR) wf.send_feedback() return it = wf.add_item(u'Add "{}"'.format(search['name']), u'↩ to add search', valid=True, icon=search['icon']) for k, v in search.items(): it.setvar(k, v) else: progress = int(os.getenv('progress') or '0') i = progress % len(ICONS_PROGRESS) picon = ICONS_PROGRESS[i] log.debug('progress=%d, i=%d, picon=%s', progress, i, picon) wf.setvar('progress', progress + 1) if not is_running('import'): run_in_background('import', ['./searchio', 'fetch', url]) status = wf.cached_data('import-status', None, max_age=0, session=True) title = status or u'Fetching OpenSearch Configuration …' wf.rerun = 0.2 wf.add_item(title, u'Results will be shown momentarily', icon=picon) wf.send_feedback()
def run(wf, argv): """Run ``searchio list`` sub-command.""" args = docopt(usage(wf), argv) ctx = Context(wf) query = wf.decode(args.get('<query>') or '').strip() ICON_BACK = ctx.icon('back') engs = engines.load(*ctx.engine_dirs) # engs = engines.engines() if query: engs = wf.filter(query, engs, key=attrgetter('title')) else: it = wf.add_item( u'Configuration', 'Back to configuration', arg='back', valid=True, icon=ICON_BACK) it.setvar('action', 'back') if args.get('--text') or util.textmode(): # Display for terminal print(_text_help, file=sys.stderr) table = util.Table([u'ID', u'Name', u'Description', u'Variants']) for e in engs: n = '{:>8}'.format(len(e.variants)) table.add_row((e.uid, e.title, e.description, n)) print(table) print() else: # Display for Alfred for e in engs: title = u'{} …'.format(e.title) subtitle = (str(len(e.variants)) + ' variant' + ('s', '')[len(e.variants) == 1]) it = wf.add_item( title, subtitle, autocomplete=e.title, arg=e.uid, valid=True, icon=ctx.icon(e.uid)) it.setvar('engine', e.uid) it.setvar('action', 'searches') wf.send_feedback()
def do_toggle_alfred_sorts(wf): """Toggle "Alfred sorts results" setting.""" ctx = Context(wf) v = ctx.getbool('ALFRED_SORTS_RESULTS') if v: new = '0' status = 'off' else: new = '1' status = 'on' log.debug('turning "ALFRED_SORTS_RESULTS" %s ...', status) set_config('ALFRED_SORTS_RESULTS', new) print(Variables(title='Alfred sorts results', text='Turned ' + status))
def do_toggle_show_query(wf): """Toggle "show query in results" setting.""" ctx = Context(wf) v = ctx.getbool('SHOW_QUERY_IN_RESULTS') if v: new = '0' status = 'off' else: new = '1' status = 'on' log.debug('turning "SHOW_QUERY_IN_RESULTS" %s ...', status) set_config('SHOW_QUERY_IN_RESULTS', new) print(Variables(title='Show query in results', text='Turned ' + status))
def do_get_url(wf, args): """Check clipboard and browsers for a URL.""" ctx = Context(wf) ICON_CLIPBOARD = ctx.icon('clipboard') items = [] url = clipboard_url() if url: items.append(dict(title='Clipboard', subtitle=url, autocomplete=url, icon=ICON_CLIPBOARD)) # Browsers cmd = ['/usr/bin/osascript', '-l', 'JavaScript', '-e', JXA_RUNNING] + \ BROWSERS.keys() output = subprocess.check_output(cmd) running = json.loads(output) for name in BROWSERS: app, script = BROWSERS[name] if not running[name]: log.debug('"%s" not running', name) continue log.debug('fetching current tab from "%s" ...', name) script = script % dict(appname=name) cmd = ['/usr/bin/osascript', '-l', 'JavaScript', '-e', script] output = subprocess.check_output(cmd) data = json.loads(output) if not data: log.debug('no URL for "%s"', name) continue title = data['title'] or 'Untitled' url = data['url'] items.append(dict(title=title, subtitle=url, autocomplete=url, icon=app, icontype='fileicon')) if not items: wf.add_item('No Clipboard or Browser URL', 'Enter a URL or copy one to clipboard.', icon=ICON_WARNING) for item in items: wf.add_item(**item) wf.send_feedback()
def run(wf, argv): """Run ``searchio add`` sub-command.""" args = docopt(usage(wf), argv) ctx = Context(wf) d = parse_args(wf, args) s = engines.Search.from_dict(d) if not util.valid_url(d['search_url']): raise ValueError('Invalid search URL: {!r}'.format(d['search_url'])) if d['suggest_url'] and not util.valid_url(d['suggest_url']): raise ValueError('Invalid suggest URL: {!r}'.format(d['suggest_url'])) p = ctx.search(s.uid) with open(p, 'wb') as fp: json.dump(s.dict, fp, sort_keys=True, indent=2) # m.save(**d) log.debug('Adding new search to info.plist ...') notify('Added New Search', d['title'])
def add_script_filters(wf, data, searches=None): """Add user searches to info.plist data.""" ctx = Context(wf) only = set() if searches: # add them to the user's searches dir for s in searches: path = os.path.join(ctx.searches_dir, s.uid + '.json') with open(path, 'wb') as fp: json.dump(s.dict, fp) only.add(s.uid) log.info('Saved search "%s"', s.title) f = util.FileFinder([ctx.searches_dir], ['json']) searches = [Search.from_file(p) for p in f] if only: searches = [s for s in searches if s.uid in only] searches.sort(key=lambda s: s.title) ypos = YPOS for s in searches: if not s.keyword: log.error('No keyword for search "%s" (%s)', s.title, s.uid) continue d = readPlistFromString(SCRIPT_FILTER) d['uid'] = s.uid d['config']['title'] = s.title # d['config']['script'] = './searchio search {} "$1"'.format(s.uid) d['config']['script'] = './search {} "$1"'.format(s.uid) d['config']['keyword'] = s.keyword data['objects'].append(d) data['connections'][s.uid] = [{ 'destinationuid': OPEN_URL_UID, 'modifiers': 0, 'modifiersubtext': '', 'vitoclose': False, }] data['uidata'][s.uid] = { 'note': s.title, 'xpos': XPOS, 'ypos': ypos, } ypos += YOFFSET log.info('Added Script Filter "%s" (%s)', s.title, s.uid) link_icons(wf, searches)
def run(wf, argv): """Run ``searchio variants`` sub-command.""" args = docopt(usage(wf), argv) ctx = Context(wf) engine_id = wf.decode(args.get('<engine>') or '').strip() query = wf.decode(args.get('<query>') or '').strip() ICON_BACK = ctx.icon('back') engs = engines.load(*ctx.engine_dirs) for e in engs: if e.uid == engine_id: engine = e break else: raise ValueError('Unknown engine : {!r}'.format(engine_id)) # get user searches so we can highlight already-installed searches uids = set([ util.path2uid(p) for p in util.FileFinder([ctx.searches_dir], ['json']) ]) log.debug('engine=%r', engine) variants = engine.variants def _key(s): return u'{} {}'.format(s.uid, s.title.lower()) if query: variants = wf.filter(query, variants, _key) else: it = wf.add_item(u'All Engines \U00002026', u'Go back to engine list', arg=engine.uid, valid=True, icon=ICON_BACK) it.setvar('action', 'back') variants.sort() # --------------------------------------------------------- # Show results if args.get('--text') or util.textmode(): # Display for terminal print(_text_help.format(engine=engine), file=sys.stderr) table = util.Table([u'ID', u'Installed', u'Title']) for v in variants: installed = (u'', u'yes')[v.uid in uids] table.add_row((v.uid, installed, v.title)) print(table) print() else: # Display for Alfred wf.setvar('action', 'new') # TODO: Delimited browse action instead of nested Script Filters? icon = ctx.icon(engine.uid) for v in variants: it = wf.add_item(v.title, u'{} > {}'.format(engine.title, v.name), arg=v.uid, valid=True, icon=icon) it.setvar('engine', engine.uid) it.setvar('uid', v.uid) it.setvar('title', v.title) it.setvar('name', v.name) it.setvar('icon', icon) it.setvar('jsonpath', v.jsonpath) it.setvar('search_url', v.search_url) it.setvar('suggest_url', v.suggest_url) if v.pcencode: it.setvar('pcencode', '1') wf.send_feedback() return
def run(wf, argv): """Run ``searchio variants`` sub-command.""" args = docopt(usage(wf), argv) ctx = Context(wf) engine_id = wf.decode(args.get('<engine>') or '').strip() query = wf.decode(args.get('<query>') or '').strip() ICON_BACK = ctx.icon('back') engs = engines.load(*ctx.engine_dirs) for e in engs: if e.uid == engine_id: engine = e break else: raise ValueError('Unknown engine : {!r}'.format(engine_id)) # get user searches so we can highlight already-installed searches uids = set([util.path2uid(p) for p in util.FileFinder([ctx.searches_dir], ['json'])]) log.debug('engine=%r', engine) variants = engine.variants def _key(s): return u'{} {}'.format(s.uid, s.title.lower()) if query: variants = wf.filter(query, variants, _key) else: it = wf.add_item( u'All Engines \U00002026', u'Go back to engine list', arg=engine.uid, valid=True, icon=ICON_BACK) it.setvar('action', 'back') variants.sort() # --------------------------------------------------------- # Show results if args.get('--text') or util.textmode(): # Display for terminal print(_text_help.format(engine=engine), file=sys.stderr) table = util.Table([u'ID', u'Installed', u'Title']) for v in variants: installed = (u'', u'yes')[v.uid in uids] table.add_row((v.uid, installed, v.title)) print(table) print() else: # Display for Alfred wf.setvar('action', 'new') # TODO: Delimited browse action instead of nested Script Filters? icon = ctx.icon(engine.uid) for v in variants: it = wf.add_item( v.title, u'{} > {}'.format(engine.title, v.name), arg=v.uid, valid=True, icon=icon) it.setvar('engine', engine.uid) it.setvar('uid', v.uid) it.setvar('title', v.title) it.setvar('name', v.name) it.setvar('icon', icon) it.setvar('jsonpath', v.jsonpath) it.setvar('search_url', v.search_url) it.setvar('suggest_url', v.suggest_url) if v.pcencode: it.setvar('pcencode', '1') wf.send_feedback() return
def run(wf, argv): """Run ``searchio list`` sub-command.""" ctx = Context(wf) ICON_ACTIVE = ctx.icon('searches-active') ICON_UPDATE_AVAILABLE = ctx.icon('update-available') ICON_UPDATE_NONE = ctx.icon('update-check') ICON_HELP = ctx.icon('help') ICON_IMPORT = ctx.icon('import') ICON_RELOAD = ctx.icon('reload') ICON_ON = ctx.icon('toggle-on') ICON_OFF = ctx.icon('toggle-off') args = docopt(usage(wf), argv) log.debug('args=%r', args) query = wf.decode(args.get('<query>') or '').strip() # --------------------------------------------------------- # Configuration items items = [] if wf.update_available: items.append(dict( title=u'Update Available \U00002026', subtitle=u'Action to install now', autocomplete=u'workflow:update', valid=False, icon=ICON_UPDATE_AVAILABLE, )) items.append(dict( title=u'Installed Searches \U00002026', subtitle=u'Your configured searches', arg=u'user', valid=True, icon=ICON_ACTIVE, )) items.append(dict( title=u'All Engines \U00002026', subtitle=u'View supported engines and add new searches', arg=u'engines', valid=True, icon=u'icon.png', )) items.append(dict( title=u'Import Search \U00002026', subtitle=u'Add a search from a URL', arg=u'import', valid=True, icon=ICON_IMPORT, )) items.append(dict( title=u'Reload', subtitle=u'Re-create your searches', arg=u'reload', valid=True, # autocomplete=u'workflow:help', # valid=False, icon=ICON_RELOAD, )) icon = ICON_ON if ctx.getbool('SHOW_QUERY_IN_RESULTS') else ICON_OFF items.append(dict( title=u'Show Query in Results', subtitle=u'Always add query to end of results', arg=u'toggle-show-query', valid=True, # autocomplete=u'workflow:help', # valid=False, icon=icon, )) icon = ICON_ON if ctx.getbool('ALFRED_SORTS_RESULTS') else ICON_OFF items.append(dict( title=u'Alfred Sorts Results', subtitle=u"Apply Alfred's knowledge to suggestions", arg=u'toggle-alfred-sorts', valid=True, # autocomplete=u'workflow:help', # valid=False, icon=icon, )) items.append(dict( title=u'Online Help', subtitle=u'Open the help page in your browser', arg=u'help', valid=True, # autocomplete=u'workflow:help', # valid=False, icon=ICON_HELP, )) if not wf.update_available: items.append(dict( title=u'Workflow up to Date', subtitle=u'Action to check for a newer version now', autocomplete=u'workflow:update', valid=False, icon=ICON_UPDATE_NONE, )) # --------------------------------------------------------- # Show results if query: items = wf.filter(query, items, key=itemgetter('title')) if not items: wf.add_item('No matching items', 'Try a different query?', icon=ICON_WARNING) for d in items: wf.add_item(**d) wf.send_feedback() return