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")