diff options
author | Bjørn Erik Pedersen <[email protected]> | 2022-02-26 12:52:06 +0100 |
---|---|---|
committer | Bjørn Erik Pedersen <[email protected]> | 2022-02-26 21:54:36 +0100 |
commit | 928a89696273b1a5defa0e85115c9bd0e167cbf0 (patch) | |
tree | 3c9eb330385a01e230036dc018817cb50e65cbef /markup | |
parent | 2e54c009331dc8c6260d16a55d4a13cfff55054d (diff) | |
download | hugo-928a89696273b1a5defa0e85115c9bd0e167cbf0.tar.gz hugo-928a89696273b1a5defa0e85115c9bd0e167cbf0.zip |
markup/goldmark: Add Position to CodeblockContext
But note that this is not particulary fast and the recommendad usage is error logging only.
Updates #9574
Diffstat (limited to 'markup')
-rw-r--r-- | markup/converter/converter.go | 6 | ||||
-rw-r--r-- | markup/converter/hooks/hooks.go | 14 | ||||
-rw-r--r-- | markup/goldmark/codeblocks/integration_test.go | 67 | ||||
-rw-r--r-- | markup/goldmark/codeblocks/render.go | 62 | ||||
-rw-r--r-- | markup/goldmark/codeblocks/transform.go | 1 |
5 files changed, 133 insertions, 17 deletions
diff --git a/markup/converter/converter.go b/markup/converter/converter.go index 30addfec6..dac0a6b73 100644 --- a/markup/converter/converter.go +++ b/markup/converter/converter.go @@ -128,9 +128,13 @@ type DocumentContext struct { // RenderContext holds contextual information about the content to render. type RenderContext struct { - Src []byte + // Src is the content to render. + Src []byte + + // Whether to render TableOfContents. RenderTOC bool + // GerRenderer provides hook renderers on demand. GetRenderer hooks.GetRendererFunc } diff --git a/markup/converter/hooks/hooks.go b/markup/converter/hooks/hooks.go index 987cb1dc3..a570113ff 100644 --- a/markup/converter/hooks/hooks.go +++ b/markup/converter/hooks/hooks.go @@ -17,6 +17,7 @@ import ( "io" "github.com/gohugoio/hugo/common/hugio" + "github.com/gohugoio/hugo/common/text" "github.com/gohugoio/hugo/identity" "github.com/gohugoio/hugo/markup/internal/attributes" ) @@ -37,6 +38,7 @@ type LinkContext interface { type CodeblockContext interface { AttributesProvider + text.Positioner Options() map[string]interface{} Lang() string Code() string @@ -59,6 +61,10 @@ type CodeBlockRenderer interface { identity.Provider } +type IsDefaultCodeBlockRendererProvider interface { + IsDefaultCodeBlockRenderer() bool +} + // HeadingContext contains accessors to all attributes that a HeadingRenderer // can use to render a heading. type HeadingContext interface { @@ -84,6 +90,14 @@ type HeadingRenderer interface { identity.Provider } +// ElementPositionRevolver provides a way to resolve the start Position +// of a markdown element in the original source document. +// This may be both slow and aproximate, so should only be +// used for error logging. +type ElementPositionRevolver interface { + ResolvePosition(ctx interface{}) text.Position +} + type RendererType int const ( diff --git a/markup/goldmark/codeblocks/integration_test.go b/markup/goldmark/codeblocks/integration_test.go index 01510fc81..68f6b809e 100644 --- a/markup/goldmark/codeblocks/integration_test.go +++ b/markup/goldmark/codeblocks/integration_test.go @@ -141,3 +141,70 @@ echo "p1"; b.AssertFileContent("public/p1/index.html", "|echo \"p1\";|") } + +func TestCodePosition(t *testing.T) { + t.Parallel() + + files := ` +-- config.toml -- +-- content/p1.md -- +--- +title: "p1" +--- + +## Code + +§§§ +echo "p1"; +§§§ +-- layouts/_default/single.html -- +{{ .Content }} +-- layouts/_default/_markup/render-codeblock.html -- +Position: {{ .Position | safeHTML }} + + +` + + b := hugolib.NewIntegrationTestBuilder( + hugolib.IntegrationTestConfig{ + T: t, + TxtarString: files, + }, + ).Build() + + b.AssertFileContent("public/p1/index.html", "Position: \"content/p1.md:7:1\"") +} + +// Issue 9571 +func TestOptionsNonChroma(t *testing.T) { + t.Parallel() + + files := ` +-- config.toml -- +-- content/p1.md -- +--- +title: "p1" +--- + +## Code + +§§§bash {style=monokai} +echo "p1"; +§§§ +-- layouts/_default/single.html -- +{{ .Content }} +-- layouts/_default/_markup/render-codeblock.html -- +Style: {{ .Attributes }}| + + +` + + b := hugolib.NewIntegrationTestBuilder( + hugolib.IntegrationTestConfig{ + T: t, + TxtarString: files, + }, + ).Build() + + b.AssertFileContent("public/p1/index.html", "asdfadf") +} diff --git a/markup/goldmark/codeblocks/render.go b/markup/goldmark/codeblocks/render.go index 6cc43128b..bbf15bef3 100644 --- a/markup/goldmark/codeblocks/render.go +++ b/markup/goldmark/codeblocks/render.go @@ -16,7 +16,9 @@ package codeblocks import ( "bytes" "fmt" + "sync" + "github.com/gohugoio/hugo/common/herrors" htext "github.com/gohugoio/hugo/common/text" "github.com/gohugoio/hugo/markup/converter/hooks" "github.com/gohugoio/hugo/markup/goldmark/internal/render" @@ -59,6 +61,8 @@ func (r *htmlRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) { } func (r *htmlRenderer) renderCodeBlock(w util.BufWriter, src []byte, node ast.Node, entering bool) (ast.WalkStatus, error) { + defer herrors.Recover() + ctx := w.(*render.Context) if entering { @@ -67,6 +71,11 @@ func (r *htmlRenderer) renderCodeBlock(w util.BufWriter, src []byte, node ast.No n := node.(*codeBlock) lang := string(n.b.Language(src)) + renderer := ctx.RenderContext().GetRenderer(hooks.CodeBlockRendererType, lang) + if renderer == nil { + return ast.WalkStop, fmt.Errorf("no code renderer found for %q", lang) + } + ordinal := n.ordinal var buff bytes.Buffer @@ -77,30 +86,37 @@ func (r *htmlRenderer) renderCodeBlock(w util.BufWriter, src []byte, node ast.No buff.Write(line.Value(src)) } - text := htext.Chomp(buff.String()) + s := htext.Chomp(buff.String()) var info []byte if n.b.Info != nil { info = n.b.Info.Segment.Value(src) } attrs := getAttributes(n.b, info) + cbctx := &codeBlockContext{ + page: ctx.DocumentContext().Document, + lang: lang, + code: s, + ordinal: ordinal, + AttributesHolder: attributes.New(attrs, attributes.AttributesOwnerCodeBlock), + } - v := ctx.RenderContext().GetRenderer(hooks.CodeBlockRendererType, lang) - if v == nil { - return ast.WalkStop, fmt.Errorf("no code renderer found for %q", lang) + cbctx.createPos = func() htext.Position { + if resolver, ok := renderer.(hooks.ElementPositionRevolver); ok { + return resolver.ResolvePosition(cbctx) + } + return htext.Position{ + Filename: ctx.DocumentContext().Filename, + LineNumber: 0, + ColumnNumber: 0, + } } - cr := v.(hooks.CodeBlockRenderer) + cr := renderer.(hooks.CodeBlockRenderer) err := cr.RenderCodeblock( w, - codeBlockContext{ - page: ctx.DocumentContext().Document, - lang: lang, - code: text, - ordinal: ordinal, - AttributesHolder: attributes.New(attrs, attributes.AttributesOwnerCodeBlock), - }, + cbctx, ) ctx.AddIdentity(cr) @@ -113,25 +129,39 @@ type codeBlockContext struct { lang string code string ordinal int + + // This is only used in error situations and is expensive to create, + // to deleay creation until needed. + pos htext.Position + posInit sync.Once + createPos func() htext.Position + *attributes.AttributesHolder } -func (c codeBlockContext) Page() interface{} { +func (c *codeBlockContext) Page() interface{} { return c.page } -func (c codeBlockContext) Lang() string { +func (c *codeBlockContext) Lang() string { return c.lang } -func (c codeBlockContext) Code() string { +func (c *codeBlockContext) Code() string { return c.code } -func (c codeBlockContext) Ordinal() int { +func (c *codeBlockContext) Ordinal() int { return c.ordinal } +func (c *codeBlockContext) Position() htext.Position { + c.posInit.Do(func() { + c.pos = c.createPos() + }) + return c.pos +} + func getAttributes(node *ast.FencedCodeBlock, infostr []byte) []ast.Attribute { if node.Attributes() != nil { return node.Attributes() diff --git a/markup/goldmark/codeblocks/transform.go b/markup/goldmark/codeblocks/transform.go index 791e99a5c..be5334b5f 100644 --- a/markup/goldmark/codeblocks/transform.go +++ b/markup/goldmark/codeblocks/transform.go @@ -40,6 +40,7 @@ func (*Transformer) Transform(doc *ast.Document, reader text.Reader, pctx parser } codeBlocks = append(codeBlocks, cb) + return ast.WalkContinue, nil }) |