summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStuart Rackham <srackham@methods.co.nz>2009-04-21 08:49:37 +1200
committerStuart Rackham <srackham@methods.co.nz>2009-04-21 08:49:37 +1200
commitb244ea953b9e032aba3295a28fcceb5524400bd6 (patch)
treeb4527403ae8f5c87d31ac49cd69c5310db749e84
parent8e538d71d3132a9ffc9bb700dcfe761703d20d17 (diff)
downloadasciidoc-py3-b244ea953b9e032aba3295a28fcceb5524400bd6.tar.gz
Implemented table column spanning.
-rw-r--r--asciidoc.conf2
-rwxr-xr-xasciidoc.py109
-rw-r--r--doc/asciidoc.txt2
-rw-r--r--docbook.conf10
-rw-r--r--examples/website/newtables.txt28
-rw-r--r--html4.conf12
-rw-r--r--vim/syntax/asciidoc.vim2
-rw-r--r--xhtml11.conf10
8 files changed, 122 insertions, 53 deletions
diff --git a/asciidoc.conf b/asciidoc.conf
index 3906245..eba5dac 100644
--- a/asciidoc.conf
+++ b/asciidoc.conf
@@ -487,7 +487,7 @@ asciidoc-style=tags="asciidoc",subs=[],filter='python "{asciidoc-file}" -b {back
[tabledef-nested]
# Same as [tabledef-default] but with different delimiter and separator.
delimiter=^!={3,}$
-separator=((?P<cellcount>\d+)\*)?!
+separator=((?P<cellcount>\d+)(?P<cellop>[*+]))?!
posattrs=style
template=table
verse-style=tags="verse"
diff --git a/asciidoc.py b/asciidoc.py
index 201e8d2..5f4a725 100755
--- a/asciidoc.py
+++ b/asciidoc.py
@@ -2590,13 +2590,20 @@ class Column:
self.abswidth=None # 1.. (page units).
self.pcwidth=None # 1..99 (percentage).
+class Cell:
+ def __init__(self, data, span=1):
+ self.data = data
+ self.span = span
+ def __repr__(self):
+ return '<Cell: %d+|%s>' % (self.span, self.data)
+
class Table(AbstractBlock):
ALIGNMENTS = {'<':'left', '>':'right', '^':'center'}
FORMATS = ('psv','csv','dsv')
SEPARATORS = dict(
csv=',',
dsv=r':|\n',
- psv=r'((?P<cellcount>\d+)\*)?\|',
+ psv=r'((?P<cellcount>\d+)(?P<cellop>[*+]))?\|',
)
def __init__(self):
AbstractBlock.__init__(self)
@@ -2608,7 +2615,7 @@ class Table(AbstractBlock):
# Calculated parameters.
self.abswidth=None # 1.. (page units).
self.pcwidth = None # 1..99 (percentage).
- self.rows=[] # Parsed rows, each row is a list of cell data.
+ self.rows=[] # Parsed rows, each row is a list of Cells.
self.columns=[] # List of Columns.
def load(self,name,entries):
AbstractBlock.load(self,name,entries)
@@ -2770,40 +2777,62 @@ class Table(AbstractBlock):
Generate column related substitution attributes.
"""
cols = []
- i = 0
+ i = 1
for col in self.columns:
- i += 1
colspec = self.get_tags(col.style).colspec
if colspec:
self.attributes['colalign'] = col.colalign
self.attributes['colabswidth'] = col.abswidth
self.attributes['colpcwidth'] = col.pcwidth
- self.attributes['colnumber'] = str(i+1)
+ self.attributes['colnumber'] = str(i)
s = subs_attrs(colspec, self.attributes)
if not s:
warning('colspec dropped: contains undefined attribute')
else:
cols.append(s)
+ i += 1
if cols:
self.attributes['colspecs'] = writer.newline.join(cols)
def parse_rows(self, text):
"""
Parse the table source text into self.rows (a list of rows, each row
- is a list of raw cell text.
+ is a list of Cells.
"""
if self.parameters.format in ('psv','dsv'):
cells = self.parse_psv_dsv(text)
colcount = len(self.columns)
- for i in range(0, len(cells), colcount):
- self.rows.append(cells[i:i+colcount])
+ row = []
+ i = 0
+ for cell in cells:
+ i += cell.span
+ if i <= colcount:
+ row.append(cell)
+ if i >= colcount:
+ self.rows.append(row)
+ row = []
+ i = 0
+ if i > colcount:
+ warning('table row %d: span exceeds number of columns'
+ % len(self.rows))
elif self.parameters.format == 'csv':
- self.parse_csv(text)
+ self.rows = self.parse_csv(text)
else:
assert True,'illegal table format'
+ # Check that all row spans match.
+ for i,row in enumerate(self.rows):
+ row_span = 0
+ for cell in row:
+ row_span += cell.span
+ if i == 0:
+ header_span = row_span
+ if row_span < header_span:
+ warning('table row %d: does not span all columns' % (i+1))
+ if row_span > header_span:
+ warning('table row %d: exceeds columns span' % (i+1))
def subs_rows(self, rows, rowtype='body'):
"""
Return a string of output markup from a list of rows, each row
- is a list of raw cell text.
+ is a list of raw data text.
"""
tags = tables.tags[self.parameters.tags]
if rowtype == 'header':
@@ -2821,21 +2850,21 @@ class Table(AbstractBlock):
return writer.newline.join(result)
def subs_row(self, row, rowtype):
"""
- Substitute the list of cells using the cell data tag.
+ Substitute the list of Cells using the data tag.
Returns a list of marked up table cell elements.
"""
- if len(row) < len(self.columns):
- warning('fewer row data items than table columns')
- if len(row) > len(self.columns):
- warning('more row data items than table columns')
result = []
- for i in range(len(self.columns)):
+ i = 0
+ for cell in row:
col = self.columns[i]
tags = self.get_tags(col.style)
self.attributes['colalign'] = col.colalign
self.attributes['colabswidth'] = col.abswidth
self.attributes['colpcwidth'] = col.pcwidth
self.attributes['colnumber'] = str(i+1)
+ self.attributes['colspan'] = str(cell.span)
+ self.attributes['colstart'] = self.attributes['colnumber']
+ self.attributes['colend'] = str(i+cell.span)
if rowtype == 'header':
dtag = tags.headdata
elif rowtype == 'footer':
@@ -2843,10 +2872,10 @@ class Table(AbstractBlock):
else:
dtag = tags.bodydata
# Fill missing column data with blanks.
- if i > len(row) - 1:
+ if i > len(self.columns) - 1:
data = ''
else:
- data = row[i]
+ data = cell.data
# Format header cells with the table style not column style.
if rowtype == 'header':
colstyle = None
@@ -2868,51 +2897,61 @@ class Table(AbstractBlock):
data += dovetail_tags([stag],para.split('\n'),[etag])
stag,etag = subs_tag(dtag,self.attributes)
result = result + dovetail_tags([stag],data,[etag])
+ i += cell.span
return result
def parse_csv(self,text):
"""
Parse the table source text and return a list of rows, each row
- is a list of raw cell text.
+ is a list of Cells.
"""
import StringIO
import csv
- self.rows = []
+ rows = []
rdr = csv.reader(StringIO.StringIO('\r\n'.join(text)),
delimiter=self.parameters.separator, skipinitialspace=True)
try:
for row in rdr:
- self.rows.append(row)
+ rows.append([Cell(data) for data in row])
except Exception:
self.error('csv parse error: %s' % row)
+ return rows
def parse_psv_dsv(self,text):
"""
Parse list of PSV or DSV table source text lines and return a list of
- cells.
+ Cells.
"""
+ def append_cell(data, cellcount, cellop):
+ if cellop == '*': # Cell multiplier.
+ for i in range(cellcount):
+ cells.append(Cell(data))
+ elif cellop == '+': # Cell spanner.
+ cells.append(Cell(data, cellcount))
+ else:
+ self.error('illegal table cell operator')
text = '\n'.join(text)
separator = '(?msu)'+self.parameters.separator
format = self.parameters.format
start = 0
cellcount = 1
+ cellop = '*'
cells = []
- cell = ''
+ data = ''
for mo in re.finditer(separator,text):
- cell += text[start:mo.start()]
- if cell.endswith('\\'):
- cell = cell[:-1]+mo.group() # Reinstate escaped separators.
+ data += text[start:mo.start()]
+ if data.endswith('\\'):
+ data = data[:-1]+mo.group() # Reinstate escaped separators.
else:
- for i in range(cellcount):
- cells.append(cell)
+ append_cell(data, cellcount, cellop)
cellcount = int(mo.groupdict().get('cellcount') or '1')
- cell = ''
+ cellop = mo.groupdict().get('cellop') or '*'
+ data = ''
start = mo.end()
# Last cell follows final separator.
- cell += text[start:]
- for i in range(cellcount):
- cells.append(cell)
+ data += text[start:]
+ append_cell(data, cellcount, cellop)
# We expect a dummy blank item preceeding first PSV cell.
if format == 'psv':
- if cells[0] != '':
+ if cells[0].data != '':
self.error('missing leading separator: %s' % separator,
self.start)
else:
@@ -2951,7 +2990,9 @@ class Table(AbstractBlock):
if self.parameters.format == 'csv':
cols = text[0].count(self.parameters.separator)
else:
- cols = len(self.parse_psv_dsv(text[:1]))
+ cols = 0
+ for cell in self.parse_psv_dsv(text[:1]):
+ cols += cell.span
self.parse_cols(cols)
# Set calculated attributes.
self.attributes['colcount'] = len(self.columns)
diff --git a/doc/asciidoc.txt b/doc/asciidoc.txt
index 72f0381..d22c926 100644
--- a/doc/asciidoc.txt
+++ b/doc/asciidoc.txt
@@ -2656,7 +2656,7 @@ as a sequence of cells -- there are no mandatory row separators.
cells with a separator, that really the only difference (apart from
different default separators).
- 'psv' and 'dsv' separators are Python regular expressions.
-- The default 'psv' separator is `((?P<cellcount>\d+)\*)?\|` (a pipe
+- The default 'psv' separator is `((?P<cellcount>\d+)[*+])?\|` (a pipe
character with optional cell multiplier prefix); the default 'dsv'
separator is `:|\n` (a colon or a line break).
- 'psv' and 'dsv' cell separators can be escaped by preceding them
diff --git a/docbook.conf b/docbook.conf
index 9df1a87..fc0d102 100644
--- a/docbook.conf
+++ b/docbook.conf
@@ -335,10 +335,10 @@ template::[verseblock]
# Tables.
[tabletags-default]
-colspec=<colspec colwidth="{width!{colpcwidth}*}{width?{colabswidth}{pageunits}}" align="{colalign}"/>
+colspec=<colspec colname="col_{colnumber}" colwidth="{width!{colpcwidth}*}{width?{colabswidth}{pageunits}}" align="{colalign}"/>
bodyrow=<row>|</row>
-headdata=<entry>|</entry>
-bodydata=<entry>|</entry>
+headdata=<entry{colspan@1:: namest="col_{colstart}" nameend="col_{colend}"}>|</entry>
+bodydata=<entry{colspan@1:: namest="col_{colstart}" nameend="col_{colend}"}>|</entry>
paragraph=<simpara>|</simpara>
[tabletags-emphasis]
@@ -351,11 +351,11 @@ paragraph=<simpara><emphasis role="strong">|</emphasis></simpara>
paragraph=<simpara><literal>|</literal></simpara>
[tabletags-verse]
-bodydata=<entry><literallayout>|</literallayout></entry>
+bodydata=<entry{colspan@1:: namest="col_{colstart}" nameend="col_{colend}"}><literallayout>|</literallayout></entry>
paragraph=
[tabletags-literal]
-bodydata=<entry><literallayout class="monospaced">|</literallayout></entry>
+bodydata=<entry{colspan@1:: namest="col_{colstart}" nameend="col_{colend}"}><literallayout class="monospaced">|</literallayout></entry>
paragraph=
[tabletags-asciidoc]
diff --git a/examples/website/newtables.txt b/examples/website/newtables.txt
index 370eda5..9f34a5c 100644
--- a/examples/website/newtables.txt
+++ b/examples/website/newtables.txt
@@ -395,3 +395,31 @@ Nested table
|==============================================
---------------------------------------------------------------------
+
+
+Spanned table
+-------------
+
+.An example table
+[width="75%",cols="s,^2,^2m,^2e",options="header,footer"]
+|========================================================
+|Column 1 2+|Columns 2 and 3 |Column 4
+|1 3+|Item 1
+|2 |Item 2 |Item 2 |Item 2
+2+|3 |Item 3 |Item 3
+4+|4
+|footer 1 |footer 2 2+|footer 3 and 4
+|========================================================
+
+.AsciiDoc source
+---------------------------------------------------------------------
+[width="75%",cols="s,^2,^2m,^2e",options="header,footer"]
+|========================================================
+|Column 1 2+|Columns 2 and 3 |Column 4
+|1 3+|Item 1
+|2 |Item 2 |Item 2 |Item 2
+2+|3 |Item 3 |Item 3
+4+|4
+|footer 1 |footer 2 2+|footer 3 and 4
+|========================================================
+---------------------------------------------------------------------
diff --git a/html4.conf b/html4.conf
index b8f1627..f4ec963 100644
--- a/html4.conf
+++ b/html4.conf
@@ -289,9 +289,9 @@ template::[quoteblock]
# Tables.
[tabletags-default]
bodyrow=<tr>|</tr>
-headdata=<th align="{colalign}"{autowidth-option! width="{colpcwidth}%"}>|</th>
-footdata=<td align="{colalign}"{autowidth-option! width="{colpcwidth}%"} style="font-weight:bold">|</td>
-bodydata=<td align="{colalign}"{autowidth-option! width="{colpcwidth}%"} valign="{valign=top}">|</td>
+headdata=<th {colspan@1::colspan="{colspan}" }align="{colalign}"{autowidth-option! width="{colpcwidth}%"}>|</th>
+footdata=<td {colspan@1::colspan="{colspan}" }align="{colalign}"{autowidth-option! width="{colpcwidth}%"} style="font-weight:bold">|</td>
+bodydata=<td {colspan@1::colspan="{colspan}" }align="{colalign}"{autowidth-option! width="{colpcwidth}%"} valign="{valign=top}">|</td>
paragraph=<p>|</p>
[tabletags-emphasis]
@@ -304,15 +304,15 @@ paragraph=<p><strong>|</strong></p>
paragraph=<p><tt>|</tt></p>
[tabletags-verse]
-bodydata=<td align="{colalign}"{autowidth-option! width="{colpcwidth}%"} valign="{valign=top}"><pre style="font-family: inherit;">|</pre></td>
+bodydata=<td {colspan@1::colspan="{colspan}" }align="{colalign}"{autowidth-option! width="{colpcwidth}%"} valign="{valign=top}"><pre style="font-family: inherit;">|</pre></td>
paragraph=
[tabletags-literal]
-bodydata=<td align="{colalign}"{autowidth-option! width="{colpcwidth}%"} valign="{valign=top}"><pre><tt>|</tt></pre></td>
+bodydata=<td {colspan@1::colspan="{colspan}" }align="{colalign}"{autowidth-option! width="{colpcwidth}%"} valign="{valign=top}"><pre><tt>|</tt></pre></td>
paragraph=
[tabletags-asciidoc]
-bodydata=<td align="{colalign}"{autowidth-option! width="{colpcwidth}%"} valign="{valign=top}"><div>|</div></td>
+bodydata=<td {colspan@1::colspan="{colspan}" }align="{colalign}"{autowidth-option! width="{colpcwidth}%"} valign="{valign=top}"><div>|</div></td>
paragraph=
[table]
diff --git a/vim/syntax/asciidoc.vim b/vim/syntax/asciidoc.vim
index c92664f..fdd8a90 100644
--- a/vim/syntax/asciidoc.vim
+++ b/vim/syntax/asciidoc.vim
@@ -72,7 +72,7 @@ syn match asciidocSidebarDelimiter /^*\{4,}$/
"See http://vimdoc.sourceforge.net/htmldoc/usr_44.html for excluding region
"contents from highlighting.
-syn match asciidocTablePrefix /\(\d\+\*\)\?|/ containedin=asciidocTableBlock contained
+syn match asciidocTablePrefix /\(\d\+[*+]\)\?|/ containedin=asciidocTableBlock contained
syn region asciidocTableBlock matchgroup=asciidocTableDelimiter start=/^|=\{3,}$/ end=/^|=\{3,}$/ keepend contains=ALL
syn match asciidocTablePrefix2 /\(\d\+\*\)\?!/ containedin=asciidocTableBlock2 contained
syn region asciidocTableBlock2 matchgroup=asciidocTableDelimiter2 start=/^!=\{3,}$/ end=/^!=\{3,}$/ keepend contains=ALL
diff --git a/xhtml11.conf b/xhtml11.conf
index 6228180..c8232b6 100644
--- a/xhtml11.conf
+++ b/xhtml11.conf
@@ -324,8 +324,8 @@ template::[quoteblock]
# {colalign} should be in <col> but Firefox 2 & 3 require it in <td>.
colspec=<col{autowidth-option! width="{colpcwidth}%"} />
bodyrow=<tr>|</tr>
-headdata=<th align="{colalign}">|</th>
-bodydata=<td align="{colalign}">|</td>
+headdata=<th {colspan@1::colspan="{colspan}" }align="{colalign}">|</th>
+bodydata=<td {colspan@1::colspan="{colspan}" }align="{colalign}">|</td>
paragraph=<p class="table">|</p>
[tabletags-emphasis]
@@ -338,15 +338,15 @@ paragraph=<p class="table"><strong>|</strong></p>
paragraph=<p class="table"><tt>|</tt></p>
[tabletags-verse]
-bodydata=<td align="{colalign}"><div class="verse">|</div></td>
+bodydata=<td {colspan@1::colspan="{colspan}" }align="{colalign}"><div class="verse">|</div></td>
paragraph=
[tabletags-literal]
-bodydata=<td align="{colalign}"><div class="literal"><pre><tt>|</tt></pre></div></td>
+bodydata=<td {colspan@1::colspan="{colspan}" }align="{colalign}"><div class="literal"><pre><tt>|</tt></pre></div></td>
paragraph=
[tabletags-asciidoc]
-bodydata=<td align="{colalign}"><div>|</div></td>
+bodydata=<td {colspan@1::colspan="{colspan}" }align="{colalign}"><div>|</div></td>
paragraph=
[table]