Cuaderno de GNUtas

Lanzadores interactivos en Emacs: Ido y Helm

Lo que podríamos denominar «lanzadores interactivos (y autocompletivos)» constituyen todo un universo propio dentro de GNU Emacs, además de ser una gran fuente de felicidad. Podemos escoger entre varios, que aunque sean diversas maneras de entender el concepto, éste se resume en la acción de desplegar una lista de cosas que se va acotando y abreviando a mendida que escribimos en el minibúfer, hasta que por fin hallamos el candidato que buscábamos. Y sobre este candidato, una vez individualizado, se pueden aplicar una o varias acciones.

En mi uso cotidiando de Emacs mis preferencias se decantan por una combinación de Ido y Helm, de entre la variada oferta de esos lanzadores. Ido, que viene de fábrica en Emacs, es ligero y rápido, de modo que lo tengo dedicado a acciones que requieren una pareja rapidez y agilidad, sin tener que pararme a pensar mucho. Helm, por contra, que es una biblioteca externa (lo podemos instalar desde Melpa), resulta algo más pesado e intrusivo que Ido. Pero son tantas las virtudes que tiene que es imposible no enamorarse de él. Aunque a éste lo tengo reservado para acciones donde suelo demorarme un poco más. Algo de barroquismo en ocasiones, o en dosis controladas, no está reñido con la practicidad. Comentaré a continuación sobre el uno y el otro, así como de la configuración que les aplico y me resulta más eficaz y confortable.

Ido, el rápido

Como decía más arriba, éste nos viene dentro de Emacs, por tanto si lo queremos usar bastaría con evaluar el ido-mode en nuestro archivo de inicio. Yo no lo hago así, porque no quiero tener a Ido hasta en la sopa, sino sólo en unos pocos escenarios. Por ejemplo, para saltar de un búfer a otro mediante la orden clásica C-x b. Por tanto, lo que tengo en mi inicio es:

(ido-mode 'buffers)

También añado este par de variables para que me ignore ciertos búferes molestos cada vez que despliegue su lista de los que están abiertos, y para dar una preferencia en esa lista dependiendo del tipo de archivo.

(setq ido-ignore-buffers '("^ " "*Completions*" "*Shell Command Output*"
               "*Messages*" "Async Shell Command" "KILL"))
