Skip to content

Commit

Permalink
Merge pull request #52 from Omikhleia/another_attempt_nested_divs
Browse files Browse the repository at this point in the history
fix: Another attempt fixing nested fenced divs keeping structure
  • Loading branch information
Witiko authored Dec 14, 2022
2 parents 947d97b + 2770232 commit 9501ccf
Show file tree
Hide file tree
Showing 4 changed files with 168 additions and 76 deletions.
103 changes: 52 additions & 51 deletions lunamark/reader/markdown.lua
Original file line number Diff line number Diff line change
Expand Up @@ -932,17 +932,17 @@ function M.new(writer, options)
-- Parsers used for fenced divs (local)
-----------------------------------------------------------------------------

larsers.fenced_div_begin = parsers.colon^3
larsers.fenced_div_begin = parsers.nonindentspace
* parsers.colon^3
* parsers.optionalspace
* parsers.attributes
* Cg(parsers.attributes)
* parsers.optionalspace
* (parsers.newline + parsers.eof)
/ writer.div_begin

larsers.fenced_div_end = parsers.colon^3
larsers.fenced_div_end = parsers.nonindentspace
* parsers.colon^3
* parsers.optionalspace
* (parsers.newline + parsers.eof)
/ writer.div_end

------------------------------------------------------------------------------
-- Parsers used for citations (local)
Expand Down Expand Up @@ -1121,22 +1121,18 @@ function M.new(writer, options)
parsers.tilde_infostring)
end

local div_level = 0
if options.fenced_divs then
local function check_div_level(s, i, current_level) -- luacheck: ignore s i
current_level = tonumber(current_level)
return current_level > 0
end

if not options.fenced_divs then
larsers.fenceend = parsers.fail
else
larsers.fenceend = larsers.fenced_div_end
* Cmt(P(true), function(_, i)
local result
-- check whether we are at top level without decrementing div_level
if div_level <= 0 then
result = nil
else
result = i
end
return result
end)
local is_inside_div = Cmt(Cb("div_level"), check_div_level)

larsers.fencestart = larsers.fencestart
+ is_inside_div -- break out of a paragraph when we
-- are inside a div and see a closing tag
* larsers.fenced_div_end
end

larsers.Endline = parsers.newline * -( -- newline, but not before...
Expand All @@ -1146,7 +1142,6 @@ function M.new(writer, options)
+ larsers.bqstart
+ larsers.headerstart
+ larsers.fencestart
+ larsers.fenceend
) * parsers.spacechar^0 / writer.space

larsers.linebreak = parsers.spacechar^2 * larsers.Endline
Expand All @@ -1168,7 +1163,6 @@ function M.new(writer, options)
+ larsers.bqstart
+ larsers.headerstart
+ larsers.fencestart
+ larsers.fenceend
) * parsers.spacechar^0 / writer.nbsp

larsers.NonbreakingSpace
Expand Down Expand Up @@ -1325,27 +1319,28 @@ function M.new(writer, options)
writer.string(infostring))
end

local function increment_div_level(increment)
local function update_div_level(s, i, current_level) -- luacheck: ignore s i
current_level = tonumber(current_level)
local next_level = tostring(current_level + increment)
return true, next_level
end

larsers.FencedDivBegin = larsers.fenced_div_begin
* Cmt(P(true), function(_, i)
-- keep track how deep nested we are
div_level = div_level + 1
return i
end)

larsers.FencedDivEnd = larsers.fenced_div_end
* Cmt(P(true), function(_, i)
local result
-- check whether we are at top level and decrement
-- div_level otherwise
if div_level <= 0 then
result = nil
else
div_level = div_level - 1
result = i
end
return result
end)
return Cg( Cmt(Cb("div_level"), update_div_level)
, "div_level")
end

larsers.FencedDiv = larsers.fenced_div_begin * increment_div_level(1)
* parsers.skipblanklines
* Ct( (V("Block") - larsers.fenced_div_end)^-1
* (parsers.blanklines / function()
return writer.interblocksep
end
* (V("Block") - larsers.fenced_div_end))^0)
* parsers.skipblanklines
* larsers.fenced_div_end * increment_div_level(-1)
/ function (attr, div) return div, attr end
/ writer.div

