\n")
+ w.WriteString("\n")
}
func withHTMLAttributes(input string, kvs ...string) string {
diff --git a/org/org.go b/org/org.go
index d715f79..841fe77 100644
--- a/org/org.go
+++ b/org/org.go
@@ -41,6 +41,12 @@ func (w *OrgWriter) emptyClone() *OrgWriter {
return &wcopy
}
+func (w *OrgWriter) nodesAsString(nodes ...Node) string {
+ tmp := w.emptyClone()
+ tmp.writeNodes(nodes...)
+ return tmp.String()
+}
+
func (w *OrgWriter) writeNodes(ns ...Node) {
for _, n := range ns {
switch n := n.(type) {
@@ -65,12 +71,6 @@ func (w *OrgWriter) writeNodes(ns ...Node) {
case Table:
w.writeTable(n)
- case TableHeader:
- w.writeTableHeader(n)
- case TableRow:
- w.writeTableRow(n)
- case TableSeparator:
- w.writeTableSeparator(n)
case Paragraph:
w.writeParagraph(n)
@@ -206,34 +206,45 @@ func (w *OrgWriter) writeListItem(li ListItem) {
}
func (w *OrgWriter) writeTable(t Table) {
- w.writeNodes(t.Header)
- w.writeNodes(t.Rows...)
-}
+ for _, row := range t.Rows {
+ w.WriteString(w.indent)
+ if len(row.Columns) == 0 {
+ w.WriteString(`|`)
+ for i := 0; i < len(t.ColumnInfos); i++ {
+ w.WriteString(strings.Repeat("-", t.ColumnInfos[i].Len+2))
+ if i < len(t.ColumnInfos)-1 {
+ w.WriteString("+")
+ }
+ }
+ w.WriteString(`|`)
-func (w *OrgWriter) writeTableHeader(th TableHeader) {
- w.writeNodes(th.SeparatorBefore)
- w.writeTableColumns(th.Columns)
- w.writeNodes(th.SeparatorAfter)
-}
-
-func (w *OrgWriter) writeTableRow(tr TableRow) {
- w.writeTableColumns(tr.Columns)
-}
-
-func (w *OrgWriter) writeTableSeparator(ts TableSeparator) {
- w.WriteString(w.indent + ts.Content + "\n")
-}
-
-func (w *OrgWriter) writeTableColumns(columns [][]Node) {
- w.WriteString(w.indent + "| ")
- for i, columnNodes := range columns {
- w.writeNodes(columnNodes...)
- w.WriteString(" |")
- if i < len(columns)-1 {
- w.WriteString(" ")
+ } else {
+ w.WriteString(`|`)
+ for _, column := range row.Columns {
+ w.WriteString(` `)
+ content := w.nodesAsString(column.Children...)
+ if content == "" {
+ content = " "
+ }
+ n := column.Len - len(content)
+ if n < 0 {
+ n = 0
+ }
+ if column.Align == "center" {
+ if n%2 != 0 {
+ w.WriteString(" ")
+ }
+ w.WriteString(strings.Repeat(" ", n/2) + content + strings.Repeat(" ", n/2))
+ } else if column.Align == "right" {
+ w.WriteString(strings.Repeat(" ", n) + content)
+ } else {
+ w.WriteString(content + strings.Repeat(" ", n))
+ }
+ w.WriteString(` |`)
+ }
}
+ w.WriteString("\n")
}
- w.WriteString("\n")
}
func (w *OrgWriter) writeHorizontalRule(hr HorizontalRule) {
diff --git a/org/table.go b/org/table.go
index ccd282d..30e94f8 100644
--- a/org/table.go
+++ b/org/table.go
@@ -2,27 +2,35 @@ package org
import (
"regexp"
+ "strconv"
"strings"
)
type Table struct {
- Header Node
- Rows []Node
+ Rows []Row
+ ColumnInfos []ColumnInfo
}
-type TableSeparator struct{ Content string }
-
-type TableHeader struct {
- SeparatorBefore Node
- Columns [][]Node
- SeparatorAfter Node
+type Row struct {
+ Columns []Column
+ IsSpecial bool
}
-type TableRow struct{ Columns [][]Node }
+type Column struct {
+ Children []Node
+ *ColumnInfo
+}
+
+type ColumnInfo struct {
+ Align string
+ Len int
+}
var tableSeparatorRegexp = regexp.MustCompile(`^(\s*)(\|[+-|]*)\s*$`)
var tableRowRegexp = regexp.MustCompile(`^(\s*)(\|.*)`)
+var columnAlignRegexp = regexp.MustCompile(`^<(l|c|r)>$`)
+
func lexTable(line string) (token, bool) {
if m := tableSeparatorRegexp.FindStringSubmatch(line); m != nil {
return token{"tableSeparator", len(m[1]), m[2], m}, true
@@ -33,43 +41,87 @@ func lexTable(line string) (token, bool) {
}
func (d *Document) parseTable(i int, parentStop stopFn) (int, Node) {
- rows, start := []Node{}, i
- for !parentStop(d, i) && (d.tokens[i].kind == "tableRow" || d.tokens[i].kind == "tableSeparator") {
- consumed, row := d.parseTableRowOrSeparator(i, parentStop)
- i += consumed
- rows = append(rows, row)
- }
-
- consumed := i - start
-
- if len(rows) >= 2 {
- if row, ok := rows[0].(TableRow); ok {
- if separator, ok := rows[1].(TableSeparator); ok {
- return consumed, Table{TableHeader{nil, row.Columns, separator}, rows[2:]}
+ rawRows, start := [][]string{}, i
+ for ; !parentStop(d, i); i++ {
+ if t := d.tokens[i]; t.kind == "tableRow" {
+ rawRow := strings.FieldsFunc(d.tokens[i].content, func(r rune) bool { return r == '|' })
+ for i := range rawRow {
+ rawRow[i] = strings.TrimSpace(rawRow[i])
}
+ rawRows = append(rawRows, rawRow)
+ } else if t.kind == "tableSeparator" {
+ rawRows = append(rawRows, nil)
+ } else {
+ break
}
}
- if len(rows) >= 3 {
- if separatorBefore, ok := rows[0].(TableSeparator); ok {
- if row, ok := rows[1].(TableRow); ok {
- if separatorAfter, ok := rows[2].(TableSeparator); ok {
- return consumed, Table{TableHeader{separatorBefore, row.Columns, separatorAfter}, rows[3:]}
+
+ table := Table{nil, getColumnInfos(rawRows)}
+ for _, rawColumns := range rawRows {
+ row := Row{nil, isSpecialRow(rawColumns)}
+ if len(rawColumns) != 0 {
+ for i := range table.ColumnInfos {
+ column := Column{nil, &table.ColumnInfos[i]}
+ if i < len(rawColumns) {
+ column.Children = d.parseInline(rawColumns[i])
}
+ row.Columns = append(row.Columns, column)
}
}
+ table.Rows = append(table.Rows, row)
+ }
+ return i - start, table
+}
+
+func getColumnInfos(rows [][]string) []ColumnInfo {
+ columnCount := 0
+ for _, columns := range rows {
+ if n := len(columns); n > columnCount {
+ columnCount = n
+ }
}
- return consumed, Table{nil, rows}
+ columnInfos := make([]ColumnInfo, columnCount)
+ for i := 0; i < columnCount; i++ {
+ countNumeric, countNonNumeric := 0, 0
+ for _, columns := range rows {
+ if !(i < len(columns)) {
+ continue
+ }
+
+ if len(columns[i]) > columnInfos[i].Len {
+ columnInfos[i].Len = len(columns[i])
+ }
+
+ if m := columnAlignRegexp.FindStringSubmatch(columns[i]); m != nil && isSpecialRow(columns) {
+ switch m[1] {
+ case "l":
+ columnInfos[i].Align = "left"
+ case "c":
+ columnInfos[i].Align = "center"
+ case "r":
+ columnInfos[i].Align = "right"
+ }
+ } else if _, err := strconv.ParseFloat(columns[i], 32); err == nil {
+ countNumeric++
+ } else if strings.TrimSpace(columns[i]) != "" {
+ countNonNumeric++
+ }
+ }
+
+ if columnInfos[i].Align == "" && countNumeric >= countNonNumeric {
+ columnInfos[i].Align = "right"
+ }
+ }
+ return columnInfos
}
-func (d *Document) parseTableRowOrSeparator(i int, _ stopFn) (int, Node) {
- if d.tokens[i].kind == "tableSeparator" {
- return 1, TableSeparator{d.tokens[i].content}
+func isSpecialRow(rawColumns []string) bool {
+ isAlignRow := true
+ for _, rawColumn := range rawColumns {
+ if !columnAlignRegexp.MatchString(rawColumn) && rawColumn != "" {
+ isAlignRow = false
+ }
}
- fields := strings.FieldsFunc(d.tokens[i].content, func(r rune) bool { return r == '|' })
- row := TableRow{}
- for _, field := range fields {
- row.Columns = append(row.Columns, d.parseInline(strings.TrimSpace(field)))
- }
- return 1, row
+ return isAlignRow
}
diff --git a/org/testdata/footnotes.html b/org/testdata/footnotes.html
index c1eac09..78f9668 100644
--- a/org/testdata/footnotes.html
+++ b/org/testdata/footnotes.html
@@ -53,13 +53,16 @@ and tables
-
1
a
+
1
+
a
-
2
b
+
2
+
b
-
3
c
+
3
+
c
diff --git a/org/testdata/lists.html b/org/testdata/lists.html
index c50f03f..bae58c4 100644
--- a/org/testdata/lists.html
+++ b/org/testdata/lists.html
@@ -60,11 +60,17 @@ and another one with a table