(setq ido-file-extensions-order '(".org" ".tex" ".emacs" ".pdf" ".txt"))

Otra acción para la que Ido me viene perfecto es para llamar a funciones y comandos interactivos desde una lista. Sí, el M-x de toda la vida. Lo mejor para esto es instalar el paquete smex, que se integra a las mil maravillas con Ido. Mi configuración en el archivo de inicio es la siguiente. Primero, claro, requerimos smex:

(require 'smex)

Y le asociamos el antiguo atajo M-x, si bien mantenemos el comportamiento por defecto del execute-extended-command de Emacs anclado al atajo que le sigue a continuación:

(global-set-key (kbd "M-x") 'smex)
  (global-set-key (kbd "M-X") 'smex-major-mode-commands)
  ;; El antiguo comportamiento de M-x.
  (global-set-key (kbd "C-c C-c M-x") 'execute-extended-command)

Ido, ¿mejor en vertical?

Me cuento entre aquellos a quienes no les gusta el aspecto por defecto de Ido, donde la lista interactiva se despliega en horizontal (un poco al estilo de la conocida aplicación dmenu). Es curioso, pero a mí esa manera de mostrar los candidatos pasando como en desfile me resulta confusa y mareante. Por suerte, contamos con el ido-vertical-mode, también instalable desde el repositorio Melpa. Mi configuración se parece a ésta:

(require 'ido-vertical-mode)
(ido-vertical-mode 1)

A lo que le añado esta variable para navegar por Ido (por cualquier Ido que se abra) con las teclas de arriba/abajo y las secuencias C-n / C-p, indistintamente:

(setq ido-vertical-define-keys 'C-n-C-p-up-and-down)

ido-helm1.png

Figura 1: Ido lanzando aplicaciones con smex

Idos caseros

Una cosa muy grata de este lanzador es la facilidad con que podemos crearnos nuestras propias versiones para ejecutar acciones personalizadas. Eso ya va en gustos y necesidad. En mi caso, cada vez que quiero tener a mano una lista rápida sobre la que ejecutar determinada acción, sin más adornos, requiero sin pensármelo dos veces a los fiables Ido. Y tengo definidos unos cuantos. La estructura, no obstante, de lo que escribo a la hora de crear uno nuevo casi siempre es:

  1. (Si es necesario) definir una variable que tenga como valor una lista de cosas.
  2. Crear una función que (re)genere la lista a fin de entregársela a Ido y que él la pueda gestionar. Aquí suele ser muy útil contar con una expresión mapcar rondando por en cuerpo de la función.
  3. La función de Ido propiamente dicha, que despliega la lista anterior y ejecuta una determinada acción sobre el candidato escogido.

Un ejemplo de lo dicho lo tenemos en este Ido que me escribí hace no mucho para lanzar aplicaciones externas en Exwm. Primero, la función que genera la lista de aplicaciones, actualizada:

(defun crea-lista-programas ()
  (interactive)
  (seq-mapcat
   (lambda (bin)
     (directory-files bin nil "^[^.]"))
   exec-path))

Y a continuación el lanzador Ido, que ejecuta el comando start-process-shell-command sobre la aplicación-candidata seleccionada.

(defun ido-lanzador (comando)
  (interactive  (list (ido-completing-read "$: " (crea-lista-programas) nil t)))
  (start-process-shell-command comando nil comando))

Rápido, limpio y eficaz. Son las mayores virtudes del entrañable, aunque no suficientemente conocido Ido Mode.

Helm, el polivalente

Pero Ido se ve limitado a realizar una única acción sobre el candidato electo de la lista, y de entrada no está diseñado para aplicar un menú de acciones sobre el elemento con el foco. Lo cual es, precisamente, uno de los puntos fuertes de Helm. Si desplegamos, en efecto, una lista Helm y pulsamos la tecla tabulador obtendremos al instante un menú de acciones para ejecutar sobre la opción que tiene el foco. La acción por defecto, la primera, es la que se invoca simplemente con la tecla ENTER, al igual que sucede en Ido. Pero si queremos ejecutar otras cosas, el menú de acciones puede llegar a ser en ocasiones no sólo variopinto y heterogéneo sino hasta intimidador y difícil de digerir en un primer vistazo.

Helm, como ya comentamos antes, es una biblioteca externa a Emacs (y a la vez el framework para desarrollar nuevas extensiones de ella) que podemos instalar fácilmente desde Melpa. Viene ya con muchísimas extensiones, todas ellas comenzando por «helm-lo-que-sea». Si a eso le sumamos las extensiones escritas por terceros, la oferta resulta sin duda apabullante. Incluso un servidor no hace mucho sucumbió a la fiebre de escribir extensiones para Helm, y acabé perpetrando esta (modesta, bien es cierto) para gestionar las fuentes en los documentos de LATEX.

De entre las extensiones que vienen ya de fábrica con Helm, diré un par de palabras a continuación sobre aquellas que considero imperdibles en mi uso diario de Emacs, y que suelo tener asociadas a un atajo de teclado. A saber.

helm-locate

Una maravilla, para buscar archivos y directorios en un periquete mediante el comando locate. La acción por defecto es, naturalmente, visitar el archivo seleccionado. Pero el menú de acciones secundarias es de lo más prodigo y prolijo. Destaco, por su utilidad, cosas como poder visitar el directorio en que está el archivo, adjuntar el archivo a un correo electrónico, introducirlo como enlace en un documento de Org, compararlo con otro fichero o buscar contenidos de pdf con pdfgrep. Pero hay muchas más.

helm-find-files

Esta es otra de mis indispensables. Como sólo tiene sentido llamarla cuando estamos dentro del navegador de archivos de Emacs, Dired, lo asocié sin pensármelo dos veces a un atajo de éste último, concretamente a la letra «f»:

(with-eval-after-load 'dired
    (define-key dired-mode-map (kbd "f") 'helm-find-files))

Es ideal para buscar y acotar archivos en directorios extensos de Dired. Las acciones secundarias (de nuevo, muchísimas) a que más recurro son las de renombrado masivo (incluso con numeración añadida) o las de incluir el archivo que tiene el foco como adjunto a un correo electrónico. Para renombrar y numerar archivos en Dired, por cierto, escribí hace tiempo esta función casera, que viene a hacer lo mismo. Si en una lista de helm-find-files queremos marcar más de un archivo, hacemos como si estuviésemos en un texto normal: control más espacio para activar la marca y teclas arriba/abajo para extenderla.

ido-helm3.png

Figura 2: Menú de acciones en helm-find-files

Helm-bookmarks

Yo uso muchísimo los marcadores de Emacs, así que tenerlos a mano y poder realizar muchas (una vez más) y variopintas acciones sobre ellos es como viajar en primera clase.

Helm-surfraw

Para entender helm-surfraw conviene entender antes qué es surfraw. Y éste no es otra cosa que una serie de scripts, escritos en origen por Julian Assange, dedicados a lanzar toda clase de búsquedas desde la terminal en Internet y en los sitios más diversos y hasta peregrinos, desde DuckDuckGo y Google a la Wikipedia, pasando por Github, Bugzilla, Ebay e incluso la página de la CIA. Cada tipo de búsqueda está gestionada por un script menor, que dentro de este peculiar ecosistema atiende al nombre de «elvi». Los «elvis» disponibles, como decimos, son legión. Pero también el usuario puede escribirse los suyos propios si aprende cómo se hace, que no es muy complicado.

Para que esta extensión de Helm tenga sentido, por tanto, hay que instalar antes surfraw, un paquete que suele estar disponible en las distribuciones de GNU/Linux más comunes. Mi configuración es la siguiente. Por defecto quiero lanzar helm-surfraw para que las búsquedas se abran en el navegador interno de Emacs, el eww-mode. Así que, primero, defino esto:

(defun mi-helm-surfraw ()
  (interactive)
  (let
      ((browse-url-browser-function 'eww-browse-url))
    (call-interactively 'helm-surfraw)))

Y esta función lanza una u otra variante dependiendo de si lleve o no el prefix-argument C-u:

(defun lanza-helm-surfraw ()
  (interactive)
  (if (equal current-prefix-arg nil)
      ;; sin C-u
      (mi-helm-surfraw)
    ;; con C-u (abre las búsquedas en firefox)
    (call-interactively 'helm-surfraw)))

Cuando llamamos a la extensión, se nos pregunta por el término a buscar y, acto seguido, Helm despliega una lista con los buscadores disponibles (esto es, los «elvis» de marras). Un plus agradable es que también conserva un histórico de los buscadores recientemente cpnsultados.

Y más helm-(…)

Las que he descrito las tiendo a usar bastante. Pero la lista de extensiones de Helm puede perpetuarse casi hasta el infinito. Destaquemos, por ir echando el cierre, a helm-info (para buscar en las páginas info de Emacs: utilísima, sobre todo si escribimos código); a helm-man-woman (para navegar por las man pages o, en cristiano, las páginas de manual de Gnu/Linux); a helm-colors (para listar colores, guardar sus nombres hexadecimales y muchas otras acciones bastante prácticas, también a la hora de escribir algo de código que toque cuestiones de aspecto); a helm-show-kill-ring (para buscar por el kill-ring, es decir, el portapapeles de Emacs). Y eso sólo para las extensiones nativas. Si nos metemos ya a bucear entre las de terceros, por cierto da para no aburrirse. Entre este grupo, destacaría helm-bbdb, muy útil para listar y buscar entre nuestros contactos almacenados en la big brother data base de Emacs. Y se integra muy bien con el cliente de correo y otras mensajerías emacsiano Gnus. Pero de esta extensión hablaremos en otra GNUta, a cuento de Emacs y los correos electrónicos.

ido-helm2.png

Figura 3: helm-surfraw en todo su esplendor

Publicado: 12/06/20

Última actualización: 21/01/22


Índice general

Acerca de...

Esta obra está bajo una licencia de Creative Commons Reconocimiento-NoComercial 4.0 Internacional.

© Juan Manuel Macías
Creado con esmero en
GNU Emacs