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

Do not end paragraph before ::: in fenced divs #431

Merged
merged 13 commits into from
Apr 3, 2024
Merged
3 changes: 3 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ Fixes:
sponsored by @istqborg)
- Comply with CommonMark 0.31.2. (#416, 40b516ee, de8d137d,
contributed by @lostenderman)
- Do not end a paragraph before a `:::` in fenced divs.
(#407, lostenderman/markdown#157, #427, #428, lostenderman/markdown#158,
#431, contributed by @lostenderman)

Documentation:

Expand Down
113 changes: 88 additions & 25 deletions markdown.dtx
Original file line number Diff line number Diff line change
Expand Up @@ -26202,14 +26202,18 @@ end
%
% \end{markdown}
% \begin{macrocode}
local function traverse_indent(s, i, indent_table, is_optional, is_blank)
local function traverse_indent(s, i, indent_table, is_optional, is_blank, current_line_indents)
local new_index = i

local preceding_indentation = 0
local current_trail = {}

local blank_starter = left_blank_starter(indent_table)

if current_line_indents == nil then
current_line_indents = {}
end

for index = 1,#indent_table.indents do
local value = indent_table.indents[index]
local pattern = decode_pattern(value.name)
Expand All @@ -26219,10 +26223,10 @@ local function traverse_indent(s, i, indent_table, is_optional, is_blank)
if new_indent_info == nil then
local blankline_end = lpeg.match(Ct(parsers.blankline * Cg(Cp(), "pos")), s, new_index)
if is_optional or not indent_table.ignore_blockquote_blank or not blankline_end then
return is_optional, new_index, current_trail
return is_optional, new_index, current_trail, current_line_indents
end

return traverse_indent(s, tonumber(blankline_end.pos), indent_table, is_optional, is_blank)
return traverse_indent(s, tonumber(blankline_end.pos), indent_table, is_optional, is_blank, current_line_indents)
end

local raw_last_trail = new_indent_info[1]
Expand All @@ -26241,7 +26245,7 @@ local function traverse_indent(s, i, indent_table, is_optional, is_blank)

if next(current_trail) ~= nil then
if not space_only and current_trail.is_code then
return is_optional, new_index, current_trail
return is_optional, new_index, current_trail, current_line_indents
end
if current_trail.internal_remainder ~= nil then
raw_last_trail = current_trail.internal_remainder
Expand Down Expand Up @@ -26273,7 +26277,7 @@ local function traverse_indent(s, i, indent_table, is_optional, is_blank)
local sp = process_starter_spacing(total_indent_level, spacing_to_process, minimum, left_strip_length)

if space_only and not sp.is_minimum then
return is_optional or (is_blank and blank_starter <= index), new_index, current_trail
return is_optional or (is_blank and blank_starter <= index), new_index, current_trail, current_line_indents
end

local indent_length = raw_last_trail_length + delimiter_length + sp.left_total_stripped
Expand All @@ -26287,10 +26291,12 @@ local function traverse_indent(s, i, indent_table, is_optional, is_blank)

current_trail = {is_code=sp.is_code, remainder=sp.remainder, internal_remainder=sp.minimum_remainder,
total_length=sp.total_length, full_remainder=sp.full_remainder}

current_line_indents[#current_line_indents + 1] = new_indent_info
new_index = next_index
end

return true, new_index, current_trail
return true, new_index, current_trail, current_line_indents
end

% \end{macrocode}
Expand Down Expand Up @@ -26378,9 +26384,11 @@ local function check_continuation_indentation(s, i, indent_table, is_optional, i
return true
end

local passes, new_index, current_trail = traverse_indent(s, i, indent_table, is_optional, is_blank)
local passes, new_index, current_trail, current_line_indents =
traverse_indent(s, i, indent_table, is_optional, is_blank)

if passes then
indent_table.current_line_indents = current_line_indents
indent_table = add_trail(indent_table, current_trail)
return new_index, indent_table
end
Expand Down Expand Up @@ -30946,24 +30954,57 @@ M.extensions.fenced_divs = function(blank_before_div_fence)
% \par
% \begin{markdown}
%
% Initialize a named group named `div_level` for tracking how deep we are
% nested in divs.
% Initialize a named group named `fenced_div_level` for tracking how deep
% we are nested in divs and the named group `fenced_div_num_opening_indents`
% for tracking the indent of the starting div fence. The former named group
% is immutable and should roll back properly when we fail to match a fenced
% div. The latter is mutable and may contain items from unsuccessful matches
% on top. However, we always know how many items at the head of the latter we
% can trust by consulting the former.
%
% \end{markdown}
% \begin{macrocode}
self.initialize_named_group("div_level", "0")
self.initialize_named_group("fenced_div_level", "0")
self.initialize_named_group("fenced_div_num_opening_indents")

local function increment_div_level()
local function push_indent_table(s, i, indent_table, -- luacheck: ignore s i
fenced_div_num_opening_indents, fenced_div_level)
fenced_div_level = tonumber(fenced_div_level) + 1
local num_opening_indents = 0
if indent_table.indents ~= nil then
num_opening_indents = #indent_table.indents
end
fenced_div_num_opening_indents[fenced_div_level] = num_opening_indents
return true, fenced_div_num_opening_indents
end

local function increment_level(s, i, fenced_div_level) -- luacheck: ignore s i
fenced_div_level = tonumber(fenced_div_level) + 1
return true, tostring(fenced_div_level)
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
return Cg( Cmt( Cb("indent_info")
* Cb("fenced_div_num_opening_indents")
* Cb("fenced_div_level"), push_indent_table)
, "fenced_div_num_opening_indents")
* Cg( Cmt( Cb("fenced_div_level"), increment_level)
, "fenced_div_level")
end

local function decrement_div_level()
local function pop_indent_table(s, i, fenced_div_indent_table, fenced_div_level) -- luacheck: ignore s i
fenced_div_level = tonumber(fenced_div_level)
fenced_div_indent_table[fenced_div_level] = nil
return true, tostring(fenced_div_level - 1)
end

return Cg( Cmt(Cb("div_level"), update_div_level)
, "div_level")
return Cg( Cmt( Cb("fenced_div_num_opening_indents")
* Cb("fenced_div_level"), pop_indent_table)
, "fenced_div_level")
end


local non_fenced_div_block = parsers.check_minimal_indent * V("Block")
- parsers.check_minimal_indent_and_trail * fenced_div_end

Expand Down Expand Up @@ -30998,11 +31039,12 @@ M.extensions.fenced_divs = function(blank_before_div_fence)
return attr
end
/ writer.div_begin
* increment_div_level(1)
* increment_div_level()
* parsers.skipblanklines
* Ct(content_loop)
* parsers.minimally_indented_blank^0
* parsers.check_minimal_indent_and_trail * fenced_div_end * increment_div_level(-1)
* parsers.check_minimal_indent_and_trail * fenced_div_end
* decrement_div_level()
* (Cc("") / writer.div_end)

self.insert_pattern("Block after Verbatim",
Expand All @@ -31016,17 +31058,38 @@ M.extensions.fenced_divs = function(blank_before_div_fence)
%
% If the `blank_before_div_fence` parameter is `false`, we will have the
% closing div at the beginning of a line break the current paragraph if
% we are currently nested in a div.
% we are currently nested in a div and the indentation matches the opening
% div fence.
%
% \end{markdown}
% \begin{macrocode}
local function check_div_level(s, i, current_level) -- luacheck: ignore s i
current_level = tonumber(current_level)
return current_level > 0
local function is_inside_div()
local function check_div_level(s, i, fenced_div_level) -- luacheck: ignore s i
fenced_div_level = tonumber(fenced_div_level)
return fenced_div_level > 0
end

return Cmt(Cb("fenced_div_level"), check_div_level)
end

local function check_indent()
local function compare_indent(s, i, indent_table, -- luacheck: ignore s i
fenced_div_num_opening_indents, fenced_div_level)
fenced_div_level = tonumber(fenced_div_level)
local num_current_indents = (indent_table.current_line_indents ~= nil and
#indent_table.current_line_indents) or 0
local num_opening_indents = fenced_div_num_opening_indents[fenced_div_level]
return num_current_indents == num_opening_indents
end

return Cmt( Cb("indent_info")
* Cb("fenced_div_num_opening_indents")
* Cb("fenced_div_level"), compare_indent)
end

local is_inside_div = Cmt(Cb("div_level"), check_div_level)
local fencestart = is_inside_div * fenced_div_end
local fencestart = is_inside_div()
* fenced_div_end
* check_indent()

if not blank_before_div_fence then
self.update_rule("EndlineExceptions", function(previous_pattern)
Expand Down
2 changes: 1 addition & 1 deletion tests/testfiles/lunamark-markdown/fenced-divs.test
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ BEGIN fencedDivAttributeContext
attributeIdentifier: some-identifier
interblockSeparator
blockQuoteBegin
paragraphSeparator
softLineBreak
blockQuoteEnd
interblockSeparator
END fencedDivAttributeContext
Expand Down
Loading