Created
May 12, 2012 02:09
-
-
Save FichteFoll/2663710 to your computer and use it in GitHub Desktop.
Plugin for Sublime Text 2 that "prettifies" MultiMarkdown tables and aligns their vertical separators
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from sublime import Region | |
import sublime_plugin | |
""" | |
This plugin for Sublime Text 2 searches for tables with a style like: | |
| I'm a header | I'm second|lolol| | |
|:-|-:|::| | |
| I'm a cell | yeah, you bet || | |
in either the whole file or just your selections | |
and aligns the vertical separators accordingly. | |
/////////////////////////////////////// | |
Usage: | |
{ "keys": ["alt+m", "alt+t"], "command": "prettify_markdown_table" } | |
Known issues: | |
- Due to str.splitlines() trimming blank lines at the end the resulting | |
string (file) will have no new line at the end. | |
/////////////////////////////////////// | |
Original source: | |
http://www.leancrew.com/all-this/2008/08/tables-for-markdown-and-textmate/ | |
Modified: | |
FichteFoll, for use with ST2 and multiple tables in one string | |
""" | |
def just(string, type, n): | |
"""Justify a string to length n according to type.""" | |
if type == '::': | |
return string.center(n) | |
elif type == '-:': | |
return string.rjust(n) | |
elif type == ':-': | |
return string.ljust(n) | |
else: | |
return string | |
def norm_tables(text): | |
"""Finds and aligns the vertical bars in a text table.""" | |
# Start by turning the text into a list of lines. | |
lines = text.splitlines() | |
rows = len(lines) | |
# iterrate over all lines | |
i = -1 | |
while i < rows - 1: | |
i += 1 | |
# First, find the separator line. | |
if not ( lines[i] and set(lines[i]).issubset('|:.-') ): | |
continue | |
# Determine how each column is to be justified. | |
fstrings = lines[i].strip('|').split('|') | |
justify = [] | |
try: | |
for cell in fstrings: | |
ends = cell[0] + cell[-1] | |
if ends == '::': | |
justify.append('::') | |
elif ends == '-:': | |
justify.append('-:') | |
else: | |
justify.append(':-') | |
except IndexError: | |
# The cell is not correctly formatted (i.e. too short) | |
print("Parse error at line %i: Malformatted format line. (%r)" % (i, lines[i])) | |
continue | |
# Assume the number of columns in the separator line is the number | |
# for the entire table. | |
columns = len(justify) | |
if columns == 0: | |
continue | |
# print("justify:" + str(justify)) | |
# Extract the content into a matrix (lists within a list). | |
content = [] | |
# Begin parsing upwards. | |
j = i - 1 | |
while j >= 0 and lines[j].startswith('|'): | |
cells = lines[j].strip('| ').split('|') | |
# Surround b one space as "bumpers." | |
content.append([ ' ' + x.strip() + ' ' for x in cells ]) | |
j -= 1 | |
# Reverse the list, append a dummy for the format line and continue parsing downwards. | |
content.reverse() | |
content.append([]) | |
contentstart = j + 1 | |
j = i + 1 | |
while j < lines and lines[j].startswith('|'): | |
cells = lines[j].strip('| ').split('|') | |
# Surround b one space as "bumpers." | |
content.append([ ' ' + x.strip() + ' ' for x in cells ]) | |
j += 1 | |
# Append cells to rows that don't have enough. | |
subrows = len(content) | |
if subrows == 0: | |
# `subrows = 1` doesn't make much sense as well (since it's only the format line), | |
# but keep in anyway | |
continue | |
for j in range(subrows): | |
content[j].extend( ('',) * (columns - len(content[j])) ) | |
# Get the width of the content in each column. The minimum width will | |
# be 2, because that's the shortest length of a formatting string and | |
# because that also matches an empty column with "bumper" spaces. | |
widths = [2] * columns | |
for row in content: | |
for j in range(columns): | |
widths[j] = max(len(row[j]), widths[j]) | |
# Add whitespace to make all the columns the same width | |
formatted = [] | |
for row in content: | |
formatted.append('|' + '|'.join([ just(s, j, w) for (s, j, w) in zip(row, justify, widths) ]) + '|') | |
# Recreate the format line with the appropriate column widths. | |
formatline = '|' + '|'.join([ j[0] + '-' * (n - 2) + j[-1] for (j, n) in zip(justify, widths) ]) + '|' | |
# "Replace" the newly formatted lines | |
lines[contentstart:contentstart + subrows] = formatted | |
# "Replace" the format line back into the list. | |
lines[i] = formatline | |
i = contentstart + subrows | |
# Return the formatted table. | |
return '\n'.join(lines) | |
class PrettifyMarkdownTableCommand(sublime_plugin.TextCommand): | |
def run(self, edit): | |
sel = self.view.sel() | |
if sum(1 for reg in sel) == 1 and sel[0].empty(): | |
# collect stuff | |
reg = sel[0] | |
row, col = self.view.rowcol(reg.begin()) | |
reg_all = Region(0, self.view.size()) | |
text = self.view.substr(reg_all) | |
# prettify | |
self.view.erase(edit, reg_all) | |
self.view.insert(edit, 0, norm_tables(text)) | |
# scroll back to old line | |
pt = self.view.text_point(row, col) | |
self.view.sel().clear() | |
self.view.sel().add(Region(pt)) | |
self.view.show(pt) | |
else: | |
for reg in sel: | |
if not reg.empty(): | |
# just iterate over every non-empty selection | |
self.view.replace(edit, reg, | |
norm_tables(self.view.substr(reg)) | |
) |
Fixed bug where a row had an empty cell at front or at end (in the forked gist).
Still fails to handle the multi-column cell denoted by two (or more) || at either start of end. There is perl code table_cleanup.pl in Fletcher Penney's Markdown utilities as noted in Brett Terpstra's article.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thanks for sharing!
Made a couple of minor changes :