diff --git a/context/context.go b/context/context.go new file mode 100644 index 00000000..0fd637cd --- /dev/null +++ b/context/context.go @@ -0,0 +1,43 @@ +package context + +import ( + "context" + "time" + + "github.com/bytesparadise/libasciidoc/types" +) + +// Context is a custom implementation of the standard golang context.Context interface, +// which carries the types.Document which is being processed +type Context struct { + context context.Context + Document types.Document +} + +// Wrap wraps the given `ctx` context into a new context which will contain the given `document` document. +func Wrap(ctx context.Context, document types.Document) Context { + return Context{ + context: ctx, + Document: document, + } +} + +// Deadline wrapper implementation of context.Context.Deadline() +func (ctx *Context) Deadline() (deadline time.Time, ok bool) { + return ctx.context.Deadline() +} + +// Done wrapper implementation of context.Context.Done() +func (ctx *Context) Done() <-chan struct{} { + return ctx.Done() +} + +// Err wrapper implementation of context.Context.Err() +func (ctx *Context) Err() error { + return ctx.Err() +} + +// Value wrapper implementation of context.Context.Value(interface{}) +func (ctx *Context) Value(key interface{}) interface{} { + return ctx.Value(key) +} diff --git a/libasciidoc.go b/libasciidoc.go index 6e9deb2b..7adfc74e 100644 --- a/libasciidoc.go +++ b/libasciidoc.go @@ -4,6 +4,7 @@ import ( "context" "io" + asciidoc "github.com/bytesparadise/libasciidoc/context" "github.com/bytesparadise/libasciidoc/parser" "github.com/bytesparadise/libasciidoc/renderer" htmlrenderer "github.com/bytesparadise/libasciidoc/renderer/html5" @@ -15,7 +16,7 @@ import ( // ConvertToHTMLBody converts the content of the given reader `r` into an set of
elements for an HTML/BODY document. // The conversion result is written in the given writer `w`, whereas the document metadata (title, etc.) (or an error if a problem occurred) is returned // as the result of the function call. -func ConvertToHTMLBody(r io.Reader, w io.Writer) (*types.DocumentAttributes, error) { +func ConvertToHTMLBody(ctx context.Context, r io.Reader, w io.Writer) (*types.DocumentAttributes, error) { doc, err := parser.ParseReader("", r) if err != nil { return nil, errors.Wrapf(err, "error while parsing the document") @@ -23,7 +24,7 @@ func ConvertToHTMLBody(r io.Reader, w io.Writer) (*types.DocumentAttributes, err document := doc.(*types.Document) options := renderer.Options{} options[renderer.IncludeHeaderFooter] = false // force value - err = htmlrenderer.Render(context.Background(), *document, w, options) + err = htmlrenderer.Render(asciidoc.Wrap(ctx, *document), w, options) if err != nil { return nil, errors.Wrapf(err, "error while rendering the document") } @@ -33,15 +34,14 @@ func ConvertToHTMLBody(r io.Reader, w io.Writer) (*types.DocumentAttributes, err // ConvertToHTML converts the content of the given reader `r` into a full HTML document, written in the given writer `w`. // Returns an error if a problem occurred -func ConvertToHTML(r io.Reader, w io.Writer, options renderer.Options) error { - +func ConvertToHTML(ctx context.Context, r io.Reader, w io.Writer, options renderer.Options) error { doc, err := parser.ParseReader("", r) if err != nil { return errors.Wrapf(err, "error while parsing the document") } document := doc.(*types.Document) options[renderer.IncludeHeaderFooter] = true // force value - err = htmlrenderer.Render(context.Background(), *document, w, options) + err = htmlrenderer.Render(asciidoc.Wrap(ctx, *document), w, options) if err != nil { return errors.Wrapf(err, "error while rendering the document") } diff --git a/libasciidoc_test.go b/libasciidoc_test.go index fcf73bce..2b95c7ec 100644 --- a/libasciidoc_test.go +++ b/libasciidoc_test.go @@ -2,6 +2,7 @@ package libasciidoc_test import ( "bytes" + "context" "strings" "time" @@ -179,8 +180,8 @@ Last updated {{.LastUpdated}} func verifyDocumentBody(t GinkgoTInterface, expectedTitle *string, expectedContent, source string) { t.Logf("processing '%s'", source) sourceReader := strings.NewReader(source) - resultWriter := bytes.NewBuffer(make([]byte, 0)) - metadata, err := ConvertToHTMLBody(sourceReader, resultWriter) + resultWriter := bytes.NewBuffer(nil) + metadata, err := ConvertToHTMLBody(context.Background(), sourceReader, resultWriter) require.Nil(t, err, "Error found while parsing the document") require.NotNil(t, metadata) t.Log("Done processing document") @@ -199,10 +200,10 @@ func verifyDocumentBody(t GinkgoTInterface, expectedTitle *string, expectedConte func verifyCompleteDocument(t GinkgoTInterface, expectedContent, source string) { t.Logf("processing '%s'", source) sourceReader := strings.NewReader(source) - resultWriter := bytes.NewBuffer(make([]byte, 0)) + resultWriter := bytes.NewBuffer(nil) options := renderer.Options{} options[renderer.LastUpdated] = time.Now() - err := ConvertToHTML(sourceReader, resultWriter, options) + err := ConvertToHTML(context.Background(), sourceReader, resultWriter, options) require.Nil(t, err, "Error found while parsing the document") t.Log("Done processing document") result := string(resultWriter.Bytes()) diff --git a/parser/asciidoc-grammar.peg b/parser/asciidoc-grammar.peg index 5338c134..02f3e60c 100644 --- a/parser/asciidoc-grammar.peg +++ b/parser/asciidoc-grammar.peg @@ -17,7 +17,7 @@ DocumentBlock <- !EOF content:(Section / StandaloneBlock) { return content.(types.DocElement), nil } -StandaloneBlock <- DocumentAttribute / List / BlockImage / DelimitedBlock / Paragraph / ElementAttribute / BlankLine //TODO: should Paragraph be the last type ? +StandaloneBlock <- DocumentAttributeDeclaration / DocumentAttributeReset / List / BlockImage / DelimitedBlock / Paragraph / ElementAttribute / BlankLine //TODO: should Paragraph be the last type ? Section <- Section1 / Section2 / Section3 / Section4 / Section5 / Section6 @@ -101,18 +101,38 @@ Heading6 <- attributes:(ElementAttribute)* level:("======") WS+ content:InlineCo // ------------------------------------------ -// Document Attribute +// Document Attributes // ------------------------------------------ -DocumentAttribute <- DocumentAttributeWithNameOnly / DocumentAttributeWithNameAndValue +DocumentAttributeDeclaration <- DocumentAttributeDeclarationWithNameOnly / DocumentAttributeDeclarationWithNameAndValue -DocumentAttributeWithNameOnly <- ":" name:((!NEWLINE !":" !WS .)+) ":" WS* EOL { - return types.NewDocumentAttribute(name.([]interface{}), nil) +DocumentAttributeDeclarationWithNameOnly <- ":" name:(AttributeName) ":" WS* EOL { + return types.NewDocumentAttributeDeclaration(name.([]interface{}), nil) } -DocumentAttributeWithNameAndValue <- ":" name:((!NEWLINE !":" !WS .)+) ":" WS+ value:(!NEWLINE .)* EOL { - return types.NewDocumentAttribute(name.([]interface{}), value.([]interface{})) +DocumentAttributeDeclarationWithNameAndValue <- ":" name:(AttributeName) ":" WS+ value:(!NEWLINE .)* EOL { + return types.NewDocumentAttributeDeclaration(name.([]interface{}), value.([]interface{})) } +DocumentAttributeReset <- DocumentAttributeResetWithHeadingBangSymbol / DocumentAttributeResetWithTrailingBangSymbol + +DocumentAttributeResetWithHeadingBangSymbol <- ":!" name:(AttributeName) ":" WS* EOL { + return types.NewDocumentAttributeReset(name.([]interface{})) +} + +DocumentAttributeResetWithTrailingBangSymbol <- ":" name:(AttributeName) "!:" WS* EOL { + return types.NewDocumentAttributeReset(name.([]interface{})) +} + + +DocumentAttributeSubstitution <- "{" name:(AttributeName) "}" { + return types.NewDocumentAttributeSubstitution(name.([]interface{})) +} + +// AttributeName must be at least one character long, +// must begin with a word character (A-Z, a-z, 0-9 or _) and +// must only contain word characters and hyphens ('-'). +AttributeName <- ([A-Z] / [a-z] / [0-9] / "_") ([A-Z] / [a-z] / [0-9] / "-")* + // ------------------------------------------ // List Items // ------------------------------------------ @@ -137,14 +157,16 @@ Paragraph <- attributes:(ElementAttribute)* lines:(InlineContent)+ { return types.NewParagraph(c.text, lines.([]interface{}), attributes.([]interface{})) } -// an inline content element may begin and end with spaces, -// but it must contain at least an image, a quoted text, an external link or a word -InlineContent <- elements:(WS* (InlineImage / QuotedText / ExternalLink / Word) WS*)+ EOL { +// an inline content element may start with and end with spaces, +// but it must contain at least an inline element (image, quoted text, external link, document attribute substitution, word, etc.) +InlineContent <- elements:(WS* InlineElement WS*)+ EOL { return types.NewInlineContent(c.text, elements.([]interface{})) } +InlineElement <- InlineImage / QuotedText / ExternalLink / DocumentAttributeSubstitution / Word + // ------------------------------------------ -// Quote Texts (bold, italic and monospace) +// Quoted Texts (bold, italic and monospace) // ------------------------------------------ QuotedText <- BoldText / ItalicText / MonospaceText diff --git a/parser/document_attributes_test.go b/parser/document_attributes_test.go index d471c315..d25ecc57 100644 --- a/parser/document_attributes_test.go +++ b/parser/document_attributes_test.go @@ -9,6 +9,28 @@ var _ = Describe("Parsing Document Attributes", func() { Context("Valid document attributes", func() { + It("valid attribute names", func() { + + actualContent := `:a: +:author: Xavier +:_author: Xavier +:Author: Xavier +:0Author: Xavier +:Auth0r: Xavier` + expectedDocument := &types.Document{ + Attributes: &types.DocumentAttributes{}, + Elements: []types.DocElement{ + &types.DocumentAttributeDeclaration{Name: "a"}, + &types.DocumentAttributeDeclaration{Name: "author", Value: "Xavier"}, + &types.DocumentAttributeDeclaration{Name: "_author", Value: "Xavier"}, + &types.DocumentAttributeDeclaration{Name: "Author", Value: "Xavier"}, + &types.DocumentAttributeDeclaration{Name: "0Author", Value: "Xavier"}, + &types.DocumentAttributeDeclaration{Name: "Auth0r", Value: "Xavier"}, + }, + } + verify(GinkgoT(), expectedDocument, actualContent) + }) + It("heading section with attributes", func() { actualContent := `= a heading @@ -35,9 +57,9 @@ a paragraph` }, }, Elements: []types.DocElement{ - &types.DocumentAttribute{Name: "toc"}, - &types.DocumentAttribute{Name: "date", Value: "2017-01-01"}, - &types.DocumentAttribute{Name: "author", Value: "Xavier"}, + &types.DocumentAttributeDeclaration{Name: "toc"}, + &types.DocumentAttributeDeclaration{Name: "date", Value: "2017-01-01"}, + &types.DocumentAttributeDeclaration{Name: "author", Value: "Xavier"}, &types.Paragraph{ Lines: []*types.InlineContent{ &types.InlineContent{ @@ -63,9 +85,9 @@ a paragraph` expectedDocument := &types.Document{ Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ - &types.DocumentAttribute{Name: "toc"}, - &types.DocumentAttribute{Name: "date", Value: "2017-01-01"}, - &types.DocumentAttribute{Name: "author", Value: "Xavier"}, + &types.DocumentAttributeDeclaration{Name: "toc"}, + &types.DocumentAttributeDeclaration{Name: "date", Value: "2017-01-01"}, + &types.DocumentAttributeDeclaration{Name: "author", Value: "Xavier"}, &types.Paragraph{ Lines: []*types.InlineContent{ &types.InlineContent{ @@ -90,9 +112,9 @@ a paragraph` expectedDocument := &types.Document{ Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ - &types.DocumentAttribute{Name: "toc"}, - &types.DocumentAttribute{Name: "date", Value: "2017-01-01"}, - &types.DocumentAttribute{Name: "author", Value: "Xavier"}, + &types.DocumentAttributeDeclaration{Name: "toc"}, + &types.DocumentAttributeDeclaration{Name: "date", Value: "2017-01-01"}, + &types.DocumentAttributeDeclaration{Name: "author", Value: "Xavier"}, &types.Paragraph{ Lines: []*types.InlineContent{ &types.InlineContent{ @@ -118,9 +140,9 @@ a paragraph` expectedDocument := &types.Document{ Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ - &types.DocumentAttribute{Name: "toc"}, - &types.DocumentAttribute{Name: "date", Value: "2017-01-01"}, - &types.DocumentAttribute{Name: "author", Value: "Xavier"}, + &types.DocumentAttributeDeclaration{Name: "toc"}, + &types.DocumentAttributeDeclaration{Name: "date", Value: "2017-01-01"}, + &types.DocumentAttributeDeclaration{Name: "author", Value: "Xavier"}, &types.Paragraph{ Lines: []*types.InlineContent{ &types.InlineContent{ @@ -154,16 +176,70 @@ a paragraph` }, }, }, - &types.DocumentAttribute{Name: "toc"}, - &types.DocumentAttribute{Name: "date", Value: "2017-01-01"}, - &types.DocumentAttribute{Name: "author", Value: "Xavier"}, + &types.DocumentAttributeDeclaration{Name: "toc"}, + &types.DocumentAttributeDeclaration{Name: "date", Value: "2017-01-01"}, + &types.DocumentAttributeDeclaration{Name: "author", Value: "Xavier"}, + }, + } + verify(GinkgoT(), expectedDocument, actualContent) + }) + + It("paragraph with attribute substitution", func() { + + actualContent := `:author: Xavier + +a paragraph written by {author}.` + expectedDocument := &types.Document{ + Attributes: &types.DocumentAttributes{}, + Elements: []types.DocElement{ + &types.DocumentAttributeDeclaration{Name: "author", Value: "Xavier"}, + &types.Paragraph{ + Lines: []*types.InlineContent{ + &types.InlineContent{ + Elements: []types.InlineElement{ + &types.StringElement{Content: "a paragraph written by "}, + &types.DocumentAttributeSubstitution{Name: "author"}, + &types.StringElement{Content: "."}, + }, + }, + }, + }, + }, + } + verify(GinkgoT(), expectedDocument, actualContent) + }) + + It("paragraph with attribute resets", func() { + + actualContent := `:author: Xavier + +:!author1: +:author2!: +a paragraph written by {author}.` + expectedDocument := &types.Document{ + Attributes: &types.DocumentAttributes{}, + Elements: []types.DocElement{ + &types.DocumentAttributeDeclaration{Name: "author", Value: "Xavier"}, + &types.DocumentAttributeReset{Name: "author1"}, + &types.DocumentAttributeReset{Name: "author2"}, + &types.Paragraph{ + Lines: []*types.InlineContent{ + &types.InlineContent{ + Elements: []types.InlineElement{ + &types.StringElement{Content: "a paragraph written by "}, + &types.DocumentAttributeSubstitution{Name: "author"}, + &types.StringElement{Content: "."}, + }, + }, + }, + }, }, } verify(GinkgoT(), expectedDocument, actualContent) }) }) - Context("Valid document attributes", func() { + Context("Invalid document attributes", func() { It("paragraph and without blank line in between", func() { actualContent := `a paragraph @@ -201,5 +277,31 @@ a paragraph` } verify(GinkgoT(), expectedDocument, actualContent) }) + + It("invalid attribute names", func() { + + actualContent := `:@date: 2017-01-01 +:{author}: Xavier` + expectedDocument := &types.Document{ + Attributes: &types.DocumentAttributes{}, + Elements: []types.DocElement{ + &types.Paragraph{ + Lines: []*types.InlineContent{ + &types.InlineContent{ + Elements: []types.InlineElement{ + &types.StringElement{Content: ":@date: 2017-01-01"}, + }, + }, + &types.InlineContent{ + Elements: []types.InlineElement{ + &types.StringElement{Content: ":{author}: Xavier"}, + }, + }, + }, + }, + }, + } + verify(GinkgoT(), expectedDocument, actualContent) + }) }) }) diff --git a/renderer/html5/delimited_block.go b/renderer/html5/delimited_block.go index 4b703511..6023d3ed 100644 --- a/renderer/html5/delimited_block.go +++ b/renderer/html5/delimited_block.go @@ -2,9 +2,9 @@ package html5 import ( "bytes" - "context" "html/template" + asciidoc "github.com/bytesparadise/libasciidoc/context" "github.com/bytesparadise/libasciidoc/types" "github.com/pkg/errors" log "github.com/sirupsen/logrus" @@ -21,9 +21,9 @@ func init() {
`) } -func renderDelimitedBlock(ctx context.Context, block types.DelimitedBlock) ([]byte, error) { +func renderDelimitedBlock(ctx asciidoc.Context, block types.DelimitedBlock) ([]byte, error) { log.Debugf("rendering delimited block with content: %s", block.Content) - result := bytes.NewBuffer(make([]byte, 0)) + result := bytes.NewBuffer(nil) err := sourceBlockTmpl.Execute(result, block) if err != nil { return nil, errors.Wrapf(err, "unable to render delimited block") diff --git a/renderer/html5/document_attribute.go b/renderer/html5/document_attribute.go new file mode 100644 index 00000000..0c1457c2 --- /dev/null +++ b/renderer/html5/document_attribute.go @@ -0,0 +1,30 @@ +package html5 + +import ( + "bytes" + "fmt" + + asciidoc "github.com/bytesparadise/libasciidoc/context" + "github.com/bytesparadise/libasciidoc/types" +) + +func processAttributeDeclaration(ctx asciidoc.Context, attribute types.DocumentAttributeDeclaration) error { + ctx.Document.Attributes.Add(attribute) + return nil +} + +func processAttributeReset(ctx asciidoc.Context, attribute types.DocumentAttributeReset) error { + ctx.Document.Attributes.Reset(attribute) + return nil +} + +func renderAttributeSubstitution(ctx asciidoc.Context, attribute types.DocumentAttributeSubstitution) ([]byte, error) { + result := bytes.NewBuffer(nil) + value := ctx.Document.Attributes.Get(attribute) + if value == nil { + result.WriteString(fmt.Sprintf("{%s}", attribute.Name)) + } else { + result.WriteString(*value) + } + return result.Bytes(), nil +} diff --git a/renderer/html5/document_attribute_test.go b/renderer/html5/document_attribute_test.go index 4c4d7ddb..2671d412 100644 --- a/renderer/html5/document_attribute_test.go +++ b/renderer/html5/document_attribute_test.go @@ -29,4 +29,43 @@ var _ = Describe("Rendering With Attributes", func() { ` verify(GinkgoT(), expected, content) }) + + It("a paragraph with substitution", func() { + content := `:author: Xavier + +a paragraph written by {author}` + + expected := `
+

a paragraph written by Xavier

+
` + verify(GinkgoT(), expected, content) + }) + + It("paragraphs with definitions, substitutions and resets", func() { + content := `author is {author}. + +:author: me +author is now {author}. + +:author: you +author is now {author}. + +:author!: +author is now {author}.` + + expected := `
+

author is {author}.

+
+
+

author is now me.

+
+
+

author is now you.

+
+
+

author is now {author}.

+
` + verify(GinkgoT(), expected, content) + }) + }) diff --git a/renderer/html5/image.go b/renderer/html5/image.go index 88b4becb..35756413 100644 --- a/renderer/html5/image.go +++ b/renderer/html5/image.go @@ -2,9 +2,9 @@ package html5 import ( "bytes" - "context" "html/template" + asciidoc "github.com/bytesparadise/libasciidoc/context" "github.com/bytesparadise/libasciidoc/types" "github.com/pkg/errors" log "github.com/sirupsen/logrus" @@ -25,8 +25,8 @@ func init() { inlineImageTmpl = newHTMLTemplate("inline image", `{{.Macro.Alt}}`) } -func renderBlockImage(ctx context.Context, img types.BlockImage) ([]byte, error) { - result := bytes.NewBuffer(make([]byte, 0)) +func renderBlockImage(ctx asciidoc.Context, img types.BlockImage) ([]byte, error) { + result := bytes.NewBuffer(nil) err := blockImageTmpl.Execute(result, img) if err != nil { return nil, errors.Wrapf(err, "unable to render block image") @@ -35,8 +35,8 @@ func renderBlockImage(ctx context.Context, img types.BlockImage) ([]byte, error) return result.Bytes(), nil } -func renderInlineImage(ctx context.Context, img types.InlineImage) ([]byte, error) { - result := bytes.NewBuffer(make([]byte, 0)) +func renderInlineImage(ctx asciidoc.Context, img types.InlineImage) ([]byte, error) { + result := bytes.NewBuffer(nil) err := inlineImageTmpl.Execute(result, img) if err != nil { return nil, errors.Wrapf(err, "unable to render inline image") diff --git a/renderer/html5/inline_content.go b/renderer/html5/inline_content.go index 17cf2b4b..7a1a6942 100644 --- a/renderer/html5/inline_content.go +++ b/renderer/html5/inline_content.go @@ -3,16 +3,15 @@ package html5 import ( "bytes" - "context" - + asciidoc "github.com/bytesparadise/libasciidoc/context" "github.com/bytesparadise/libasciidoc/types" "github.com/pkg/errors" ) -func renderInlineContent(ctx context.Context, content types.InlineContent) ([]byte, error) { - renderedElementsBuff := bytes.NewBuffer(make([]byte, 0)) +func renderInlineContent(ctx asciidoc.Context, content types.InlineContent) ([]byte, error) { + renderedElementsBuff := bytes.NewBuffer(nil) for _, element := range content.Elements { - renderedElement, err := renderElement(ctx, element) + renderedElement, err := processElement(ctx, element) if err != nil { return nil, errors.Wrapf(err, "unable to render paragraph element") } diff --git a/renderer/html5/list_item.go b/renderer/html5/list_item.go index 329c2f03..10f1a4b9 100644 --- a/renderer/html5/list_item.go +++ b/renderer/html5/list_item.go @@ -2,9 +2,9 @@ package html5 import ( "bytes" - "context" "html/template" + asciidoc "github.com/bytesparadise/libasciidoc/context" "github.com/bytesparadise/libasciidoc/types" "github.com/pkg/errors" log "github.com/sirupsen/logrus" @@ -30,8 +30,8 @@ func init() { listItemContentTmpl = newHTMLTemplate("list item content", `

{{.}}

`) } -func renderList(ctx context.Context, list types.List) ([]byte, error) { - renderedElementsBuff := bytes.NewBuffer(make([]byte, 0)) +func renderList(ctx asciidoc.Context, list types.List) ([]byte, error) { + renderedElementsBuff := bytes.NewBuffer(nil) for i, item := range list.Items { renderedListItem, err := renderListItem(ctx, *item) if err != nil { @@ -43,7 +43,7 @@ func renderList(ctx context.Context, list types.List) ([]byte, error) { } } - result := bytes.NewBuffer(make([]byte, 0)) + result := bytes.NewBuffer(nil) // here we must preserve the HTML tags err := unorderedListTmpl.Execute(result, struct { ID *types.ElementID @@ -59,12 +59,12 @@ func renderList(ctx context.Context, list types.List) ([]byte, error) { return result.Bytes(), nil } -func renderListItem(ctx context.Context, item types.ListItem) ([]byte, error) { +func renderListItem(ctx asciidoc.Context, item types.ListItem) ([]byte, error) { renderedItemContent, err := renderListItemContent(ctx, *item.Content) if err != nil { return nil, errors.Wrapf(err, "unable to render list item") } - result := bytes.NewBuffer(make([]byte, 0)) + result := bytes.NewBuffer(nil) var renderedChildrenOutput *template.HTML if item.Children != nil { childrenOutput, err := renderList(ctx, *item.Children) @@ -88,8 +88,8 @@ func renderListItem(ctx context.Context, item types.ListItem) ([]byte, error) { return result.Bytes(), nil } -func renderListItemContent(ctx context.Context, content types.ListItemContent) ([]byte, error) { - renderedLinesBuff := bytes.NewBuffer(make([]byte, 0)) +func renderListItemContent(ctx asciidoc.Context, content types.ListItemContent) ([]byte, error) { + renderedLinesBuff := bytes.NewBuffer(nil) for _, line := range content.Lines { renderedLine, err := renderInlineContent(ctx, *line) if err != nil { @@ -97,7 +97,7 @@ func renderListItemContent(ctx context.Context, content types.ListItemContent) ( } renderedLinesBuff.Write(renderedLine) } - result := bytes.NewBuffer(make([]byte, 0)) + result := bytes.NewBuffer(nil) err := listItemContentTmpl.Execute(result, renderedLinesBuff.String()) if err != nil { return nil, errors.Wrapf(err, "unable to render list item") diff --git a/renderer/html5/paragraph.go b/renderer/html5/paragraph.go index 86f68f23..32049be9 100644 --- a/renderer/html5/paragraph.go +++ b/renderer/html5/paragraph.go @@ -1,12 +1,10 @@ package html5 import ( - "html/template" - "bytes" + "html/template" - "context" - + asciidoc "github.com/bytesparadise/libasciidoc/context" "github.com/bytesparadise/libasciidoc/types" "github.com/pkg/errors" ) @@ -22,8 +20,8 @@ func init() { `) } -func renderParagraph(ctx context.Context, paragraph types.Paragraph) ([]byte, error) { - renderedLinesBuff := bytes.NewBuffer(make([]byte, 0)) +func renderParagraph(ctx asciidoc.Context, paragraph types.Paragraph) ([]byte, error) { + renderedLinesBuff := bytes.NewBuffer(nil) for i, line := range paragraph.Lines { renderedLine, err := renderInlineContent(ctx, *line) if err != nil { @@ -35,7 +33,7 @@ func renderParagraph(ctx context.Context, paragraph types.Paragraph) ([]byte, er } } - result := bytes.NewBuffer(make([]byte, 0)) + result := bytes.NewBuffer(nil) err := paragraphTmpl.Execute(result, struct { ID *types.ElementID Title *types.ElementTitle diff --git a/renderer/html5/quoted_text.go b/renderer/html5/quoted_text.go index 19fdcec2..d07b1eb8 100644 --- a/renderer/html5/quoted_text.go +++ b/renderer/html5/quoted_text.go @@ -1,11 +1,10 @@ package html5 import ( - "context" - "html/template" - "bytes" + "html/template" + asciidoc "github.com/bytesparadise/libasciidoc/context" "github.com/bytesparadise/libasciidoc/types" "github.com/pkg/errors" log "github.com/sirupsen/logrus" @@ -22,10 +21,10 @@ func init() { monospaceTextTmpl = newHTMLTemplate("monospace text", "{{.}}") } -func renderQuotedText(ctx context.Context, t types.QuotedText) ([]byte, error) { - elementsBuffer := bytes.NewBuffer(make([]byte, 0)) +func renderQuotedText(ctx asciidoc.Context, t types.QuotedText) ([]byte, error) { + elementsBuffer := bytes.NewBuffer(nil) for _, element := range t.Elements { - b, err := renderElement(ctx, element) + b, err := processElement(ctx, element) if err != nil { return nil, errors.Wrapf(err, "unable to render text quote") } @@ -34,7 +33,7 @@ func renderQuotedText(ctx context.Context, t types.QuotedText) ([]byte, error) { return nil, errors.Wrapf(err, "unable to render text quote") } } - result := bytes.NewBuffer(make([]byte, 0)) + result := bytes.NewBuffer(nil) var tmpl *template.Template switch t.Kind { case types.Bold: diff --git a/renderer/html5/renderer.go b/renderer/html5/renderer.go index 7ad46339..9bb6fb94 100644 --- a/renderer/html5/renderer.go +++ b/renderer/html5/renderer.go @@ -2,15 +2,14 @@ package html5 import ( "bytes" - "context" "html/template" "io" texttemplate "text/template" + asciidoc "github.com/bytesparadise/libasciidoc/context" "github.com/bytesparadise/libasciidoc/renderer" "github.com/bytesparadise/libasciidoc/types" "github.com/pkg/errors" - log "github.com/sirupsen/logrus" ) var documentTmpl *texttemplate.Template @@ -42,7 +41,7 @@ Last updated {{.LastUpdated}} } // Render renders the given document in HTML and writes the result in the given `writer` -func Render(ctx context.Context, document types.Document, output io.Writer, options renderer.Options) error { +func Render(ctx asciidoc.Context, output io.Writer, options renderer.Options) error { includeHeaderFooter, err := options.IncludeHeaderFooter() if err != nil { return errors.Wrap(err, "error while rendering the HTML document") @@ -55,13 +54,12 @@ func Render(ctx context.Context, document types.Document, output io.Writer, opti if *includeHeaderFooter { // use a temporary writer for the document's content - renderedElementsBuff := bytes.NewBuffer(make([]byte, 0)) - renderElements(ctx, document.Elements, renderedElementsBuff) + renderedElementsBuff := bytes.NewBuffer(nil) + processElements(ctx, renderedElementsBuff) renderedHTMLElements := template.HTML(renderedElementsBuff.String()) - title := "undefined" - if document.Attributes.GetTitle() != nil { - title = *document.Attributes.GetTitle() + if ctx.Document.Attributes.GetTitle() != nil { + title = *ctx.Document.Attributes.GetTitle() } err := documentTmpl.Execute(output, struct { Generator string @@ -79,21 +77,20 @@ func Render(ctx context.Context, document types.Document, output io.Writer, opti } return nil } - return renderElements(ctx, document.Elements, output) + return processElements(ctx, output) } -func renderElements(ctx context.Context, elements []types.DocElement, output io.Writer) error { +func processElements(ctx asciidoc.Context, output io.Writer) error { hasContent := false - for _, element := range elements { - content, err := renderElement(ctx, element) + for _, element := range ctx.Document.Elements { + content, err := processElement(ctx, element) if err != nil { return errors.Wrapf(err, "failed to render the document") } // if there's already some content, we need to insert a `\n` before writing // the rendering output of the current element (if application, ie, not empty) if hasContent && len(content) > 0 { - log.Debugf("Appending '\\n' after element of type '%T'", element) output.Write([]byte("\n")) } // if the element was rendering into 'something' (ie, not enpty result) @@ -105,7 +102,7 @@ func renderElements(ctx context.Context, elements []types.DocElement, output io. return nil } -func renderElement(ctx context.Context, element types.DocElement) ([]byte, error) { +func processElement(ctx asciidoc.Context, element types.DocElement) ([]byte, error) { switch element.(type) { case *types.Section: return renderSection(ctx, *element.(*types.Section)) @@ -125,9 +122,14 @@ func renderElement(ctx context.Context, element types.DocElement) ([]byte, error return renderInlineContent(ctx, *element.(*types.InlineContent)) case *types.StringElement: return renderStringElement(ctx, *element.(*types.StringElement)) - case *types.DocumentAttribute: - // for now, silently ignored in the output - return make([]byte, 0), nil + case *types.DocumentAttributeDeclaration: + // 'process' function do not return any rendered content, but may return an error + return nil, processAttributeDeclaration(ctx, *element.(*types.DocumentAttributeDeclaration)) + case *types.DocumentAttributeReset: + // 'process' function do not return any rendered content, but may return an error + return nil, processAttributeReset(ctx, *element.(*types.DocumentAttributeReset)) + case *types.DocumentAttributeSubstitution: + return renderAttributeSubstitution(ctx, *element.(*types.DocumentAttributeSubstitution)) default: return nil, errors.Errorf("unsupported type of element: %T", element) } diff --git a/renderer/html5/renderer_test.go b/renderer/html5/renderer_test.go index 58342412..6b45bcb8 100644 --- a/renderer/html5/renderer_test.go +++ b/renderer/html5/renderer_test.go @@ -5,6 +5,7 @@ import ( "context" "strings" + asciidoc "github.com/bytesparadise/libasciidoc/context" "github.com/bytesparadise/libasciidoc/parser" . "github.com/bytesparadise/libasciidoc/renderer/html5" "github.com/bytesparadise/libasciidoc/types" @@ -21,8 +22,9 @@ func verify(t GinkgoTInterface, expected, content string) { require.Nil(t, err, "Error found while parsing the document") actualDocument := doc.(*types.Document) t.Logf("Actual document:\n%s", actualDocument.String(1)) - buff := bytes.NewBuffer(make([]byte, 0)) - err = Render(context.Background(), *actualDocument, buff, nil) + buff := bytes.NewBuffer(nil) + ctx := asciidoc.Wrap(context.Background(), *actualDocument) + err = Render(ctx, buff, nil) t.Log("Done processing document") require.Nil(t, err) require.Empty(t, err) diff --git a/renderer/html5/section.go b/renderer/html5/section.go index 8c77799f..a01684a1 100644 --- a/renderer/html5/section.go +++ b/renderer/html5/section.go @@ -2,10 +2,11 @@ package html5 import ( "bytes" - "context" "html/template" "strconv" + asciidoc "github.com/bytesparadise/libasciidoc/context" + "github.com/bytesparadise/libasciidoc/types" "github.com/pkg/errors" log "github.com/sirupsen/logrus" @@ -46,7 +47,7 @@ func init() { `{{.Content}}`) } -func renderSection(ctx context.Context, section types.Section) ([]byte, error) { +func renderSection(ctx asciidoc.Context, section types.Section) ([]byte, error) { switch section.Heading.Level { case 1: return renderSectionLevel1(ctx, section) @@ -55,7 +56,7 @@ func renderSection(ctx context.Context, section types.Section) ([]byte, error) { } } -func renderSectionLevel1(ctx context.Context, section types.Section) ([]byte, error) { +func renderSectionLevel1(ctx asciidoc.Context, section types.Section) ([]byte, error) { // only applies if the first element (if exists) is not a nested section var preambleElements []types.DocElement var otherElements []types.DocElement @@ -74,9 +75,9 @@ func renderSectionLevel1(ctx context.Context, section types.Section) ([]byte, er } } // log.Debugf("Preamble elements: %d", len(preambleElements)) - renderedPreambleElementsBuff := bytes.NewBuffer(make([]byte, 0)) + renderedPreambleElementsBuff := bytes.NewBuffer(nil) for i, element := range preambleElements { - renderedElement, err := renderElement(ctx, element) + renderedElement, err := processElement(ctx, element) if err != nil { return nil, errors.Wrapf(err, "unable to render preamble element") } @@ -86,9 +87,9 @@ func renderSectionLevel1(ctx context.Context, section types.Section) ([]byte, er } } renderedHTMLPreamble := template.HTML(renderedPreambleElementsBuff.String()) - renderedElementsBuff := bytes.NewBuffer(make([]byte, 0)) + renderedElementsBuff := bytes.NewBuffer(nil) for i, element := range otherElements { - renderedElement, err := renderElement(ctx, element) + renderedElement, err := processElement(ctx, element) if err != nil { return nil, errors.Wrapf(err, "unable to render section element") } @@ -98,7 +99,7 @@ func renderSectionLevel1(ctx context.Context, section types.Section) ([]byte, er } } renderedHTMLElements := template.HTML(renderedElementsBuff.String()) - result := bytes.NewBuffer(make([]byte, 0)) + result := bytes.NewBuffer(nil) err := section1ContentTmpl.Execute(result, struct { Class string Preamble template.HTML @@ -115,14 +116,14 @@ func renderSectionLevel1(ctx context.Context, section types.Section) ([]byte, er return result.Bytes(), nil } -func renderOtherSection(ctx context.Context, section types.Section) ([]byte, error) { +func renderOtherSection(ctx asciidoc.Context, section types.Section) ([]byte, error) { renderedHeading, err := renderHeading(ctx, section.Heading) if err != nil { return nil, errors.Wrapf(err, "error while rendering section heading") } - renderedElementsBuff := bytes.NewBuffer(make([]byte, 0)) + renderedElementsBuff := bytes.NewBuffer(nil) for i, element := range section.Elements { - renderedElement, err := renderElement(ctx, element) + renderedElement, err := processElement(ctx, element) if err != nil { return nil, errors.Wrapf(err, "unable to render section element") } @@ -131,7 +132,7 @@ func renderOtherSection(ctx context.Context, section types.Section) ([]byte, err renderedElementsBuff.WriteString("\n") } } - result := bytes.NewBuffer(make([]byte, 0)) + result := bytes.NewBuffer(nil) // select the appropriate template for the section var tmpl *template.Template if section.Heading.Level == 1 { @@ -159,9 +160,9 @@ func renderOtherSection(ctx context.Context, section types.Section) ([]byte, err return result.Bytes(), nil } -func renderHeading(ctx context.Context, heading types.Heading) ([]byte, error) { - result := bytes.NewBuffer(make([]byte, 0)) - renderedContent, err := renderElement(ctx, heading.Content) +func renderHeading(ctx asciidoc.Context, heading types.Heading) ([]byte, error) { + result := bytes.NewBuffer(nil) + renderedContent, err := processElement(ctx, heading.Content) if err != nil { return nil, errors.Wrapf(err, "error while rendering heading content") } diff --git a/renderer/html5/string.go b/renderer/html5/string.go index 37649401..ee562e2f 100644 --- a/renderer/html5/string.go +++ b/renderer/html5/string.go @@ -2,9 +2,9 @@ package html5 import ( "bytes" - "context" "html/template" + asciidoc "github.com/bytesparadise/libasciidoc/context" "github.com/bytesparadise/libasciidoc/types" "github.com/pkg/errors" ) @@ -16,8 +16,8 @@ func init() { stringElementTmpl = newHTMLTemplate("string element", "{{.}}") } -func renderStringElement(ctx context.Context, str types.StringElement) ([]byte, error) { - result := bytes.NewBuffer(make([]byte, 0)) +func renderStringElement(ctx asciidoc.Context, str types.StringElement) ([]byte, error) { + result := bytes.NewBuffer(nil) err := stringElementTmpl.Execute(result, str.Content) if err != nil { return nil, errors.Wrapf(err, "unable to render string element") diff --git a/types/document_metadata.go b/types/document_metadata.go index bc703ec3..19ae9fc0 100644 --- a/types/document_metadata.go +++ b/types/document_metadata.go @@ -1,7 +1,7 @@ package types // DocumentAttributes the document metadata -type DocumentAttributes map[string]interface{} +type DocumentAttributes map[string]string const ( title string = "title" @@ -10,9 +10,7 @@ const ( // GetTitle retrieves the document title in its metadata, or returns nil if the title was not specified func (m DocumentAttributes) GetTitle() *string { if t, ok := m[title]; ok { - if t, ok := t.(string); ok { - return &t - } + return &t } return nil } @@ -23,7 +21,21 @@ func (m DocumentAttributes) SetTitle(t string) { } // Add adds the given attribute -func (m DocumentAttributes) Add(a *DocumentAttribute) { +func (m DocumentAttributes) Add(a DocumentAttributeDeclaration) { // TODO: raise a warning if there was already a name/value m[a.Name] = a.Value } + +// Reset resets the given attribute +func (m DocumentAttributes) Reset(a DocumentAttributeReset) { + delete(m, a.Name) +} + +// Get gets the given value for the given attribute, or nil if none was found +func (m DocumentAttributes) Get(a DocumentAttributeSubstitution) *string { + // TODO: raise a warning if there was no entry found + if value, ok := m[a.Name]; ok { + return &value + } + return nil +} diff --git a/types/grammar_types.go b/types/grammar_types.go index 9b33d63e..6592dd41 100644 --- a/types/grammar_types.go +++ b/types/grammar_types.go @@ -83,11 +83,11 @@ func (d *Document) initAttributes() { // String implements the DocElement#String() method func (d *Document) String(indentLevel int) string { - result := bytes.NewBuffer(make([]byte, 0)) + result := bytes.NewBuffer(nil) for i := range d.Elements { result.WriteString(fmt.Sprintf("\n%s", d.Elements[i].String(0))) } - log.Debug(fmt.Sprintf("Printing document:\n%s", result.String())) + // log.Debug(fmt.Sprintf("Printing document:\n%s", result.String())) return result.String() } @@ -95,38 +95,96 @@ func (d *Document) String(indentLevel int) string { // Document Attributes // ------------------------------------------ -// DocumentAttribute the type for Document Attributes -type DocumentAttribute struct { +// DocumentAttributeDeclaration the type for Document Attribute Declarations +type DocumentAttributeDeclaration struct { Name string Value string } -// NewDocumentAttribute initializes a new Document Attribute -func NewDocumentAttribute(name []interface{}, value []interface{}) (*DocumentAttribute, error) { +// NewDocumentAttributeDeclaration initializes a new DocumentAttributeDeclaration +func NewDocumentAttributeDeclaration(name []interface{}, value []interface{}) (*DocumentAttributeDeclaration, error) { attrName, err := stringify(name) if err != nil { - return nil, errors.Wrapf(err, "error while initializing a DocumentAttribute") + return nil, errors.Wrapf(err, "error while initializing a DocumentAttributeDeclaration") } attrValue, err := stringify(value) if err != nil { - return nil, errors.Wrapf(err, "error while initializing a DocumentAttribute") + return nil, errors.Wrapf(err, "error while initializing a DocumentAttributeDeclaration") } - log.Debugf("Initialized a new DocumentAttribute: '%s' -> '%s'", *attrName, *attrValue) - return &DocumentAttribute{ + log.Debugf("Initialized a new DocumentAttributeDeclaration: '%s' -> '%s'", *attrName, *attrValue) + return &DocumentAttributeDeclaration{ Name: *attrName, Value: *attrValue, }, nil } // String implements the DocElement#String() method -func (a *DocumentAttribute) String(indentLevel int) string { - result := bytes.NewBuffer(make([]byte, 0)) - result.WriteString(fmt.Sprintf("%s '%s' -> '%s'\n", indent(indentLevel), a.Name, a.Value)) - return result.String() +func (a *DocumentAttributeDeclaration) String(indentLevel int) string { + return fmt.Sprintf("%s '%s' -> '%s'\n", indent(indentLevel), a.Name, a.Value) +} + +// Accept implements DocElement#Accept(Visitor) +func (a *DocumentAttributeDeclaration) Accept(v Visitor) error { + return nil +} + +// DocumentAttributeReset the type for DocumentAttributeReset +type DocumentAttributeReset struct { + Name string +} + +// NewDocumentAttributeReset initializes a new Document Attribute Resets. +func NewDocumentAttributeReset(name []interface{}) (*DocumentAttributeReset, error) { + attrName, err := stringify(name) + if err != nil { + return nil, errors.Wrapf(err, "error while initializing a DocumentAttributeReset") + } + log.Debugf("Initialized a new DocumentAttributeReset: '%s'", *attrName) + return &DocumentAttributeReset{Name: *attrName}, nil +} + +// String implements the DocElement#String() method +func (a *DocumentAttributeReset) String(indentLevel int) string { + return fmt.Sprintf("%s '%s'\n", indent(indentLevel), a.Name) +} + +// PlainString implements the InlineElement#PlainString() method +func (a *DocumentAttributeReset) PlainString() string { + return fmt.Sprintf("{%s}'\n", a.Name) +} + +// Accept implements DocElement#Accept(Visitor) +func (a *DocumentAttributeReset) Accept(v Visitor) error { + return nil +} + +// DocumentAttributeSubstitution the type for DocumentAttributeSubstitution +type DocumentAttributeSubstitution struct { + Name string +} + +// NewDocumentAttributeSubstitution initializes a new Document Attribute Substitutions +func NewDocumentAttributeSubstitution(name []interface{}) (*DocumentAttributeSubstitution, error) { + attrName, err := stringify(name) + if err != nil { + return nil, errors.Wrapf(err, "error while initializing a DocumentAttributeSubstitution") + } + log.Debugf("Initialized a new DocumentAttributeSubstitution: '%s'", *attrName) + return &DocumentAttributeSubstitution{Name: *attrName}, nil +} + +// String implements the DocElement#String() method +func (a *DocumentAttributeSubstitution) String(indentLevel int) string { + return fmt.Sprintf("%s '%s'\n", indent(indentLevel), a.Name) +} + +// PlainString implements the InlineElement#PlainString() method +func (a *DocumentAttributeSubstitution) PlainString() string { + return fmt.Sprintf("{%s}'\n", a.Name) } // Accept implements DocElement#Accept(Visitor) -func (a *DocumentAttribute) Accept(v Visitor) error { +func (a *DocumentAttributeSubstitution) Accept(v Visitor) error { return nil } @@ -153,7 +211,7 @@ func NewSection(heading *Heading, blocks []interface{}) (*Section, error) { // String implements the DocElement#String() method func (s *Section) String(indentLevel int) string { - result := bytes.NewBuffer(make([]byte, 0)) + result := bytes.NewBuffer(nil) result.WriteString(fmt.Sprintf("%s
'%s'\n", indent(indentLevel), s.Heading.Level, s.Heading.Content.String(0))) for _, element := range s.Elements { result.WriteString(fmt.Sprintf("%s", element.String(indentLevel+1))) @@ -225,7 +283,7 @@ func (h *Heading) String(indentLevel int) string { // PlainString returns a plain string version of all elements in this Heading's Content, without any rendering func (h *Heading) PlainString() string { - result := bytes.NewBuffer(make([]byte, 0)) + result := bytes.NewBuffer(nil) for i, element := range h.Content.Elements { result.WriteString(element.PlainString()) if i < len(h.Content.Elements)-1 { @@ -489,7 +547,7 @@ func NewParagraph(text []byte, lines []interface{}, attributes []interface{}) (* // String implements the DocElement#String() method func (p *Paragraph) String(indentLevel int) string { - result := bytes.NewBuffer(make([]byte, 0)) + result := bytes.NewBuffer(nil) result.WriteString(fmt.Sprintf("%s

", indent(indentLevel))) for _, line := range p.Lines { result.WriteString(fmt.Sprintf("%s\n", line.String(0))) @@ -533,18 +591,19 @@ type InlineContent struct { // NewInlineContent initializes a new `InlineContent` from the given values func NewInlineContent(text []byte, elements []interface{}) (*InlineContent, error) { - mergedElements := make([]InlineElement, 0) - for _, e := range merge(elements) { - mergedElements = append(mergedElements, e.(InlineElement)) + mergedElements := merge(elements) + mergedInlineElements := make([]InlineElement, len(mergedElements)) + for i, element := range mergedElements { + mergedInlineElements[i] = element.(InlineElement) } - result := &InlineContent{Elements: mergedElements} + result := &InlineContent{Elements: mergedInlineElements} log.Debugf("Initialized new InlineContent with %d elements: '%s'", len(result.Elements), result.String(0)) - return &InlineContent{Elements: mergedElements}, nil + return result, nil } // String implements the DocElement#String() method func (c *InlineContent) String(indentLevel int) string { - result := bytes.NewBuffer(make([]byte, 0)) + result := bytes.NewBuffer(nil) result.WriteString(indent(indentLevel)) for i, element := range c.Elements { result.WriteString(fmt.Sprintf("%s", element.String(0))) @@ -1017,7 +1076,7 @@ func (t *QuotedText) String(indentLevel int) string { // PlainString implements the InlineElement#PlainString() method func (t *QuotedText) PlainString() string { - result := bytes.NewBuffer(make([]byte, 0)) + result := bytes.NewBuffer(nil) for i, element := range t.Elements { result.WriteString(element.PlainString()) if i < len(t.Elements)-1 { diff --git a/types/type_utils.go b/types/type_utils.go index 948a41cd..f31c3b02 100644 --- a/types/type_utils.go +++ b/types/type_utils.go @@ -6,7 +6,6 @@ import ( "unicode" "github.com/pkg/errors" - log "github.com/sirupsen/logrus" ) func indent(indentLevel int) string { @@ -44,7 +43,7 @@ func merge(elements []interface{}, extraElements ...interface{}) []interface{} { result := make([]interface{}, 0) allElements := append(elements, extraElements...) // log.Debugf("Merging %d element(s):", len(allElements)) - buff := bytes.NewBuffer(make([]byte, 0)) + buff := bytes.NewBuffer(nil) for _, v := range allElements { if v == nil { continue @@ -89,7 +88,7 @@ func merge(elements []interface{}, extraElements ...interface{}) []interface{} { // the given arguments if the buffer was empty func appendBuffer(elements []interface{}, buff *bytes.Buffer) ([]interface{}, *bytes.Buffer) { if buff.Len() > 0 { - return append(elements, NewStringElement(buff.String())), bytes.NewBuffer(make([]byte, 0)) + return append(elements, NewStringElement(buff.String())), bytes.NewBuffer(nil) } return elements, buff } @@ -102,7 +101,7 @@ func stringify(elements []interface{}) (*string, error) { if element == nil { continue } - log.Debugf("%[1]v (%[1]T) ", element) + // log.Debugf("%[1]v (%[1]T) ", element) switch element := element.(type) { case string: buff.WriteString(element) @@ -127,7 +126,7 @@ func stringify(elements []interface{}) (*string, error) { } result := buff.String() - log.Debugf("stringified %v -> '%s' (%v characters)", elements, result, len(result)) + // log.Debugf("stringified %v -> '%s' (%v characters)", elements, result, len(result)) return &result, nil } @@ -142,7 +141,7 @@ func isMn(r rune) bool { // in the given 'source' with the given 'replacement'. func NewReplaceNonAlphanumericsFunc(replacement string) NormalizationFunc { return func(source string) ([]byte, error) { - buf := bytes.NewBuffer(make([]byte, 0)) + buf := bytes.NewBuffer(nil) lastCharIsSpace := false for _, r := range strings.TrimLeft(source, " ") { // ignore heading spaces if unicode.Is(unicode.Letter, r) || unicode.Is(unicode.Number, r) { @@ -183,7 +182,7 @@ type ReplaceNonAlphanumericsVisitor struct { } func NewReplaceNonAlphanumericsVisitor() *ReplaceNonAlphanumericsVisitor { - buf := bytes.NewBuffer(make([]byte, 0)) + buf := bytes.NewBuffer(nil) return &ReplaceNonAlphanumericsVisitor{ buf: *buf, normalize: NewReplaceNonAlphanumericsFunc("_"),