apostrophe/apostrophe/tables.py

210 lines
5.7 KiB
Python
Raw Normal View History

2014-07-06 20:35:24 +00:00
# -*- encoding: utf-8 -*-
import re
import vim
def cjk_width(text):
import sys
if sys.version_info[0] < 3:
if not isinstance(text, unicode):
text = text.decode("utf-8")
from unicodedata import east_asian_width
return sum(1+(east_asian_width(c) in "WF") for c in text)
def create_separarator(widths, char):
"""Genera una linea para separar filas de una tabla.
El parametro `widths` es un lista indicando el ancho de cada
columna. En cambio el argumento `char` es el caracter que se
tiene que utilizar para imprimir.
El valor retornado es un string.
Por ejemplo::
>>> create_separarator([2, 4], '-')
'+----+------+'
"""
line = []
for w in widths:
line.append("+" + char * (w + 2))
line.append("+")
return ''.join(line)
def create_line(columns, widths):
"""Crea una fila de la tabla separando los campos con un '|'.
El argumento `columns` es una lista con las celdas que se
quieren imprimir y el argumento `widths` tiene el ancho
de cada columna. Si la columna es mas ancha que el texto
a imprimir se agregan espacios vacíos.
Por ejemplo::
>>> create_line(['nombre', 'apellido'], [7, 10])
'| nombre | apellido |'
"""
line = zip(columns, widths)
result = []
for text, width in line:
spaces = " " * (width - cjk_width(text))
text = "| " + text + spaces + " "
result.append(text)
result.append("|")
return ''.join(result)
def create_table(content):
"""Imprime una tabla en formato restructuredText.
El argumento `content` tiene que ser una lista
de celdas.
Por ejemplo::
>>> print create_table([['software', 'vesion'], ['python', '3.2'], ['vim', '7.3']])
+----------+--------+
| software | vesion |
+==========+========+
| python | 3.2 |
+----------+--------+
| vim | 7.3 |
+----------+--------+
"""
# obtiene las columnas de toda la tabla.
columns = zip(*content)
# calcula el tamano maximo que debe tener cada columna.
widths = [max([cjk_width(x) for x in i]) for i in columns]
result = []
result.append(create_separarator(widths, '-'))
result.append(create_line(content[0], widths))
result.append(create_separarator(widths, '='))
for line in content[1:]:
result.append(create_line(line, widths))
result.append(create_separarator(widths, '-'))
return '\n'.join(result)
def are_in_a_table(current_line):
"Indica si el cursor se encuentra dentro de una tabla."
return "|" in current_line or "+" in current_line
def are_in_a_paragraph(current_line):
"Indica si la linea actual es parte de algun parrafo"
return len(current_line.strip()) >= 1
def get_table_bounds(current_row_index, are_in_callback):
"""Obtiene el numero de fila donde comienza y termina la tabla.
el argumento `are_in_callback` tiene que ser una función
que indique si una determinada linea pertenece o no
a la tabla que se quiere medir (o crear).
Retorna ambos valores como una tupla.
"""
top = 0
buffer = vim.current.buffer
max = len(buffer)
bottom = max - 1
for a in range(current_row_index, top, -1):
if not are_in_callback(buffer[a]):
top = a + 1
break
for b in range(current_row_index, max):
if not are_in_callback(buffer[b]):
bottom = b - 1
break
return top, bottom
def remove_spaces(string):
"Elimina los espacios innecesarios de una fila de tabla."
return re.sub("\s\s+", " ", string)
def create_separators_removing_spaces(string):
return re.sub("\s\s+", "|", string)
def extract_cells_as_list(string):
"Extrae el texto de una fila de tabla y lo retorna como una lista."
string = remove_spaces(string)
return [item.strip() for item in string.split('|') if item]
def extract_table(buffer, top, bottom):
full_table_text = buffer[top:bottom]
# selecciona solamente las lineas que tienen celdas con texto.
only_text_lines = [x for x in full_table_text if '|' in x]
# extrae las celdas y descarta los separadores innecesarios.
return [extract_cells_as_list(x) for x in only_text_lines]
def extract_words_as_lists(buffer, top, bottom):
"Genera una lista de palabras para crear una lista."
lines = buffer[top:bottom + 1]
return [create_separators_removing_spaces(line).split('|') for line in lines]
def copy_to_buffer(buffer, string, index):
lines = string.split('\n')
for line in lines:
buffer[index] = line
index += 1
def fix_table(current_row_index):
"""Arregla una tabla para que todas las columnas tengan el mismo ancho.
`initial_row` es un numero entero que indica en que
linea se encuenta el cursor."""
# obtiene el indice donde comienza y termina la tabla.
r1, r2 = get_table_bounds(current_row_index, are_in_a_table)
# extrae de la tabla solo las celdas de texto
table_as_list = extract_table(vim.current.buffer, r1, r2)
# genera una nueva tabla tipo restructured text y la dibuja en el buffer.
table_content = create_table(table_as_list)
copy_to_buffer(vim.current.buffer, table_content, r1)
def FixTable():
(row, col) = vim.current.window.cursor
line = vim.current.buffer[row - 1]
if are_in_a_table(line):
fix_table(row - 1)
else:
print("No estoy en una tabla. Terminando...")
def CreateTable():
(row, col) = vim.current.window.cursor
top, bottom = get_table_bounds(row - 1, are_in_a_paragraph)
lines = extract_words_as_lists(vim.current.buffer, top, bottom)
table_content = create_table(lines)
vim.current.buffer[top:bottom + 1] = table_content.split('\n')