FormatTable - butscher/WikidPad GitHub Wiki

!FormatTable Plugin

########################################################################
#
# FormatTable.py
# ------------
# v0.1 by [email protected]
# ------------
# Script to allow easy formating of tables within wikidpad
#
# The script has 2 basic functions depending on the context it is used.
# Both require the text to be formated to be selected.
#
# Default shortcut is Ctrl+Alt+T (can be changed below)
#
# 1. Format a preexisting table.
#    If a table is selected the script will attempt to align the
#    columns as best it can. It should work with either \t or | table
#    formats. This option will be used if the selected text starts with
#    "<<|" and ends with ">>". If not the section function will be used.
#    (See - 2. Create table - below)
#    NOTE: This may not work well with long columns.
#
#    e.g.
#    The following table:
#
#    <<|
#    this | is | a | table
#    which | is | not | well
#    aligned | or | formatted | nicely
#    >>
#
#    will be replaced by:
# 
#    <<|
#    this    | is | a         | table
#    which   | is | not       | well
#    aligned | or | formatted | nicely
#    >>
#
#    Linebreaks using "\" are also supported
#
# 2. Create Table
#    If not table is selected the script will attempt to create
#    a table from the selected text, assuming that the table is currently
#    in the format of 
#
#    colum 1 row 1
#    colum 1 row 2
#    colum 1 row 3
#    colum 1 row ...
#    colum 2 row 1
#    colum 2 row 2
#    colum 2 row 3
#    colum 2 row ...
#    colum 3 row 1
#    colum 3 row 2
#    colum 3 row 3
#    colum 3 row ...
#    colum ... row ...
#    etc....
#
#    Which can be rewrapped into
#    <<|
#    colum 1 row 1   | colum 2 row 1   | colum 3 row 1
#    colum 1 row 2   | colum 2 row 2   | colum 3 row 2
#    colum 1 row 3   | colum 2 row 3   | colum 3 row 3
#    colum 1 row ... | colum 2 row ... | colum 3 row ...
#    >>
#    By selected the text and entering the desired number of columns.
#    It is important you have the correct number of rows selected.
#
########################################################################

import urllib, wx, wx.stc, re
import collections

WIKIDPAD_PLUGIN = (("MenuFunctions",1),)

# NOTE: Some of the code below is specific for my custom parser
#       it should however be fully backwards compatible with the
#       wikidpad default 2.0 parser.

REMOVE_BLANK_LINES = True
USE_PLUS_FORMATTING = True

def describeMenuItems(wiki):
    return ((buildTable, "Format Table\tCtrl+Alt+T", "Format a table."),)

class TableDlg(wx.Dialog):

    def __init__(self, *args, **kwds):

        kwds["style"] = wx.DEFAULT_DIALOG_STYLE
        wx.Dialog.__init__(self, *args, **kwds)

    def TableNo(self, no):

        pos = []
        for i in range(2, no/2+1):
            if not no % i:
                pos.append(str(i))

        dlg = wx.TextEntryDialog(self,
        'How many columns? (N = {0}, pos = {1})'.format(no, ",".join(pos)),
        )

        if dlg.ShowModal() == wx.ID_OK:
            return int(dlg.GetValue())

        dlg.Destroy()

def splitLineIntoColumns(sep, line):
    """
    The column has to be split by a regex if "|" formating is being used
    as otherwise we can get into trouble with links (which can also contain
    "|".
    """
    if sep == u"|":
        # We cheat a bit here (this is by no means foolproof)
        # but it seems to work in most cases I've encountered so far
        # (providing [ ] are matched)
        return re.split("\|(?!(?:[^[])*\])", line)
    else:
        return line.split(sep)

