SGCG

…esto no es un subtítulo…

Ir a: contenido categorías calendario archivo suscripción

Volver arriba

Emacs Lisp en casos prácticos (4): cargar todos los ficheros de Emacs Lisp de un directorio

2015-07-16

GNU Emacs es un editor de textos potentísimo. Por debajo, es un intérprete de Emacs Lisp, un lenguaje de programación de la familia de Lisp. En esta serie de artículos vamos a mostrar algunas formas de adaptar Emacs a nuestras necesidades con algunos casos prácticos.

Hoy vamos a crear unas funciones para cargar todos los ficheros de Emacs Lisp de un directorio. Muchos usuarios de Emacs tienen configuraciones complicadas que están repartidas en ficheros diferentes dedicado cada uno a cubrir cierta funcionalidad; en tales casos puede ser útil tener una función para cargar todo el código de un directorio.

El problema

La función load sirve para cargar un fichero de Emacs Lisp, pero no para cargar varios. Queremos crear una función nueva, load-directory, encargada de hacer esto. También puede ser interesante hacer que esta función trabaje de forma recursiva en un árbol de directorios.

Función de carga

Si asumimos que disponemos de una función llamada file-name-is-elisp? que determina si un nombre de fichero hace referencia a código de Emacs Lisp, la carga de todos los ficheros de Emacs Lisp de un directorio es fácil. Podríamos hacer lo siguiente:

(defun load-directory (directory) "Load the Emacs Lisp files in a DIRECTORY." (dolist (name (directory-files directory t)) (when (and (file-regular-p name) (file-name-is-elisp? name)) (load (file-name-sans-extension name)))))

Esta función lista el contenido de un directorio con directory-files, itera sobre esta lista y carga todos los ficheros regulares de Emacs Lisp (que cumplen file-regular-p y file-name-is-elisp?) y los carga con load. Para este paso de la carga, hay que usar el nombre de fichero sin extensión (lo que se consigue conj file-name-sans-extension).

La función anterior está bien, pero no es recursiva. Para cargar el contenido de un árbol de directorios de forma recursiva, añadimos un parámetro opcional y complicamos un poco la función:

(defun load-directory (directory &optional recursive) "Load the Emacs Lisp files in a DIRECTORY. Operate recursively in its subdirectories if RECURSIVE is non-nil." (dolist (name (directory-files directory t)) (if (not (string-match "/\\.[^/]*$" name)) (if (file-directory-p name) (when recursive (load-directory name t)) (when (file-name-is-elisp? name) (load (file-name-sans-extension name)))))))

La primera comprobación que hacemos, la de string-match, sirve para descargar entradas que empiezan con un punto, que en *nix son ficheros ocultos y también incluyen la entrada «.» (el directorio actual) y la entrada «..» (el directorio padre), con lo que es necesario dejar fuera estos elementos para procesar el árbol correctamente. Lo demás es muy similar, pero añadimos la posibilidad de operar de forma recursiva si nos encontramos con un subdirectorio (comprobado con file-directory-p). La comprobación file-regular-p queda fuera al ser casi complementaria con file-directory-p. Si realmente necesitáramos comprobar que trabajamos con ficheros regulares, podríamos añadir la instrucción correspondiente.

Si en un directorio hay algún fichero de Emacs Lisp presente tanto en su forma original como compilada, la función anterior lo carga varias veces. Para evitarlo, podemos llevar una lista de entradas vistas:

(defun load-directory (directory &optional recursive) "Load the Emacs Lisp files in a DIRECTORY. Operate recursively in its subdirectories if RECURSIVE is non-nil." (let ((visited nil)) (dolist (name (directory-files directory t)) (if (not (string-match "/\\.[^/]*$" name)) (if (file-directory-p name) (when recursive (load-directory name t)) (when (file-name-is-elisp? name) (let ((file (file-name-sans-extension name))) (unless (member file visited) (push file visited) (load file)))))))))

Comprobar si un nombre de fichero es de Emacs Lisp

Queda escribir la función file-name-is-elisp?. Esta función es muy fácil: extrae la extensión (con file-name-extension) del fichero que se le pasa y la busca en load-suffixes, que contiene las extensiones (con punto delante, así que como file-name-extension devuelve la extensión sin punto, habrá que añadirlo para poder comparar). La función es así:

(defun file-name-is-elisp? (file-name) "Say if the extension of a FILE NAME implies Emacs Lisp code. This is done by checking if the extension is present in `load-suffixes'." (let ((file-suffix (concat "." (downcase (file-name-extension file-name)))) (emacs-lisp-suffixes (mapcar 'downcase load-suffixes))) (if (member file-suffix emacs-lisp-suffixes) t nil)))

Hacemos las comparaciones sin distinguir entre mayúsculas y minúsculas; de ahí las llamadas a downcase.

Uso

Digamos que tenemos un árbol de directorios repleto de código de Emacs Lisp en ~/.emacs.d/custom/. Podemos cargar todo este código dentro de ~/.emacs.d/custom/ y sus subdirectorios con la siguiente orden que podría ir dentro de nuestro ~/.emacs.d/init.el:

(load-directory "~/.emacs.d/custom/" t)


Categorías: Informática

Permalink: http://sgcg.es/articulos/2015/07/16/emacs-lisp-en-casos-practicos-4-cargar-todos-los-ficheros-de-emacs-lisp-de-un-directorio/