#!/usr/bin/env nix-shell #! nix-shell -i oil -p coreutils ripgrep handlr gnused xxd # A ripoff from Duckduckgo bangs. # Examples: # ``` # bangs hello there ~g ~aw # ``` # will open a search result page on Google and Arch Wiki proc usage() { cat <<HELP bangs - a ripoff from Duckduckgo bangs, except you can open multiple pages in one go. Usage: bangs [SEARCH_QUERY...] [BANGS...] A bang is absolutely necessary to indicate what search pages to open. While there is a default config, you should create your own list by setting a config file at '\${XDG_CONFIG_HOME}/bangs/config.json'. The config is simply a JSON file with the bang as the key and an object with 'url' and 'name'. Examples: - Google and Duckduckgo search bangs hello world ~g ~ddg - Change the bangs prefix BANGS_PREFIX="--" bangs how to program in python --g --yt HELP } # These are the default bangs available. # Bangs are any keys that shouldn't have whitespace characters. const config_dir = "${XDG_CONFIG_HOME:-"$HOME/.config"}/bangs" const config_file = "${config_dir}/config.json" const default_config = { 'aw': { 'name': 'Arch Wiki', 'url': 'https://wiki.archlinux.org/index.php?title=Special%3ASearch&search={{{s}}}' }, 'gh': { 'name': 'GitHub', 'url': 'https://github.com/search?utf8=%E2%9C%93&q={{{s}}}' }, 'g': { 'name': 'Google', 'url': 'https://www.google.com/search?q={{{s}}}' }, 'so': { 'name': 'Stack Overflow', 'url': 'http://stackoverflow.com/search?q={{{s}}}' }, 'w': { 'name': 'Wikipedia', 'url': 'https://en.wikipedia.org/wiki/Special:Search?search={{{s}}}' } } const bangs_prefix = "${BANGS_PREFIX:-~}" const bangs_placeholder = "${BANGS_PLACEHOLDER:-{{{s}}}}" const bangs_format = / %start $bangs_prefix !space+ %end / const valid_bangs = %() const search_query = %() # Config file detection. # Otherwise, we'll just use the default config. if test -f $config_file { json read :bangs < $config_file } else { var bangs = default_config } # Stolen from https://gist.github.com/cdown/1163649 and https://gist.github.com/cdown/1163649#gistcomment-1256298 proc urlencode(msg) { for (i in 0:len(msg)) { var char = msg[i] case $char { [a-zA-Z0-9.~_-]) printf '%s' $char ;; *) printf '%s' $char | xxd -plain -cols 1 | while read :hex { printf '%%%s' $hex } ;; } } } proc warnf(format, @msg) { >&2 printf "$format\\n" @msg } if (len(ARGV) == 0) { usage exit 0 } for i in @ARGV { write -- $i | rg --quiet $bangs_format || { push :search_query $i continue } var bang = $(write -- $i | sed --regexp-extended --expression "s/^${bangs_prefix}//") if (bang in bangs) { push :valid_bangs $bang warnf "%s will be used to search." $bang } else { warnf "%s is not found in the database." $bang } } var query = join(search_query, " ") var encoded_query = $(urlencode $query) warnf "Search query is '%s'" $query warnf "Encoded form is '%s'" $encoded_query for bang in @valid_bangs { var metadata = bangs[bang] var url = $(write -- ${metadata['url']} | sed --expression "s/${bangs_placeholder}/${encoded_query}/") handlr open $url }