Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/hrs/engine-mode

Minor mode for defining and querying search engines through Emacs.
https://github.com/hrs/engine-mode

emacs search

Last synced: 9 days ago
JSON representation

Minor mode for defining and querying search engines through Emacs.

Awesome Lists containing this project

README

        

#+title: =engine-mode=
#+options: toc:nil num:nil

[[https://melpa.org/#/engine-mode][https://melpa.org/packages/engine-mode-badge.svg]]
[[https://stable.melpa.org/#/engine-mode][https://stable.melpa.org/packages/engine-mode-badge.svg]]
[[https://www.gnu.org/licenses/gpl-3.0][https://img.shields.io/badge/License-GPL%20v3-blue.svg]]
[[https://github.com/hrs/engine-mode/actions/workflows/test.yml][https://github.com/hrs/engine-mode/actions/workflows/test.yml/badge.svg?branch=main]]

~engine-mode~ is a global minor mode for Emacs. It enables you to easily define
search engines, bind them to keybindings, and query them from the comfort of
your editor.

#+ATTR_HTML: :alt Demo searching for a term, with the results opening in a browser window.
#+ATTR_HTML: :width 100%
[[file:./doc/demo.gif]]

For example, suppose we want to be able to easily search GitHub:

#+begin_src emacs-lisp
(defengine github
"https://github.com/search?ref=simplesearch&q=%s")
#+end_src

This defines an interactive function ~engine/search-github~. When executed it will
take the selected region (or prompt for input, if no region is selected) and
search GitHub for it, displaying the results in your default browser.

The ~defengine~ macro can also take an optional key combination, prefixed with
~engine/keymap-prefix~ (which defaults to =C-x /=). That keybinding will be wrapped
in a call to ~kbd~.

#+begin_src emacs-lisp
(defengine duckduckgo
"https://duckduckgo.com/?q=%s"
:keybinding "d")
#+end_src

=C-x / d= is now bound to the new function ~engine/search-duckduckgo~! Nifty.

If you'd like to see a video on the whys and wherefores of this mode, check out
[[https://www.youtube.com/watch?v=MBhJBMYfWUo][the talk @hrs gave at EmacsNYC]].

** Installation

~engine-mode~ is available on MELPA.

Using ~use-package~:

#+begin_src emacs-lisp
(use-package engine-mode
:ensure t

:config
(engine-mode t))
#+end_src

You can also install it like any other elisp file by adding it to your load path
and globally enabling it:

#+begin_src emacs-lisp
(require 'engine-mode)
(engine-mode t)
#+end_src

** Changing your default browser

~engine-mode~ uses the ~engine/browser-function~ variable to determine which browser
it should use to open the URL it constructs. To change the default browser,
redefine ~engine/browser-function~. For example, to always use Emacs' built-in ~eww~
browser:

#+begin_src emacs-lisp
(setq engine/browser-function 'eww-browse-url)
#+end_src

~engine/browser-function~ defaults to ~browse-url-browser-function~, which Emacs
uses globally to open links.

The implementation of the ~browse-url-browser-function~ variable contains a
comprehensive list of possible browser functions. You can get to that by hitting
=C-h v browse-url-browser-function = and following the link to
=browse-url.el=.

** Changing your browser on a per-engine basis

To only change the browser for a single engine, use the ~:browser~ keyword
argument when you define the engine. For example, to use ~eww~ only for your
GitHub search results, try:

#+begin_src emacs-lisp
(defengine github
"https://github.com/search?ref=simplesearch&q=%s"
:browser 'eww-browse-url)
#+end_src

As mentioned about, see the implementation of the ~browse-url-browser-function~
for a definitive list of browsers.

** Changing the keymap prefix

The default keymap prefix for ~engine-mode~ is =C-x /=. If you'd like to bind
the keymap to an additional prefix (say, =C-c s=), you totally can:

#+begin_src emacs-lisp
(engine/set-keymap-prefix (kbd "C-c s"))
#+end_src

If you use ~use-package~, you can achieve the same thing with:

#+begin_src emacs-lisp
:bind-keymap ("C-c s" . engine-mode-prefixed-map)
#+end_src

** Custom docstrings

~defengine~ assigns each engine a reasonable default docstring, but you can
override that on a case-by-case basis with the ~:docstring~ keyword argument:

#+begin_src emacs-lisp
(defengine ctan
"https://www.ctan.org/search/?x=1&PORTAL=on&phrase=%s"
:docstring "Search the Comprehensive TeX Archive Network (ctan.org)")
#+end_src

** Modifying the search term before sending it

An engine might want to transform a search term in some way before it
interpolates the term into the URL. Maybe the term should have a different
encoding, or be capitalized differently, or, uh, be passed through [[https://en.wikipedia.org/wiki/ROT13][ROT13]].
Whatever the reason, you can apply a custom transformation to a search term by
passing a function to ~defengine~ through the ~:term-transformation-hook~ keyword
argument.

For example, to UPCASE all of your DuckDuckGo searches:

#+begin_src emacs-lisp
(defengine duckduckgo
"https://duckduckgo.com/?q=%s"
:term-transformation-hook upcase)
#+end_src

Or, to ensure that all your queries are encoded as latin-1:

#+begin_src emacs-lisp
(defengine diec2
"dlc.iec.cat/results.asp?txtEntrada=%s"
:term-transformation-hook (lambda (term) (encode-coding-string term latin-1))
:keybinding "c")
#+end_src

You could also use a ~:term-transformation-hook~ to make an engine behave
differently when given a [[https://www.gnu.org/software/emacs/manual/html_node/elisp/Prefix-Command-Arguments.html][prefix argument]] (i.e. typing =C-u= before invoking the
engine).

Some search engines support querying for exact phrases by enclosing the search
string with double quotes. Transformations could be useful in this case to
perform a literal search instead if the universal argument is present:

#+begin_src emacs-lisp
(defengine duckduckgo
"https://duckduckgo.com/?q=%s"
:term-transformation-hook (lambda (term) (if current-prefix-arg
(concat "\"" term "\"")
term))
:keybinding "d")
#+end_src

Typing =C-x / d= will perform a regular search, but typing =C-u C-x / d= will
wrap your query in quotes before searching for it. That's especially useful when
searching for the contents of the region.

** Importing keyword searches from other browsers

Since many browsers save keyword searches using the same format as engine-mode
(that is, by using ~%s~ in a url to indicate a search term), it's not too hard to
import them into Emacs.

[[https://github.com/sshaw][@sshaw]] has written a script to [[https://gist.github.com/sshaw/9b635eabde582ebec442][import from Chrome on OS X]]. Thanks for that!

** Comparison with =webjump=

Emacs has a perfectly lovely built-in =webjump= package which allows the user to
define a set of URLs, interpolate search terms into them, and visit them in the
browser.

Why might you use =engine-mode= instead of =webjump=?

- You want to bind specific searches to keybindings. Because =engine-mode= defines
a function for each engine, keybindings in =engine-mode= can be associated
directly with specific searches.
- You'd like to associate browser functions with engines on a case-by-case
basis. For example, if you want to perform some searches in Firefox, and other
searches in =eww=, that's trivial in =engine-mode=.
- You like some of =engine-mode='s minor UI conveniences. If you've got a region
selected, for example, =engine-mode= will use that as the search query, while
=webjump= will ignore it and offer an empty prompt.

If you're not interested in these features, =webjump= is a great choice! Honestly,
the author of =engine-mode= probably wouldn't have bothered writing it if they'd
known =webjump= existed at the time. :sweat_smile:

** Engine examples

#+begin_src emacs-lisp
(defengine amazon
"https://www.amazon.com/s/ref=nb_sb_noss?url=search-alias%3Daps&field-keywords=%s")

(defengine duckduckgo
"https://duckduckgo.com/?q=%s"
:keybinding "d")

(defengine github
"https://github.com/search?ref=simplesearch&q=%s")

(defengine google
"https://www.google.com/search?ie=utf-8&oe=utf-8&q=%s"
:keybinding "g")

(defengine google-images
"https://www.google.com/images?hl=en&source=hp&biw=1440&bih=795&gbv=2&aq=f&aqi=&aql=&oq=&q=%s")

(defengine google-maps
"https://maps.google.com/maps?q=%s"
:docstring "Mappin' it up.")

(defengine project-gutenberg
"https://www.gutenberg.org/ebooks/search/?query=%s")

(defengine qwant
"https://www.qwant.com/?q=%s")

(defengine stack-overflow
"https://stackoverflow.com/search?q=%s")

(defengine twitter
"https://twitter.com/search?q=%s")

(defengine wikipedia
"https://www.wikipedia.org/search-redirect.php?language=en&go=Go&search=%s"
:keybinding "w"
:docstring "Searchin' the wikis.")

(defengine wiktionary
"https://www.wikipedia.org/search-redirect.php?family=wiktionary&language=en&go=Go&search=%s")

(defengine wolfram-alpha
"https://www.wolframalpha.com/input/?i=%s")

(defengine youtube
"https://www.youtube.com/results?aq=f&oq=&search_query=%s")
#+end_src