Use DuckDuckGo's !Bangs from the comfort of Emacs!
https://github.com/Aelfsyg/bang.el
I use DuckDuckGo. Its become popular for its emphasis on user privacy in contrast to the giant of the market, Google, who it seems every six months is implicated in unethical behaviour. But honestly, I use DuckDuckGo because it looks nicer. Search for something in Google (bang "g" "foo") and as well as the expected search results, one is shown 'people also ask', top news stories, video thumbnails from the Foo Fighters YouTube channel, and a summary of their latest tweets. When one expects a uniform list of simple search results, one is instead shown a noisy list of varying components.
Bang
And yet I've ended up using DDG for something almost unrelated, Bang. If you search DDG for !x foo, it delegates that search to the website represented by the Bang prefix x and searches it for foo. For example, you might want to search for the bang.el GitHub page, !gh bang.el, or Wikipedia for Emacs, !w Emacs, see here for a list of prefixes. Instead of going to the website's homepage, and then searching, Bang takes you straight to the results page. Called without a term, the Bang will usually take you to the homepage. Without a prefix, it takes you to DGG's first result. If DDG is your browser's default search engine this becomes really fast.
By my estimates, Bang shaves 10–30 seconds off every search, something that I do 10–100 times every day. By the reasoning of this classic xkcd comic, this is exactly the sort of task we should be making more efficient.

Over five years, 15 seconds saved 30 times every day nets us a total of 10 days! (bang "wa" "15 seconds * 30 * 365 * 5")
Another key to greater productivity is cutting out what might disrupt ones flow. If I see the Wikipedia homepage, there's a 50% chance that three hours later I'll have 30 tabs open, and my previous work will be long forgotten. Bang takes me straight to the page I want, after which I can just close the browser.
Examples
!gh- GitHub repositories
!clj- ClojureDocs
!xiny- Learn X in Y minutes, a introduction to any programming language
!pm- PubMed, biomedical papers
!arx- arXiv, physics papers
!wa- WolframAlpha, computational knowledge engine
!ma- MemoryAlpha, the Star Trek Wiki
!awoiaf- A Wiki of Ice and Fire
What is missing from Bang?
There are four types of Bangs that are disallowed:
- those for illegal websites
- those for websites with explicit content
- those that would be overly niche
- those that would violate the website's terms of service
bang.el
bang.el takes this a step further. Not only does one get to skip the process of going to the website, now one can use the Bang from Emacs itself.
browse-url
All bang.el functions ultimately call browse-url. To choose which program is used by Emacs to open the link, one can edit the values of browse-url-browser-function and browse-url-generic-function as documented here.
bang
The general purpose function, that accepts a prefix and the thing to be searched, and opens it with browse-url.
bang-info
This no-argument function takes the caller to the Bang homepage where one can learn more about it.
bang-find-prefix
If one wishes to learn which prefix is needed for which website, this function can be called that uses Bang's own search to find it.
bang.el and Org-roam
What is Org-roam?
A plain-text knowledge management system.
Inspired by Roam Research, org-roam is the best tool for creating a personal wiki, knowledge store, or Zettelkasten. I've accumulated >8500 nodes after a year of use !wa 8500/365 so I can attest to its brilliance. I originally started writing bang.el to quickly view the Wikipedia page of the subject I was writing about. For this reason, the initial name of the project was roam-to-wiki.el and the first function became bang-roam-wiki.el, which searches the current org-roam node in Wikipedia.
I find this to often be the case. A small simple function … we grow scope. Introduce variable where there was none previously.
roam-to-wiki.el became bang-roam.el and then, simply, bang.el. The bang-roam and bang-roam-wiki functions still exist, so you can easily search for a node's title, either in Wikipedia, or anywhere else.
bang-roam
Calls bang with the caller's choice of prefix and the current org-roam node's title.
bang-roam-wiki
Calls bang with the "w" prefix and the current org-roam node's title.
Ideas
I have wondered if bang might be a good tool for improving the experience of searching for documentation whilst programming. I commonly find myself highlighting a symbol and calling (bang "clj" ...) to find its definition in in ClojureDocs. Perhaps a function called bang-docs which determined its prefix from whichever major mode was currently active, and the term defaulting to the symbol at point, would provide, regardless of the language used, an harmonious solution to documentation searching.
There are tools like Zeal and Dash, but I have found them to be a pain as documentation is kept in an external application. Helm Dash might be a good alternative as it supposedly runs everything from inside Emacs, but I have yet to try it out.
The code
| |
Until I can work out how to make org-roam optional, it is required due to the bang-roam functions.
| |
bang is the central function of the package. If there is an active region, it is used as the default TERM argument. See the section on default arguments for an explanation of how this works, and here for more information on elisp's interactive forms.
bang-make-link demonstrates a good habit of extracting sections of code into pure functions.
bang-info and bang-find-prefix just call the functions (bang "bang" "") and (bang "bang" WEBSITE), to access Bang's own information page.
| |
condition-case is elisp's equivalent of a try-catch statement. It executes the bodyform, in this case a call to bang with a prefix and the node's title as its arguments, but in the case that one is not inside a org-roam node it throws an exception. After the bodyform, one can provide a number of handlers of the form (ERROR-TYPE body) which are executed if the throw exception matches the ERROR-TYPE. In bang-roam it just informs the caller that the function cannot be called outside of a org-roam node.
| |
Using the region as a default argument
Previously, the bang function took no notice of whether a region was active or not.
| |
This could be frustrating, as it might require one to carefully retype something that is already present in ones buffer. With thanks to Prof Hugo Nobrega, I've rewritten this function so that if there is an active region it will be displayed in the minibuffer when one is prompted for the term argument, allowing one to either confirm it or enter something different.
| |
This also demonstrates the comprehensive form of interactive that allows one to form a generic list that is that passed as the arguments of the function. buffer-substring copies text from the buffer between two marks, being region-beginning and region-end.
Conclusions
Emacs's extensibility is fantastic — no other editor makes it so easy to write new functions, leveraging existing functions and those of all installed packages. One can just write a tiny elisp file, whereas something like Visual Studio Code requires one to create a new Node.js package. The development experience is also great, being able to define and evaluate in the running editor, the feedback cycle is almost instant. It is vanishingly rare for developers to write their own editor plugins or extensions and yet for Emacs users it is a right of passage from when one first writes ones own init.el.
Elisp's interactive forms are adaptable, and let us create a nice UI / UX. I was quickly frustrated with the interactive short forms s and r, but glad to find out that the longer form where one must create the argument list is not unwieldy.
One should streamline those processes that one spends the most total time on. Some of these tasks are so small that they are basically invisible, but with the frequency with which we do them, they add up to a large total time. A similar approach can be taken to reducing distractions.
One function can snowball into a whole project, this can be good or bad. Will it lead to a piece of work that will be useful to yourself and others? Or will it never be finished, with nothing learnt? What's the pay-off? I've seen mistakes made like this, particularly when one aims for too much abstraction or generalisation, like a layer to abstract over all databases whether they be SQL, document, or graph. But then the opposite is true for something like Git which grew out Linus Torvald's need for a source control system when developing Linux.