Cuaderno de GNUtas

Mostrar numeración de versos en un bloque VERSE de Org Mode

Gnu Emacs (como cualquier otro editor de texto que se precie, por otra parte) puede mostrar u ocultar los números de línea lógicos del documento. Esto tiene su utilidad para programación, y yo suelo usar, si ando en esos menesteres, el display-line-numbers-mode, cuya activación o desactivación tengo anclada a una secuencia de teclas. Hace bien su trabajo y no le pido más. Por supuesto, hay muchos modos más para numeración de líneas, incluso numeración relativa, pero ninguno (ni siquiera display-line-numbers) me resulta satisfactorio si de lo que se trata es de numerar los versos de un poema. Y ante esta carencia, me dediqué hace no mucho a escribir esta función, con la idea de que realice lo siguiente:

  1. Si estamos en un bloque verse de Org Mode (el lugar más idóneo para poner un poema en Emacs) y llamamos a la función, los versos se numerarán en el margen (mediante overlays temporales, que es el mismo procedimiento que emplean los otros modos para numerar las líneas) en secuencia de cinco, pero sin contar el primer verso. Y
  2. Es importante que en el cómputo de versos no se tenga en cuenta las líneas blancas que separan las estrofas. Ahí es una de las cosas en que la numeración «tradicional» de líneas en Emacs me falla, ya que numera todas las líneas reales, ya sean blancas o con contenido.

Como se ve, es la numeración clásica de los versos, algo que en LaTeX se puede conseguir fácilmente. De hecho, en esta GNUta explico cómo exporto versos desde Org a LaTeX y que queden en su destino, automáticamente, numerados. Pero mientras trabajo en Emacs, en el paraíso del texto plano, también me resulta práctico de cuando en vez contar con ese tipo de numeración, especialmente en mi traducción de la Odisea, work in progress. Echemos un vistazo rápido a cómo quedó, finalmente, mi código.

Función para mostrar u ocultar la numeración de versos

Primero, necesité definir antes esta pequeña función que avanza cuatro líneas pero ignorando las blancas. Como no me servía el comando forward-line (que no ignora las bancas), me inventé uno nuevo ad hoc, que repite cuatro veces una búsqueda con expresión regular:

(defun cuatro-versos-no-vacia ()
    (dotimes (numero 4)
      (re-search-forward "^." nil t)))

Nuestra función para numerar los versos, que expongo a continuación, tiene un esquema muy simple: se define un contador como una variable local, se avanza líneas y cada avance se añade un overlay en el margen:

(defun numera-versos ()
    (interactive)
    (setq left-margin-width 4)
    (set-window-buffer (selected-window) (current-buffer))
    (save-excursion
      (save-restriction
	(org-narrow-to-block)
	(goto-char (point-min))
	(end-of-line)
	(let
	    ((numverso 0))
	  (save-excursion
	    (while
		(re-search-forward "^." nil t)
	      (cuatro-versos-no-vacia)
	      (let
		  ((ov (make-overlay (point) (point))))
		(overlay-put ov 'overlay-num-verso t)
		(overlay-put ov 'before-string
			     (propertize " "
					 'display
					 `((margin left-margin)
					   ,(propertize
					     (format "%s" (number-to-string (setf numverso (+ numverso 5))))
					     'face 'linum)))))
	      (forward-line 1)))))))

La función se comporta excelentemente y numera los versos de maravilla. Lo que yo buscaba. No obstante, tiene un pequeño problema de orden estético, y es que deja también una numeración espuria en el marcador de fin de bloque #+end_verse. Ninguna de las opciones «elegantes» que he ensayado para evitar ese comportamiento me ha surtido efecto (sospecho que tiene que ver con el propio código del bloque verse en los intríngulis de Org). Se puede optar, por supuesto, por la fuerza bruta e insertar un remiendo sucio, pero es algo que afearía nuestra función y tampoco lo considero esencial. Al fin y al cabo, los versos los numera bien, y mientras no encuentre una solución más idiomática a lo otro, prefiero dejarlo así.

Ya sólo nos quedaría definir esta otra función que oculte los números de verso y restituya los márgenes normales:

(defun elimina-num-versos ()
    (interactive)
    (remove-overlays nil nil 'overlay-num-verso t)
    (setq left-margin-width 0)
    (set-window-buffer (selected-window) (current-buffer)))

Y podemos ya anclar las funciones a un atajo de teclado (por ejemplo, M-j o el que se nos antoje). Si, además, queremos guardar la cortesía con nosotros mismos, y que se nos recuerde que este código sólo tiene sentido usarlo en un bloque verse, podemos incluir un condicional y un mensaje de error:

(global-set-key (kbd "M-j")
		  '(lambda ()
			   (interactive)
			   (save-excursion
			     (save-restriction
			       (org-narrow-to-element)
			       (goto-char (point-min))
			       (if (not (looking-at-p "#\\+begin_verse"))
				   (error "No es un bloque VERSE..."))
			       (numera-versos))))

num-versos-org.gif

¿Y si queremos saltar a un verso concreto?

Pues, como bonus track, y ya venido arriba, también se me ocurrió esta otra función, muy útil, que nos preguntará en el minibúfer a qué número queremos saltar:

(defun localiza-verso ()
    (interactive)
    (save-restriction
      (org-narrow-to-block)
      (let
	  ((numero (read-number "ir a verso...")))
	(goto-char (point-min))
	(end-of-line)
	(dotimes (x numero)
	  (re-search-forward "^." nil t)))))

Publicado: 22/10/2019

Última actualización: 22/10/2019


Í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