From ec6c3a24b0e7dba0024aebd956dd04a92c98a30a Mon Sep 17 00:00:00 2001 From: Thomas Leonard Date: Sun, 24 Oct 2021 17:38:20 +0100 Subject: [PATCH 01/11] Make definitions block-level This allows multiple paragraphs in a definition. See examples at https://michelf.ca/projects/php-markdown/extra/#def-list --- src/ast.ml | 12 ++++---- src/block.ml | 53 +++++++++++++++++++++++++-------- src/html.ml | 9 ++++-- src/omd.mli | 6 ++-- src/parser.ml | 8 ++--- src/sexp.ml | 4 +-- tests/def_list.md | 75 ++++++++++++++++++++++++++++++++++++++++++++--- 7 files changed, 134 insertions(+), 33 deletions(-) diff --git a/src/ast.ml b/src/ast.ml index 8495e14a..e4462767 100644 --- a/src/ast.ml +++ b/src/ast.ml @@ -22,14 +22,14 @@ end module MakeBlock (I : T) = struct type 'attr def_elt = { term : 'attr I.t - ; defs : 'attr I.t list + ; defs : 'attr block list list } (* A value of type 'attr is present in all variants of this type. We use it to associate extra information to each node in the AST. In the common case, the attributes type defined above is used. We might eventually have an alternative function to parse blocks while keeping concrete information such as source location and we'll use it for that as well. *) - type 'attr block = + and 'attr block = | Paragraph of 'attr * 'attr I.t | List of 'attr * list_type * list_spacing * 'attr block list list | Blockquote of 'attr * 'attr block list @@ -37,7 +37,7 @@ module MakeBlock (I : T) = struct | Heading of 'attr * int * 'attr I.t | Code_block of 'attr * string * string | Html_block of 'attr * string - | Definition_list of 'attr * 'attr def_elt list + | Definition_list of 'attr * list_spacing * 'attr def_elt list end type 'attr link = @@ -83,11 +83,11 @@ module MakeMapper (Src : T) (Dst : T) = struct | Blockquote (attr, xs) -> Blockquote (attr, List.map (map f) xs) | Thematic_break attr -> Thematic_break attr | Heading (attr, level, text) -> Heading (attr, level, f text) - | Definition_list (attr, l) -> + | Definition_list (attr, sp, l) -> let f { SrcBlock.term; defs } = - { DstBlock.term = f term; defs = List.map f defs } + { DstBlock.term = f term; defs = List.map (List.map (map f)) defs } in - Definition_list (attr, List.map f l) + Definition_list (attr, sp, List.map f l) | Code_block (attr, label, code) -> Code_block (attr, label, code) | Html_block (attr, x) -> Html_block (attr, x) end diff --git a/src/block.ml b/src/block.ml index cda413bb..31733c37 100644 --- a/src/block.ml +++ b/src/block.ml @@ -21,7 +21,12 @@ module Pre = struct * attributes | Rindented_code of string list | Rhtml of Parser.html_kind * string list - | Rdef_list of string * string list + | Rdef_list of + string + * int + * bool (* true if we've seen a blank line - disables lazy mode) *) + * attributes Raw.block list list + * t | Rempty and t = @@ -69,13 +74,25 @@ module Pre = struct Code_block (attr, label, "") :: blocks | Rfenced_code (_, _, _kind, (label, _other), l, attr) -> Code_block (attr, label, concat l) :: blocks - | Rdef_list (term, defs) -> - let l, blocks = + | Rdef_list (term, _, _, defs, state) -> + let def = finish state in + let defs = def :: defs in + let this_sp = + match def with + | [Paragraph _] -> Tight + | _ -> Loose + in + let l, sp, blocks = match blocks with - | Definition_list (_, l) :: b -> (l, b) - | b -> ([], b) + | Definition_list (_, sp, l) :: b -> + let sp = match sp, this_sp with + | Tight, Tight -> Tight + | Loose, _ | _, Loose -> Loose + in + (l, sp, b) + | b -> ([], this_sp, b) in - Definition_list ([], l @ [ { term; defs = List.rev defs } ]) :: blocks + Definition_list ([], sp, l @ [ { term; defs = List.rev defs } ]) :: blocks | Rindented_code l -> (* TODO: trim from the right *) let rec loop = function @@ -116,10 +133,10 @@ module Pre = struct } | Rempty, (Lsetext_heading _ | Lparagraph | Ldef_list _) -> { blocks; next = Rparagraph [ Sub.to_string s ] } - | Rparagraph [ h ], Ldef_list def -> - { blocks; next = Rdef_list (h, [ def ]) } - | Rdef_list (term, defs), Ldef_list def -> - { blocks; next = Rdef_list (term, def :: defs) } + | Rparagraph [ h ], Ldef_list (indent, def) -> + { blocks; next = Rdef_list (h, indent, false, [], process empty (Sub.of_string def)) } + | Rdef_list (term, _, false, defs, state), Ldef_list (indent, def) -> + { blocks; next = Rdef_list (term, indent, false, finish state :: defs, process empty (Sub.of_string def)) } | Rparagraph _, Llist_item ((Ordered (1, _) | Bullet _), _, s1) when not (Parser.is_empty (Parser.P.of_string (Sub.to_string s1))) -> process { blocks = close { blocks; next }; next = Rempty } s @@ -147,9 +164,21 @@ module Pre = struct { blocks ; next = Rfenced_code (ind, num, q, info, Sub.to_string s :: lines, a) } - | Rdef_list (term, d :: defs), Lparagraph -> + | Rdef_list (term, ind, _, defs, state), Lempty -> + { blocks + ; next = Rdef_list (term, ind, true, defs, process state s) + } + | Rdef_list (term, ind, seen_empty, defs, state), _ + when Parser.indent s >= ind -> + let s = Sub.offset ind s in + let state = process state s in + { blocks + ; next = Rdef_list (term, ind, seen_empty, defs, state) + } + | Rdef_list (term, ind, false, defs, state), Lparagraph -> (* Lazy wrapping *) + let state = process state s in { blocks - ; next = Rdef_list (term, (d ^ "\n" ^ Sub.to_string s) :: defs) + ; next = Rdef_list (term, ind, false, defs, state) } | Rdef_list _, _ -> process { blocks = close { blocks; next }; next = Rempty } s diff --git a/src/html.ml b/src/html.ml index c5bd6212..f7d9cf3a 100644 --- a/src/html.ml +++ b/src/html.ml @@ -196,11 +196,16 @@ let rec block = function | _ -> "p" in elt Block name attr (Some (inline text)) - | Definition_list (attr, l) -> + | Definition_list (attr, sp, l) -> + let block' t = + match (t, sp) with + | Paragraph (_, t), Tight -> concat (inline t) nl + | _ -> block t + in let f { term; defs } = concat (elt Block "dt" [] (Some (inline term))) - (concat_map (fun s -> elt Block "dd" [] (Some (inline s))) defs) + (concat_map (fun s -> elt Block "dd" [] (Some (concat nl (concat_map block' s)))) defs) in elt Block "dl" attr (Some (concat_map f l)) diff --git a/src/omd.mli b/src/omd.mli index f0f055bd..1ac57344 100644 --- a/src/omd.mli +++ b/src/omd.mli @@ -30,10 +30,10 @@ and 'attr inline = type 'attr def_elt = { term : 'attr inline - ; defs : 'attr inline list + ; defs : 'attr block list list } -type 'attr block = +and 'attr block = | Paragraph of 'attr * 'attr inline | List of 'attr * list_type * list_spacing * 'attr block list list | Blockquote of 'attr * 'attr block list @@ -41,7 +41,7 @@ type 'attr block = | Heading of 'attr * int * 'attr inline | Code_block of 'attr * string * string | Html_block of 'attr * string - | Definition_list of 'attr * 'attr def_elt list + | Definition_list of 'attr * list_spacing * 'attr def_elt list type doc = attributes block list (** A markdown document *) diff --git a/src/parser.ml b/src/parser.ml index bc9f4d7e..39b69dc8 100644 --- a/src/parser.ml +++ b/src/parser.ml @@ -311,7 +311,7 @@ type t = | Lhtml of bool * html_kind | Llist_item of list_type * int * Sub.t | Lparagraph - | Ldef_list of string + | Ldef_list of int * string let sp3 s = match Sub.head s with @@ -959,11 +959,11 @@ let tag_string s = in loop (ws s) -let def_list s = +let def_list ind s = let s = Sub.tail s in match Sub.head s with | Some (' ' | '\t' | '\010' .. '\013') -> - Ldef_list (String.trim (Sub.to_string s)) + Ldef_list (ind + 2, String.trim (Sub.to_string s)) | _ -> raise Fail let indented_code ind s = @@ -992,7 +992,7 @@ let parse s0 = | Some '*' -> (thematic_break ||| unordered_list_item ind) s | Some '+' -> unordered_list_item ind s | Some '0' .. '9' -> ordered_list_item ind s - | Some ':' -> def_list s + | Some ':' -> def_list ind s | Some _ -> (blank ||| indented_code ind) s | None -> Lempty diff --git a/src/sexp.ml b/src/sexp.ml index 86caba9a..1469c5d4 100644 --- a/src/sexp.ml +++ b/src/sexp.ml @@ -39,13 +39,13 @@ let rec block = function List [ Atom "heading"; Atom (string_of_int level); inline text ] | Code_block (_, info, _) -> List [ Atom "code-block"; Atom info ] | Html_block (_, s) -> List [ Atom "html"; Atom s ] - | Definition_list (_, l) -> + | Definition_list (_, _, l) -> List [ Atom "def-list" ; List (List.map (fun elt -> - List [ inline elt.term; List (List.map inline elt.defs) ]) + List [ inline elt.term; List (List.map (fun def -> List (List.map block def)) elt.defs) ]) l) ] diff --git a/tests/def_list.md b/tests/def_list.md index 10d617c5..41edb9c0 100644 --- a/tests/def_list.md +++ b/tests/def_list.md @@ -10,13 +10,80 @@ Second Term which is multiline : This is not a correct definition list + +# With multiple paragraphs + +First Term +: This is the definition of the first term. + +Second Term +: This is one paragraph of the second term. + + This is another paragraph of the second term. + +# Nesting + +Root +: Level one + + Nested + : Level two + ``` + code + ``` + + More + : Level three + +Root again +: Level one again + .
First Term
-
This is the definition of the first term.
+
+This is the definition of the first term. +
Second Term
-
This is one definition of the second term.
-
This is another definition of the second term. -which is multiline
+
+This is one definition of the second term. +
+
+This is another definition of the second term. +which is multiline +

