SGCG

…esto no es un subtítulo…

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

Volver arriba

Emacs Lisp en casos prácticos (9): ejecutar un programa externo periódicamente (3)

2015-07-26

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.

El monitor en acción.
El monitor en acción.

En el artículo de hoy, continuación del anterior, creamos unas funciones para ejecutar un programa externo periódicamente y mostrar su salida en un búfer: una réplica de las herramientas watch de GNU/Linux, cmdwatch de FreeBSD y gnuwatch de OpenBSD.

El código de este artículo funciona en GNU Emacs 24.4.

Hoy terminamos el trabajo empezado en el artículo anterior.

Cancelación del temporizador al cerrar el búfer

Ayer quedó por describir la función hook que se ejecuta al cerrar el búfer y que sirve para cancelar el temporizador en marcha. Es muy sencilla:

(defun watch--kill-buffer-hook () "Cancel the currently running timer when killing the buffer." (when watch--timer (cancel-timer watch--timer)))

Ejecución del programa externo

El temporizador watch--timer que creamos ayer se encarga de llamar a la función watch--calback cada vez que salta el tiempo de espera. Esta función recibe como argumento el búfer en el que tiene que trabajar y lo primero que hace es elegir tal búfer, ya que Emacs puede estar operando en cualquier otro sitio en el momento en el que salta el temporizador. Es así:

(defun watch--callback (buffer) "Callback function to be executed by Watch mode's timer. To be executed on Watch's BUFFER." (with-current-buffer buffer (let ((inhibit-read-only t)) (erase-buffer) (watch--insert-header-text)) (setq watch--process (start-process-shell-command watch--command buffer watch--command)) (set-process-filter watch--process 'watch--filter) (set-process-sentinel watch--process 'watch--sentinel) (let ((inhibit-read-only t)) (watch--show-command-state))))

Analicémosla punto por punto:

Cabecera y notificación del estado del programa externo

La cabecera contendrá la fecha en la que se ordenó la ejecución del programa externo (obtenida con current-time-string), el intervalo de tiempo entre ejecuciones (nuestra variable watch--interval), la orden con la que se ejecuta el programa externo (nuestra variable watch--command) y el estado de la ejecución del programa externo. Este último dato lo escribiremos con la función watch--show-command-state, mientras que lo primero irá con watch--show-header-text:

(defun watch--insert-header-text () "Insert the header for the current watch process." (let ((text (format "%s\nEvery %g s: %s\n\n\n" (current-time-string) watch--interval watch--command))) (insert (propertize text 'face 'bold)))

(defun watch--show-command-state () "Show the state of the currently Watched command." (save-excursion (goto-char (point-min)) (forward-line 2) (delete-region (progn (beginning-of-line) (point)) (progn (end-of-line) (point))) (let ((text (format "Command state: %s" (process-status watch--process)))) (insert (propertize text 'face 'bold)))))

Lo interesante de estas funciones es que escriben en texto en negrita gracias al uso de propertize.

La función watch--show-command-state mueve el punto de un lado para otro internamente, así que protege la posición inicial con save-excursion.

Función filtro

La función filtro watch--filter se limita a escribir en el búfer la salida del programa externo:

(defun watch--filter (process string) "Filter function for the output STRING of Watch mode's running command PROCESS." (let ((buffer (process-buffer process))) (when (buffer-live-p buffer) (with-current-buffer buffer (let ((inhibit-read-only t)) (save-excursion (goto-char (point-max)) (insert string)))))))

La función recibe como argumentos el proceso (process) y la salida más reciente del programa (string). Obtiene el búfer de trabajo, que es el asociado al proceso, con process-buffer. Solamente si el búfer está vivo (buffer-live-p), hace lo siguiente: selecciona este búfer con with-current-buffer porque Emacs podría estar en cualquier otro lugar. Después, inhibe la protección de escritura (afirma la variable inhibit-read-only y escribe el texto (con insert) al final del búfer (goto-char a la salida de point-max, que es la posición final, tal como indica la documentación de end-of-buffer que habría que hacer en los programas escritos en Emacs Lisp). La función mueve el punto internamente; para evitar que estos movimientos queden reflejados tras ejecutar la función, la posición está protegida con save-excursion.

Función centinela

La función centinela watch--sentinel se ejecuta cuando el proceso del programa externo cambia de estado. La función recibe como argumentos el proceso y el estado nuevo. La lógica inicial es similar a la de watch--filter, pero no escribe texto del programa, sino que se limita a actualizar la línea de estado con watch--show-command-state.

(defun watch--sentinel (process state) "Sentinel function for Watch. Run when the Watched PROCESS changes its STATE." (let ((buffer (process-buffer process))) (when (buffer-live-p buffer) (with-current-buffer buffer (let ((inhibit-read-only t)) (watch--show-command-state))))))

El código completo

El modo completo está disponible para descarga en el siguiente enlace: watch-mode.el


Categorías: Informática

Permalink: http://sgcg.es/articulos/2015/07/26/emacs-lisp-en-casos-practicos-9-ejecutar-un-programa-externo-periodicamente-3/