Vim scripting con Python
Vim permite agregar funcionalidades mediante varios lenguajes externos, entre ellos Perl, Python, Ruby y Tcl. Para poder hacer uso de esta característica es necesario haber compilado Vim con el soporte para el lenguaje necesario (con el flag-python, para soporte python). En debian existe un paquete llamado vim-nox (no X) que contiene una versión compilada con soporte para los cuatro lenguajes mencionados:
Para comprobar que efectivamente se encuentre habilitado el soporte para Python ejecutar Vim y en modo comando escribir:
Un 1 indica que Vim interpreta Python ;)
Es muy recomendable leer la ayuda en línea:
También se puede acceder vía web desde http://vimdoc.sourceforge.net/htmldoc/if_pyth.html
Ejecución de código Python
La ejecución de una línea de código Python se realiza según la sintaxis:
Ejemplo:
Aquí el prompt mostrará el mensaje “hello world”.
En cambio para escribir código en varias líneas hay que usar la siguiente forma:
{script}
{endmarker}
Ejemplo:
def get_user():
import os
return os.getenv('USER')
EOF
De esta manera la función queda guardada en la memoria de la sesión actual. Luego su uso podría ser el siguiente:
Tambien es posible cargar y/o ejecutar código desde un archivo externo. La sintaxis es:
Ejemplo:
Simyscript.pynecesitara parámetros la forma de pasárselos es la siguiente:
:py sys.argv = ['foo', 'bar']
:pyf myscript.py
El módulo vim
La comunicación entre Python y Vim se realiza a través del módulo vim, el cual hay que importar antes de usar:
Como indica la ayuda de Vim, el módulo implementa dos métodos (vim.command(str)yvim.eval(str)), tres constantes (vim.buffers,vim.windowsyvim.current) y un objeto error (vim.error). Las tres constantes mencionadas no son realmente constantes sino variables que pueden ser reasignadas pero, como dice también la ayuda, esto sería absurdo ya que se perdería acceso a los objetos de Vim que referencian. Por otra parte cabe recordar que no existen constantes en Python. Como cita el libro “Inmersión en Python“:
Todo puede cambiar si lo intenta con ahínco. Esto se ajusta a uno de los principios básicos de Python: los comportamientos inadecuados sólo deben desaconsejarse, no prohibirse.
A continuación transcribo un detalle de cada una de las constantes mencionadas:
- vim.buffers
-
Objeto de tipo secuencia que provee acceso al listado de buffers actuales. El objeto soporta las siguiente operaciones:
:py b = vim.buffers[i] # Indexación (Read-Only)
:py b in vim.buffers # Prueba de existencia
:py n = len(vim.buffers) # Número de elementos
:py for b in vim.buffers: # Acceso secuencial - vim.windows
-
Objeto de tipo secuencia que provee acceso al listado de ventanas actuales. El objeto soporta las mismas operaciones que vim.buffers:
:py w = vim.windows[i] # Indexación (RO)
:py w in vim.windows # Prueba de existencia
:py n = len(vim.windows) # Número de elementos
:py for w in vim.windows: # Acceso secuencial - vim.current
-
Objeto que provee acceso (vía atributos específicos) a distintos objetos actuales:
vim.current.line # (RW) String
vim.current.buffer # (RO) Buffer
vim.current.window # (RO) Window
vim.current.range # (RO) Range
Ejemplos básicos de uso del módulo vim
Usandovim.current.line:
:py vim.current.line = upper(vim.current.line)
Usandovim.current.buffer:
:py name = b.name # nombre de archivo del buffer actual
:py num = len(b) # total de líneas en el buffer
:py line = b[n] # obtiene la línea n+1
:py lines = b[n:m] # obtiene una lista de líneas
:py b[n] = str # reemplaza la línea n+1
:py b[n:m] = [str1, str2, str3] # reemplaza varias líneas a la vez
:py b[0:0] = ["hola mundo"] # inserta una línea al comienzo
:py b.append("fin") # inserta una línea al final
:py del b[n] # borra la línea n+1
:py del b[n:m] # borra varias líneas a la vez
:py b[:] = None # borra el buffer completo
Usandovim.current.window:
:py cw.height = 30 # setea el alto de la ventana
:py cw.width = 80 # setea el ancho
:py pos = cw.cursor # posicion del cursor (row, col)
Script para subrayado de títulos
El siguiente script (heading.vim) lo escribí para no tener que, manualmente, subrayar títulos y delimitar bloques de texto. Especialmente quería que reconociera cuando se tratara de texto dentro de comentarios de código fuente y que respetara los caracteres especiales al comienzo (y final) de los mismos.
El script tiene una función que decora la línea actual colocando él o los caracteres decoradores tanto arriba como debajo de la misma (comportamiento por defecto). El largo (también por defecto) es el largo de la línea, pero se puede fijar un límite (ejemplo 80 columnas) y pedirle que ocupe todo el espacio disponible. Esta opción funciona bien sólamente cuando el indentado es con espacios. La opción uppercase funciona como se espera.
El código es el siguiente:
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | " ============================================================================ " File: heading.vim " Version: 0.1 " Description: vim global plugin that provides a way to create headings. " Maintainer: Leonardo Vidarte <lvidarte at gmail dot com> " Last Change: 21 February, 2010 " ============================================================================ if has('python') python << EOF def heading(decorator='=', top=True, bottom=True, limit=0, uppercase=False): import vim from string import upper, rstrip, lstrip if not top and not bottom: return None # nothing to do (row, col) = vim.current.window.cursor line = vim.current.buffer[row-1].rstrip() line_length = len(line.decode('utf8')) title = line.lstrip() word_length = len(title.decode('utf8')) whitespace = line[0:line_length - word_length] if limit > 0: decorator *= limit total_length = limit else: decorator *= word_length total_length = line_length if title[0:3] in ('// ', '/* ', '-- '): decoline = whitespace + title[0:3] + decorator elif title[0:2] in ('//', '/*', '--', '# ', '* ', '" '): decoline = whitespace + title[0:2] + decorator elif title[0:1] in ('#', '*', '"'): decoline = whitespace + title[0:1] + decorator else: decoline = whitespace + decorator # Special case: comments like C /* hello world */ cend = '' if title[0:2] == '/*': if title[-3:] == ' */': cend = ' */' elif title[-2:] == '*/': cend = '*/' decoline = decoline[0:total_length - len(cend)] + cend final_heading = [] if top: final_heading.append(decoline) if uppercase: final_heading.append(line.upper()) else: final_heading.append(line) if bottom: final_heading.append(decoline) del vim.current.buffer[row-1] vim.current.buffer[row-1:0] = final_heading # Set final cursor position if top and bottom: vim.current.window.cursor = (row + 2, 0) else: vim.current.window.cursor = (row + 1, 0) H = heading # shortcut EOF " ============================================================================ " Vim maps (see :help leader) " ============================================================================ nnoremap <silent> <Leader>hh :python heading()<CR> nnoremap <silent> <Leader>h1 :python heading('#', uppercase=True)<CR> nnoremap <silent> <Leader>h2 :python heading('*', uppercase=True)<CR> nnoremap <silent> <Leader>h3 :python heading('=')<CR> nnoremap <silent> <Leader>h4 :python heading('-')<CR> nnoremap <silent> <Leader>h5 :python heading('~', top=False)<CR> nnoremap <silent> <Leader>HH :python heading(limit=78)<CR> nnoremap <silent> <Leader>Hb :python heading(limit=78, top=False)<CR> nnoremap <silent> <Leader>Ht :python heading(limit=78, bottom=False)<CR> endif |
Para usarlo hay que colocar el archivo heading.vim en el directorio~/.vim/plugins.
Ejemplos de uso
1. Texto normal
Título
====== Título ======
2. Comentario en línea (Python, Bash, Perl, PHP, Ruby)
# Comment
# =======
# Comment
# =======
3. Comentario en bloque, estilo javadoc (C, C++, Java, Javascript, CSS, PHP, SQL)
/**
* Comment
*/
/**
* #######
* COMMENT
* #######
*/
4. Comentario en línea (C, C++, Java, Javascript, CSS, PHP, SQL)
/* Comment */
/* ~+~+~+~ */ /* Comment */ /* ~+~+~+~ */
5. Comentario en línea (C, Java, Javascript, PHP)
// Comment
// ===================================== // Comment // =====================================
6. Comentario en línea (Vim script)
" Comment
" Comment " ======
7. Comentario en línea (Lua, SQL)
-- Comment
-- ======= -- Comment -- =======
Atajos de teclado
El script también define algunos atajos en modo normal para Vim. Para los mismos se usa la tecla especial<Leader>que es definida por la variablemapleadery que generealmente es el caracter\.
| Atajo | Comando |
|---|---|
| \hh | :python heading() |
| \h1 | :python heading(‘#’, uppercase=True) |
| \h2 | :python heading(‘*’, uppercase=True) |
| \h3 | :python heading(‘=’) |
| \h4 | :python heading(‘-’) |
| \h5 | :python heading(‘~’, top=False) |
| \HH | :python heading(limit=78) |
| \Hb | :python heading(limit=78, top=False) |
| \Ht | :python heading(limit=78, bottom=False) |



