#+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]]

~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 <RETURN>= 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

** 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!

** 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
