From 627e0eb148c094a7ea2f9e4efcb7f7bdbc0f5544 Mon Sep 17 00:00:00 2001 From: Xavier Coulon Date: Sat, 11 Jun 2022 10:29:22 +0200 Subject: [PATCH] fix(renderer): section numbering disabled and re-enabled also, moved logix to traverse the document in 'pkg/types' Fixes #1039 Signed-off-by: Xavier Coulon --- pkg/renderer/context.go | 2 +- pkg/renderer/section_numbering.go | 42 ----- pkg/renderer/section_numbering_test.go | 47 ------ pkg/renderer/sgml/elements.go | 3 +- pkg/renderer/sgml/html5/section_test.go | 60 ++++++- pkg/renderer/sgml/inline_elements.go | 7 +- pkg/renderer/sgml/renderer.go | 9 +- pkg/types/section_numbering_test.go | 207 ++++++++++++++++++++++++ pkg/types/types.go | 52 ++++++ 9 files changed, 325 insertions(+), 104 deletions(-) delete mode 100644 pkg/renderer/section_numbering.go delete mode 100644 pkg/renderer/section_numbering_test.go create mode 100644 pkg/types/section_numbering_test.go diff --git a/pkg/renderer/context.go b/pkg/renderer/context.go index 376cb7db..e44001f7 100644 --- a/pkg/renderer/context.go +++ b/pkg/renderer/context.go @@ -15,7 +15,7 @@ type Context struct { Attributes types.Attributes ElementReferences types.ElementReferences HasHeader bool - SectionNumbering SectionNumbers + SectionNumbering types.SectionNumbers } // NewContext returns a new rendering context for the given document. diff --git a/pkg/renderer/section_numbering.go b/pkg/renderer/section_numbering.go deleted file mode 100644 index d6f59275..00000000 --- a/pkg/renderer/section_numbering.go +++ /dev/null @@ -1,42 +0,0 @@ -package renderer - -import ( - "strconv" - - "github.com/bytesparadise/libasciidoc/pkg/types" -) - -// SectionNumbers a registry of section numbers - -type SectionNumbers map[string]string // assigned number by section id - -// NewSectionNumbers initializes the registry with the content of the given doc -// (ie, it traverses the doc to look for sections, and assigns them numbers, incrementally) -func NewSectionNumbers(doc *types.Document) (SectionNumbers, error) { - // traverse doc and its sections and assign numbers to the latters - return traverseElements(doc.Elements, "") -} - -func traverseElements(elements []interface{}, prefix string) (map[string]string, error) { - result := map[string]string{} - counter := 0 - for _, e := range elements { - if s, ok := e.(*types.Section); ok { - id, err := s.GetID() - if err != nil { - return nil, err - } - counter++ - n := prefix + strconv.Itoa(counter) - result[id] = n - numbers, err := traverseElements(s.Elements, n+".") - if err != nil { - return nil, err - } - for id, n := range numbers { - result[id] = n - } - } - } - return result, nil -} diff --git a/pkg/renderer/section_numbering_test.go b/pkg/renderer/section_numbering_test.go deleted file mode 100644 index 5a2ab28d..00000000 --- a/pkg/renderer/section_numbering_test.go +++ /dev/null @@ -1,47 +0,0 @@ -package renderer_test - -import ( - "github.com/bytesparadise/libasciidoc/pkg/renderer" - "github.com/bytesparadise/libasciidoc/pkg/types" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" -) - -var _ = Describe("section numbering", func() { - - It("should number sections", func() { - // given - doc := &types.Document{ - Elements: []interface{}{ - &types.Section{ - Attributes: types.Attributes{ - types.AttrID: "_section_1", - }, - Elements: []interface{}{ - &types.Section{ - Attributes: types.Attributes{ - types.AttrID: "_section_1a", - }, - Elements: []interface{}{}, - }, - &types.Section{ - Attributes: types.Attributes{ - types.AttrID: "_section_1b", - }, - Elements: []interface{}{}, - }, - }, - }, - }, - } - // when - n, err := renderer.NewSectionNumbers(doc) - - // then - Expect(err).NotTo(HaveOccurred()) - Expect(n["_section_1"]).To(Equal("1")) - Expect(n["_section_1a"]).To(Equal("1.1")) - Expect(n["_section_1b"]).To(Equal("1.2")) - }) -}) diff --git a/pkg/renderer/sgml/elements.go b/pkg/renderer/sgml/elements.go index 5892fd9c..a4600d97 100644 --- a/pkg/renderer/sgml/elements.go +++ b/pkg/renderer/sgml/elements.go @@ -7,7 +7,6 @@ import ( "github.com/bytesparadise/libasciidoc/pkg/types" "github.com/pkg/errors" - log "github.com/sirupsen/logrus" ) func (r *sgmlRenderer) renderElements(ctx *renderer.Context, elements []interface{}) (string, error) { @@ -119,7 +118,7 @@ func (r *sgmlRenderer) renderElement(ctx *renderer.Context, element interface{}) } func (r *sgmlRenderer) renderPlainText(ctx *renderer.Context, element interface{}) (string, error) { - log.Debugf("rendering plain string for element of type %T", element) + // log.Debugf("rendering plain string for element of type %T", element) switch e := element.(type) { case []interface{}: return r.renderInlineElements(ctx, e, withRenderer(r.renderPlainText)) diff --git a/pkg/renderer/sgml/html5/section_test.go b/pkg/renderer/sgml/html5/section_test.go index 1e4436b4..af0af1f2 100644 --- a/pkg/renderer/sgml/html5/section_test.go +++ b/pkg/renderer/sgml/html5/section_test.go @@ -153,7 +153,7 @@ var _ = Describe("sections", func() { Expect(RenderHTML(source)).To(MatchHTML(expected)) }) - It("with numbering", func() { + It("with numbering always enabled", func() { source := `= A title :sectnums: @@ -198,6 +198,64 @@ var _ = Describe("sections", func() {
+` + Expect(RenderHTML(source)).To(MatchHTML(expected)) + }) + + It("with numbering disabled and enabled again", func() { + source := `= A title +:sectnums!: + +== Disclaimer + +:sectnums: + +== Section A + +=== Section A.a + +=== Section A.b + +==== Section that shall not be in ToC + +== Section B + +=== Section B.a + +== Section C` + + expected := `
+

