/bbangsearch

DuckDuckGo's bang searches from the commandline

Primary LanguageClojureMIT LicenseMIT

bbangsearch❗

A CLI for DuckDuckGo's bang❗ searches written in Babashka.

Rationale

I think bang searches are really great. Having DDG as my default search-engine, it's often easier to just type !gh project in the address bar than to cycle through open browser tabs to find the page of said GitHub project.

I found there's also some shortcomings:

  • I miss a 'jump-to'-mode
    often there's no need to search, just to visit the thing you know is there.
  • some bangs are no longer working
  • no option to add custom bangs
  • they only work in the browser

Enter bbang:

  • open (or get the url of) any of the ~14k bang search pages via the commandline
  • allow overriding/adding bangs
  • list all bangs (in an easily grep-able format)
  • allow for 'jump-to' functionality

Examples

# I. Use any of the regular DDG bangs (opens default browser),
# e.g. gh (github), yt (youtube), dd (devdocs.io) etc.
$ bbang gh some project
$ bbang yt topic "and exact phrase"
$ bbang dd git rebase

# ...get the url
$ bbang dd git rebase --url
# => https://devdocs.io/#q=git+rebase


# II. Additional built-in bangs
# ghrepo: visit the GitHub project of the repository you're working on
$ bbang ghrepo

# ...search it
$ bbang ghrepo _ some query

# ...visit/search any one of your repositories
$ bbang ghrepo deps-try
$ bbang ghrepo deps-try some query

# III. Add custom bangs
# search all tickets
$ bbang proj/tickets some query

# visit specific ticket
$ bbang proj/tickets PROJ-123

Installation

Homebrew (Linux and macOS)

Install

$ brew install eval/brew/bbang
# For future upgrades do:
$ brew update && brew upgrade bbang

There's also the unstable releases (tracking the mainline):

$ brew install --head eval/brew/bbang
# For future upgrades do:
$ brew update && brew reinstall bbang

bbin

bbin allows for easy installation of Babashka scripts.

Prerequisites

install bbin (make sure to adjust $PATH).

# latest version from mainline
$ bbin install io.github.eval/bbangsearch
$ bbang -h

# as something else
$ bbin install io.github.eval/bbangsearch --as b

manual (Windows, Linux and macOS)

Prerequisites

Verify that the following commands work:

$ bb --version
babashka v1.3.186

Installation

#!/usr/bin/env sh

exec bb /absolute/path/to/bbang-bb.jar "$@"

standalone

Download, chmod +x and put somewhere on PATH:

# Mac aarch64
$ curl -sL https://github.com/eval/bbangsearch/releases/download/stable/bbang-mac-aarch64-standalone -o bbang

# Linux amd64
$ curl -sL https://github.com/eval/bbangsearch/releases/download/stable/bbang-linux-amd64-standalone -o bbang

Usage

$ bbang -h
bbang v0.7.0

A CLI for DuckDuckGo's bang searches written in Babashka.

USAGE
  $ bbang [bang [& terms] [--url]]
  $ bbang [COMMAND]

OPTIONS
  --url  Print url instead of opening browser.

COMMANDS
  bangs:ls  List all bangs (or via `bbang bangs [& terms]`)

Show me the ❗s

All roughly 14k bang searches from DuckDuckGo are included.

# print table
$ bbang bangs:ls

# grep, e.g. bangs from specific domain
$ bbang bangs:ls | grep -e '\.dk'

# Using DuckDuckGo's bang search
$ bbang bangs github

Additional ❗s

Additional built-in bangs:

Bang Description
@gem Jump to gem on rubygems.org
@rdoc Jump to gem on rubydoc.info
cljd Alias for cljdoc
drdk Denmark Radio (fixes default)
drtv Denmark TV (fixes default)
gem rubygems.org (jump-to via @gem)
ghcclj Clojure code on GitHub (similar to ghc)
ghclj Clojure projects on GitHub (similar to gh)
ghdbf GitHub feed (no search)
ghrel Visit/search GitHub releases (see doc below)
ghrepo Visit/search repo on GitHub (see doc below)
grep Grep.app
grepclj Grep.app for Clojure code
java19 Java v19 docs
java20 Java v20 docs
java21 Java v21 docs
java Alias of java21
pgdoc14 Postgresql docs v14
pgdoc15 Postgresql docs v15
pgdoc16 Postgresql docs v16
pgdoc Postgresql docs current version
rails61 Rails API latest v6.1.x (aliased as rails6)
rails70 Rails API latest v7.0.x
rails71 Rails API latest v7.1.x (aliased as rails7)
rdoc rubydoc.info, gems only (fixes default, jump-to via @gem)

