lunes, 16 de marzo de 2009

HAL y w3m (IV)

Para demostrar la versatilidad de la consola, vamos a ver hoy dos ejemplos de interacción entre Vim y w3m.

Imaginemos que estamos editando con Vim un documento sobre el lenguaje de marcas HTML y consultando a la vez en nuestro navegador la especificación que el W3C distribuye sobre ese lenguaje. Al llegar, digamos, a la sección sobre el etiquetado de listas vemos que hay ahí mucha información interesante, dispersa a lo largo de la página, que queremos incluir y comentar en nuestro documento. Sería demasiado pesado y extremadamente lento tener que ir del navegador al editor una y otra vez para copiar y pegar (con ratón) el texto de la referencia del W3C que correspondiese. Lo mejor es traer esa sección de la especificación a nuestro propio Vim. Podemos, por ejemplo, abrirlo en una ventana dentro de este nuestro editor favorito y operar sobre la página del W3C, como si se tratase de un fichero de texto cualquiera, con las órdenes de edición habituales (y mucho más rápidas) que expusimos días pasados. [Entonces no comentamos nada sobre el soporte de ventanas en Vim. El lector puede informarse de este aspecto del editor y de las órdenes relacionadas solicitando información a Vim mediante la orden :help windows .].

Si hubiésemos estado visitando la página con un buen navegador gráfico que disponga de la opción de guardar las páginas web como texto, tendríamos que realizar al menos dos acciones, ambas independientes:

  1. Ir al menú "Archivo" del navegador y guardar como texto la página web. (Lo que además implica seleccionar un nombre de fichero y un directorio).

  2. Crear en Vim una nueva ventana para editar en ella el documento que acabamos de guardar, mediante la orden :new ruta_fichero.


Si navegamos con w3m, y gracias a la interacción entre las aplicaciones de consola, bastaría ejecutar una única acción desde el propio Vim, que, además, podríamos abreviar para reproducirla en situaciones semejantes. Veamos cómo.

Con w3m el proceso de convertir una página web en texto es absolutamente elemental: añadir la opción -dump. Por ejemplo:

w3m -dump http://www.w3.org/TR/html401/struct/lists.html

nos devuelve la página dada como argumento, tal cual se presenta en el navegador, pero como puro texto.

Por otra parte, sabemos que desde Vim podemos solicitar la ejecución de órdenes externas. Podríamos, pues, solicitar la ejecución de w3m con la opción y el argumento anteriores:

:!w3m -dump http://www.w3.org/TR/html401/struct/lists.html

También sabemos que existe una orden de Vim apropiada para leer la salida de cualquier orden externa, esto es, para introducirla en el documento actual. Por tanto, si queremos leer en la ventana actual del editor la salida de la orden anterior, tendríamos que decirle a Vim lo siguiente:

:r !w3m -dump http://www.w3.org/TR/html401/struct/lists.html

Finalmente, la forma de pedir a Vim que abra una nueva ventana y ponga en ella el resultado de una orden cualquiera es éste:

:new | orden

donde la orden :new crea una nueva ventana con un fichero vacío y sin nombre. y el carácter especial '|' sirve para separar esta orden de aquella otra que se ponga a continuación ---y que afectará a la nueva ventana---, de tal manera que en una sola línea dentro de la línea de órdenes puedan ejecutarse dos órdenes diferentes, una tras otra.

Sólo queda unir todas las piezas para que nuestra orden completa reciba su forma final:

:new | :r !w3m -dump http://www.w3.org/TR/html401/struct/lists.html

que es, en resumen, la construcción lingüística que entiende Vim para indicarle algo parecido a la siguiente parrafada:

Vim, abre una nueva ventana y pon en ella la página web, convertida en texto, a la que apunta la dirección 'http://www.w3.org/TR/html401/struct/lists.html'

Puesto que esta orden es demasiado larga y puede que vayamos a utilizarla en distintas ocasiones para URLs diferentes, una abreviatura nos puede ser muy útil. Vim dispone de varias órdenes para crear abreviaturas. Cuando el propósito es que la abreviatura funcione sólo en la línea de órdenes, la orden adecuada es :cabbrev. Así, la orden:

:cabbrev wwd new \| :r !w3m -dump

asocia la abreviatura wwd a la orden larga new | :r !w3m -dump.

