Easi lets you search multiple information sources at once—it is a federated search system in Emacs. Search engines or other sources of information are declared once in the init file and arbitrary groups can be used together. After searching, the results are collected together, sorted, and presented in Emacs. They can be viewed as a whole collection or individually, or both. Configurable and user-defined actions can be run on the results. Every aspect of Easi’s operation is meant to be highly configurable.
The project is not currently on MELPA or Gnu ELPA. You can install it manually, or with a helper like straight. A pure straight form might look like this:
(straight-use-package
'(easi :type git :host github :repo "Hugo-Heagren/easi"))
Easi aggregates (‘federates’) results obtained by sending the same
query to multiple search engines. When called interactively, it
prompts for search engines to use from a customisable list
easi-searchables
. A searchable is any of:
- an
easi-search-engine
object (the most basic searchable type) - an
easi-search-engine-group
object (when certain combinations of searchables are used together often, a group can be defined. The group is effectively an alias for just using them all as usual) - a list of searchables
- a symbol whose value is a searchable
So if engine-foo
and engine-bar
are easi-search-engine
s, and
group-foo
and group-bar
are easi-search-engine-group
s, all the
following are valid searchables:
engine-foo
(engine-foo engine-bar)
(group-foo (engine-foo group-bar))
(engine-foo ((engine-foo) engine-bar) (group-foo group-bar))
The most important configuration is to populate ~easi-searchables~.
Easi is designed so that searchables can be defined and shared as Emacs packages. Eventually I might get round to doing that, but for now there aren’t any. As an example though, you might define a searchable like this:
(require 'url)
(require 'easi-result)
(defun my/ddg-get-result-from-divs (divs)
(cl-loop for div in divs
collect
(let* ((raw (easi--structured-object-get-field '(cdr 2 cdr) div))
(title (easi--structured-object-get-field
'(4 3 2) raw))
(url (easi--structured-object-get-field
'(8 1 "href" url-generic-parse-url url-path-and-query
cdr url-parse-query-string "uddg" car)
raw)))
`(("title" . ,title)
("url" . ,url)))))
(setq my/duckduckgo-search-engine
(easi-search-engine-create
:name "DuckDuckGo"
:suggestions-getter "https://duckduckgo.com/ac/?q=%s&client=opensearch&type=list"
:suggestion-post-processor
'(easi-utils-get-http-body easi-utils-json-parse-with-lists 1)
:queryable-results-getter "https://html.duckduckgo.com/html/?q=%s&client=opensearch&type=list"
:results-post-processor
'(easi-utils-parse-html-body
2 20 9 3 3
cddr
(lambda (ls) (cl-delete-if #'stringp ls))
(lambda (ls) (butlast ls 3))
my/ddg-get-result-from-divs)
:field-aliases '(("id" . "url"))))
(add-to-list 'easi-searchables 'my/duckduckgo-search-engine)
Easi’s main entry point is easi-search
. Once easi-searchables
is
configured, call this function, select some search engines, and enter
a query.
This package was largely inspired by engine mode, and the search facilities in w3m. I am also indebted to the frankly astonishing documentation of SWIRL, the only other generic federated searching package I have found so far.