…esto no es un subtítulo…
lu | ma | mi | ju | vi | sá | do |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | 31 | ||
2015-07-31
Continuamos con nuestra serie de artículos dedicados a palabras raras, llamativas o divertidas. La palabra de hoy es «sicofanta», una palabra rimbombante como pocas que hace referencia a una persona que acusa falsamente a otra de algo: un calumniador. La palabra viene del griego «συκοφάντης», algo así como «revelador de higos», pero inmediatamente referido a las personas que se aprovechan del sistema legal acusando falsamente. Hay varias hipótesis sobre qué tiene que ver el tema de los higos con esto: quizá se refiere a quienes acusaban de robo o contrabando de higos para obtener una recompensa o quizá se refiere a un gesto obsceno hecho por una parte acusadora excesivamente impetuosa.
Es interesante que la palabra inglesa «sycophant» no se refiere a los calumniadores, sino a los que adulan falsamente. Hay un tema común: ambos significados tratan sobre personas que actúan con falta de sinceridad, posiblemente para obtener un beneficio injusto.
Categorías: Lingüística
Permalink: https://sgcg.es/articulos/2015/07/31/palabras-curiosas-11-sicofanta/
2015-07-29
La típica caja de cartón de 2 kg de galletas María tiene el tamaño adecuado para almacenar tres zapatos. Dos cajas puestas una junto a la otra, por lo tanto, sirven para almacenar tres pares de zapatos con la mínima inconveniencia de que uno de ellos queda dividido por el tabique central formado por las paredes contiguas de las cajas. Con suficientes cajas apiladas y unidas con cinta de doble cara, sale un mueble zapatero humilde, pero funcional, que permite aprovechar bien el espacio en un armario o una estantería.
Zapatero de bajo coste.
Categorías: DIY
Permalink: https://sgcg.es/articulos/2015/07/29/idea-zapatero-hecho-con-cajas-de-galletas/
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.
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.
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)))
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:
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.
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.
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 modo completo está disponible para descarga en el siguiente enlace: watch-mode.el
Categorías: Informática
2015-07-25
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.
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.
El código del artículo anterior tiene algunas deficiencias:
Cada vez que termina la ejecución del programa externo con async-shell-command, aparece un mensaje en el minibúfer. Esto puede ser muy molesto, así que buscaremos una forma de evitarlo.
Además de esto, puede ser interesante mostrar alguna información adicional en la parte superior del búfer: el momento en el que se ejecutó por última vez el programa externo, el tiempo que transcurre entre ejecuciones, qué programa se ejecuta y en qué estado se encuentra la ejecución actual. Finalmente, como queremos poder detener y reanudar la monitorización (la ejecución periódica del programa externo), también es conveniente mostrar si estamos en pausa o en acción; la línea de modo es un buen lugar para mostrar esta información.
En vez de hacer malabarismos alrededor de async-shell-command (la función que utilizábamos para ordenar la ejecución del programa externo), vamos a usar una función de un nivel un poco más bajo, start-process-shell-command, que nos permite un control más detallado de lo que estamos haciendo.
Vamos a crear un modo mayor nuevo, watch-mode, que recogerá la funcionalidad.
Aunque no es la forma más limpia de trabajar, vamos a definir 4 variables ligadas al búfer en el que se ejecuta nuestro watch y que servirán para recoger el estado del modo:
(defvar watch--command nil
"Shell command executed by Watch.")
(defvar watch--interval 0
"Number of seconds Watch will wait between command
executions.")
(defvar watch--process nil
"Currently running Watch process.")
(defvar watch--timer nil
"Currently running Watch timer.")
(make-variable-buffer-local 'watch--command)
(make-variable-buffer-local 'watch--interval)
(make-variable-buffer-local 'watch--process)
(make-variable-buffer-local 'watch--timer)
Crearemos un búfer de monitorización igual que antes: con la función interactiva watch. El código es ahora muy sencillo gracias a que la lógica está encapsulada en otras funciones:
(defun watch (command interval)
"Run shell COMMAND every INTERVAL seconds showing the results
in a dedicated buffer."
(interactive (list (read-shell-command "Shell command: " nil nil)
(read-number "Update interval (seconds): ")))
(let* ((buffer-name (format "*Watch: %s*" command))
(buffer (generate-new-buffer buffer-name)))
(switch-to-buffer buffer))
(watch-mode)
(watch--start command interval)
(add-hook 'kill-buffer-hook
'watch--kill-buffer-hook nil t))
Lo que hacemos es crear un búfer nuevo (igual que antes), ponerlo en nuestro nuevo modo watch-mode. Seguidamente, hacemos todas las labores de arranque del proceso de monitorización con una función que llamaremos watch--start. Finalmente, añadimos una función hook a kill-buffer-hook para cancelar el temporizador cuando se cierra el búfer.
El nuevo modo mayor watch-mode deriva de special-mode, un modo que está pensado para contenido generado dinámicamente y que no es texto que queramos editar. La declaración de nuestro modo se encarga únicamente de asignar algunas órdenes del teclado:
(define-derived-mode watch-mode special-mode "Watch"
"Major mode for executing periodically an external shell
command."
(local-set-key (kbd "i") 'watch-set-interval)
(local-set-key (kbd "p") 'watch-pause)
(local-set-key (kbd "r") 'watch-resume))
El modo special-mode hace que el búfer sea de solo lectura. Evidentemente, como tenemos que llenarlo con la salida del programa externo, habrá que hacer algo especial; lo veremos mañana.
La función watch--start, que arranca el temporizador, hace un trabajo similar al que realizaba directamente la versión de watch del artículo anterior:
(defun watch--start (command interval)
"Start Watching (periodically running a COMMMAND every INTERVAL seconds)."
(interactive)
(setq watch--command command)
(setq watch--interval interval)
(setq watch--timer
(run-with-timer 0
interval
'watch--callback
(current-buffer)))
(setq mode-line-process ": Running"))
Las principales novedades son el uso de las variables que declaramos antes y la escritura del texto ": Running" en la línea de modo.
La función interactiva watch-pause sirve para detener la monitorización. Solamente tiene sentido pausar una monitorización en marcha; indicamos que la monitorización se ha detenido asignando un valor nulo al temporizador watch--timer después de detenerlo. Tras detener el temporizador, mostramos el estado de pausa de la monitorización en la línea de modo (asignamos el valor ": Paused" a mode-line-process) y redibujamos con redraw-display para forzar que se muestre el cambio que hemos hecho, ya que a menudo puede no actualizarse la pantalla. Finalmente, matamos el proceso watch--process si está activo. El código es el siguiente:
(defun watch-pause ()
"Stop Watching (periodically running a command)."
(interactive)
(when watch--timer
(cancel-timer watch--timer)
(setq watch--timer nil)
(setq mode-line-process ": Paused")
(redraw-display))
(when (and watch--process
(not (process-live-p watch--process)))
(kill-process watch--process)))
La función interactiva watch-resume sirve para reanudar la ejecución del temporizador y es muy sencilla, ya que se limita a llamar a watch--start. Como solamente tiene sentido reanudar la ejecución cuando está detenida, nos fijamos en el valor del temporizador watch--timer: si es nulo, la monitorización está en pausa y podemos reanudar.
(defun watch-resume ()
"Resume Watching (periodically running a command)."
(interactive)
(unless watch--timer
(watch--start watch--command watch--interval)))
Cambiamos el intervalo entre ejecuciones con la función interactiva watch-set-interval, que acepta un número de segundos, pausa la monitorización, asigna el nuevo intervalo y reanuda la monitorización:
(defun watch-set-interval (seconds)
"Set the interval SECONDS between command executions in the
current Watch buffer."
(interactive "nInterval (seconds): ")
(watch-pause)
(setq watch--interval seconds)
(watch-resume))
Falta poco trabajo para tener terminado el monitorizador de programas externos watch. Mañana veremos cómo ejecutar el programa externo, qué hacer con su salida y qué hacer cuando se detiene su ejecución.
Categorías: Informática
2015-07-23
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.
En el artículo de hoy, crearemos una función 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. Vamos a usar temporizadores, así que el gestor de temporizadores del último artículo puede ayudarnos durante el desarrollo.
El código de este artículo funciona en GNU Emacs 24.4.
Las herramientas watch, cmdwatch y gnuwatch sirven para ejecutar periódicamente una orden y mostrar su salida a pantalla completa en una consola. Queremos hacer algo equivalente en Emacs: ejecutar periódicamente un programa externo y mostrar su última salida en un búfer.
Vamos a construir una función interactiva que preguntará al usuario qué orden ejecutar y cada cuánto tiempo hacerlo. Después, la función creará un temporizador (con run-with-timer) que se ocupará de ejecutar nuestro programa externo de forma asíncrona con async-shell-command. La salida irá en un búfer con el nombre *Watch: PROGRAMA* (donde PROGRAMA es el programa externo que hemos mandado ejecutar); si eliminamos este búfer, ya no tiene sentido que se ejecute el temporizador, así que lo eliminaremos con una función hook añadida a kill-buffer-hook (para que se ejecute al matar el búfer).
La función, que acepta la orden a ejecutar (command) y los segundos entre ejecuciones (interval), es así:
(defun watch (command interval)
"Run shell COMMAND every INTERVAL seconds showing the results
in a dedicated buffer."
(interactive (list (read-shell-command "Shell command: " nil nil)
(read-number "Update interval (seconds): ")))
(let* ((buffer-name (format "*Watch: %s*" command))
(buffer (generate-new-buffer buffer-name)))
(switch-to-buffer buffer)
(lexical-let ((timer (run-with-timer 0
interval
'watch--callback
buffer
command)))
(add-hook 'kill-buffer-hook
(lambda () (cancel-timer timer)) nil t))))
Analicémosla punto por punto:
(interactive "sShell command: \nnUpdate interval (seconds): ")
La función watch--callback, que es la que se ejecuta cada vez que salta el temporizador, lo único que hace es llamar a la orden con async-shell-command:
(defun watch--callback (buffer command)
(async-shell-command command buffer))ime
Esto tiene el problema de que se muestra el búfer cada vez que salta el temporizador, lo que puede ser molesto. Para evitarlo, hacemos lo que recomienda la documentación de async-shell-command:
(push '("^\*Watch: .*\*" display-buffer-no-window)
display-buffer-alist)
Ejecutamos esto solamente una vez. El elemento que hemos añadido a display-buffer-alist es una lista con dos campos: una expresión regular que casa con el esquema de nombres que hemos usado para nuestros búferes y la función display-buffer-no-window, que se encarga de que no se muestre el búfer si ya está oculto.
Cada vez termina la ejecución del programa externo con async-shell-command, aparece un mensaje en el minibúfer. Esto puede ser muy molesto, así que buscaremos una forma de evitarlo.
No estamos permitiendo una forma sencilla de parar el temporizador o cambiar el intervalo entre ejecuciones. Podemos usar nuestro nuevo y flamante gestor de temporizadores, pero es mejor ofrecer esa funcionalidad directamente.
Categorías: Informática
2015-07-20
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.
Menú de temporizadores.
En el artículo de hoy, continuación del de ayer, crearemos un modo mayor que nos servirá para gestionar temporizadores, algo que es útil si vamos a desarrollar algún código que los use.
Ayer creamos un modo mayor derivado de tabulated-list-mode, timer-menu-mode, que sirve para mostrar y gestionar temporizadores (unos elementos que sirven para ejecutar tareas tras cierto tiempo, opcionalmente de forma periódica). La definición del modo hace refencia a varias funciones que hoy definiremos.
Ayer hicimos que la tabla de temporizadores se actualizara con la función timer-menu--refresh que añadimos como función hook a tabulated-list-revert-hook. Lo único que tiene que hacer nuestra función timer-menu--refresh es actualizar el contenido de tabulated-list-entries:
(defun timer-menu--refresh ()
"Recompute the list of timers for the Timer Menu buffer."
(setq tabulated-list-entries (timer-menu--entries)))
¡Así, llamando a otra función (timer-menu--entries) para generar las entradas, es fácil! ¿Qué hace esta función? Lo siguiente:
(defun timer-menu--entries ()
"Compute the list of entries for the tabulated list."
(mapcar (lambda (timer)
(list timer
(vector ""
(format "%S" (timer--repeat-delay timer))
(format "%S" (timer--function timer)))))
timer-list))
Lo que generamos es lo que dice la documentación de tabulated-list-entries: una lista cuyos elementos son a su vez listas cuyo primer elemento es el identificador de la entrada y cuyo segundo elemento es un vector con el texto de cada celda que hay que mostrar. En nuestro caso, construimos la lista con mapcar sobre timer-list (que contiene los temporizadores vivos en este momento); para cada temporizador, construimos una entrada: el propio temporizador actúa como identificador y las celdas son un texto vacío (que se transformará en «D» en las entradas que marquemos para cancelar), el tiempo entre repeticiones (obtenido con timer--repeat-delay) y la función a llamar cuando el temporizador salta (obtenida con timer--function).
El código es similar al que creamos en el artículo en el que añadimos funciones de eliminación de subprocesos a process-menu-mode. Podemos marcar temporizadores para cancelar (con lo que aparecen con la marca «D»), desmarcarlos (con lo que la marca desaparece) y cancelar los temporizadores marcados. El código es casi idéntico al de aquel artículo y sería razonable sacar la funcionalidad común y meterla en una biblioteca. Sin más explicación, ahí queda:
(defun timer-menu-mark-for-deletion ()
"Mark the timer on this Timer List buffer line for deletion."
(interactive)
(tabulated-list-set-col 0 "D" t)
(forward-line))
(defun timer-menu-unmark ()
"Unmark the timer on this Timer List buffer line."
(interactive "P")
(tabulated-list-set-col 0 "" t)
(forward-line))
(defun timer-menu-execute ()
"Delete the timers marked for deletion."
(interactive)
(dolist (entry tabulated-list-entries)
(let ((timer (elt entry 0))
(mark (elt (elt entry 1) 0)))
(if (string-equal "D" mark)
(cancel-timer timer))))
(revert-buffer))
Sería fácil hacer una función interactiva que pidiera los segundos de espera entre repeticiones y los aplicara al temporizador elegido:
(defun timer-menu-set-repeat-delay (delay)
"Set the repeat DELAY seconds of the selected timer."
(interactive "nSeconds: ")
(let ((timer (tabulated-list-get-id)))
(when timer
(timer-set-time timer (timer--time timer) delay)
(revert-buffer))))
Esta función pide de forma interactiva un número de segundos con interactive. Después, obtiene el temporizador seleccionado (con tabulated-list-get-id, que devuelve el identificador de la entrada seleccionado; construimos las entradas para que los identificadores fueran los temporizadores correspondientes) y, si este temporizador no es nulo (si hay realmente una entrada seleccionada), modifica el retardo con timer-set-time y actualiza la tabla de temporizadores con revert-buffer.
Sería conveniente hacer la función un poquito más inteligente: que pida los segundos entre repeticiones solamente si hay una entrada seleccionada y que ofrezca como valor por defecto el tiempo asignado actualmente. Podemos hacer esto con una declaración de función interactiva un poco más complicada que construya la lista de argumentos de forma explícita:
(defun timer-menu-set-repeat-delay (timer delay)
"Set the repeat DELAY seconds of the selected TIMER."
(interactive
(let ((id (tabulated-list-get-id)))
(list id
(if id
(read-number "Seconds: " (timer--repeat-delay id))
nil))))
(when timer
(timer-set-time timer (timer--time timer) delay)
(revert-buffer)))
Añadimos un argumento adicional, timer, para poder pasar el temporizador elegido al cuerpo de la función y así evitar llamar a tabulated-list-get-id por segunda vez.
El convenio para los modos derivados de tabulated-list-mode es que haya una función interactiva cuyo nombre empiece por list- y que sirva para crear un nuevo búfer con el modo activado. Vamos a hacer lo propio con la siguiente función, list-timers:
(defun list-timers ()
"Display a list of all the timers managed by Emacs."
(interactive)
(let ((buffer (get-buffer-create "*Timer List*")))
(with-current-buffer buffer
(timer-menu-mode)
(list-timers--refresh)
(tabulated-list-print))
(display-buffer buffer)))
El código está disponible en el siguiente enlace: timer-menu-mode.el
Categorías: Informática
Permalink: https://sgcg.es/articulos/2015/07/20/emacs-lisp-en-casos-practicos-6-gestor-de-temporizadores-2/
2015-07-19
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.
En el artículo de hoy y el artículo de mañana, crearemos un modo mayor que nos servirá para gestionar temporizadores, algo que es útil si vamos a desarrollar algún código que los use.
Los temporizadores ofrecen una manera de ejecutar funciones cuando transcurre cierto tiempo, quizá de forma periódica. La última versión publicada de GNU Emacs en el momento de escribir estas líneas dispone de gestores para cosas como búferes y subprocesos, pero no para temporizadores. Estaría bien tener una interfaz similar a la de list-buffers para hacer algunas gestiones mínimas sobre los temporizadores que está manejando Emacs. Como ejemplo y por no complicarnos mucho, vamos a ofrecer dos opciones de gestión:
Los temporizadores en Emacs son estructuras del tipo timer con varios campos. Uno de ellos, repeat-delay (accesible mediante timer--repeat-delay) es el tiempo de repetición expresado en segundos. Otro campo que podría interesarnos es time (accesible mediante timer--time): el momento de la próxima ejecución del temporizador; ahora bien, con el fin de mantener cierta sencillez, ignoraremos este campo. Lo que sí vamos a mostrar es el campo function (accesible mediante timer--function): la función que se ejecuta cuando el temporizador salta.
Si tenemos un temporizador llamado my-timer, accedemos a
su tiempo de repetición mediante
(timer--repeat-time my-timer)
Algo similar puede hacerse para acceder a otros campos; por
ejemplo, así podemos ver la función a la que llama el temporizador
al saltar:
(timer--function my-timer)
Emacs tiene varias funciones para mostrar tablas con listas de cosas abiertas: la lista de búferes abiertos (que se abre con la función interactiva list-buffers, C-x C-b) y la lista de subprocesos (que se abre con la función interactiva list-processes) son dos de ellas. Estas tablas se muestran con modos mayores derivados de tabulated-list-mode. Nuestra lista de temporizadores también puede aprovechar la funcionalidad ofrecida por tabulated-list-mode.
Lo primero que tenemos que hacer es requerir tabulated-list:
(require 'tabulated-list)
Para definir un modo mayor derivado de tabulated-list-mode, usamos define-derived-mode, que se encarga de hacer todo el trabajo rutinario que habría que hacer siempre para definir de un modo mayor derivado de otro y deja en nuestras manos el trabajo despecífico del modo. En nuestro caso, seguimos las instrucciones que aparecen en la documentación de tabulated-list-mode:
Antes de llegar al último paso, establecemos cómo ordenar la tabla (con la variable tabulated-list-sort-key) y asignamos las combinaciones de teclas (con local-set-key) para poder manejar el menú de temporizadores con el teclado. La definición del modo queda así:
(define-derived-mode timer-menu-mode tabulated-list-mode "Timer Menu"
"Major mode for listing the timers managed by Emacs."
(setq tabulated-list-format [("" 1 t)
("Repeat delay" 16 t)
("Function" 40 t)])
(setq tabulated-list-sort-key '("Function"))
(local-set-key (kbd "\C-k") 'timer-menu-mark-for-deletion)
(local-set-key (kbd "k") 'timer-menu-mark-for-deletion)
(local-set-key (kbd "\C-d") 'timer-menu-mark-for-deletion)
(local-set-key (kbd "d") 'timer-menu-mark-for-deletion)
(local-set-key (kbd "u") 'timer-menu-unmark)
(local-set-key (kbd "x") 'timer-menu-execute)
(local-set-key (kbd "t") 'timer-menu-set-repeat-delay)
(add-hook 'tabulated-list-revert-hook 'list-timers--refresh nil t)
(tabulated-list-init-header))
Analicemos lo que este código hace:
Mañana veremos qué hacen las funciones asignadas a las combinaciones de teclas y la función que actualiza las entradas de la tabla.
Categorías: Informática
Permalink: https://sgcg.es/articulos/2015/07/19/emacs-lisp-en-casos-practicos-5-gestor-de-temporizadores-1/
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.
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.
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)))))))))
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.
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
2015-07-14
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.
En el artículo anterior y el presente artículo vemos la forma de completar cómo calcular el dígito de control de un NIF (como la letra del DNI) y completar un texto.
Las funciones para calcular el dígito de control del NIF del artículo anterior estaban incompletas: no cubrían los NIF de tipo CIF. Un CIF empieza por una letra mayúscula (a excepción de la «Ñ», la «X», la «Y» y la «Z»), sigue con 7 dígitos numéricos y termina con el dígito de control, que es un número o una letra en función de la letra inicial. El cálculo del dígito de control es más complicado que en el caso del DNI y el NIE; se hace de la manera siguiente:
Vamos a completar nuestro código para calcular el dígito de control de un CIF.
Asumamos que disponemos de dos funciones:
Con estas dos funciones, calcular el dígito de control es una mera aplicación directa del algoritmo expresado arriba:
(defun nif-digito-cif (cif)
"Devuelve el dígito de control correspondiente a un CIF."
(let* ((inicial (elt cif 0))
(digitos (nif--digitos (substring cif 1)))
(pares (list (nth 1 digitos)
(nth 3 digitos)
(nth 5 digitos)))
(impares (list (nth 0 digitos)
(nth 2 digitos)
(nth 4 digitos)
(nth 6 digitos)))
(suma-pares (apply '+ pares))
(dobles-impares (mapcar (lambda (numero)
(* 2 numero))
impares))
(sumas-impares (mapcar 'nif--sumar-digitos
dobles-impares))
(suma-impares (apply '+ sumas-impares))
(suma (+ suma-pares suma-impares))
(unidades (car (last (nif--digitos suma))))
(digito (mod (- 10 unidades) 10)))
(case inicial
((?N ?P ?Q ?R ?S ?W) (substring "JABCDEFGHI" digito (1+ digito)))
(otherwise (number-to-string digito)))))
Para la función nif--digitos que devuelve la lista de dígitos de un número, lo que hacemos es convertir el número a una cadena de texto (con number-to-string) de la que es fácil separar los dígitos corresopndientes (con split-string):
(defun nif--digitos (numero)
"Devuelve la lista de dígitos de un NÚMERO."
(mapcar 'string-to-number
(split-string (number-to-string numero) "" t)))
Para la función de sumar dígitos nif--sumar-digitos, explotamos la función nif--digitos que acabamos de escribir:
(defun nif--sumar-digitos (numero)
"Suma los dígitos de un NÚMERO."
(apply '+ (nif--digitos numero)))
Para poder usar las funciones nif-digito-de-control, nif-completar y nif-completar-region que desarrollamos en el artículo anterior, hace falta simplemente actualizar nif--calculador-del-digito-de-control para que devuelva la función para calcular el dígito de control de un CIF en los casos relevantes:
(defun nif--calculador-del-digito-de-control (nif)
(cond ((not (= 8 (length nif))) 'nif-digito-nulo)
((string-match "[0-9]\\{8\\}" nif) 'nif-letra-dni)
((string-match "[XYZ][0-9]\\{7\\}" nif) 'nif-letra-nie)
((string-match "[ABCDEFGHIJKLMNOPQRSTUVW][0-9]\\{7\\}" nif)
'nif-digito-cif)
(t 'nif-digito-nulo)))
El código de este artículo y el anterior está disponible aquí: nif.el
Categorías: Informática
Permalink: https://sgcg.es/articulos/2015/07/14/emacs-lisp-en-casos-practicos-3-calculo-de-la-letra-del-nif-2/
2015-07-08
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.
En el artículo de hoy y el siguiente artículo veremos la forma de completar cómo calcular el dígito de control de un NIF (como la letra del DNI) y completar un texto.
El Número de Identificación Fiscal (NIF) es un código que identifica a efectos fiscales en España a las personas físicas y a las personas jurídicas. Este código es una combinación de 9 caracteres alfanuméricos (con las letras en mayúsculas) en la que el último carácter es un dígito de control que se calcula a partir de los 8 primeros caracteres. Hay diferentes tiposd de NIF, cada uno con su tipo de cálculo de dígito de control correspodiente:
El dígito de control del NIF y el CIF sale de la siguiente relación entre el resto y la letra correspondiente:
Hoy buscamos calcular el dígito de control de un DNI o un NIE e incluso completar un código al que le falta su dígito.
La letra del DNI es fácil de calcular con la siguiente función. Esta función acepta un texto que consiste en los 8 primeros dígitos del DNI (es decir, el código a falta del dígito de control), convierte dicho texto a un número para calcular el resto, y finalmente devuelve la letra que corresponde al resto.
(defun nif-letra-dni (dni)
"Devuelve la letra correspondiente a un número de DNI."
(let ((table "TRWAGMYFPDXBNJZSQVHLCKE")
(remainder (mod (if (stringp dni) (string-to-number dni)
dni)
23)))
(substring table remainder (1+ remainder))))
Para calcular la letra de un NIE, reutilizamos la función que hace el trabajo con el NIF, a la que le pasamos el NIE tras retirar la letra inicial:
(defun nif-letra-nie (nie)
"Devuelve la letra correspondiente a un NIE."
(nif-letra-dni (substring nie 1)))
Además de esto, vamos a definir una función que devuelve un dígito de control nulo (una cadena de texto vacía) para los casos en los que no podemos calcular un dígito de control real:
(defun nif-digito-nulo (nif)
"")
Es conveniente tener una función capaz de discernir el tipo de NIF que se le pasa y calcular el dígito de control de acuerdo al algoritmo correspondiente. Para ello, imaginamos que disponemos de una función llamada nif--calculador-del-digito-de-control que acepta un NIF y devuelve la función que se encarga de calcular el dígito de control (con lo que tenemos definido ahora, la que corresponda entre nif-letra-dni, nif-letra-nie o nif-digito-nulo). Si disponemos de esta función, el trabajo de calcular el dígito de control es así de fácil:
(defun nif-digito-de-control (nif)
"Obtiene el dígito de control de un NIF."
(funcall (nif--calculador-del-digito-de-control nif) nif))
La función que nos devuelve el calculador del dígito de control puede basarse tanto en el tamaño del código que le pasamos (si no es de 8 caracteres, no vale) como en el propio contenido (cazado con expresiones regulares mediante string-match):
(defun nif--calculador-del-digito-de-control (nif)
(cond ((not (= 8 (length nif))) 'nif-digito-nulo)
((string-match "[0-9]\\{8\\}" nif) 'nif-letra-dni)
((string-match "[XYZ][0-9]\\{7\\}" nif) 'nif-letra-nie)
(t 'nif-digito-nulo)))
Lo último que nos falta es poder completar un NIF. Esto lo hacemos añadiendo el dígito de control (calculado con la función nif-digito-de-control definida arriba) al final del código:
(defun nif-completar (nif)
"Completa un NIF añadiéndole el dígito de control que le corresponde."
(concat nif (nif-digito-de-control nif)))
Digamos que usamos esta función con el DNI 12345678:
(nif-completar "12345678")
Obtenemos el siguiente resultado:
"12345678Z"
Finalmente, creamos una función interactiva para completar un NIF
que hayamos seleccionado mientras editamos un texto. Esta función
obtiene como argumentos las posiciones inicial y final de la región
seleccionada (que obtenemos en la invocación interactiva mediante el
código "r"
), extrae el texto en esta región
mediante buffer-substring-no-properties (que sirve para
obtener el texto simple sin información que ahora no nos interesa como
los tipos de letra y los colores) e inserta al final el dígito de
control calculado con nuestra
función nif-digito-de-control:
(defun nif-completar-region (start end)
"Añade la letra al nif en la región marcada entre START y END."
(interactive "r")
(let ((nif (buffer-substring-no-properties start end)))
(save-excursion
(goto-char end)
(insert (nif-digito-de-control nif)))))
Categorías: Informática
Permalink: https://sgcg.es/articulos/2015/07/08/emacs-lisp-en-casos-practicos-2-calculo-de-la-letra-del-nif-1/
2015-07-05
Esta noticia me llega un poco tarde, pero voy a exponerla por su importancia y relativa oscuridad.
Oracle (la compañía que está actualmente detrás del lenguaje de programación Java tras adquirir Sun Microsystems) y Google mantienen un conflicto judicial desde hace años con motivo de la interfaz de programación del lenguaje Java. Oracle afirma poseer el copyright sobre dicha interfaz y espera obtener rentas por el uso que le da Google. El sistema operativo Android, propiedad de Google, contiene bibliotecas informáticas implementadas independientemente de las de Java de Oracle, pero que exponen la misma interfaz de programación. Por lo tanto, se trata de una hipotética violación no del copyright no del Java de Oracle, sino del de la interfaz de programación que tiene Java. Si la interfaz de programación está sujeta a copyright, entonces no está permitido desarrollar de forma independiente un producto compatible con Java.
Hace 3 años, en 2012, el juez Alsup del Distrito Norte de
California se pronunció sobre el copyright de la interfaz de
programación de
Java: tal copyright
era inaplicable por inexistente. Oracle apeló y, en mayo de 2014,
el Circuito Federal de los Estados
Unidos revirtió
la decisión californiana y estableció tanto que Oracle posee
el copyright sobre la interfaz de programación de Java como que
Google violó dicho copyright. Google elevó una petición al
Tribunal Supremo de los Estados Unidos y, hace unos días, a finales de
junio de 2015, dicho
tribunal se
negó a revisar la decisión del Circuito Federal. El litigio no ha
terminado y Google tiene derecho a plantear una defensa sobre el
concepto de uso justo («fair use» en inglés), pero esta
estrategia de defensa, más complicada que un
simple el copyright no se aplica aquí
, es menos
accesible para otras compañías y otros individuos con menos recursos
que el gigante Google.
La situación es jugosa para unos pocos mastodontes y muy desagradable para todos los demás innovadores del sector informático.
Categorías: Actualidad, Informática