diff --git a/markup/lexer.go b/markup/lexer.go index b1c3abc..b69a3cf 100644 --- a/markup/lexer.go +++ b/markup/lexer.go @@ -27,7 +27,8 @@ type GemLexerState struct { id int buf string // Temporaries - img *Img + img *Img + table *Table } type Line struct { @@ -80,6 +81,8 @@ func lineToAST(line string, state *GemLexerState, ast *[]Line) { switch state.where { case "img": goto imgState + case "table": + goto tableState case "pre": goto preformattedState case "list": @@ -99,6 +102,13 @@ imgState: } return +tableState: + if shouldGoBackToNormal := state.table.Process(line); shouldGoBackToNormal { + state.where = "" + addLine(*state.table) + } + return + preformattedState: switch { case startsWith("```"): @@ -209,6 +219,9 @@ normalState: state.where = "img" state.img = img } + case MatchesTable(line): + state.where = "table" + state.table = TableFromFirstLine(line, state.name) default: addLine(fmt.Sprintf("
%s
", state.id, ParagraphToHtml(state.name, line))) } diff --git a/markup/parser.go b/markup/parser.go index 50ff158..665f8ba 100644 --- a/markup/parser.go +++ b/markup/parser.go @@ -13,6 +13,8 @@ func Parse(ast []Line, from, to int, recursionLevel int) (html string) { html += Transclude(v, recursionLevel) case Img: html += v.ToHtml() + case Table: + html += v.asHtml() case string: html += v default: diff --git a/markup/table.go b/markup/table.go new file mode 100644 index 0000000..f6c6886 --- /dev/null +++ b/markup/table.go @@ -0,0 +1,215 @@ +package markup + +import ( + "fmt" + "regexp" + "strings" + "unicode" + // "github.com/bouncepaw/mycorrhiza/util" +) + +var tableRe = regexp.MustCompile(`^table\s+{`) + +func MatchesTable(line string) bool { + return tableRe.MatchString(line) +} + +func TableFromFirstLine(line, hyphaName string) *Table { + return &Table{ + hyphaName: hyphaName, + caption: line[strings.IndexRune(line, '{')+1:], + rows: make([]*tableRow, 0), + } +} + +func (t *Table) Process(line string) (shouldGoBackToNormal bool) { + if strings.TrimSpace(line) == "}" && !t.inMultiline { + return true + } + if !t.inMultiline { + t.pushRow() + } + var ( + escaping bool + lookingForNonSpace = !t.inMultiline + countingColspan bool + ) + for i, r := range line { + switch { + case lookingForNonSpace && unicode.IsSpace(r): + case lookingForNonSpace && (r == '!' || r == '|'): + t.currCellMarker = r + t.currColspan = 1 + lookingForNonSpace = false + countingColspan = true + case lookingForNonSpace: + t.currCellMarker = '^' // ^ represents implicit |, not part of syntax + t.currColspan = 1 + lookingForNonSpace = false + t.currCellBuilder.WriteRune(r) + + case escaping: + t.currCellBuilder.WriteRune(r) + + case t.inMultiline && r == '}': + t.inMultiline = false + case t.inMultiline && i == len(line)-1: + t.currCellBuilder.WriteRune('\n') + case t.inMultiline: + t.currCellBuilder.WriteRune(r) + + // Not in multiline: + case (r == '|' || r == '!') && !countingColspan: + t.pushCell() + t.currCellMarker = r + t.currColspan = 1 + countingColspan = true + case r == t.currCellMarker && (r == '|' || r == '!') && countingColspan: + t.currColspan++ + case r == '{': + t.inMultiline = true + countingColspan = false + case i == len(line)-1: + t.pushCell() + default: + t.currCellBuilder.WriteRune(r) + countingColspan = false + } + } + return false +} + +type Table struct { + // data + hyphaName string + caption string + rows []*tableRow + // state + inMultiline bool + // tmp + currCellMarker rune + currColspan uint + currCellBuilder strings.Builder +} + +func (t *Table) pushRow() { + t.rows = append(t.rows, &tableRow{ + cells: make([]*tableCell, 0), + }) +} + +func (t *Table) pushCell() { + tc := &tableCell{ + content: t.currCellBuilder.String(), + colspan: t.currColspan, + } + switch t.currCellMarker { + case '|', '^': + tc.kind = tableCellDatum + case '!': + tc.kind = tableCellHeader + } + // We expect the table to have at least one row ready, so no nil-checking + tr := t.rows[len(t.rows)-1] + tr.cells = append(tr.cells, tc) + t.currCellBuilder = strings.Builder{} +} + +func (t *Table) asHtml() (html string) { + if t.caption != "" { + html += fmt.Sprintf("