forked from Mirrors/apostrophe
211 lines
6.5 KiB
Python
211 lines
6.5 KiB
Python
|
import re
|
|||
|
import gi
|
|||
|
gi.require_version('Gtk', '3.0')
|
|||
|
from gi.repository import Gtk
|
|||
|
|
|||
|
import logging
|
|||
|
logger = logging.getLogger('apostrophe')
|
|||
|
|
|||
|
class FixTable():
|
|||
|
|
|||
|
def __init__(self, TextBuffer):
|
|||
|
self.TextBuffer = TextBuffer
|
|||
|
|
|||
|
@staticmethod
|
|||
|
def create_seperator(widths, char):
|
|||
|
"""
|
|||
|
Generate a line of + and - as sepeartor
|
|||
|
|
|||
|
Example:
|
|||
|
>>> create_separarator([2, 4], '-')
|
|||
|
'+----+------+'
|
|||
|
"""
|
|||
|
line = []
|
|||
|
|
|||
|
for w in widths:
|
|||
|
line.append("+" + char * (w + 2))
|
|||
|
|
|||
|
line.append("+")
|
|||
|
return ''.join(line)
|
|||
|
|
|||
|
@staticmethod
|
|||
|
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.
|
|||
|
|
|||
|
Example
|
|||
|
>>> create_line(['nombre', 'apellido'], [7, 10])
|
|||
|
'| nombre | apellido |'
|
|||
|
"""
|
|||
|
|
|||
|
line = zip(columns, widths)
|
|||
|
result = []
|
|||
|
|
|||
|
for text, width in line:
|
|||
|
spaces = " " * (width - len(text))
|
|||
|
text = "| " + text + spaces + " "
|
|||
|
result.append(text)
|
|||
|
|
|||
|
result.append("|")
|
|||
|
return ''.join(result)
|
|||
|
|
|||
|
@staticmethod
|
|||
|
def create_table(content):
|
|||
|
"""Imprime una tabla en formato restructuredText.
|
|||
|
|
|||
|
El argumento `content` tiene que ser una lista
|
|||
|
de celdas.
|
|||
|
|
|||
|
Example:
|
|||
|
|
|||
|
>>> 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.
|
|||
|
# replace with len()
|
|||
|
widths = [max([len(x) for x in i]) for i in columns]
|
|||
|
|
|||
|
result = []
|
|||
|
|
|||
|
result.append(FixTable.create_seperator(widths, '-'))
|
|||
|
print(content, widths)
|
|||
|
result.append(FixTable.create_line(content[0], widths))
|
|||
|
result.append(FixTable.create_seperator(widths, '='))
|
|||
|
|
|||
|
for line in content[1:]:
|
|||
|
result.append(FixTable.create_line(line, widths))
|
|||
|
result.append(FixTable.create_seperator(widths, '-'))
|
|||
|
|
|||
|
return '\n'.join(result)
|
|||
|
|
|||
|
@staticmethod
|
|||
|
def are_in_a_table(current_line):
|
|||
|
"Line in a table?"
|
|||
|
return "|" in current_line or "+" in current_line
|
|||
|
|
|||
|
@staticmethod
|
|||
|
def are_in_a_paragraph(current_line):
|
|||
|
"Line in a paragraph?"
|
|||
|
return len(current_line.strip()) >= 1
|
|||
|
|
|||
|
def get_table_bounds(self, are_in_callback):
|
|||
|
"""
|
|||
|
Gets the row number where the table begins and ends.
|
|||
|
are_in_callback argument must be a function
|
|||
|
indicating whether a particular line belongs or not
|
|||
|
to the table to be measured (or create).
|
|||
|
Returns two values as a tuple
|
|||
|
"""
|
|||
|
top = 0
|
|||
|
|
|||
|
buf = self.TextBuffer
|
|||
|
start_iter = buf.get_start_iter()
|
|||
|
end_iter = buf.get_end_iter()
|
|||
|
|
|||
|
text = self.TextBuffer.get_text(start_iter, end_iter, False).split('\n')
|
|||
|
logger.debug(text)
|
|||
|
length = len(text)
|
|||
|
bottom = length - 1
|
|||
|
|
|||
|
insert_mark = self.TextBuffer.get_insert()
|
|||
|
insert_iter = self.TextBuffer.get_iter_at_mark(insert_mark)
|
|||
|
current_row_index = insert_iter.get_line()
|
|||
|
|
|||
|
for a in range(current_row_index, top, -1):
|
|||
|
if not are_in_callback(text[a]):
|
|||
|
top = a + 1
|
|||
|
break
|
|||
|
|
|||
|
for b in range(current_row_index, length):
|
|||
|
if not are_in_callback(text[b]):
|
|||
|
bottom = b - 1
|
|||
|
break
|
|||
|
|
|||
|
return top, bottom
|
|||
|
|
|||
|
@staticmethod
|
|||
|
def remove_spaces(string):
|
|||
|
"""Remove unnecessary spaces"""
|
|||
|
return re.sub("\s\s+", " ", string)
|
|||
|
|
|||
|
@staticmethod
|
|||
|
def create_separators_removing_spaces(string):
|
|||
|
return re.sub("\s\s+", "|", string)
|
|||
|
|
|||
|
@staticmethod
|
|||
|
def extract_cells_as_list(string):
|
|||
|
"Extrae el texto de una fila de tabla y lo retorna como una lista."
|
|||
|
string = FixTable.remove_spaces(string)
|
|||
|
return [item.strip() for item in string.split('|') if item]
|
|||
|
|
|||
|
@staticmethod
|
|||
|
def extract_table(text, top, bottom):
|
|||
|
full_table_text = text[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 [FixTable.extract_cells_as_list(x) for x in only_text_lines]
|
|||
|
|
|||
|
@staticmethod
|
|||
|
def extract_words_as_lists(text, top, bottom):
|
|||
|
"Genera una lista de palabras para crear una lista."
|
|||
|
|
|||
|
lines = text[top:bottom + 1]
|
|||
|
return [FixTable.create_separators_removing_spaces(line).split('|') for line in lines]
|
|||
|
|
|||
|
def fix_table(self):
|
|||
|
"""
|
|||
|
Fix Table, so all columns have the same width (again)
|
|||
|
|
|||
|
`initial_row` is a int idicationg the current row index
|
|||
|
"""
|
|||
|
|
|||
|
cursor_mark = self.TextBuffer.get_insert()
|
|||
|
cursor_iter = self.TextBuffer.get_iter_at_mark(cursor_mark)
|
|||
|
cursor_iter.set_line(cursor_iter.get_line())
|
|||
|
|
|||
|
end_line = cursor_iter.copy()
|
|||
|
end_line.forward_to_line_end()
|
|||
|
|
|||
|
|
|||
|
line_text = self.TextBuffer.get_text(cursor_iter, end_line, False)
|
|||
|
if FixTable.are_in_a_table(line_text):
|
|||
|
|
|||
|
# obtiene el indice donde comienza y termina la tabla.
|
|||
|
r1, r2 = self.get_table_bounds(FixTable.are_in_a_table)
|
|||
|
|
|||
|
logger.debug('asdasd ')
|
|||
|
|
|||
|
# extrae de la tabla solo las celdas de texto
|
|||
|
buf = self.TextBuffer
|
|||
|
start_iter = buf.get_start_iter()
|
|||
|
end_iter = buf.get_end_iter()
|
|||
|
|
|||
|
text = self.TextBuffer.get_text(start_iter, end_iter, False).split('\n')
|
|||
|
|
|||
|
table_as_list = FixTable.extract_table(text, r1, r2)
|
|||
|
logger.debug(table_as_list)
|
|||
|
# genera una nueva tabla tipo restructured text y la dibuja en el buffer.
|
|||
|
table_content = FixTable.create_table(table_as_list)
|
|||
|
logger.debug(table_content)
|
|||
|
# Insert table back into Buffer ...
|
|||
|
self.TextBuffer.insert(start_iter, table_content, -1)
|
|||
|
else:
|
|||
|
logger.debug("Not in a table")
|
|||
|
print("Not in table")
|