Skip to content

Commit

Permalink
fix(parser/renderer): support quoted text in links (#360)
Browse files Browse the repository at this point in the history
Also, significant refactoring on the parsing of quoted text
Also, when a link has no `text` attribute, then omit in the
output AST.

Fixes #356

Signed-off-by: Xavier Coulon <[email protected]>
  • Loading branch information
xcoulon authored May 23, 2019
1 parent 3f3f349 commit e0ac68b
Show file tree
Hide file tree
Showing 21 changed files with 90,791 additions and 98,446 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ generate: prebuild-checks
## generate the .go file based on the asciidoc grammar
generate-optimized:
@echo "generating the parser (optimized)..."
@pigeon -optimize-grammar -alternate-entrypoints PreparsedDocument,InlineElementsWithoutSubtitution,VerbatimBlock ./pkg/parser/asciidoc-grammar.peg > ./pkg/parser/asciidoc_parser.go
@pigeon -optimize-grammar -alternate-entrypoints PreparsedDocument,InlineElementsWithoutSubtitution,VerbatimBlock -o ./pkg/parser/asciidoc_parser.go ./pkg/parser/asciidoc-grammar.peg

.PHONY: test
## run all tests excluding fixtures and vendored packages
Expand Down
271 changes: 184 additions & 87 deletions pkg/parser/asciidoc-grammar.peg

Large diffs are not rendered by default.

188,411 changes: 90,253 additions & 98,158 deletions pkg/parser/asciidoc_parser.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion pkg/parser/bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ puts "Hello, World!"
func parseReader(content string) (parser.Stats, error) {
reader := strings.NewReader(content)
stats := parser.Stats{}
allOptions := make([]parser.Option, 0)
allOptions := []parser.Option{}
allOptions = append(allOptions, parser.AllowInvalidUTF8(false), parser.Statistics(&stats, "no match"))
if os.Getenv("DEBUG") == "true" {
allOptions = append(allOptions, parser.Debug(true))
Expand Down
29 changes: 18 additions & 11 deletions pkg/parser/document_preprocessing.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func preparseDocument(filename string, r io.Reader, levelOffset string, opts ...
}
doc := d.(types.PreparsedDocument)
result := bytes.NewBuffer(nil)
attrs := map[string]string{}
attrs := types.DocumentAttributes{}
for i, e := range doc.Elements {
switch e := e.(type) {
case types.FileInclusion:
Expand Down Expand Up @@ -86,13 +86,16 @@ func init() {
}
}

func invalidFileErrMsg(incl types.FileInclusion) []byte {
func invalidFileErrMsg(incl types.FileInclusion) ([]byte, error) {
buf := bytes.NewBuffer(nil)
invalidFileTmpl.Execute(buf, incl.RawText)
return buf.Bytes()
err := invalidFileTmpl.Execute(buf, incl.RawText)
if err != nil {
return nil, err
}
return buf.Bytes(), nil
}

func parseFileToInclude(incl types.FileInclusion, attrs map[string]string, opts ...Option) ([]byte, error) {
func parseFileToInclude(incl types.FileInclusion, attrs types.DocumentAttributes, opts ...Option) ([]byte, error) {
path := incl.Location.Resolve(attrs)
log.Debugf("parsing '%s'...", path)
// manage new working directory based on the file's location
Expand All @@ -104,12 +107,12 @@ func parseFileToInclude(incl types.FileInclusion, attrs map[string]string, opts
}
absPath, err := filepath.Abs(path)
if err != nil {
return invalidFileErrMsg(incl), err
return invalidFileErrMsg(incl)
}
dir := filepath.Dir(absPath)
err = os.Chdir(dir)
if err != nil {
return invalidFileErrMsg(incl), err
return invalidFileErrMsg(incl)
}
defer func() {
err = os.Chdir(wd) // restore the previous working directory
Expand All @@ -120,7 +123,7 @@ func parseFileToInclude(incl types.FileInclusion, attrs map[string]string, opts
// read the file per-se
f, err := os.Open(absPath)
if err != nil {
return invalidFileErrMsg(incl), err
return invalidFileErrMsg(incl)
}
defer func() {
if closeErr := f.Close(); closeErr != nil {
Expand All @@ -147,17 +150,21 @@ func parseFileToInclude(incl types.FileInclusion, attrs map[string]string, opts
if lineRanges.Match(line) {
_, err := content.WriteString(scanner.Text())
if err != nil {
return invalidFileErrMsg(incl), err
return invalidFileErrMsg(incl)
}
_, err = content.WriteString("\n")
if err != nil {
return invalidFileErrMsg(incl), err
return invalidFileErrMsg(incl)
}
}
line++
}
if err := scanner.Err(); err != nil {
return invalidFileErrMsg(incl), errors.Wrap(err, "unable to read file to include")
msg, err2 := invalidFileErrMsg(incl)
if err2 != nil {
return nil, err2
}
return msg, errors.Wrap(err, "unable to read file to include")
}
if levelOffset, ok := incl.Attributes[types.AttrLevelOffset].(string); ok {
return preparseDocument(path, content, levelOffset, opts...)
Expand Down
165 changes: 122 additions & 43 deletions pkg/parser/external_link_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@ var _ = Describe("links", func() {
{
types.StringElement{Content: "a link to "},
types.InlineLink{
URL: "https://foo.bar",
Attributes: types.ElementAttributes{
"text": "",
Location: types.Location{
types.StringElement{
Content: "https://foo.bar",
},
},
Attributes: types.ElementAttributes{},
},
},
},
Expand All @@ -37,10 +39,12 @@ var _ = Describe("links", func() {
{
types.StringElement{Content: "a link to "},
types.InlineLink{
URL: "https://foo.bar",
Attributes: types.ElementAttributes{
"text": "",
Location: types.Location{
types.StringElement{
Content: "https://foo.bar",
},
},
Attributes: types.ElementAttributes{},
},
},
},
Expand All @@ -56,9 +60,18 @@ var _ = Describe("links", func() {
{
types.StringElement{Content: "a link to "},
types.InlineLink{
URL: "mailto:foo@bar",
Location: types.Location{
types.StringElement{
Content: "mailto:foo@bar",
},
},

Attributes: types.ElementAttributes{
"text": "the foo@bar email",
types.AttrInlineLinkText: types.InlineElements{
types.StringElement{
Content: "the foo@bar email",
},
},
},
},
},
Expand All @@ -75,37 +88,26 @@ var _ = Describe("links", func() {
{
types.StringElement{Content: "a link to "},
types.InlineLink{
URL: "mailto:foo@bar",
Attributes: types.ElementAttributes{
"text": "the foo@bar email",
"foo": "bar",
Location: types.Location{
types.StringElement{
Content: "mailto:foo@bar",
},
},
},
},
},
}
verifyWithPreprocessing(GinkgoT(), expectedResult, actualContent, parser.Entrypoint("DocumentBlock"))
})

It("external link with extra attributes only", func() {
actualContent := "a link to mailto:foo@bar[foo=bar]"
expectedResult := types.Paragraph{
Attributes: types.ElementAttributes{},
Lines: []types.InlineElements{
{
types.StringElement{Content: "a link to "},
types.InlineLink{
URL: "mailto:foo@bar",
Attributes: types.ElementAttributes{
"text": "",
"foo": "bar",
types.AttrInlineLinkText: types.InlineElements{
types.StringElement{
Content: "the foo@bar email",
},
},
"foo": "bar",
},
},
},
},
}
verifyWithPreprocessing(GinkgoT(), expectedResult, actualContent, parser.Entrypoint("DocumentBlock"))
})

})

Context("relative links", func() {
Expand All @@ -115,10 +117,12 @@ var _ = Describe("links", func() {
expectedResult := types.InlineElements{
types.StringElement{Content: "a link to "},
types.InlineLink{
URL: "foo.adoc",
Attributes: types.ElementAttributes{
"text": "",
Location: types.Location{
types.StringElement{
Content: "foo.adoc",
},
},
Attributes: types.ElementAttributes{},
},
}
verifyWithPreprocessing(GinkgoT(), expectedResult, actualContent, parser.Entrypoint("InlineElements"))
Expand All @@ -129,9 +133,17 @@ var _ = Describe("links", func() {
expectedResult := types.InlineElements{
types.StringElement{Content: "a link to "},
types.InlineLink{
URL: "foo.adoc",
Location: types.Location{
types.StringElement{
Content: "foo.adoc",
},
},
Attributes: types.ElementAttributes{
"text": "foo doc",
types.AttrInlineLinkText: types.InlineElements{
types.StringElement{
Content: "foo doc",
},
},
},
},
}
Expand All @@ -143,9 +155,17 @@ var _ = Describe("links", func() {
expectedResult := types.InlineElements{
types.StringElement{Content: "a link to "},
types.InlineLink{
URL: "https://foo.bar",
Location: types.Location{
types.StringElement{
Content: "https://foo.bar",
},
},
Attributes: types.ElementAttributes{
"text": "foo doc",
types.AttrInlineLinkText: types.InlineElements{
types.StringElement{
Content: "foo doc",
},
},
},
},
}
Expand All @@ -157,10 +177,18 @@ var _ = Describe("links", func() {
expectedResult := types.InlineElements{
types.StringElement{Content: "a link to "},
types.InlineLink{
URL: "https://foo.bar",
Location: types.Location{
types.StringElement{
Content: "https://foo.bar",
},
},
Attributes: types.ElementAttributes{
"text": "foo doc",
"foo": "bar",
types.AttrInlineLinkText: types.InlineElements{
types.StringElement{
Content: "foo doc",
},
},
"foo": "bar",
},
},
}
Expand All @@ -172,10 +200,13 @@ var _ = Describe("links", func() {
expectedResult := types.InlineElements{
types.StringElement{Content: "a link to "},
types.InlineLink{
URL: "https://foo.bar",
Location: types.Location{
types.StringElement{
Content: "https://foo.bar",
},
},
Attributes: types.ElementAttributes{
"text": "",
"foo": "bar",
"foo": "bar",
},
},
}
Expand All @@ -191,6 +222,54 @@ var _ = Describe("links", func() {
}
verifyWithPreprocessing(GinkgoT(), expectedResult, actualContent, parser.Entrypoint("InlineElements"))
})

It("relative link with quoted text", func() {
actualContent := "link:/[_a_ *b* `c`]"
expectedResult := types.InlineLink{
Location: types.Location{
types.StringElement{
Content: "/",
},
},
Attributes: types.ElementAttributes{
types.AttrInlineLinkText: types.InlineElements{
types.QuotedText{
Kind: types.Italic,
Elements: types.InlineElements{
types.StringElement{
Content: "a",
},
},
},
types.StringElement{
Content: " ",
},
types.QuotedText{
Kind: types.Bold,
Elements: types.InlineElements{
types.StringElement{
Content: "b",
},
},
},
types.StringElement{
Content: " ",
},
types.QuotedText{
Kind: types.Monospace,
Elements: types.InlineElements{
types.StringElement{
Content: "c",
},
},
},
},
},
}
// verifyWithPreprocessing(GinkgoT(), expectedResult, actualContent, parser.Entrypoint("RelativeLink"), parser.Debug(true))
verifyWithPreprocessing(GinkgoT(), expectedResult, actualContent, parser.Entrypoint("RelativeLink"))
})

})

})
14 changes: 11 additions & 3 deletions pkg/parser/footnote_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ var _ = Describe("footnotes", func() {
})

It("footnote with single-line rich content", func() {
actualContent := `foo footnote:[some *rich* http://foo.com[content]]`
actualContent := `foo footnote:[some *rich* https://foo.com[content]]`
footnote1 := types.Footnote{
ID: 0,
Elements: types.InlineElements{
Expand All @@ -71,9 +71,17 @@ var _ = Describe("footnotes", func() {
},
types.InlineLink{
Attributes: types.ElementAttributes{
types.AttrInlineLinkText: "content",
types.AttrInlineLinkText: types.InlineElements{
types.StringElement{
Content: "content",
},
},
},
Location: types.Location{
types.StringElement{
Content: "https://foo.com",
},
},
URL: "http://foo.com",
},
},
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/parser/image_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ image::appa.png[]`
Path: "images/foo.png",
},
}
verifyWithPreprocessing(GinkgoT(), expectedResult, actualContent, parser.Entrypoint("InlineElements"), parser.Debug(false))
verifyWithPreprocessing(GinkgoT(), expectedResult, actualContent, parser.Entrypoint("InlineElements"))
})

It("inline image with multiple other attributes only", func() {
Expand Down
1 change: 0 additions & 1 deletion pkg/parser/inline_elements_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ var _ = Describe("inline elements", func() {
},
types.StringElement{Content: ")"},
}

verifyWithPreprocessing(GinkgoT(), expectedResult, actualContent, parser.Entrypoint("InlineElements"))
})

Expand Down
Loading

0 comments on commit e0ac68b

Please sign in to comment.