ghrepo

This bang deserves it's own paragraph as it's quite powerful.

In its simplest form it allows for visiting/searching a GitHub repository you pass it:

# visit
$ bbang ghrepo eval/bbangsearch

# search
$ bbang ghrepo eval/bbangsearch some issue

It also has some implied defaults.
E.g. with the right settings, you can leave out the GitHub organization and only provide a project-name:

# visit
$ bbang ghrepo bbangsearch

# search
$ bbang ghrepo bbangsearch some issue

bbang derives the GitHub organization from the following settings (in order of precedence):

  • env-var BBANG_GITHUB_ORG
  • env-var GITHUB_ORG
  • git setting github.org
    $ git config --global github.org mycom (leave out --global to set it for the current repos).
  • set env-var BBANG_GITHUB_USER
  • set env-var GITHUB_USER
  • git setting github.user
    $ git config --global github.user eval (leave out --global to set it for the current repos).

Finally, when in a git working directory that has a remote pointing to GitHub1, you neither need to provide org or project:

# visit
$ bbang ghrepo

# search
$ bbang ghrepo _ some issue

ghrel

This bang works like ghrepo:

  • bbang ghrel jumps to the releases page on GitHub of the project derived from the git working directory.
  • bbang ghrel _ some query searches releases of said project.
  • bbang ghrel my-other-repos [some query] visits/searches releases of another project from the current organization.
  • bbang ghrel org/project [some query] visits/searches releases of specified GitHub project.

Customizations

Custom/overriding bangs are defined in files named bangs.edn in the following places:

  • user-config
    ~/.config/bbang/bangs.edn (or $XDG_CONFIG_HOME/bbang/bangs.edn)
  • current working directory and its direct ancestors

So when executing bbang from a project-folder (~/projects/foo), all bangs from the following places are (deep-)merged (last one wins):

  • built-in
  • user-config
  • bangs.edn in any folder starting at /, down to ~/projects/foo

Example

Here's an example bangs.edn in some project-folder:

;; ~/projects/foo/bangs.edn
{
  "apidocs12" {:desc "API docs v1.2"
               :tpl  "http://localhost:3333/v1.2?q={{s|urlescape}}"
               :aliases ["apidocs"]}
}

It's defined in the EDN-format (which is like JSON if you squint a bit).
A bang has a name (i.e. apidocs12) and a map ({,,,}) containing a description (:desc), a template (:tpl) and (optionally) aliases.

As you have guessed, the template is what ultimately yields the url. The templating system used is Selmer (more info below), but most templates are of the form https://some-url?q={{s|urlsescape}}. So bbang apidocs12 some query will open url http://localhost:3333/v1.2?q=some+query (quickly test bangs with the url-flag, e.g. bbang mybang query --url).

Aliases make a (new or existing) bang available under shorter (possibly existing) names. In this case we point a bang apidocs to apidocs12. You could imagine that in a big repository there might be a lot of apidoc variants: apidocs11, apidocs13 etc. Combining these with an alias ensures users can always use apidocs and get the right version.

Users using apidocs a lot could even decide to have an alias for this alias in their user-config to make it even shorter:

# ~/.config/bbang/bangs.edn
{
  "apidocs" {:aliases ["ad"]}
}

Using Selmer's tags you can do nifty things:

{
  "mybang"          {:desc "Some bang"
                     :tpl "{% if s|empty? %}https://example.org/all{% else %}https://example.org/search?q={{s|urlescape}}{% endif %}"}
  "project/tickets" {:desc "Visit/search tickets"
                     :tpl "{% ifmatches #\"^PROJ-\" s %}https://tickets.com/show/{{s}}{% else %}https://tickets.com/search?q={{s|urlescape}}{% endifmatches %}"
  "java19"          {:aliases ["java"]}}
}

The first example shows how to distinguish between merely visiting a resource (i.e. bbang mybang) and doing a search (e.g. bbang mybang some query) using Selmer's if-tag.
The second example shows how to distinguish between jumping to a known ticket (e.g. bbang project/tickets PROJ-123) and doing a search. It uses the bbang-specific ifmatches-tag.
The last example shows how to alias an existing bang. Both java19 and java exist (resp. search java docs v19 and (currently) v21). This alias ensures that java is equivalent to java19.

License

Copyright (c) 2023 Gert Goet, ThinkCreate. Distributed under the MIT license. See LICENSE.

Footnotes

  1. in order of preference: the origin-url, any remote with an ssh-url