-- strip off leading > and indents, and run through blocks
larsers.Blockquote = Cs((((parsers.leader * parsers.more * parsers.space^-1)/""
Expand All @@ -1367,7 +1362,6 @@ function M.new(writer, options)
* ( parsers.blankline^1
+ #parsers.hash
+ #(parsers.leader * parsers.more * parsers.space^-1)
+ #larsers.fenceend
)
/ writer.paragraph

Expand Down Expand Up @@ -1714,14 +1708,21 @@ function M.new(writer, options)
* larsers.table_caption^-1
/ writer.table

------------------------------------------------------------------------------
-- Parser state initialization
------------------------------------------------------------------------------

larsers.InitializeState = Cg(Ct("") / "0", "div_level") -- initialize named groups

------------------------------------------------------------------------------
-- Syntax specification
------------------------------------------------------------------------------

local syntax =
{ "Blocks",

Blocks = larsers.Blank^0 * parsers.Block^-1
Blocks = larsers.InitializeState
* larsers.Blank^0 * parsers.Block^-1
* (larsers.Blank^0 / function()
return writer.interblocksep
end
Expand All @@ -1734,8 +1735,7 @@ function M.new(writer, options)
+ V("PipeTable")
+ V("Verbatim")
+ V("FencedCodeBlock")
+ V("FencedDivBegin")
+ V("FencedDivEnd")
+ V("FencedDiv")
+ V("HorizontalRule")
+ V("TaskList") -- Must have precedence over BulletList
+ V("BulletList")
Expand All @@ -1750,8 +1750,7 @@ function M.new(writer, options)
Blockquote = larsers.Blockquote,
Verbatim = larsers.Verbatim,
FencedCodeBlock = larsers.FencedCodeBlock,
FencedDivBegin = larsers.FencedDivBegin,
FencedDivEnd = larsers.FencedDivEnd,
FencedDiv = larsers.FencedDiv,
HorizontalRule = larsers.HorizontalRule,
TaskList = larsers.TaskList,
BulletList = larsers.BulletList,
Expand Down Expand Up @@ -1860,8 +1859,7 @@ function M.new(writer, options)
end

if not options.fenced_divs then
syntax.FencedDivBegin = parsers.fail
syntax.FencedDivEnd = parsers.fail
syntax.FencedDiv = parsers.fail
end

if not options.task_list then
Expand All @@ -1884,7 +1882,10 @@ function M.new(writer, options)

local inlines_t = util.table_copy(syntax)
inlines_t[1] = "Inlines"
inlines_t.Inlines = parsers.Inline^0 * (parsers.spacing^0 * parsers.eof / "")
inlines_t.Inlines = larsers.InitializeState
* parsers.Inline^0
* (parsers.spacing^0
* parsers.eof / "")
larsers.inlines = Ct(inlines_t)

local inlines_no_link_t = util.table_copy(inlines_t)
Expand Down
8 changes: 2 additions & 6 deletions lunamark/writer/generic.lua
Original file line number Diff line number Diff line change
Expand Up @@ -255,12 +255,8 @@ function M.new(options)
end

-- (Block) Div with attributes `attr`
function W.div_begin(_)
return {}
end

function W.div_end()
return {}
function W.div(s)
return s
end

--- Inline raw code, with format and
Expand Down
10 changes: 4 additions & 6 deletions lunamark/writer/html.lua
Original file line number Diff line number Diff line change
Expand Up @@ -144,15 +144,13 @@ function M.new(options)
return {opentag, s, closetag}
end

function Html.div_begin(attr)
function Html.div(s, attr)
local class = attr.class and attr.class ~="" and ' class="'..attr.class..'"' or ""
local id = attr.id and ' id="'..attr.id..'"' or ""
local lang = attr.lang and ' lang="'..attr.lang..'"' or ""
return '<div'..id..class..lang..'>'
end

function Html.div_end()
return "</div>"
local opentag = '<div'..id..class..lang..'>'
local closetag = "</div>"
return {opentag, s, closetag}
end

function Html.blockquote(s)
Expand Down
123 changes: 110 additions & 13 deletions tests/lunamark/ext_fenced_divs.test
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
lunamark -Xfenced_divs
lunamark -Xfenced_divs,fenced_code_blocks
<<<
:::
This is not a div
:::

::: {.myclass lang=fr}
::: {.myclass lang=fr}
Some better div
:::
:::

:::: {.level1}
Fenced divs can be nested
Expand All @@ -30,29 +30,126 @@ with another nested div inside
::::
::::

::: {.some-classname}
::: {#some-id}
::: {lang=some}
Divs can be nested directly inside one another without surrounding text.
:::
:::
:::

::: {.level1}
This is the beginning of a div

> This is a blockquote
>
> ::: {.level2-inside-blockquote}
> This is a div inside a blockquote
> :::

:::

::: {#some-identifier}
This is the beginning of a div

> This is a blockquote that contains three colons:
> :::

This is the end of a div
:::

Here are some extra colons that will be shown as just text:

::::

::: {.not-a-div}
here is a code span `
:::
` that contains three colons

::: {.some-classname}
This is a div with a fenced code inside

```
:::
```
:::

::: {.cit custom-style=raggedleft}
I am a _fenced_ div
:::

::: {.cit custom-style=raggedleft}

I am a _fenced_ div

:::

::: {.some-classname}
This is not a div
```
:::
```

::: {.some-classname}
This is not a div
>>>
<p>::: This is not a div :::</p>
<div class="myclass" lang="fr">
<p>Some better div</p>
</div>

<div class="myclass" lang="fr">Some better div</div>

<div class="level1">
<p>Fenced divs can be nested</p>
<div class="level2-more-colons">
<p>This is a nested div</p>
</div>
<div class="level2-more-colons">This is a nested div</div>
<div class="level2-fewer-colons">
<p>This is also a nested div that contains</p>
<p>several paragraphs of text</p>
</div>
several paragraphs of text</div>
<div class="level2-same-number-of-colons">
<p>This is another nested div</p>
<div class="level3">
<p>with another nested div inside</p>
<div class="level3">with another nested div inside</div>
</div>
</div>

<div class="some-classname">
<div id="some-id">
<div lang="some">Divs can be nested directly inside one another without surrounding text.</div>
</div>
</div>

<div class="level1">
<p>This is the beginning of a div</p>
<blockquote>
<p>This is a blockquote</p>
<div class="level2-inside-blockquote">This is a div inside a blockquote</div>
</blockquote>
</div>

<div id="some-identifier">
<p>This is the beginning of a div</p>
<blockquote>
<p>This is a blockquote that contains three colons: :::</p>
</blockquote>
This is the end of a div</div>

<p>Here are some extra colons that will be shown as just text:</p>
<p>::::</p>

<p>::: {.not-a-div} here is a code span <code>:::</code> that contains three colons</p>

<div class="some-classname">
<p>This is a div with a fenced code inside</p>
<pre><code>:::
</code></pre>
</div>

<div class="cit">I am a <em>fenced</em> div</div>

<div class="cit">
<p>I am a <em>fenced</em> div</p>
</div>

::: {.some-classname} This is not a div
<pre><code>:::
</code></pre>

<p>::: {.some-classname} This is not a div</p>

0 comments on commit 9501ccf

Please sign in to comment.