1// based on HUGO markup/goldmark/tables/tables.go -- Pet
  2
  3// Copyright 2024 The Hugo Authors. All rights reserved.
  4//
  5// Licensed under the Apache License, Version 2.0 (the "License");
  6// you may not use this file except in compliance with the License.
  7// You may obtain a copy of the License at
  8// http://www.apache.org/licenses/LICENSE-2.0
  9//
 10// Unless required by applicable law or agreed to in writing, software
 11// distributed under the License is distributed on an "AS IS" BASIS,
 12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13// See the License for the specific language governing permissions and
 14// limitations under the License.
 15
 16package goldmark_extensions
 17/*
 18package tables
 19
 20import (
 21    "github.com/gohugoio/hugo/common/herrors"
 22    "github.com/gohugoio/hugo/common/types/hstring"
 23    "github.com/gohugoio/hugo/markup/converter/hooks"
 24    "github.com/gohugoio/hugo/markup/goldmark/internal/render"
 25    "github.com/gohugoio/hugo/markup/internal/attributes"
 26    "github.com/yuin/goldmark"
 27    "github.com/yuin/goldmark/ast"
 28    gast "github.com/yuin/goldmark/extension/ast"
 29    "github.com/yuin/goldmark/renderer"
 30    "github.com/yuin/goldmark/util"
 31)
 32
 33type (
 34    ext          struct{}
 35    htmlRenderer struct{}
 36)
 37
 38func New() goldmark.Extender {
 39    return &ext{}
 40}
 41
 42func (e *ext) Extend(m goldmark.Markdown) {
 43    m.Renderer().AddOptions(renderer.WithNodeRenderers(
 44        util.Prioritized(newHTMLRenderer(), 100),
 45    ))
 46}
 47
 48func newHTMLRenderer() renderer.NodeRenderer {
 49    r := &htmlRenderer{}
 50    return r
 51}
 52
 53func (r *htmlRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
 54    reg.Register(gast.KindTable, r.renderTable)
 55    reg.Register(gast.KindTableHeader, r.renderHeaderOrRow)
 56    reg.Register(gast.KindTableRow, r.renderHeaderOrRow)
 57    reg.Register(gast.KindTableCell, r.renderCell)
 58}
 59
 60func (r *htmlRenderer) renderTable(w util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) {
 61    ctx := w.(*render.Context)
 62    if entering {
 63        // This will be modified below.
 64        table := &hooks.Table{}
 65        ctx.PushValue(gast.KindTable, table)
 66        return ast.WalkContinue, nil
 67    }
 68
 69    v := ctx.PopValue(gast.KindTable)
 70    if v == nil {
 71        panic("table not found")
 72    }
 73
 74    table := v.(*hooks.Table)
 75
 76    renderer := ctx.RenderContext().GetRenderer(hooks.TableRendererType, nil)
 77    if renderer == nil {
 78        panic("table hook renderer not found")
 79    }
 80
 81    ordinal := ctx.GetAndIncrementOrdinal(gast.KindTable)
 82
 83    tctx := &tableContext{
 84        BaseContext:      render.NewBaseContext(ctx, renderer, n, source, nil, ordinal),
 85        AttributesHolder: attributes.New(n.Attributes(), attributes.AttributesOwnerGeneral),
 86        tHead:            table.THead,
 87        tBody:            table.TBody,
 88    }
 89
 90    cr := renderer.(hooks.TableRenderer)
 91
 92    err := cr.RenderTable(
 93        ctx.RenderContext().Ctx,
 94        w,
 95        tctx,
 96    )
 97    if err != nil {
 98        return ast.WalkContinue, herrors.NewFileErrorFromPos(err, tctx.Position())
 99    }
100
101    return ast.WalkContinue, nil
102}
103
104func (r *htmlRenderer) peekTable(ctx *render.Context) *hooks.Table {
105    v := ctx.PeekValue(gast.KindTable)
106    if v == nil {
107        panic("table not found")
108    }
109    return v.(*hooks.Table)
110}
111
112func (r *htmlRenderer) renderCell(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
113    ctx := w.(*render.Context)
114
115    if entering {
116        // Store the current pos so we can capture the rendered text.
117        ctx.PushPos(ctx.Buffer.Len())
118        return ast.WalkContinue, nil
119    }
120
121    n := node.(*gast.TableCell)
122
123    text := ctx.PopRenderedString()
124
125    table := r.peekTable(ctx)
126
127    var alignment string
128    switch n.Alignment {
129    case gast.AlignLeft:
130        alignment = "left"
131    case gast.AlignRight:
132        alignment = "right"
133    case gast.AlignCenter:
134        alignment = "center"
135    default:
136        alignment = ""
137    }
138
139    cell := hooks.TableCell{Text: hstring.HTML(text), Alignment: alignment}
140
141    if node.Parent().Kind() == gast.KindTableHeader {
142        table.THead[len(table.THead)-1] = append(table.THead[len(table.THead)-1], cell)
143    } else {
144        table.TBody[len(table.TBody)-1] = append(table.TBody[len(table.TBody)-1], cell)
145    }
146
147    return ast.WalkContinue, nil
148}
149
150func (r *htmlRenderer) renderHeaderOrRow(w util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) {
151    ctx := w.(*render.Context)
152    table := r.peekTable(ctx)
153    if entering {
154        if n.Kind() == gast.KindTableHeader {
155            table.THead = append(table.THead, hooks.TableRow{})
156        } else {
157            table.TBody = append(table.TBody, hooks.TableRow{})
158        }
159        return ast.WalkContinue, nil
160    }
161
162    return ast.WalkContinue, nil
163}
164
165type tableContext struct {
166    hooks.BaseContext
167    *attributes.AttributesHolder
168
169    tHead []hooks.TableRow
170    tBody []hooks.TableRow
171}
172
173func (c *tableContext) THead() []hooks.TableRow {
174    return c.tHead
175}
176
177func (c *tableContext) TBody() []hooks.TableRow {
178    return c.tBody
179}
180*/