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