: This is not a correct definition list

+

With multiple paragraphs

+
First Term
+
+

This is the definition of the first term.

+
+
Second Term
+
+

This is one paragraph of the second term.

+

This is another paragraph of the second term.

+
+
+

Nesting

+
Root
+
+

Level one

+
Nested
+
+

Level two

+
code
+
+
More
+
+Level three +
+
+
+
+
+
Root again
+
+

Level one again

+
+
```````````````````````````````` From 4ae638aae329b4b650a63c2582b832e810e2e210 Mon Sep 17 00:00:00 2001 From: Shon Feder Date: Sun, 31 Oct 2021 17:28:51 -0400 Subject: [PATCH 02/11] Make principle type first --- src/ast.ml | 13 +++++++------ src/omd.mli | 13 +++++++------ 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/ast.ml b/src/ast.ml index e4462767..1b52ad27 100644 --- a/src/ast.ml +++ b/src/ast.ml @@ -20,16 +20,11 @@ module type T = sig end module MakeBlock (I : T) = struct - type 'attr def_elt = - { term : 'attr I.t - ; defs : 'attr block list list - } - (* A value of type 'attr is present in all variants of this type. We use it to associate extra information to each node in the AST. In the common case, the attributes type defined above is used. We might eventually have an alternative function to parse blocks while keeping concrete information such as source location and we'll use it for that as well. *) - and 'attr block = + type 'attr block = | Paragraph of 'attr * 'attr I.t | List of 'attr * list_type * list_spacing * 'attr block list list | Blockquote of 'attr * 'attr block list @@ -38,6 +33,12 @@ module MakeBlock (I : T) = struct | Code_block of 'attr * string * string | Html_block of 'attr * string | Definition_list of 'attr * list_spacing * 'attr def_elt list + + and 'attr def_elt = + { term : 'attr I.t + ; defs : 'attr block list list + } + end type 'attr link = diff --git a/src/omd.mli b/src/omd.mli index 1ac57344..32cf91a5 100644 --- a/src/omd.mli +++ b/src/omd.mli @@ -28,12 +28,7 @@ and 'attr inline = | Image of 'attr * 'attr link | Html of 'attr * string -type 'attr def_elt = - { term : 'attr inline - ; defs : 'attr block list list - } - -and 'attr block = +type 'attr block = | Paragraph of 'attr * 'attr inline | List of 'attr * list_type * list_spacing * 'attr block list list | Blockquote of 'attr * 'attr block list @@ -43,6 +38,12 @@ and 'attr block = | Html_block of 'attr * string | Definition_list of 'attr * list_spacing * 'attr def_elt list +and 'attr def_elt = + { term : 'attr inline + ; defs : 'attr block list list + } + + type doc = attributes block list (** A markdown document *) From a8d88a866c26d323f20a363bd85dab082f06ee59 Mon Sep 17 00:00:00 2001 From: Shon Feder Date: Sun, 31 Oct 2021 17:31:22 -0400 Subject: [PATCH 03/11] Fix formatting --- src/ast.ml | 1 - src/block.ml | 44 +++++++++++++++++++++++++++----------------- src/html.ml | 5 ++++- src/omd.mli | 1 - src/sexp.ml | 8 +++++++- 5 files changed, 38 insertions(+), 21 deletions(-) diff --git a/src/ast.ml b/src/ast.ml index 1b52ad27..bdcdfa0a 100644 --- a/src/ast.ml +++ b/src/ast.ml @@ -38,7 +38,6 @@ module MakeBlock (I : T) = struct { term : 'attr I.t ; defs : 'attr block list list } - end type 'attr link = diff --git a/src/block.ml b/src/block.ml index 31733c37..6d5c40c6 100644 --- a/src/block.ml +++ b/src/block.ml @@ -24,7 +24,7 @@ module Pre = struct | Rdef_list of string * int - * bool (* true if we've seen a blank line - disables lazy mode) *) + * bool (* true if we've seen a blank line - disables lazy mode) *) * attributes Raw.block list list * t | Rempty @@ -79,20 +79,24 @@ module Pre = struct let defs = def :: defs in let this_sp = match def with - | [Paragraph _] -> Tight + | [ Paragraph _ ] -> Tight | _ -> Loose in let l, sp, blocks = match blocks with | Definition_list (_, sp, l) :: b -> - let sp = match sp, this_sp with + let sp = + match (sp, this_sp) with | Tight, Tight -> Tight - | Loose, _ | _, Loose -> Loose + | Loose, _ + | _, Loose -> + Loose in (l, sp, b) | b -> ([], this_sp, b) in - Definition_list ([], sp, l @ [ { term; defs = List.rev defs } ]) :: blocks + Definition_list ([], sp, l @ [ { term; defs = List.rev defs } ]) + :: blocks | Rindented_code l -> (* TODO: trim from the right *) let rec loop = function @@ -134,9 +138,20 @@ module Pre = struct | Rempty, (Lsetext_heading _ | Lparagraph | Ldef_list _) -> { blocks; next = Rparagraph [ Sub.to_string s ] } | Rparagraph [ h ], Ldef_list (indent, def) -> - { blocks; next = Rdef_list (h, indent, false, [], process empty (Sub.of_string def)) } + { blocks + ; next = + Rdef_list (h, indent, false, [], process empty (Sub.of_string def)) + } | Rdef_list (term, _, false, defs, state), Ldef_list (indent, def) -> - { blocks; next = Rdef_list (term, indent, false, finish state :: defs, process empty (Sub.of_string def)) } + { blocks + ; next = + Rdef_list + ( term + , indent + , false + , finish state :: defs + , process empty (Sub.of_string def) ) + } | Rparagraph _, Llist_item ((Ordered (1, _) | Bullet _), _, s1) when not (Parser.is_empty (Parser.P.of_string (Sub.to_string s1))) -> process { blocks = close { blocks; next }; next = Rempty } s @@ -165,21 +180,16 @@ module Pre = struct ; next = Rfenced_code (ind, num, q, info, Sub.to_string s :: lines, a) } | Rdef_list (term, ind, _, defs, state), Lempty -> - { blocks - ; next = Rdef_list (term, ind, true, defs, process state s) - } + { blocks; next = Rdef_list (term, ind, true, defs, process state s) } | Rdef_list (term, ind, seen_empty, defs, state), _ when Parser.indent s >= ind -> let s = Sub.offset ind s in let state = process state s in - { blocks - ; next = Rdef_list (term, ind, seen_empty, defs, state) - } - | Rdef_list (term, ind, false, defs, state), Lparagraph -> (* Lazy wrapping *) + { blocks; next = Rdef_list (term, ind, seen_empty, defs, state) } + | Rdef_list (term, ind, false, defs, state), Lparagraph -> + (* Lazy wrapping *) let state = process state s in - { blocks - ; next = Rdef_list (term, ind, false, defs, state) - } + { blocks; next = Rdef_list (term, ind, false, defs, state) } | Rdef_list _, _ -> process { blocks = close { blocks; next }; next = Rempty } s | Rindented_code lines, Lindented_code s -> diff --git a/src/html.ml b/src/html.ml index f7d9cf3a..a2d9f3f2 100644 --- a/src/html.ml +++ b/src/html.ml @@ -205,7 +205,10 @@ let rec block = function let f { term; defs } = concat (elt Block "dt" [] (Some (inline term))) - (concat_map (fun s -> elt Block "dd" [] (Some (concat nl (concat_map block' s)))) defs) + (concat_map + (fun s -> + elt Block "dd" [] (Some (concat nl (concat_map block' s)))) + defs) in elt Block "dl" attr (Some (concat_map f l)) diff --git a/src/omd.mli b/src/omd.mli index 32cf91a5..63471fc3 100644 --- a/src/omd.mli +++ b/src/omd.mli @@ -43,7 +43,6 @@ and 'attr def_elt = ; defs : 'attr block list list } - type doc = attributes block list (** A markdown document *) diff --git a/src/sexp.ml b/src/sexp.ml index 1469c5d4..6d87f839 100644 --- a/src/sexp.ml +++ b/src/sexp.ml @@ -45,7 +45,13 @@ let rec block = function ; List (List.map (fun elt -> - List [ inline elt.term; List (List.map (fun def -> List (List.map block def)) elt.defs) ]) + List + [ inline elt.term + ; List + (List.map + (fun def -> List (List.map block def)) + elt.defs) + ]) l) ] From 34137b872155cd26dd604a936ebb8db42895952d Mon Sep 17 00:00:00 2001 From: Shon Feder Date: Sun, 31 Oct 2021 18:04:26 -0400 Subject: [PATCH 04/11] Reorder constructors The lists want to... be with the lists --- src/ast.ml | 2 +- src/omd.mli | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ast.ml b/src/ast.ml index bdcdfa0a..8536770f 100644 --- a/src/ast.ml +++ b/src/ast.ml @@ -27,12 +27,12 @@ module MakeBlock (I : T) = struct type 'attr block = | Paragraph of 'attr * 'attr I.t | List of 'attr * list_type * list_spacing * 'attr block list list + | Definition_list of 'attr * list_spacing * 'attr def_elt list | Blockquote of 'attr * 'attr block list | Thematic_break of 'attr | Heading of 'attr * int * 'attr I.t | Code_block of 'attr * string * string | Html_block of 'attr * string - | Definition_list of 'attr * list_spacing * 'attr def_elt list and 'attr def_elt = { term : 'attr I.t diff --git a/src/omd.mli b/src/omd.mli index 63471fc3..7142b7d3 100644 --- a/src/omd.mli +++ b/src/omd.mli @@ -31,12 +31,12 @@ and 'attr inline = type 'attr block = | Paragraph of 'attr * 'attr inline | List of 'attr * list_type * list_spacing * 'attr block list list + | Definition_list of 'attr * list_spacing * 'attr def_elt list | Blockquote of 'attr * 'attr block list | Thematic_break of 'attr | Heading of 'attr * int * 'attr inline | Code_block of 'attr * string * string | Html_block of 'attr * string - | Definition_list of 'attr * list_spacing * 'attr def_elt list and 'attr def_elt = { term : 'attr inline From 1ddf5231e937ef41be96cd67c6a88716b8bf69bc Mon Sep 17 00:00:00 2001 From: Shon Feder Date: Sun, 31 Oct 2021 18:04:54 -0400 Subject: [PATCH 05/11] Replace big tuple with record --- src/block.ml | 107 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 76 insertions(+), 31 deletions(-) diff --git a/src/block.ml b/src/block.ml index 6d5c40c6..b04b0e91 100644 --- a/src/block.ml +++ b/src/block.ml @@ -2,7 +2,12 @@ open Ast module Sub = Parser.Sub module Pre = struct - type container = + type t = + { blocks : attributes Raw.block list + ; next : container + } + + and container = | Rblockquote of t | Rlist of list_type @@ -21,17 +26,15 @@ module Pre = struct * attributes | Rindented_code of string list | Rhtml of Parser.html_kind * string list - | Rdef_list of - string - * int - * bool (* true if we've seen a blank line - disables lazy mode) *) - * attributes Raw.block list list - * t + | Rdef_list of def_list_container | Rempty - and t = - { blocks : attributes Raw.block list - ; next : container + and def_list_container = + { term : string + ; defs : attributes Raw.block list list + ; indent : int + ; empty_line_seen : bool + ; state : t } let concat l = String.concat "\n" (List.rev l) ^ "\n" @@ -74,9 +77,10 @@ module Pre = struct Code_block (attr, label, "") :: blocks | Rfenced_code (_, _, _kind, (label, _other), l, attr) -> Code_block (attr, label, concat l) :: blocks - | Rdef_list (term, _, _, defs, state) -> - let def = finish state in - let defs = def :: defs in + (* | Rdef_list (term, _, _, defs, state) -> *) + | Rdef_list deflist -> + let def = finish deflist.state in + let defs = def :: deflist.defs in let this_sp = match def with | [ Paragraph _ ] -> Tight @@ -95,7 +99,8 @@ module Pre = struct (l, sp, b) | b -> ([], this_sp, b) in - Definition_list ([], sp, l @ [ { term; defs = List.rev defs } ]) + Definition_list + ([], sp, l @ [ { term = deflist.term; defs = List.rev defs } ]) :: blocks | Rindented_code l -> (* TODO: trim from the right *) @@ -137,20 +142,31 @@ module Pre = struct } | Rempty, (Lsetext_heading _ | Lparagraph | Ldef_list _) -> { blocks; next = Rparagraph [ Sub.to_string s ] } + (* | Rparagraph [ h ], Ldef_list (indent, def) -> *) | Rparagraph [ h ], Ldef_list (indent, def) -> { blocks ; next = - Rdef_list (h, indent, false, [], process empty (Sub.of_string def)) + (* Rdef_list (h, indent, false, [], process empty (Sub.of_string def)) *) + Rdef_list + { term = h + ; indent + ; empty_line_seen = false + ; defs = [] + ; state = process empty (Sub.of_string def) + } } - | Rdef_list (term, _, false, defs, state), Ldef_list (indent, def) -> + (* | Rdef_list (term, _, false, defs, state), Ldef_list (indent, def) -> *) + | ( Rdef_list ({ empty_line_seen = false; _ } as deflist) + , Ldef_list (indent, def) ) -> { blocks ; next = Rdef_list - ( term - , indent - , false - , finish state :: defs - , process empty (Sub.of_string def) ) + { term = deflist.term + ; indent + ; empty_line_seen = deflist.empty_line_seen + ; defs = finish deflist.state :: deflist.defs + ; state = process empty (Sub.of_string def) + } } | Rparagraph _, Llist_item ((Ordered (1, _) | Bullet _), _, s1) when not (Parser.is_empty (Parser.P.of_string (Sub.to_string s1))) -> @@ -179,17 +195,46 @@ module Pre = struct { blocks ; next = Rfenced_code (ind, num, q, info, Sub.to_string s :: lines, a) } - | Rdef_list (term, ind, _, defs, state), Lempty -> - { blocks; next = Rdef_list (term, ind, true, defs, process state s) } - | Rdef_list (term, ind, seen_empty, defs, state), _ - when Parser.indent s >= ind -> - let s = Sub.offset ind s in - let state = process state s in - { blocks; next = Rdef_list (term, ind, seen_empty, defs, state) } - | Rdef_list (term, ind, false, defs, state), Lparagraph -> + (* | Rdef_list (term, ind, _, defs, state), Lempty -> *) + | Rdef_list deflist, Lempty -> + { blocks + ; next = + Rdef_list + { term = deflist.term + ; indent = deflist.indent + ; empty_line_seen = true + ; defs = deflist.defs + ; state = process deflist.state s + } + } + (* | Rdef_list (term, ind, seen_empty, defs, state), _ *) + | Rdef_list deflist, _ when Parser.indent s >= deflist.indent -> + let s = Sub.offset deflist.indent s in + let state = process deflist.state s in + { blocks + ; next = + Rdef_list + { term = deflist.term + ; indent = deflist.indent + ; empty_line_seen = deflist.empty_line_seen + ; defs = deflist.defs + ; state + } + } + (* | Rdef_list (term, ind, false, defs, state), Lparagraph -> *) + | Rdef_list ({ empty_line_seen = false; _ } as deflist), Lparagraph -> (* Lazy wrapping *) - let state = process state s in - { blocks; next = Rdef_list (term, ind, false, defs, state) } + let state = process deflist.state s in + { blocks + ; next = + Rdef_list + { term = deflist.term + ; indent = deflist.indent + ; empty_line_seen = false + ; defs = deflist.defs + ; state + } + } | Rdef_list _, _ -> process { blocks = close { blocks; next }; next = Rempty } s | Rindented_code lines, Lindented_code s -> From d50b1eed6e901a10a7a69db752e9d073178ea561 Mon Sep 17 00:00:00 2001 From: Shon Feder Date: Sun, 31 Oct 2021 19:48:08 -0400 Subject: [PATCH 06/11] Group all matches on Rdef_list together --- src/block.ml | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/src/block.ml b/src/block.ml index b04b0e91..cf1fc53c 100644 --- a/src/block.ml +++ b/src/block.ml @@ -77,7 +77,6 @@ module Pre = struct Code_block (attr, label, "") :: blocks | Rfenced_code (_, _, _kind, (label, _other), l, attr) -> Code_block (attr, label, concat l) :: blocks - (* | Rdef_list (term, _, _, defs, state) -> *) | Rdef_list deflist -> let def = finish deflist.state in let defs = def :: deflist.defs in @@ -155,19 +154,6 @@ module Pre = struct ; state = process empty (Sub.of_string def) } } - (* | Rdef_list (term, _, false, defs, state), Ldef_list (indent, def) -> *) - | ( Rdef_list ({ empty_line_seen = false; _ } as deflist) - , Ldef_list (indent, def) ) -> - { blocks - ; next = - Rdef_list - { term = deflist.term - ; indent - ; empty_line_seen = deflist.empty_line_seen - ; defs = finish deflist.state :: deflist.defs - ; state = process empty (Sub.of_string def) - } - } | Rparagraph _, Llist_item ((Ordered (1, _) | Bullet _), _, s1) when not (Parser.is_empty (Parser.P.of_string (Sub.to_string s1))) -> process { blocks = close { blocks; next }; next = Rempty } s @@ -195,7 +181,18 @@ module Pre = struct { blocks ; next = Rfenced_code (ind, num, q, info, Sub.to_string s :: lines, a) } - (* | Rdef_list (term, ind, _, defs, state), Lempty -> *) + | ( Rdef_list ({ empty_line_seen = false; _ } as deflist) + , Ldef_list (indent, def) ) -> + { blocks + ; next = + Rdef_list + { term = deflist.term + ; indent + ; empty_line_seen = deflist.empty_line_seen + ; defs = finish deflist.state :: deflist.defs + ; state = process empty (Sub.of_string def) + } + } | Rdef_list deflist, Lempty -> { blocks ; next = @@ -207,7 +204,6 @@ module Pre = struct ; state = process deflist.state s } } - (* | Rdef_list (term, ind, seen_empty, defs, state), _ *) | Rdef_list deflist, _ when Parser.indent s >= deflist.indent -> let s = Sub.offset deflist.indent s in let state = process deflist.state s in @@ -221,7 +217,6 @@ module Pre = struct ; state } } - (* | Rdef_list (term, ind, false, defs, state), Lparagraph -> *) | Rdef_list ({ empty_line_seen = false; _ } as deflist), Lparagraph -> (* Lazy wrapping *) let state = process deflist.state s in From 18e76744d37a782236f631140df941c8818a2d90 Mon Sep 17 00:00:00 2001 From: Shon Feder Date: Sun, 31 Oct 2021 20:07:43 -0400 Subject: [PATCH 07/11] Clean up records --- src/block.ml | 31 ++++++------------------------- 1 file changed, 6 insertions(+), 25 deletions(-) diff --git a/src/block.ml b/src/block.ml index cf1fc53c..7e7a0b8c 100644 --- a/src/block.ml +++ b/src/block.ml @@ -186,9 +186,8 @@ module Pre = struct { blocks ; next = Rdef_list - { term = deflist.term - ; indent - ; empty_line_seen = deflist.empty_line_seen + { deflist with + indent ; defs = finish deflist.state :: deflist.defs ; state = process empty (Sub.of_string def) } @@ -197,38 +196,20 @@ module Pre = struct { blocks ; next = Rdef_list - { term = deflist.term - ; indent = deflist.indent - ; empty_line_seen = true - ; defs = deflist.defs + { deflist with + empty_line_seen = true ; state = process deflist.state s } } | Rdef_list deflist, _ when Parser.indent s >= deflist.indent -> let s = Sub.offset deflist.indent s in let state = process deflist.state s in - { blocks - ; next = - Rdef_list - { term = deflist.term - ; indent = deflist.indent - ; empty_line_seen = deflist.empty_line_seen - ; defs = deflist.defs - ; state - } - } + { blocks; next = Rdef_list { deflist with state } } | Rdef_list ({ empty_line_seen = false; _ } as deflist), Lparagraph -> (* Lazy wrapping *) let state = process deflist.state s in { blocks - ; next = - Rdef_list - { term = deflist.term - ; indent = deflist.indent - ; empty_line_seen = false - ; defs = deflist.defs - ; state - } + ; next = Rdef_list { deflist with state } } | Rdef_list _, _ -> process { blocks = close { blocks; next }; next = Rempty } s From 88d11336ecbe62a53872db20afbce6f47a86776d Mon Sep 17 00:00:00 2001 From: Shon Feder Date: Sun, 31 Oct 2021 20:34:01 -0400 Subject: [PATCH 08/11] Update tests --- tests/def_list.md | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/tests/def_list.md b/tests/def_list.md index 41edb9c0..617ae7db 100644 --- a/tests/def_list.md +++ b/tests/def_list.md @@ -21,6 +21,22 @@ Second Term This is another paragraph of the second term. +Third term +: This is one paragraph using lazy wrapping, +so this should be part of the first paragraph +of the third term. + + But this should be the second paragraph of + the second paragram + + Note that, currently, subsequent paragraphs cannot lazy wrap. +For example, this line will be part of a new paragraph outside +of the definitoin list entirely. + +Fourth term +: > There is a block quote in the 1st definition of the fourth term. +: But not in the second. + # Nesting Root @@ -38,6 +54,9 @@ Root Root again : Level one again +# A heading +: Followed by a line beginning with a `:` is not a definition. + .
First Term
@@ -63,6 +82,27 @@ which is multiline

This is one paragraph of the second term.

This is another paragraph of the second term.

+
Third term
+
+

This is one paragraph using lazy wrapping, +so this should be part of the first paragraph +of the third term.

+

But this should be the second paragraph of +the second paragram

+

Note that, currently, subsequent paragraphs cannot lazy wrap.

+
+
+

For example, this line will be part of a new paragraph outside +of the definitoin list entirely.

+
Fourth term
+
+
+

There is a block quote in the 1st definition of the fourth term.

+
+
+
+But not in the second. +

Nesting

Root
@@ -86,4 +126,6 @@ Level three

Level one again

+

A heading

+

: Followed by a line beginning with a : is not a definition.

```````````````````````````````` From d5ab6ca45b687deab2f8afc9e373e1f350c9bad3 Mon Sep 17 00:00:00 2001 From: Shon Feder Date: Sun, 31 Oct 2021 20:34:19 -0400 Subject: [PATCH 09/11] Update formatting --- src/block.ml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/block.ml b/src/block.ml index 7e7a0b8c..f22a511b 100644 --- a/src/block.ml +++ b/src/block.ml @@ -208,9 +208,7 @@ module Pre = struct | Rdef_list ({ empty_line_seen = false; _ } as deflist), Lparagraph -> (* Lazy wrapping *) let state = process deflist.state s in - { blocks - ; next = Rdef_list { deflist with state } - } + { blocks; next = Rdef_list { deflist with state } } | Rdef_list _, _ -> process { blocks = close { blocks; next }; next = Rempty } s | Rindented_code lines, Lindented_code s -> From a243c87ba81ace8771d317a5a034fa19a0dfe5ad Mon Sep 17 00:00:00 2001 From: Shon Feder Date: Sun, 31 Oct 2021 20:36:18 -0400 Subject: [PATCH 10/11] Fix typos --- tests/def_list.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/def_list.md b/tests/def_list.md index 617ae7db..c5fff67a 100644 --- a/tests/def_list.md +++ b/tests/def_list.md @@ -27,11 +27,11 @@ so this should be part of the first paragraph of the third term. But this should be the second paragraph of - the second paragram + the second paragraph Note that, currently, subsequent paragraphs cannot lazy wrap. For example, this line will be part of a new paragraph outside -of the definitoin list entirely. +of the definition list entirely. Fourth term : > There is a block quote in the 1st definition of the fourth term. @@ -88,12 +88,12 @@ which is multiline so this should be part of the first paragraph of the third term.

But this should be the second paragraph of -the second paragram

+the second paragraph

Note that, currently, subsequent paragraphs cannot lazy wrap.

For example, this line will be part of a new paragraph outside -of the definitoin list entirely.

+of the definition list entirely.

Fourth term
From 0d8167381867aec68536bd2b5b13862a480c2469 Mon Sep 17 00:00:00 2001 From: Shon Feder Date: Tue, 9 Nov 2021 16:52:47 -0500 Subject: [PATCH 11/11] Factor out def_list tests Should make it easier to test individual properties and features of the def_list parsing, and provide better documentation. --- tests/def_list.md | 142 ++++++++++++++++++++++++++++------------------ tests/dune.inc | 40 ++++++++++++- 2 files changed, 126 insertions(+), 56 deletions(-) diff --git a/tests/def_list.md b/tests/def_list.md index c5fff67a..ab0386f0 100644 --- a/tests/def_list.md +++ b/tests/def_list.md @@ -8,11 +8,30 @@ Second Term : This is one definition of the second term. : This is another definition of the second term. which is multiline - -: This is not a correct definition list +. +
First Term
+
+This is the definition of the first term. +
+
Second Term
+
+This is one definition of the second term. +
+
+This is another definition of the second term. +which is multiline +
+
+```````````````````````````````` # With multiple paragraphs +Definition lists support "loose" formatting, in which each definition is wrapped +as a paragraph. This is triggered by including multiple-paragraph definitions, +as in the definition for the `Second Term`, below, but then effects the entire +definition list. + +```````````````````````````````` example First Term : This is the definition of the first term. @@ -29,52 +48,16 @@ of the third term. But this should be the second paragraph of the second paragraph - Note that, currently, subsequent paragraphs cannot lazy wrap. -For example, this line will be part of a new paragraph outside -of the definition list entirely. + Note that, currently, subsequent paragraphs cannot lazy wrapped. For example, +this line will be part of a new paragraph outside of the definition list +entirely. Fourth term : > There is a block quote in the 1st definition of the fourth term. : But not in the second. - -# Nesting - -Root -: Level one - - Nested - : Level two - ``` - code - ``` - - More - : Level three - -Root again -: Level one again - -# A heading -: Followed by a line beginning with a `:` is not a definition. - .
First Term
-This is the definition of the first term. -
-
Second Term
-
-This is one definition of the second term. -
-
-This is another definition of the second term. -which is multiline -
-
-

: This is not a correct definition list

-

With multiple paragraphs

-
First Term
-

This is the definition of the first term.

Second Term
@@ -89,11 +72,11 @@ so this should be part of the first paragraph of the third term.

But this should be the second paragraph of the second paragraph

-

Note that, currently, subsequent paragraphs cannot lazy wrap.

+

Note that, currently, subsequent paragraphs cannot lazy wrapped. For example,

-

For example, this line will be part of a new paragraph outside -of the definition list entirely.

+

this line will be part of a new paragraph outside of the definition list +entirely.

Fourth term
@@ -104,28 +87,79 @@ of the definition list entirely.

But not in the second.
-

Nesting

-
Root
+```````````````````````````````` + +When multiple definitions are separated by empty lines, this also triggers a +loose formatting: + +```````````````````````````````` example +First term +: Definition 1 + +: Definition 2 +. +
First term
-

Level one

-
Nested
+

Definition 1

+
-

Level two

-
code
+

Definition 2

+
+
+ +```````````````````````````````` + +# Nesting + +Definition lists can be nested. + +```````````````````````````````` example +First Root Term +: Level one definition. + + L1 Nested Term + : L2 Definition + ``` + with some code + ``` + + L2 Nested Term + : L3 Definition + +Second Root Term +: Level one definition for second root. +. +
First Root Term
+
+

Level one definition.

+
L1 Nested Term
+
+

L2 Definition

+
with some code
 
-
More
+
L2 Nested Term
-Level three +L3 Definition
-
Root again
+
Second Root Term
-

Level one again

+

Level one definition for second root.

+```````````````````````````````` + +# Non-definition lists + +A heading does not count a defined term: + +```````````````````````````````` example +# A heading +: Followed by a line beginning with a `:` is not a definition. +.

A heading

: Followed by a line beginning with a : is not a definition.

```````````````````````````````` diff --git a/tests/dune.inc b/tests/dune.inc index 7f39f1a6..69555902 100644 --- a/tests/dune.inc +++ b/tests/dune.inc @@ -665,7 +665,11 @@ attributes-013.md attributes-013.html attributes-014.md attributes-014.html attributes-015.md attributes-015.html - def_list-001.md def_list-001.html) + def_list-001.md def_list-001.html + def_list-002.md def_list-002.html + def_list-003.md def_list-003.html + def_list-004.md def_list-004.html + def_list-005.md def_list-005.html) (action (run ./extract_tests.exe -generate-test-files %{deps}))) (rule (action @@ -4673,6 +4677,34 @@ (rule (alias def_list-001) (action (diff def_list-001.html def_list-001.html.new))) +(rule + (action + (with-stdout-to def_list-002.html.new + (run ./omd.exe %{dep:def_list-002.md})))) +(rule + (alias def_list-002) + (action (diff def_list-002.html def_list-002.html.new))) +(rule + (action + (with-stdout-to def_list-003.html.new + (run ./omd.exe %{dep:def_list-003.md})))) +(rule + (alias def_list-003) + (action (diff def_list-003.html def_list-003.html.new))) +(rule + (action + (with-stdout-to def_list-004.html.new + (run ./omd.exe %{dep:def_list-004.md})))) +(rule + (alias def_list-004) + (action (diff def_list-004.html def_list-004.html.new))) +(rule + (action + (with-stdout-to def_list-005.html.new + (run ./omd.exe %{dep:def_list-005.md})))) +(rule + (alias def_list-005) + (action (diff def_list-005.html def_list-005.html.new))) (alias (name runtest) (deps @@ -5325,4 +5357,8 @@ (alias attributes-013) (alias attributes-014) (alias attributes-015) - (alias def_list-001))) + (alias def_list-001) + (alias def_list-002) + (alias def_list-003) + (alias def_list-004) + (alias def_list-005)))