¿Por qué hemos puesto el carácter '\' antes del carácter '|' en la orden :cabbrev? La razón es que el carácter '|' tiene un significado especial, como acabamos de ver (el de separar dos órdenes), y no puede formar parte de un argumento de una orden (en este caso el segundo argumento de la orden :cabbrev). Para que se interprete literalmente y no en su especial significado sintáctico debemos "escaparlo", por las mismas razones y mediante el mismo procedimiento que tuvimos que emplear para "escapar" otros caracteres especiales de la lengua de HAL.

Gracias a la anterior abreviatura, cada vez que pulsemos los dos puntos (para abrir la línea de órdenes de Vim) y tecleemos wwd, seguida de espacio, Vim escribirá por nosotros la secuencia new | :r !w3m -dump, que veremos reproducida y que podremos completar con la URL que nos interese.

Para que abreviaturas como ésta estén disponibles en futuras sesiones de edición con Vim se deben guardar en el fichero ~/.vimrc, del mismo modo que las asociaciones de teclado creadas por medio de :map, tal y como se explicó en otro lugar.

Pongámonos ahora en una situación diferente. Estamos escribiendo un texto en Vim, digamos, sobre literatura inglesa, y nos gustaría consultar versiones públicas de las obras citadas accesibles desde la Open Library. Sería ideal poder realizar esas consultas sin necesidad de moverse del editor.

Las URLs de la Open Library para búsqueda de libros escaneados accesibles en su integridad tienen actualmente la siguiente forma (por ejemplo, para la tragedia de Shakespeare King Lear):

http://openlibrary.org/search?King+Lear&ftokens=mhsncqbxgkup

Si en el documento que estamos editando con Vim tenemos escrita una línea con el nombre del drama shakespeariano King Lear podríamos tratar de enviar una orden como ésta:

:!w3m http://openlibrary.org/search?q=King+Lear&ftokens=mhsncqbxgkup

para que Vim abriese w3m en la página correspondiente de la Open Library.

Ahora bien, el contenido de la línea de nuestro documento podría ser cualquier otro en lugar de "King Lear" y perderíamos todas las ventajas de usar Vim si tuviésemos que editar a mano la URL de turno. Lo que nos interesa es que la orden anterior se genere automáticamente, es decir, que las ??? de esta orden:

:!w3m http://openlibrary.org/search?q=???&ftokens=mhsncqbxgkup

sean sustituidas por las palabras de la línea de cada caso, separadas ---nótese el matiz--- por '+', que es la forma que requieren la URLs de la Open Library.

Tratemos de resolver el problema paso a paso.

El primer paso es conseguir que en las palabras de las que consta la línea actual del documento que editamos el espacio sea sustituido por el '+'. Podemos recurrir a una orden de HAL como sed:

:!sed 's/ /+/g'

O de forma más ortodoxa, utilizando expresiones regulares (donde el '+' de la expresión regular debe escaparse en sed):

:!sed 's/[[:space:]]\+/+/g'

Con esto tenemos resuelta la primera parte del problema. La segunda consiste en introducir el resultado de la substitución realizada por sed en el lugar de la URL que corresponde, es decir, el lugar que señalamos anteriormente con las ???. Pensado en términos de la lengua de HAL, que conocemos bien, esta tarea no entraña dificultad especial. Recuérdese que en tales casos la sustitución de órdenes es nuestro aliado. Por ejemplo, en lengua de HAL podríamos decir algo parecido a lo siguiente, para buscar en Google la salida de un echo:

echo "King Lear" | w3m http://google.com/search?q=$(sed 's/[[:space:]]\+/+/g')

Lo que esta orden hace ---recuérdese--- es dirigir la salida de la orden echo a la entrada de la orden w3m. Esta última orden consta de un argumento que incluye una sustitución de órdenes, $(...). Cuando HAL interpreta dicho argumento se encuentra con la sustitución de órdenes y trata de resolverla, antes de ejecutar w3m. En el caso presente, la sustitución de órdenes es más compleja de lo habitual, pues contiene un filtro sed que, como sabemos, toma por defecto como argumento un fichero y, en caso de no haber ninguno, se nutre de la entrada estándar (la "cañería principal", como la llamamos en otra ocasión). La tubería inicial hace que lo que hay tras ella ya no se alimente de la entrada estándar, sino de la salida de echo. Por eso, cuando HAL trata de interpretar la sustitución de órdenes contenida en el argumento de w3m utiliza la salida de echo, en lugar de la entrada estándar, como alimento del sed que aparece allí. Para disipar toda duda sobre el proceso que HAL realiza en este caso, interpretemos la orden como lo haría él (en sus pasos básicos):

  1. Ejecuto echo "King Lear". Su salida es la cadena 'King Lear'.

  2. Veo la tubería. La entrada actual para el resto de la orden dejará de ser la entrada estándar, en su lugar lo será la salida de echo, o sea, la cadena 'King Lear'.

  3. Antes de ejecutar w3m analizo su argumento. Este argumento contiene la sustitución de órdenes $(sed 's/[[:space:]]\+/+/g').

  4. Procedo a sustituir:

    1. sed no tiene argumento, luego su entrada es la entrada actual, esto es, la cadena 'King Lear'.

    2. El resultado de ejecutar sed tomando como entrada la cadena 'King Lear' es la cadena 'King+Lear'.

    3. El argumento de w3m es, pues, http://google.com/search?q=King+Lear

  5. Ejecuto w3m con el argumento anterior.