def buildTable(wiki, evt):
    editor = wiki.getActiveEditor()
    
    beginSel=editor.GetSelectionStart()
    endSel=editor.GetSelectionEnd()

    oldPos=editor.GetCurrentPos()
    editor.GotoPos(beginSel)
    editor.CmdKeyExecute(wx.stc.STC_CMD_HOMEEXTEND)
    beginSel = editor.GetSelectionStart()
    editor.GotoPos(endSel)
    editor.CmdKeyExecute(wx.stc.STC_CMD_LINEENDEXTEND)
    endSel = editor.GetSelectionEnd()
    editor.SetSelection(beginSel, endSel)
    text = wiki.getActiveEditor().GetSelectedText()
    
    if not text.strip()[:3] == "<<|" or not text.strip()[-2:] == ">>":

        a = text.split("\n")
        no = TableDlg(wiki).TableNo(len(a))

        if not no:
            return

        b = []
        if not len(a) % no: 
                b.append("<<|")
                n = len(a)/no
                for i in range(n):
                        line = a[i]
                        for j in range(no)[:-1]:
                                j = j+1
                                line += " | " + a[j*n+i]
                        b.append(line)
                b.append(">>")
                c = "\n".join(b)	
        else:
                c = text

        text = c

    lines = text.split("\n")

    if REMOVE_BLANK_LINES:
        lines = [i for i in lines if len(i.strip()) > 0]

    string_length = collections.defaultdict(int)

    new_lines = []
    
    appedix_start = u"<<|"

    if lines[0][:4] == u"<<|t":
        appedix_start = u"<<|t"
        sep = u"\t"
        n = 2
    else:
        sep = u"|"
        n = 0

    start_line = 1

    appendix = lines[0][len(appedix_start):].split(";")

    # Check if caption is present
    if u"C" in appendix:
        start_line = 2

    cells = []

    # First we check the maximum number of columns present in the table
    total_count = 0
    for line in lines[start_line:-1]:
        count = line.count(sep)
        # correct for links: [link|name]
        if sep == u"|":
            count -= len(re.findall("\[.*\|.*\]", line))
        if total_count > 0:
            count = count + total_count

        if line.endswith("\\"):
            total_count = count
        else:
            cells.append(count)
            total_count = 0

    cell_no = max(cells)

    n = start_line

    column_len = collections.defaultdict(int)

    column_no = 0

    # Find max column lengths
    data = collections.defaultdict(list)
    while n < len(lines):
        columns = splitLineIntoColumns(sep, lines[n])
        print columns

        new_row = True

        for column in columns:
            skip = False

            # endswith?
            if len(column) > 0 and column[-1] == u"\\":
                column = column[:-1]
            column = column.strip()

            if new_row and column_no > 0:
                column = column[1:].strip()

            for appendix in column.split(";"):
                # TODO: size for with appendixes?
                if re.match("c\d", appendix):
                    skip = True

            if not skip:
                data[column_no].append(column)

            #if len(column) > column_len[column_no] and not skip:
            #    column_len[column_no] = len(column)
            #column_no += 1

            new_row = False

            column_no += 1

        if columns[-1].endswith("\\"):
            column_no -= 1
        else:
            column_no = 0

        n += 1

    for i in data:
        column_len[i] = max([len(column) for column in data[i]])

        
    def Blank(n):
        if n > 0:
            return n * u" "
        else:
            return u""

    n = 1
    column_no = 0
    while n < len(lines) - 1:
        columns = splitLineIntoColumns(sep, lines[n])

        newline = []

        new_row = True

        for column in columns:
            force_add_whitespace = False
            column = column.strip()

            col_len = len(column)
            if new_row and column_no > 0:
                extra = u" "
                # Test for +
                if USE_PLUS_FORMATTING and column[0] == "+":
                    extra = u"+"
                    column = column[1:].strip()
                    col_len = len(column)
                elif not column.endswith("\\") and column_no != len(columns):
                    force_add_whitespace = True

                for i in range(column_no): 
                    column = "".join([Blank(column_len[i]), column])

                column = "".join([extra, Blank(3 * column_no - 1), column])

            if (column_no < cell_no or force_add_whitespace) and not column.endswith("\\") and not column_no >= cell_no:
                extra = Blank(column_len[column_no]-col_len)
            else:
                extra = u""
            newline.append(column + extra)
            column_no += 1
            new_row = False

        if columns[-1].endswith("\\"):
            column_no -= 1
        else:
            column_no = 0

        n += 1

        new_lines.append(" {0} ".format(sep).join(newline))


    text = lines[0] + "\n" + "\n".join(new_lines) + "\n>>"

    new_text_len = len(text)

    editor.ReplaceSelection(text)
    editor.SetCurrentPos(beginSel)
    editor.SetAnchor(beginSel)
⚠️ **GitHub.com Fallback** ⚠️