diff --git a/libasciidoc.go b/libasciidoc.go index 34a9d750..6e9deb2b 100644 --- a/libasciidoc.go +++ b/libasciidoc.go @@ -15,7 +15,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.DocumentMetadata, error) { +func ConvertToHTMLBody(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") @@ -28,7 +28,7 @@ func ConvertToHTMLBody(r io.Reader, w io.Writer) (*types.DocumentMetadata, error return nil, errors.Wrapf(err, "error while rendering the document") } log.Debugf("Done processing document") - return document.Metadata, nil + return document.Attributes, nil } // ConvertToHTML converts the content of the given reader `r` into a full HTML document, written in the given writer `w`. diff --git a/parser/asciidoc-grammar.peg b/parser/asciidoc-grammar.peg index 2f649604..5338c134 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 <- List / BlockImage / DelimitedBlock / Paragraph / MetadataElement / BlankLine +StandaloneBlock <- DocumentAttribute / List / BlockImage / DelimitedBlock / Paragraph / ElementAttribute / BlankLine //TODO: should Paragraph be the last type ? Section <- Section1 / Section2 / Section3 / Section4 / Section5 / Section6 @@ -75,37 +75,51 @@ Section6Block <- !Section1 !Section2 !Section3 !Section4 !Section5 !Section6 con // ------------------------------------------ Heading <- Heading1 / Heading2 / Heading3 / Heading4 / Heading5 / Heading6 -Heading1 <- metadata:(MetadataElement)* level:("=") WS+ content:InlineContent (BlankLine? / EOF) { - return types.NewHeading(1, content.(*types.InlineContent), metadata.([]interface{})) +Heading1 <- attributes:(ElementAttribute)* level:("=") WS+ content:InlineContent (BlankLine? / EOF) { //TODO: replace `(BlankLine? / EOF)` with `EOL` to allow for immediate attributes or any other content ? + return types.NewHeading(1, content.(*types.InlineContent), attributes.([]interface{})) } -Heading2 <- metadata:(MetadataElement)* level:("==") WS+ content:InlineContent (BlankLine? / EOF) { - return types.NewHeading(2, content.(*types.InlineContent), metadata.([]interface{})) +Heading2 <- attributes:(ElementAttribute)* level:("==") WS+ content:InlineContent (BlankLine? / EOF) { + return types.NewHeading(2, content.(*types.InlineContent), attributes.([]interface{})) } -Heading3 <- metadata:(MetadataElement)* level:("===") WS+ content:InlineContent (BlankLine? / EOF) { - return types.NewHeading(3, content.(*types.InlineContent), metadata.([]interface{})) +Heading3 <- attributes:(ElementAttribute)* level:("===") WS+ content:InlineContent (BlankLine? / EOF) { + return types.NewHeading(3, content.(*types.InlineContent), attributes.([]interface{})) } -Heading4 <- metadata:(MetadataElement)* level:("====") WS+ content:InlineContent (BlankLine? / EOF) { - return types.NewHeading(4, content.(*types.InlineContent), metadata.([]interface{})) +Heading4 <- attributes:(ElementAttribute)* level:("====") WS+ content:InlineContent (BlankLine? / EOF) { + return types.NewHeading(4, content.(*types.InlineContent), attributes.([]interface{})) } -Heading5 <- metadata:(MetadataElement)* level:("=====") WS+ content:InlineContent (BlankLine? / EOF) { - return types.NewHeading(5, content.(*types.InlineContent), metadata.([]interface{})) +Heading5 <- attributes:(ElementAttribute)* level:("=====") WS+ content:InlineContent (BlankLine? / EOF) { + return types.NewHeading(5, content.(*types.InlineContent), attributes.([]interface{})) } -Heading6 <- metadata:(MetadataElement)* level:("======") WS+ content:InlineContent (BlankLine? / EOF) { - return types.NewHeading(6, content.(*types.InlineContent), metadata.([]interface{})) +Heading6 <- attributes:(ElementAttribute)* level:("======") WS+ content:InlineContent (BlankLine? / EOF) { + return types.NewHeading(6, content.(*types.InlineContent), attributes.([]interface{})) +} + + +// ------------------------------------------ +// Document Attribute +// ------------------------------------------ +DocumentAttribute <- DocumentAttributeWithNameOnly / DocumentAttributeWithNameAndValue + +DocumentAttributeWithNameOnly <- ":" name:((!NEWLINE !":" !WS .)+) ":" WS* EOL { + return types.NewDocumentAttribute(name.([]interface{}), nil) +} + +DocumentAttributeWithNameAndValue <- ":" name:((!NEWLINE !":" !WS .)+) ":" WS+ value:(!NEWLINE .)* EOL { + return types.NewDocumentAttribute(name.([]interface{}), value.([]interface{})) } // ------------------------------------------ // List Items // ------------------------------------------ -List <- metadata:(MetadataElement)* +List <- attributes:(ElementAttribute)* // list items can be followed by an optional, single blank line elements:(ListItem BlankLine?)+ { - return types.NewList(elements.([]interface{}), metadata.([]interface{})) + return types.NewList(elements.([]interface{}), attributes.([]interface{})) } ListItem <- WS* level:('*'+ / '-') WS+ content:(ListItemContent) { @@ -119,8 +133,8 @@ ListItemContent <- lines:(!(WS* ('*'+ / '-') WS+) InlineContent)+ { // Paragraphs // ------------------------------------------ // a paragraph is a group of line ending with a blank line (or end of file) -Paragraph <- metadata:(MetadataElement)* lines:(InlineContent)+ { - return types.NewParagraph(c.text, lines.([]interface{}), metadata.([]interface{})) +Paragraph <- attributes:(ElementAttribute)* lines:(InlineContent)+ { + return types.NewParagraph(c.text, lines.([]interface{}), attributes.([]interface{})) } // an inline content element may begin and end with spaces, @@ -166,9 +180,9 @@ ExternalLink <- url:(URL_SCHEME URL) text:('[' (URL_TEXT)* ']')? { // ------------------------------------------ // Images // ------------------------------------------ -BlockImage <- metadata:(MetadataElement)* image:BlockImageMacro WS* EOL { +BlockImage <- attributes:(ElementAttribute)* image:BlockImageMacro WS* EOL { // here we can ignore the blank line in the returned element - return types.NewBlockImage(c.text, *image.(*types.ImageMacro), metadata.([]interface{})) + return types.NewBlockImage(c.text, *image.(*types.ImageMacro), attributes.([]interface{})) } BlockImageMacro <- "image::" path:(URL) '[' attributes:(URL_TEXT?) ']' { @@ -201,7 +215,7 @@ SourceBlockLine <- (!EOL .)* NEWLINE // ------------------------------------------ // Meta Elements // ------------------------------------------ -MetadataElement <- meta:(ElementLink / ElementID / ElementTitle) +ElementAttribute <- meta:(ElementLink / ElementID / ElementTitle) // a link attached to an element, such as a BlockImage ElementLink <- "[" WS* "link" WS* "=" WS* path:URL WS* "]" EOL { diff --git a/parser/asciidoc_parser_test.go b/parser/asciidoc_parser_test.go index 36c72906..75be1356 100644 --- a/parser/asciidoc_parser_test.go +++ b/parser/asciidoc_parser_test.go @@ -21,7 +21,7 @@ var _ = Describe("Parsing content", func() { "\n" + "a paragraph with *bold content*" expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{ + Attributes: &types.DocumentAttributes{ "title": "a heading", }, Elements: []types.DocElement{ @@ -85,7 +85,6 @@ func verify(t GinkgoTInterface, expectedDocument *types.Document, content string } require.Nil(t, err) actualDocument := result.(*types.Document) - t.Logf("actual document structure: %+v", actualDocument.Elements) t.Logf("actual document: `%s`", actualDocument.String(0)) t.Logf("expected document: `%s`", expectedDocument.String(0)) assert.EqualValues(t, *expectedDocument, *actualDocument) diff --git a/parser/blank_line_test.go b/parser/blank_line_test.go index 335d43a0..62faec0d 100644 --- a/parser/blank_line_test.go +++ b/parser/blank_line_test.go @@ -11,7 +11,7 @@ var _ = Describe("Blank lines", func() { second paragraph` expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.Paragraph{ Lines: []*types.InlineContent{ @@ -43,7 +43,7 @@ second paragraph` second paragraph ` expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.Paragraph{ Lines: []*types.InlineContent{ diff --git a/parser/delimited_block_test.go b/parser/delimited_block_test.go index 62def971..d0e4fc5d 100644 --- a/parser/delimited_block_test.go +++ b/parser/delimited_block_test.go @@ -11,7 +11,7 @@ var _ = Describe("Delimited Blocks", func() { content := "some source code" actualContent := "```\n" + content + "\n```" expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.DelimitedBlock{ Kind: types.SourceBlock, @@ -26,7 +26,7 @@ var _ = Describe("Delimited Blocks", func() { content := "some source code\nwith an empty line\n\nin the middle" actualContent := "```\n" + content + "\n```" expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.DelimitedBlock{ Kind: types.SourceBlock, @@ -41,7 +41,7 @@ var _ = Describe("Delimited Blocks", func() { content := "" actualContent := "```\n" + content + "```" expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.DelimitedBlock{ Kind: types.SourceBlock, diff --git a/parser/document_attributes_test.go b/parser/document_attributes_test.go new file mode 100644 index 00000000..d471c315 --- /dev/null +++ b/parser/document_attributes_test.go @@ -0,0 +1,205 @@ +package parser_test + +import ( + "github.com/bytesparadise/libasciidoc/types" + . "github.com/onsi/ginkgo" +) + +var _ = Describe("Parsing Document Attributes", func() { + + Context("Valid document attributes", func() { + + It("heading section with attributes", func() { + + actualContent := `= a heading +:toc: +:date: 2017-01-01 +:author: Xavier + +a paragraph` + expectedDocument := &types.Document{ + Attributes: &types.DocumentAttributes{ + "title": "a heading", + }, + Elements: []types.DocElement{ + &types.Section{ + Heading: types.Heading{ + Level: 1, + Content: &types.InlineContent{ + Elements: []types.InlineElement{ + &types.StringElement{Content: "a heading"}, + }, + }, + ID: &types.ElementID{ + Value: "_a_heading", + }, + }, + Elements: []types.DocElement{ + &types.DocumentAttribute{Name: "toc"}, + &types.DocumentAttribute{Name: "date", Value: "2017-01-01"}, + &types.DocumentAttribute{Name: "author", Value: "Xavier"}, + &types.Paragraph{ + Lines: []*types.InlineContent{ + &types.InlineContent{ + Elements: []types.InlineElement{ + &types.StringElement{Content: "a paragraph"}, + }, + }, + }, + }, + }, + }, + }, + } + verify(GinkgoT(), expectedDocument, actualContent) + }) + + It("attributes and paragraph without blank line in-between", func() { + + actualContent := `:toc: +:date: 2017-01-01 +:author: Xavier +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.Paragraph{ + Lines: []*types.InlineContent{ + &types.InlineContent{ + Elements: []types.InlineElement{ + &types.StringElement{Content: "a paragraph"}, + }, + }, + }, + }, + }, + } + verify(GinkgoT(), expectedDocument, actualContent) + }) + + It("contiguous attributes and paragraph with blank line in-between", func() { + + actualContent := `:toc: +:date: 2017-01-01 +:author: Xavier + +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.Paragraph{ + Lines: []*types.InlineContent{ + &types.InlineContent{ + Elements: []types.InlineElement{ + &types.StringElement{Content: "a paragraph"}, + }, + }, + }, + }, + }, + } + verify(GinkgoT(), expectedDocument, actualContent) + }) + + It("splitted attributes and paragraph with blank line in-between", func() { + + actualContent := `:toc: +:date: 2017-01-01 + +:author: Xavier + +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.Paragraph{ + Lines: []*types.InlineContent{ + &types.InlineContent{ + Elements: []types.InlineElement{ + &types.StringElement{Content: "a paragraph"}, + }, + }, + }, + }, + }, + } + verify(GinkgoT(), expectedDocument, actualContent) + }) + + It("no heading and attributes in body", func() { + + actualContent := `a paragraph + +:toc: +: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: "a paragraph"}, + }, + }, + }, + }, + &types.DocumentAttribute{Name: "toc"}, + &types.DocumentAttribute{Name: "date", Value: "2017-01-01"}, + &types.DocumentAttribute{Name: "author", Value: "Xavier"}, + }, + } + verify(GinkgoT(), expectedDocument, actualContent) + }) + }) + + Context("Valid document attributes", func() { + It("paragraph and without blank line in between", func() { + + actualContent := `a paragraph +:toc: +: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: "a paragraph"}, + }, + }, + &types.InlineContent{ + Elements: []types.InlineElement{ + &types.StringElement{Content: ":toc:"}, + }, + }, + &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/parser/external_link_test.go b/parser/external_link_test.go index 767dd674..b85493c7 100644 --- a/parser/external_link_test.go +++ b/parser/external_link_test.go @@ -10,7 +10,7 @@ var _ = Describe("External Links", func() { It("external link", func() { actualContent := "a link to https://foo.bar" expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.Paragraph{ Lines: []*types.InlineContent{ @@ -32,7 +32,7 @@ var _ = Describe("External Links", func() { It("external link with empty text", func() { actualContent := "a link to https://foo.bar[]" expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.Paragraph{ Lines: []*types.InlineContent{ @@ -55,7 +55,7 @@ var _ = Describe("External Links", func() { It("external link with text", func() { actualContent := "a link to mailto:foo@bar[the foo@bar email]" expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.Paragraph{ Lines: []*types.InlineContent{ diff --git a/parser/image_test.go b/parser/image_test.go index 42e3a6a6..ea5d0357 100644 --- a/parser/image_test.go +++ b/parser/image_test.go @@ -12,7 +12,7 @@ var _ = Describe("Images", func() { It("block image with empty alt", func() { actualContent := "image::images/foo.png[]" expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.BlockImage{ Macro: types.ImageMacro{ @@ -28,7 +28,7 @@ var _ = Describe("Images", func() { It("block image with empty alt and trailing spaces", func() { actualContent := "image::images/foo.png[] \t\t " expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.BlockImage{ Macro: types.ImageMacro{ @@ -46,7 +46,7 @@ var _ = Describe("Images", func() { actualContent := `image::images/foo.png[] ` expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.BlockImage{ Macro: types.ImageMacro{ @@ -64,7 +64,7 @@ var _ = Describe("Images", func() { actualContent := `image::images/foo.png[] ` expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.BlockImage{ Macro: types.ImageMacro{ @@ -83,7 +83,7 @@ var _ = Describe("Images", func() { ` expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.BlockImage{ Macro: types.ImageMacro{ @@ -101,7 +101,7 @@ var _ = Describe("Images", func() { It("block image with alt", func() { actualContent := "image::images/foo.png[the foo.png image]" expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.BlockImage{ Macro: types.ImageMacro{ @@ -122,7 +122,7 @@ var _ = Describe("Images", func() { width := "600" height := "400" expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.BlockImage{ Macro: types.ImageMacro{ @@ -144,7 +144,7 @@ var _ = Describe("Images", func() { It("block image appending inline content", func() { actualContent := "a paragraph\nimage::images/foo.png[]" expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.Paragraph{ Lines: []*types.InlineContent{ @@ -173,7 +173,7 @@ var _ = Describe("Images", func() { It("inline image with empty alt", func() { actualContent := "image:images/foo.png[]" expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.Paragraph{ Lines: []*types.InlineContent{ @@ -197,7 +197,7 @@ var _ = Describe("Images", func() { It("inline image with empty alt and trailing spaces", func() { actualContent := "image:images/foo.png[] \t\t " expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.Paragraph{ Lines: []*types.InlineContent{ @@ -224,7 +224,7 @@ var _ = Describe("Images", func() { It("inline image surrounded with test", func() { actualContent := "a foo image:images/foo.png[] bar..." expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.Paragraph{ Lines: []*types.InlineContent{ @@ -254,7 +254,7 @@ var _ = Describe("Images", func() { It("inline image with alt", func() { actualContent := "image:images/foo.png[the foo.png image]" expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.Paragraph{ Lines: []*types.InlineContent{ @@ -279,7 +279,7 @@ var _ = Describe("Images", func() { It("inline image appending inline content", func() { actualContent := "a paragraph\nimage::images/foo.png[]" expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.Paragraph{ Lines: []*types.InlineContent{ diff --git a/parser/list_test.go b/parser/list_test.go index e7597c29..87447795 100644 --- a/parser/list_test.go +++ b/parser/list_test.go @@ -12,7 +12,7 @@ var _ = Describe("List Items", func() { It("1 list with a single item", func() { actualContent := "* a list item" expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.List{ Items: []*types.ListItem{ @@ -38,7 +38,7 @@ var _ = Describe("List Items", func() { actualContent := "[#listID]\n" + "* a list item" expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.List{ ID: &types.ElementID{Value: "listID"}, @@ -66,7 +66,7 @@ var _ = Describe("List Items", func() { actualContent := "* a first item\n" + "* a second item with *bold content*" expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.List{ Items: []*types.ListItem{ @@ -109,7 +109,7 @@ var _ = Describe("List Items", func() { actualContent := "- a first item\n" + "- a second item with *bold content*" expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.List{ Items: []*types.ListItem{ @@ -154,7 +154,7 @@ var _ = Describe("List Items", func() { "\n" + "* a second item with *bold content*" expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.List{ Items: []*types.ListItem{ @@ -199,7 +199,7 @@ var _ = Describe("List Items", func() { "* item 2\n" + "on 2 lines, too." expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.List{ Items: []*types.ListItem{ @@ -250,7 +250,7 @@ var _ = Describe("List Items", func() { "\n" + "* an item in the second list" expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.List{ Items: []*types.ListItem{ @@ -303,7 +303,7 @@ var _ = Describe("List Items", func() { "* item 2\n" + "** item 2.1\n" expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.List{ Items: []*types.ListItem{ @@ -432,7 +432,7 @@ var _ = Describe("List Items", func() { "** item 1.2\n" + "* item 2" expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.List{ Items: []*types.ListItem{ @@ -514,7 +514,7 @@ var _ = Describe("List Items", func() { It("invalid list item", func() { actualContent := "*an invalid list item" expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.Paragraph{ Lines: []*types.InlineContent{ diff --git a/parser/meta_elements_test.go b/parser/meta_elements_test.go index c7878986..b0a8f4fb 100644 --- a/parser/meta_elements_test.go +++ b/parser/meta_elements_test.go @@ -12,7 +12,7 @@ var _ = Describe("Meta Elements", func() { It("element link alone", func() { actualContent := "[link=http://foo.bar]" expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.ElementLink{Path: "http://foo.bar"}, }, @@ -23,7 +23,7 @@ var _ = Describe("Meta Elements", func() { It("element link with spaces", func() { actualContent := "[ link = http://foo.bar ]" expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.ElementLink{Path: "http://foo.bar"}, }, @@ -34,7 +34,7 @@ var _ = Describe("Meta Elements", func() { It("element link invalid", func() { actualContent := "[ link = http://foo.bar" expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.Paragraph{ Lines: []*types.InlineContent{ @@ -57,7 +57,7 @@ var _ = Describe("Meta Elements", func() { It("element id", func() { actualContent := "[#img-foobar]" expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.ElementID{Value: "img-foobar"}, }, @@ -68,7 +68,7 @@ var _ = Describe("Meta Elements", func() { It("element id with spaces", func() { actualContent := "[ #img-foobar ]" expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.ElementID{Value: "img-foobar"}, }, @@ -79,7 +79,7 @@ var _ = Describe("Meta Elements", func() { It("element id invalid", func() { actualContent := "[#img-foobar" expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.Paragraph{ Lines: []*types.InlineContent{ @@ -96,7 +96,7 @@ var _ = Describe("Meta Elements", func() { It("element title", func() { actualContent := ".a title" expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.ElementTitle{Value: "a title"}, }, @@ -107,7 +107,7 @@ var _ = Describe("Meta Elements", func() { It("element title invalid1", func() { actualContent := ". a title" expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.Paragraph{ Lines: []*types.InlineContent{ @@ -122,7 +122,7 @@ var _ = Describe("Meta Elements", func() { It("element title invalid2", func() { actualContent := "!a title" expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.Paragraph{ Lines: []*types.InlineContent{ diff --git a/parser/paragraph_test.go b/parser/paragraph_test.go index e350393c..3bc24a00 100644 --- a/parser/paragraph_test.go +++ b/parser/paragraph_test.go @@ -10,7 +10,7 @@ var _ = Describe("Paragraphs", func() { It("paragraph with 1 word", func() { actualContent := "hello" expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.Paragraph{ Lines: []*types.InlineContent{ @@ -29,7 +29,7 @@ var _ = Describe("Paragraphs", func() { It("paragraph with few words", func() { actualContent := "a paragraph with some content" expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.Paragraph{ Lines: []*types.InlineContent{ @@ -48,7 +48,7 @@ var _ = Describe("Paragraphs", func() { It("paragraph with bold content", func() { actualContent := "a paragraph with *some bold content*" expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.Paragraph{ Lines: []*types.InlineContent{ @@ -75,7 +75,7 @@ var _ = Describe("Paragraphs", func() { .a title a paragraph` expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.Paragraph{ ID: &types.ElementID{Value: "foo"}, diff --git a/parser/quoted_text_test.go b/parser/quoted_text_test.go index c0e84650..f39a5212 100644 --- a/parser/quoted_text_test.go +++ b/parser/quoted_text_test.go @@ -10,7 +10,7 @@ var _ = Describe("Quoted Texts", func() { It("bold text of 1 word", func() { actualContent := "*hello*" expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.Paragraph{ Lines: []*types.InlineContent{ @@ -34,7 +34,7 @@ var _ = Describe("Quoted Texts", func() { It("bold text of 2 words", func() { actualContent := "*bold content*" expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.Paragraph{ Lines: []*types.InlineContent{ @@ -58,7 +58,7 @@ var _ = Describe("Quoted Texts", func() { It("bold text of 3 words", func() { actualContent := "*some bold content*" expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.Paragraph{ Lines: []*types.InlineContent{ @@ -82,7 +82,7 @@ var _ = Describe("Quoted Texts", func() { It("inline with bold text", func() { actualContent := "a paragraph with *some bold content*" expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.Paragraph{ Lines: []*types.InlineContent{ @@ -107,7 +107,7 @@ var _ = Describe("Quoted Texts", func() { It("inline with invalid bold text1", func() { actualContent := "a paragraph with *some bold content" expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.Paragraph{ Lines: []*types.InlineContent{ @@ -126,7 +126,7 @@ var _ = Describe("Quoted Texts", func() { It("inline with invalid bold text2", func() { actualContent := "a paragraph with *some bold content *" expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.Paragraph{ Lines: []*types.InlineContent{ @@ -145,7 +145,7 @@ var _ = Describe("Quoted Texts", func() { It("inline with invalid bold text3", func() { actualContent := "a paragraph with * some bold content*" expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.Paragraph{ Lines: []*types.InlineContent{ @@ -164,7 +164,7 @@ var _ = Describe("Quoted Texts", func() { It("italic text with3 words", func() { actualContent := "_some italic content_" expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.Paragraph{ Lines: []*types.InlineContent{ @@ -188,7 +188,7 @@ var _ = Describe("Quoted Texts", func() { It("monospace text with3 words", func() { actualContent := "`some monospace content`" expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.Paragraph{ Lines: []*types.InlineContent{ @@ -212,7 +212,7 @@ var _ = Describe("Quoted Texts", func() { It("italic text within bold text", func() { actualContent := "some *bold and _italic content_ together*." expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.Paragraph{ Lines: []*types.InlineContent{ @@ -245,7 +245,7 @@ var _ = Describe("Quoted Texts", func() { It("invalid italic text within bold text", func() { actualContent := "some *bold and _italic content _ together*." expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.Paragraph{ Lines: []*types.InlineContent{ @@ -271,7 +271,7 @@ var _ = Describe("Quoted Texts", func() { It("italic text within invalid bold text", func() { actualContent := "some *bold and _italic content_ together *." expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.Paragraph{ Lines: []*types.InlineContent{ @@ -297,7 +297,7 @@ var _ = Describe("Quoted Texts", func() { It("bold text within italic text", func() { actualContent := "_some *bold* content_" expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.Paragraph{ Lines: []*types.InlineContent{ @@ -328,7 +328,7 @@ var _ = Describe("Quoted Texts", func() { It("monospace text within bold text within italic quote", func() { actualContent := "*some _italic and `monospaced content`_*" expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.Paragraph{ Lines: []*types.InlineContent{ @@ -364,7 +364,7 @@ var _ = Describe("Quoted Texts", func() { It("italic text within italic text", func() { actualContent := "_some _very italic_ content_" expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.Paragraph{ Lines: []*types.InlineContent{ diff --git a/parser/section_test.go b/parser/section_test.go index 4d848db8..55a03743 100644 --- a/parser/section_test.go +++ b/parser/section_test.go @@ -12,7 +12,7 @@ var _ = Describe("Sections", func() { It("section with heading only", func() { actualContent := "= a heading" expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{ + Attributes: &types.DocumentAttributes{ "title": "a heading", }, Elements: []types.DocElement{ @@ -37,7 +37,7 @@ var _ = Describe("Sections", func() { It("section level 2", func() { actualContent := `== section 2` expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.Section{ Heading: types.Heading{ @@ -61,7 +61,7 @@ var _ = Describe("Sections", func() { It("section level 2 with quoted text", func() { actualContent := `== *2 spaces and bold content*` expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.Section{ Heading: types.Heading{ @@ -91,7 +91,7 @@ var _ = Describe("Sections", func() { "\n" + "== section 2" expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{ + Attributes: &types.DocumentAttributes{ "title": "a heading", }, Elements: []types.DocElement{ &types.Section{ @@ -133,7 +133,7 @@ var _ = Describe("Sections", func() { "\n" + "=== section 3" expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{ + Attributes: &types.DocumentAttributes{ "title": "a heading", }, Elements: []types.DocElement{ @@ -175,7 +175,7 @@ var _ = Describe("Sections", func() { actualContent := `== a title and a paragraph` expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.Section{ Heading: types.Heading{ @@ -208,7 +208,7 @@ and a paragraph` It("section level 2 with a paragraph separated by empty line", func() { actualContent := "== a title\n\nand a paragraph" expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.Section{ Heading: types.Heading{ @@ -242,7 +242,7 @@ and a paragraph` It("section level 2 with a paragraph separated by non-empty line", func() { actualContent := "== a title\n \nand a paragraph" expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.Section{ Heading: types.Heading{ @@ -285,7 +285,7 @@ a paragraph == Section B a paragraph` expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{ + Attributes: &types.DocumentAttributes{ "title": "a title", }, Elements: []types.DocElement{ @@ -388,7 +388,7 @@ a paragraph` It("heading invalid - missing space", func() { actualContent := "=a heading" expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.Paragraph{ Lines: []*types.InlineContent{ @@ -406,7 +406,7 @@ a paragraph` It("heading invalid - heading space", func() { actualContent := " = a heading" expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{}, + Attributes: &types.DocumentAttributes{}, Elements: []types.DocElement{ &types.Paragraph{ Lines: []*types.InlineContent{ @@ -426,7 +426,7 @@ a paragraph` "\n" + " == section 2" expectedDocument := &types.Document{ - Metadata: &types.DocumentMetadata{ + Attributes: &types.DocumentAttributes{ "title": "a heading", }, Elements: []types.DocElement{ diff --git a/renderer/html5/blank_line_test.go b/renderer/html5/blank_line_test.go index 7cbe2ba7..b18b020c 100644 --- a/renderer/html5/blank_line_test.go +++ b/renderer/html5/blank_line_test.go @@ -24,6 +24,16 @@ second paragraph`

second paragraph

+
` + verify(GinkgoT(), expected, content) + }) + It("blank lines at end of document", func() { + content := `first paragraph + + + ` + expected := `
+

first paragraph

` verify(GinkgoT(), expected, content) }) diff --git a/renderer/html5/document_attribute_test.go b/renderer/html5/document_attribute_test.go new file mode 100644 index 00000000..4c4d7ddb --- /dev/null +++ b/renderer/html5/document_attribute_test.go @@ -0,0 +1,32 @@ +package html5_test + +import . "github.com/onsi/ginkgo" + +var _ = Describe("Rendering With Attributes", func() { + It("some attributes then a paragraph", func() { + content := `:toc: +:date: 2017-01-01 +:author: Xavier +a paragraph` + + expected := `
+

a paragraph

+
` + verify(GinkgoT(), expected, content) + }) +}) + +var _ = Describe("Rendering With Attributes", func() { + It("a paragraph then some attributes", func() { + content := `a paragraph + +:toc: +:date: 2017-01-01 +:author: Xavier` + + expected := `
+

a paragraph

+
` + verify(GinkgoT(), expected, content) + }) +}) diff --git a/renderer/html5/paragraph_test.go b/renderer/html5/paragraph_test.go index 35820985..a9d62efd 100644 --- a/renderer/html5/paragraph_test.go +++ b/renderer/html5/paragraph_test.go @@ -21,6 +21,22 @@ with more content afterwards...

expected := `
a title

bold content with more content afterwards...

+
` + verify(GinkgoT(), expected, content) + }) + + It("2 paragraphs and blank line", func() { + content := ` +*bold content* with more content afterwards... + +and here another paragraph + +` + expected := `
+

bold content with more content afterwards...

+
+
+

and here another paragraph

` verify(GinkgoT(), expected, content) }) diff --git a/renderer/html5/renderer.go b/renderer/html5/renderer.go index 24101604..7ad46339 100644 --- a/renderer/html5/renderer.go +++ b/renderer/html5/renderer.go @@ -10,6 +10,7 @@ import ( "github.com/bytesparadise/libasciidoc/renderer" "github.com/bytesparadise/libasciidoc/types" "github.com/pkg/errors" + log "github.com/sirupsen/logrus" ) var documentTmpl *texttemplate.Template @@ -59,8 +60,8 @@ func Render(ctx context.Context, document types.Document, output io.Writer, opti renderedHTMLElements := template.HTML(renderedElementsBuff.String()) title := "undefined" - if document.Metadata.GetTitle() != nil { - title = *document.Metadata.GetTitle() + if document.Attributes.GetTitle() != nil { + title = *document.Attributes.GetTitle() } err := documentTmpl.Execute(output, struct { Generator string @@ -83,15 +84,23 @@ func Render(ctx context.Context, document types.Document, output io.Writer, opti } func renderElements(ctx context.Context, elements []types.DocElement, output io.Writer) error { - for i, element := range elements { + hasContent := false + for _, element := range elements { content, err := renderElement(ctx, element) if err != nil { return errors.Wrapf(err, "failed to render the document") } - output.Write(content) - if _, ok := element.(*types.BlankLine); !ok && i < len(elements)-1 { + // 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) + if len(content) > 0 { + output.Write(content) + hasContent = true + } } return nil } @@ -116,8 +125,8 @@ 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.BlankLine, *types.ElementID, *types.ElementLink, *types.ElementTitle: - // ignored in the output + case *types.DocumentAttribute: + // for now, silently ignored in the output return make([]byte, 0), nil default: return nil, errors.Errorf("unsupported type of element: %T", element) diff --git a/types/document_metadata.go b/types/document_metadata.go index dd57dfef..bc703ec3 100644 --- a/types/document_metadata.go +++ b/types/document_metadata.go @@ -1,21 +1,29 @@ package types -// DocumentMetadata the document metadata -type DocumentMetadata map[string]string +// DocumentAttributes the document metadata +type DocumentAttributes map[string]interface{} const ( title string = "title" ) // GetTitle retrieves the document title in its metadata, or returns nil if the title was not specified -func (m DocumentMetadata) GetTitle() *string { +func (m DocumentAttributes) GetTitle() *string { if t, ok := m[title]; ok { - return &t + if t, ok := t.(string); ok { + return &t + } } return nil } // SetTitle sets the title in the document metadata -func (m DocumentMetadata) SetTitle(t string) { +func (m DocumentAttributes) SetTitle(t string) { m[title] = t } + +// Add adds the given attribute +func (m DocumentAttributes) Add(a *DocumentAttribute) { + // TODO: raise a warning if there was already a name/value + m[a.Name] = a.Value +} diff --git a/types/grammar_types.go b/types/grammar_types.go index 6999685e..9b33d63e 100644 --- a/types/grammar_types.go +++ b/types/grammar_types.go @@ -23,11 +23,13 @@ type DocElement interface { String(int) string } +// InlineElement the interface for inline elements type InlineElement interface { DocElement PlainString() string } +// Visitable the interface for visitable elements type Visitable interface { Accept(Visitor) error } @@ -45,8 +47,8 @@ type Visitor interface { // Document the top-level structure for a document type Document struct { - Metadata *DocumentMetadata - Elements []DocElement + Attributes *DocumentAttributes + Elements []DocElement } // NewDocument initializes a new `Document` from the given lines @@ -57,13 +59,13 @@ func NewDocument(blocks []interface{}) (*Document, error) { } elements := convertBlocksToDocElements(blocks) document := &Document{Elements: elements} - document.initMetadata() + document.initAttributes() return document, nil } -// initMetadata initializes the Document's metadata -func (d *Document) initMetadata() { - metadata := &DocumentMetadata{} +// initAttributes initializes the Document's attributes +func (d *Document) initAttributes() { + d.Attributes = &DocumentAttributes{} // look-up the document title in the (first) section of level 1 var headSection *Section for _, element := range d.Elements { @@ -74,21 +76,60 @@ func (d *Document) initMetadata() { } } if headSection != nil { - metadata.SetTitle(headSection.Heading.PlainString()) + d.Attributes.SetTitle(headSection.Heading.PlainString()) } - d.Metadata = metadata + } // String implements the DocElement#String() method func (d *Document) String(indentLevel int) string { - log.Debugf("Printing document...") result := bytes.NewBuffer(make([]byte, 0)) 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())) + return result.String() +} + +// ------------------------------------------ +// Document Attributes +// ------------------------------------------ + +// DocumentAttribute the type for Document Attributes +type DocumentAttribute struct { + Name string + Value string +} + +// NewDocumentAttribute initializes a new Document Attribute +func NewDocumentAttribute(name []interface{}, value []interface{}) (*DocumentAttribute, error) { + attrName, err := stringify(name) + if err != nil { + return nil, errors.Wrapf(err, "error while initializing a DocumentAttribute") + } + attrValue, err := stringify(value) + if err != nil { + return nil, errors.Wrapf(err, "error while initializing a DocumentAttribute") + } + log.Debugf("Initialized a new DocumentAttribute: '%s' -> '%s'", *attrName, *attrValue) + return &DocumentAttribute{ + 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() } +// Accept implements DocElement#Accept(Visitor) +func (a *DocumentAttribute) Accept(v Visitor) error { + return nil +} + // ------------------------------------------ // Section // ------------------------------------------ @@ -159,11 +200,11 @@ type Heading struct { Content *InlineContent } -// NewHeading initializes a new `Heading from the given level and content, with the optional metadata. -// In the metadata, only the ElementID is retained -func NewHeading(level int, inlineContent *InlineContent, metadata []interface{}) (*Heading, error) { +// NewHeading initializes a new `Heading from the given level and content, with the optional attributes. +// In the attributes, only the ElementID is retained +func NewHeading(level int, inlineContent *InlineContent, attributes []interface{}) (*Heading, error) { // counting the lenght of the 'level' value (ie, the number of `=` chars) - id, _, _ := newMetaElements(metadata) + id, _, _ := newElementAttributes(attributes) // make a default id from the heading's inline content if id == nil { replacement, err := ReplaceNonAlphanumerics(inlineContent, "_") @@ -222,8 +263,8 @@ type List struct { } // NewList initializes a new `ListItem` from the given content -func NewList(elements []interface{}, metadata []interface{}) (*List, error) { - id, _, _ := newMetaElements(metadata) +func NewList(elements []interface{}, attributes []interface{}) (*List, error) { + id, _, _ := newElementAttributes(attributes) items := make([]*ListItem, 0) log.Debugf("Initializing a new List from %d elements", len(elements)) currentLevel := 1 @@ -431,9 +472,9 @@ type Paragraph struct { } // NewParagraph initializes a new `Paragraph` -func NewParagraph(text []byte, lines []interface{}, metadata []interface{}) (*Paragraph, error) { +func NewParagraph(text []byte, lines []interface{}, attributes []interface{}) (*Paragraph, error) { log.Debugf("Initializing a new Paragraph with %d line(s)", len(lines)) - id, title, _ := newMetaElements(metadata) + id, title, _ := newElementAttributes(attributes) typedLines := make([]*InlineContent, 0) for _, line := range lines { @@ -551,9 +592,9 @@ type BlockImage struct { } // NewBlockImage initializes a new `BlockImage` -func NewBlockImage(input []byte, imageMacro ImageMacro, metadata []interface{}) (*BlockImage, error) { +func NewBlockImage(input []byte, imageMacro ImageMacro, attributes []interface{}) (*BlockImage, error) { log.Debugf("Initializing a new BlockImage from '%s'", input) - id, title, link := newMetaElements(metadata) + id, title, link := newElementAttributes(attributes) return &BlockImage{ Macro: imageMacro, ID: id, @@ -600,7 +641,7 @@ type InlineImage struct { Macro ImageMacro } -// NewInlineImage initializes a new `InlineImage` (similar to BlockImage, but without Metadata) +// NewInlineImage initializes a new `InlineImage` (similar to BlockImage, but without attributes) func NewInlineImage(input []byte, imageMacro ImageMacro) (*InlineImage, error) { log.Debugf("Initializing a new InlineImage from '%s'", input) return &InlineImage{ @@ -770,11 +811,11 @@ func (b *DelimitedBlock) Accept(v Visitor) error { // Meta Elements // ------------------------------------------ -func newMetaElements(metadata []interface{}) (*ElementID, *ElementTitle, *ElementLink) { +func newElementAttributes(attributes []interface{}) (*ElementID, *ElementTitle, *ElementLink) { var id *ElementID var title *ElementTitle var link *ElementLink - for _, item := range metadata { + for _, item := range attributes { switch item := item.(type) { case *ElementID: id = item @@ -783,7 +824,7 @@ func newMetaElements(metadata []interface{}) (*ElementID, *ElementTitle, *Elemen case *ElementTitle: title = item default: - log.Warnf("Unexpected metadata: %T", item) + log.Warnf("Unexpected attributes: %T", item) } } return id, title, link diff --git a/types/type_utils.go b/types/type_utils.go index eb81843e..948a41cd 100644 --- a/types/type_utils.go +++ b/types/type_utils.go @@ -138,15 +138,11 @@ func isMn(r rune) bool { return unicode.Is(unicode.Mn, r) // Mn: nonspacing marks } -//ReplaceNonAlphanumerics replaces all non alphanumerical characters and remove (accents) +// NewReplaceNonAlphanumericsFunc replaces all non alphanumerical characters and remove (accents) // 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)) - // _, err := buf.WriteString(replacement) - // if err != nil { - // return nil, errors.Wrapf(err, "unable to normalize value") - // } lastCharIsSpace := false for _, r := range strings.TrimLeft(source, " ") { // ignore heading spaces if unicode.Is(unicode.Letter, r) || unicode.Is(unicode.Number, r) {