Ahora bien, nuestro problema reviste una dificultad especial. La entrada de sed no va ser la salida de una orden de HAL como echo, sino justamente el contenido de la línea actual de nuestro documento. Sería una dificultad intratable si Vim no nos proporcionase una forma de hacer que fragmentos de nuestro documento se convirtieran en la fuente de alimentación, la entrada, de las órdenes externas que le ordenamos ejecutar. El recurso que Vim pone a nuestra disposición para esta clase de situaciones especiales es la expresión :

:dirección w !orden_externa

donde direccion es una especificación del ámbito del documento que queremos pasar como entrada a la orden externa, el espacio entre w y ! es obligatorio y el espacio entre dirección y w es opcional y se pone ahora para facilitar la lectura. [Sobre la noción de dirección ya comentamos lo suficiente aquí].

Lo que esta orden viene a decirle a Vim se podría expresar a nuestra manera:

Vim, coge el texto que hay en la dirección indicada y ejecuta la orden_externa suministrándole como entrada dicho texto.

Por ejemplo, si la quinta línea de nuestro documento consistiese en la cadena 'King Lear', podríamos hacer que w3m abriese la página de búsqueda de Google para ese texto con la orden:

:5w !w3m http://google.com/search?q=$(sed 's/[[:space:]]\+/+/g')

En el caso que nos interesa no podemos saber por anticipado el número de línea de la dirección. Debemos, pues, aplicar un medio genérico para referirnos a la línea actual, sea cual sea su número de línea en este momento. Tal especificador genérico de dirección existe en Vim, es el punto ('.'). Por tanto, la orden del ejemplo anterior se debería escribir así:

:.w !w3m http://google.com/search?q=$(sed 's/[[:space:]]\+/+/g')

Llegados a este punto, parece trivial completar la orden de búsqueda en la Open Library que dejamos a medio construir:

:.w !w3m http://openlibrary.org/search?q=$(sed 's/[[:space:]]\+/+/g')&ftokens=mhsncqbxgkup

La solución, no obstante, no funcionará, falta un pequeño detalle. El carácter '&' es otro carácter especial de la lengua de Vim [para más información, consultar la referencia de Vim con :help &]. Como nuestra intención es que se interprete literalmente, en cuanto parte integrante de la URL, debemos "escaparlo". En consecuencia nuestra orden definitiva sería:

:.w !w3m http://openlibrary.org/search?q=$(sed 's/[[:space:]]\+/+/g')\&ftokens=mhsncqbxgkup

Con esta orden ---que podemos abreviar con :cabbrev o que podemos, mediante :map, asociar a una tecla--- logramos nuestro objetivo: abrir w3m desde Vim para que nos muestre la página de la Open Library donde aparecen relacionados los libros escaneados cuyo título coincide con el nombre que consta en la línea actual de nuestro documento, la línea donde está ubicado el cursor.

Resulta obvio que este tipo de estrategia se podría generalizar y refinar. La línea de órdenes de Vim se complicaría en exceso en tales casos y sería necesario recurrir mejor a herramientas aún más versátiles como la creación de funciones para Vim o, incluso, de plugins [Ver :help user-manual]. Se trata de herramientas avanzadas de las que, por lo pronto, no tenemos intención de hablar en nuestras conversaciones.

Puesto que los ejemplos propuestos, son simplemente eso, ejemplos, y contienen material relativamente especializado, omitimos el resumen de siempre. Basta con degustar el sabor peculiar de esta interacción entre las aplicaciones de consola, muchas veces sazonadas con los conocidos ingredientes de la lengua de HAL y de sus característicos aromas.

No hay comentarios:

Publicar un comentario