Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix failing case of nested link references by making rawBlockParser recursive #84

Merged
merged 5 commits into from Jul 13, 2021
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions spec-results.json
Expand Up @@ -364,6 +364,7 @@
184,
185,
186,
187,
188
],
"Links": [
Expand Down Expand Up @@ -917,6 +918,7 @@
184,
185,
186,
187,
188
],
"Links": [
Expand Down
117 changes: 72 additions & 45 deletions src/Markdown/Parser.elm
Expand Up @@ -294,19 +294,16 @@ parseInlines linkReferences rawBlock =
EmptyBlock

BlockQuote rawBlocks ->
case Advanced.run rawBlockParser rawBlocks of
Ok value ->
case parseAllInlines value of
Ok parsedBlocks ->
Block.BlockQuote parsedBlocks
|> ParsedBlock

Err e ->
InlineProblem e
EmptyBlock

Err error ->
InlineProblem (Parser.Problem (deadEndsToString error))
ParsedBlockQuote rawBlocks->
case parseAllInlines {linkReferenceDefinitions = linkReferences, rawBlocks = rawBlocks} of
Ok parsedBlocks ->
Block.BlockQuote parsedBlocks
|> ParsedBlock

Err e ->
InlineProblem e
IndentedCodeBlock codeBlockBody ->
Block.CodeBlock { body = codeBlockBody, language = Nothing }
|> ParsedBlock
Expand Down Expand Up @@ -644,71 +641,88 @@ parseAllInlinesHelp state rawBlocks parsedBlocks =

completeOrMergeBlocks : State -> RawBlock -> State
completeOrMergeBlocks state newRawBlock =
{ linkReferenceDefinitions = state.linkReferenceDefinitions
, rawBlocks =
case
( newRawBlock
, state.rawBlocks
)
of
( CodeBlock block1, (CodeBlock block2) :: rest ) ->
CodeBlock
{ body = joinStringsPreserveAll block2.body block1.body
, language = Nothing
}
:: rest
{ linkReferenceDefinitions = state.linkReferenceDefinitions
, rawBlocks = CodeBlock
{ body = joinStringsPreserveAll block2.body block1.body
, language = Nothing
}
:: rest}

( IndentedCodeBlock block1, (IndentedCodeBlock block2) :: rest ) ->
IndentedCodeBlock (joinStringsPreserveAll block2 block1)
:: rest

( OpenBlockOrParagraph (UnparsedInlines body1), (BlockQuote body2) :: rest ) ->
BlockQuote (joinRawStringsWith "\n" body2 body1)
:: rest

( BlockQuote body1, (BlockQuote body2) :: rest ) ->
BlockQuote (joinStringsPreserveAll body2 body1)
:: rest
{ linkReferenceDefinitions = state.linkReferenceDefinitions
, rawBlocks = IndentedCodeBlock (joinStringsPreserveAll block2 block1)
:: rest}


( _, (BlockQuote body2) :: rest ) ->
case newRawBlock of
BlockQuote body1 ->
{ linkReferenceDefinitions = state.linkReferenceDefinitions
, rawBlocks = BlockQuote (joinStringsPreserveAll body2 body1)
:: rest}

OpenBlockOrParagraph (UnparsedInlines body1) ->
{ linkReferenceDefinitions = state.linkReferenceDefinitions
, rawBlocks = BlockQuote (joinRawStringsWith "\n" body2 body1)
:: rest}

_ ->
case Advanced.run rawBlockParser body2 of
Ok value ->
{ linkReferenceDefinitions = state.linkReferenceDefinitions ++ value.linkReferenceDefinitions
, rawBlocks = newRawBlock :: (value.rawBlocks |> ParsedBlockQuote) :: rest
}
Err e ->
-- TODO return this error
{linkReferenceDefinitions=[], rawBlocks= []}

( OpenBlockOrParagraph (UnparsedInlines body1), (OpenBlockOrParagraph (UnparsedInlines body2)) :: rest ) ->
OpenBlockOrParagraph (UnparsedInlines (joinRawStringsWith "\n" body2 body1))
:: rest
{ linkReferenceDefinitions = state.linkReferenceDefinitions
, rawBlocks = OpenBlockOrParagraph (UnparsedInlines (joinRawStringsWith "\n" body2 body1))
:: rest}

( SetextLine LevelOne _, (OpenBlockOrParagraph unparsedInlines) :: rest ) ->
Heading 1 unparsedInlines
:: rest
{ linkReferenceDefinitions = state.linkReferenceDefinitions
, rawBlocks =Heading 1 unparsedInlines
:: rest}

( SetextLine LevelTwo _, (OpenBlockOrParagraph unparsedInlines) :: rest ) ->
Heading 2 unparsedInlines
:: rest
{ linkReferenceDefinitions = state.linkReferenceDefinitions
, rawBlocks =Heading 2 unparsedInlines
:: rest}

( TableDelimiter (Markdown.Table.TableDelimiterRow text alignments), (OpenBlockOrParagraph (UnparsedInlines rawHeaders)) :: rest ) ->
case TableParser.parseHeader (Markdown.Table.TableDelimiterRow text alignments) rawHeaders of
Ok (Markdown.Table.TableHeader headers) ->
Table (Markdown.Table.Table headers []) :: rest
{ linkReferenceDefinitions = state.linkReferenceDefinitions
, rawBlocks =Table (Markdown.Table.Table headers []) :: rest}

Err _ ->
OpenBlockOrParagraph (UnparsedInlines (joinRawStringsWith "\n" rawHeaders text.raw))
:: rest
{ linkReferenceDefinitions = state.linkReferenceDefinitions
, rawBlocks =OpenBlockOrParagraph (UnparsedInlines (joinRawStringsWith "\n" rawHeaders text.raw))
:: rest}

( Table updatedTable, (Table _) :: rest ) ->
Table updatedTable :: rest
{ linkReferenceDefinitions = state.linkReferenceDefinitions
, rawBlocks = Table updatedTable :: rest}

_ ->
newRawBlock :: state.rawBlocks
}


{ linkReferenceDefinitions = state.linkReferenceDefinitions
, rawBlocks = newRawBlock :: state.rawBlocks}

-- RAW BLOCK PARSER


stepRawBlock : State -> Parser (Step State State)
stepRawBlock revStmts =
-- Some blocks can't immediately follow a body
oneOf
[ Helpers.endOfFile
|> map (\_ -> Done revStmts)
|> map (\_ -> Done (completeBlocks revStmts))
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you change this to andThen, I believe you could use succeed and fail to pass through the parsed value or error. That would allow you to resolve your todo comment -- TODO return this error.

, LinkReferenceDefinition.parser
|> Advanced.backtrackable
|> map (\reference -> Loop (addReference revStmts reference))
Expand All @@ -731,6 +745,19 @@ stepRawBlock revStmts =
]


completeBlocks state=
case state.rawBlocks of
(BlockQuote body2) :: rest ->
case Advanced.run rawBlockParser body2 of
Ok value ->
{ linkReferenceDefinitions = state.linkReferenceDefinitions ++ value.linkReferenceDefinitions
, rawBlocks = (value.rawBlocks |> ParsedBlockQuote) :: rest
}
Err e ->
-- TODO return this error
state
_-> state


-- Note [Static Parser Structure]
--
Expand Down
1 change: 1 addition & 0 deletions src/Markdown/RawBlock.elm
Expand Up @@ -37,4 +37,5 @@ type RawBlock
| TableDelimiter Markdown.Table.TableDelimiterRow
| BlankLine
| BlockQuote String
| ParsedBlockQuote (List RawBlock)
| SetextLine SetextLevel String
2 changes: 1 addition & 1 deletion test-results/failing/CommonMark/HTML blocks.md
Expand Up @@ -498,7 +498,7 @@ Should give output:
But instead was:

````````````html
ERROR Problem at row 1 Problem at row 2 Expecting symbol
<p>bar</p>
````````````
## [Example 144](https://spec.commonmark.org/0.29/#example-144)

Expand Down
22 changes: 0 additions & 22 deletions test-results/failing/CommonMark/Link reference definitions.md
Expand Up @@ -89,25 +89,3 @@ But instead was:
````````````html
<p>Foo<a href="/baz">bar</a></p>
````````````
## [Example 187](https://spec.commonmark.org/0.29/#example-187)

This markdown:

````````````markdown
[foo]

> [foo]: /url

````````````

Should give output:

````````````html
<p><a href="/url">foo</a></p><blockquote></blockquote>
````````````

But instead was:

````````````html
<p>[foo]</p><blockquote></blockquote>
````````````
2 changes: 1 addition & 1 deletion test-results/failing/GFM/HTML blocks.md
Expand Up @@ -498,7 +498,7 @@ Should give output:
But instead was:

````````````html
ERROR Problem at row 1 Problem at row 2 Expecting symbol
<p>bar</p>
````````````
## [Example 144](https://spec.commonmark.org/0.29/#example-144)

Expand Down
22 changes: 0 additions & 22 deletions test-results/failing/GFM/Link reference definitions.md
Expand Up @@ -89,25 +89,3 @@ But instead was:
````````````html
<p>Foo<a href="/baz">bar</a></p>
````````````
## [Example 187](https://spec.commonmark.org/0.29/#example-187)

This markdown:

````````````markdown
[foo]

> [foo]: /url

````````````

Should give output:

````````````html
<p><a href="/url">foo</a></p><blockquote></blockquote>
````````````

But instead was:

````````````html
<p>[foo]</p><blockquote></blockquote>
````````````
22 changes: 22 additions & 0 deletions test-results/passing-CommonMark.md
Expand Up @@ -6624,6 +6624,28 @@ Gives this correct output:

````````````

### [Example 187](https://spec.commonmark.org/0.29/#example-187)

This markdown:


````````````markdown
[foo]

> [foo]: /url

````````````

Gives this correct output:


````````````html
<p><a href="/url">foo</a></p>
<blockquote>
</blockquote>

````````````

### [Example 188](https://spec.commonmark.org/0.29/#example-188)

This markdown:
Expand Down
22 changes: 22 additions & 0 deletions test-results/passing-GFM.md
Expand Up @@ -6624,6 +6624,28 @@ Gives this correct output:

````````````

### [Example 187](https://spec.commonmark.org/0.29/#example-187)

This markdown:


````````````markdown
[foo]

> [foo]: /url

````````````

Gives this correct output:


````````````html
<p><a href="/url">foo</a></p>
<blockquote>
</blockquote>

````````````

### [Example 188](https://spec.commonmark.org/0.29/#example-188)

This markdown:
Expand Down
40 changes: 40 additions & 0 deletions tests/LinkDefinitionTests.elm
@@ -0,0 +1,40 @@
module LinkDefinitionTests exposing (suite)

import Expect exposing (Expectation)
import Markdown.Block as Block exposing (..)
import Markdown.Parser
import Test exposing (Test, describe, test)


suite : Test
suite =
describe "definition nested 1 level"
[ test "definitions" <|
\() ->
"""[foo]

> [foo]: /url"""
|> expectResolvedLinkReference
, test "definition nested 2 levels" <|
\() ->
"""[foo]

> > [foo]: /url"""
|> expectResolvedLinkReference
, test "definition nested 3 levels" <|
\() ->
"""[foo]


> > > [foo]: /url
"""
|> expectResolvedLinkReference
]


expectResolvedLinkReference : String -> Expectation
expectResolvedLinkReference markdownString =
markdownString
|> Markdown.Parser.parse
|> Result.map List.head
|> Expect.equal (Ok (Just <| Paragraph [ Link "/url" Nothing [ Text "foo" ] ]))