Disclaimer

+
+
+
+
+

1. Section A

+
+
+

1.1. Section A.a

+
+
+

1.2. Section A.b

+
+

1.2.1. Section that shall not be in ToC

+
+
+
+
+
+

2. Section B

+
+
+

2.1. Section B.a

+
+
+
+
+

3. Section C

+
+
+
` Expect(RenderHTML(source)).To(MatchHTML(expected)) }) diff --git a/pkg/renderer/sgml/inline_elements.go b/pkg/renderer/sgml/inline_elements.go index cacc4a9c..e16d9390 100644 --- a/pkg/renderer/sgml/inline_elements.go +++ b/pkg/renderer/sgml/inline_elements.go @@ -5,7 +5,6 @@ import ( "github.com/bytesparadise/libasciidoc/pkg/renderer" "github.com/bytesparadise/libasciidoc/pkg/types" - log "github.com/sirupsen/logrus" ) func (r *sgmlRenderer) renderInlineElements(ctx *renderer.Context, elements []interface{}, options ...lineRendererOption) (string, error) { @@ -32,9 +31,9 @@ func (r *sgmlRenderer) renderInlineElements(ctx *renderer.Context, elements []in buf.WriteString(renderedElement) } } - if log.IsLevelEnabled(log.DebugLevel) { - log.Debugf("rendered inline elements: '%s'", buf.String()) - } + // if log.IsLevelEnabled(log.DebugLevel) { + // log.Debugf("rendered inline elements: '%s'", buf.String()) + // } return buf.String(), nil } diff --git a/pkg/renderer/sgml/renderer.go b/pkg/renderer/sgml/renderer.go index d7366ded..6a40cdd3 100644 --- a/pkg/renderer/sgml/renderer.go +++ b/pkg/renderer/sgml/renderer.go @@ -172,13 +172,8 @@ func (r *sgmlRenderer) Render(ctx *renderer.Context, doc *types.Document, output } } } - if ctx.Attributes.Has(types.AttrSectionNumbering) || ctx.Attributes.Has(types.AttrNumbered) { - var err error - if ctx.SectionNumbering, err = renderer.NewSectionNumbers(doc); err != nil { - return metadata, errors.Wrapf(err, "unable to render full document") - } - } else { - log.Debug("section numbering is not enabled") + if ctx.SectionNumbering, err = doc.SectionNumbers(); err != nil { + return metadata, errors.Wrapf(err, "unable to render full document") } // needs to be set before rendering the content elements diff --git a/pkg/types/section_numbering_test.go b/pkg/types/section_numbering_test.go new file mode 100644 index 00000000..a913aca7 --- /dev/null +++ b/pkg/types/section_numbering_test.go @@ -0,0 +1,207 @@ +package types_test + +import ( + "github.com/bytesparadise/libasciidoc/pkg/types" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = Describe("section numbering", func() { + + It("should always number sections - explicitly in document header", func() { + // given + doc := &types.Document{ + Elements: []interface{}{ + &types.DocumentHeader{ + Elements: []interface{}{ + &types.AttributeDeclaration{ + Name: types.AttrSectionNumbering, + }, + }, + }, + &types.Section{ + Attributes: types.Attributes{ + types.AttrID: "_section_1", + }, + Elements: []interface{}{ + &types.Section{ + Attributes: types.Attributes{ + types.AttrID: "_section_1a", + }, + Elements: []interface{}{}, + }, + &types.Section{ + Attributes: types.Attributes{ + types.AttrID: "_section_1b", + }, + Elements: []interface{}{}, + }, + }, + }, + }, + } + // when + n, err := doc.SectionNumbers() + + // then + Expect(err).NotTo(HaveOccurred()) + Expect(n["_section_1"]).To(Equal("1")) + Expect(n["_section_1a"]).To(Equal("1.1")) + Expect(n["_section_1b"]).To(Equal("1.2")) + }) + + It("should always number sections - explicitly in document body", func() { + // given + doc := &types.Document{ + Elements: []interface{}{ + &types.AttributeDeclaration{ + Name: types.AttrSectionNumbering, + }, + &types.Section{ + Attributes: types.Attributes{ + types.AttrID: "_section_1", + }, + Elements: []interface{}{ + &types.Section{ + Attributes: types.Attributes{ + types.AttrID: "_section_1a", + }, + Elements: []interface{}{}, + }, + &types.Section{ + Attributes: types.Attributes{ + types.AttrID: "_section_1b", + }, + Elements: []interface{}{}, + }, + }, + }, + }, + } + // when + n, err := doc.SectionNumbers() + + // then + Expect(err).NotTo(HaveOccurred()) + Expect(n["_section_1"]).To(Equal("1")) + Expect(n["_section_1a"]).To(Equal("1.1")) + Expect(n["_section_1b"]).To(Equal("1.2")) + }) + + It("should never number sections - explicitly", func() { + // given + doc := &types.Document{ + Elements: []interface{}{ + &types.AttributeReset{ + Name: types.AttrSectionNumbering, + }, + &types.Section{ + Attributes: types.Attributes{ + types.AttrID: "_section_1", + }, + Elements: []interface{}{ + &types.Section{ + Attributes: types.Attributes{ + types.AttrID: "_section_1a", + }, + Elements: []interface{}{}, + }, + &types.Section{ + Attributes: types.Attributes{ + types.AttrID: "_section_1b", + }, + Elements: []interface{}{}, + }, + }, + }, + }, + } + // when + n, err := doc.SectionNumbers() + + // then + Expect(err).NotTo(HaveOccurred()) + Expect(n).To(BeEmpty()) + }) + + It("should never number sections - by default", func() { + // given + doc := &types.Document{ + Elements: []interface{}{ + &types.Section{ + Attributes: types.Attributes{ + types.AttrID: "_section_1", + }, + Elements: []interface{}{ + &types.Section{ + Attributes: types.Attributes{ + types.AttrID: "_section_1a", + }, + Elements: []interface{}{}, + }, + &types.Section{ + Attributes: types.Attributes{ + types.AttrID: "_section_1b", + }, + Elements: []interface{}{}, + }, + }, + }, + }, + } + // when + n, err := doc.SectionNumbers() + + // then + Expect(err).NotTo(HaveOccurred()) + Expect(n).To(BeEmpty()) + }) + + It("should number sections when enabled - case 1", func() { + // given + doc := &types.Document{ + Elements: []interface{}{ + &types.AttributeReset{ + Name: types.AttrSectionNumbering, + }, + &types.Section{ + Attributes: types.Attributes{ + types.AttrID: "_disclaimer", + }, + }, + &types.AttributeDeclaration{ + Name: types.AttrSectionNumbering, + }, + &types.Section{ + Attributes: types.Attributes{ + types.AttrID: "_section_1", + }, + Elements: []interface{}{ + &types.Section{ + Attributes: types.Attributes{ + types.AttrID: "_section_1a", + }, + Elements: []interface{}{}, + }, + &types.Section{ + Attributes: types.Attributes{ + types.AttrID: "_section_1b", + }, + Elements: []interface{}{}, + }, + }, + }, + }, + } + // when + n, err := doc.SectionNumbers() + + // then + Expect(err).NotTo(HaveOccurred()) + Expect(n["_disclaimer"]).To(BeEmpty()) + Expect(n["_section_1"]).To(Equal("1")) + Expect(n["_section_1a"]).To(Equal("1.1")) + Expect(n["_section_1b"]).To(Equal("1.2")) + }) +}) diff --git a/pkg/types/types.go b/pkg/types/types.go index c4f06c85..4913191a 100644 --- a/pkg/types/types.go +++ b/pkg/types/types.go @@ -141,6 +141,58 @@ func (d *Document) AddElement(element interface{}) error { return nil } +type SectionNumbers map[string]string // assigned number by section id + +func (d *Document) SectionNumbers() (SectionNumbers, error) { + enabled := false + if h := d.Header(); h != nil { + // lookup the `sectnums` or `numbered` attribute in the header + var err error + if _, enabled, err = traverseElements(h.Elements, false, ""); err != nil { + return nil, err + } + } + numbers, _, err := traverseElements(d.Elements, enabled, "") // disabled by default + return numbers, err +} + +func traverseElements(elements []interface{}, enabled bool, prefix string) (map[string]string, bool, error) { + result := map[string]string{} + counter := 0 + for _, e := range elements { + switch e := e.(type) { + case *AttributeDeclaration: + if e.Name == AttrSectionNumbering || e.Name == AttrNumbered { + enabled = true + } + case *AttributeReset: + if e.Name == AttrSectionNumbering || e.Name == AttrNumbered { + enabled = false + } + case *Section: + var n string + if enabled { + id, err := e.GetID() + if err != nil { + return nil, false, err + } + counter++ + n = prefix + strconv.Itoa(counter) + result[id] = n + } + numbers, en, err := traverseElements(e.Elements, enabled, n+".") + if err != nil { + return nil, false, err + } + enabled = en + for id, n := range numbers { + result[id] = n + } + } + } + return result, enabled, nil +} + // ------------------------------------------ // Document Metadata // ------------------------------------------