Repository: tlienart/Franklin.jl
Pull request #423 (2020-03-22 16:14:04)

corner case fix for previous PR
This is a patch fix for the previous PR #422 . It's a bit of a silly mistake, the way I was processing these "double brace blocks" was the laziest possible: basically just leave them as such and let the HTML converter deal with them.

The issue is that if it contains an `_` in the name, then possibly `Markdown.html` will introduce `<em>` or `</em>` in there. So in fact you have  to escape these blocks.

This PR does (just) this, unfortunately there is an ambiguity with `LXB` (latex brace blocks) which has to be avoided so this is not a completely trivial PR.


Patch:
diff --git a/Project.toml b/Project.toml
index 0ad1bc67..53fe9930 100644
--- a/Project.toml
+++ b/Project.toml
@@ -1,7 +1,7 @@
 name = "Franklin"
 uuid = "713c75ef-9fc9-4b05-94a9-213340da978e"
 authors = ["Thibaut Lienart <tlienart@me.com>"]
-version = "0.6.10"
+version = "0.6.11"
 
 [deps]
 Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
diff --git a/src/converter/markdown/md.jl b/src/converter/markdown/md.jl
index 81f022d3..1af91f12 100644
--- a/src/converter/markdown/md.jl
+++ b/src/converter/markdown/md.jl
@@ -60,9 +60,12 @@ function convert_md(mds::AS,
 
     # ------------------------------------------------------------------------
     #> 2. Open-Close blocks (OCBlocks)
-    #.> 0
     #>> a. find them
-    blocks, tokens = find_all_ocblocks(tokens, MD_OCB_ALL)
+    blocks, tokens  = find_all_ocblocks(tokens, MD_OCB)
+    toks_pre_ocb    = deepcopy(tokens) # see find_double_brace_blocks
+    #>> a'. find the rest
+    blocks2, tokens = find_all_ocblocks(tokens, vcat(MD_OCB2, MD_OCB_MATH))
+    append!(blocks, blocks2)
     #>> b. merge CODE_BLOCK_IND which are separated by emptyness
     merge_indented_blocks!(blocks, mds)
     #>> b'. only keep indented code blocks which are not contained in larger
@@ -84,6 +87,10 @@ function convert_md(mds::AS,
     #>> c. find latex commands
     lxcoms, _ = find_lxcoms(tokens, lxdefs, braces)
 
+    #> 3[ex]. find double brace blocks, note we do it on pre_ocb tokens
+    # as the step `find_all_ocblocks` possibly found and deactivated {...}.
+    dbb = find_double_brace_blocks(toks_pre_ocb)
+
     # ------------------------------------------------------------------------
     #> 4. Page variable definition (mddefs), also if in config, update lxdefs
     if has_mddefs
@@ -113,7 +120,8 @@ function convert_md(mds::AS,
 
     #> 1. Merge all the blocks that will need further processing before
     # insertion
-    b2insert = merge_blocks(lxcoms, deactivate_divs(blocks), sp_chars, fnrefs)
+    b2insert = merge_blocks(lxcoms, deactivate_divs(blocks),
+                            sp_chars, fnrefs, dbb)
 
     #> 2. Form intermediate markdown + html
     inter_md, mblocks = form_inter_md(mds, b2insert, lxdefs)
diff --git a/src/parser/markdown/tokens.jl b/src/parser/markdown/tokens.jl
index c8246b00..93ac0350 100644
--- a/src/parser/markdown/tokens.jl
+++ b/src/parser/markdown/tokens.jl
@@ -160,8 +160,10 @@ const MD_OCB = [
     OCProto(:H3,              :H3_OPEN,      L_RETURNS,     false),
     OCProto(:H4,              :H4_OPEN,      L_RETURNS,     false),
     OCProto(:H5,              :H5_OPEN,      L_RETURNS,     false),
-    OCProto(:H6,              :H6_OPEN,      L_RETURNS,     false),
-    # ------------------------------------------------------------------
+    OCProto(:H6,              :H6_OPEN,      L_RETURNS,     false)
+    ]
+# the split is due to double brace blocks being allowed in markdown
+const MD_OCB2 = [
     OCProto(:LXB,             :LXB_OPEN,     (:LXB_CLOSE,), true ),
     OCProto(:DIV,             :DIV_OPEN,     (:DIV_CLOSE,), true ),
     ]
@@ -223,7 +225,7 @@ MD_OCB_ALL
 
 Combination of all `MD_OCB` in order.
 """
-const MD_OCB_ALL = vcat(MD_OCB, MD_OCB_MATH) # order matters
+const MD_OCB_ALL = vcat(MD_OCB, MD_OCB2, MD_OCB_MATH) # order matters
 
 
 """
diff --git a/src/parser/markdown/validate.jl b/src/parser/markdown/validate.jl
index 45de70b6..7cc91c3b 100644
--- a/src/parser/markdown/validate.jl
+++ b/src/parser/markdown/validate.jl
@@ -125,3 +125,51 @@ function validate_and_store_link_defs!(blocks::Vector{OCBlock})::Nothing
     isempty(rm) || deleteat!(blocks, rm)
     return nothing
 end
+
+"""
+    find_double_brace_blocks(tokens)
+
+Find `{{` and `}}` then form double brace blocks `{{ ... }}` (in markdown).
+"""
+function find_double_brace_blocks(tokens)
+    # keep track of `{{` and `}}`
+    db_tokens = Token[]
+    # find `{` or `}` if next one exists and is the same
+    # then make the first one a double with an increased substring
+    # and remove the second one
+    head = 1
+    while head < length(tokens)
+        thead = tokens[head]
+        if thead.name in (:LXB_OPEN, :LXB_CLOSE)
+            tcand = tokens[head+1]
+            if tcand.name == thead.name
+                name = ifelse(thead.name == :LXB_OPEN, :DB_OPEN, :DB_CLOSE)
+                ss   = subs(str(thead), from(thead), to(tcand))
+                push!(db_tokens, Token(name, ss, 0))
+                head += 1
+            end
+        end
+        head += 1
+    end
+    # Now go back over the list of tokens and form blocks
+    # these are non-nestable so it's easy.
+    dbb = OCBlock[]
+    head = 1
+    while head < length(db_tokens)
+        thead = db_tokens[head]
+        if thead.name == :DB_OPEN
+            hnext = findfirst(h -> db_tokens[h].name == :DB_CLOSE,
+                              head+1:length(db_tokens))
+            if isnothing(hnext)
+                throw(OCBlockError("I found the opening token '{{' but not " *
+                                   "the corresponding closing token '}}'.",
+                                   context(thead)))
+            end
+            hnext += head
+            push!(dbb, OCBlock(:DOUBLE_BRACE, thead => db_tokens[hnext]))
+            head = hnext
+        end
+        head += 1
+    end
+    return dbb
+end
diff --git a/src/parser/ocblocks.jl b/src/parser/ocblocks.jl
index f022ee03..31a90a0a 100644
--- a/src/parser/ocblocks.jl
+++ b/src/parser/ocblocks.jl
@@ -11,7 +11,7 @@ function find_ocblocks(tokens::Vector{Token}, ocproto::OCProto;
 
     ntokens       = length(tokens)
     active_tokens = ones(Bool, length(tokens))
-    ocblocks      = Vector{OCBlock}()
+    ocblocks      = OCBlock[]
     nestable      = ocproto.nest
 
     # go over active tokens check if there's an opening token


Test patch:
diff --git a/test/parser/markdown-extra.jl b/test/parser/markdown-extra.jl
index 609e838f..4eac5a28 100644
--- a/test/parser/markdown-extra.jl
+++ b/test/parser/markdown-extra.jl
@@ -117,5 +117,11 @@ end
         @def title = "hello"
         {{title}}{{title}}
         """ |> fd2html_td
-    @test isapproxstr(s, "<p>hellohello</p>")
+    @test isapproxstr(s, "hellohello")
+    s = """
+        @def a_b = "hello"
+        @def c_d = "goodbye"
+        {{a_b}}{{c_d}}
+        """ |> fd2html_td
+    @test isapproxstr(s, "hellogoodbye")
 end