diff --git a/src/ast.ml b/src/ast.ml index 8495e14a..8536770f 100644 --- a/src/ast.ml +++ b/src/ast.ml @@ -20,11 +20,6 @@ module type T = sig end module MakeBlock (I : T) = struct - type 'attr def_elt = - { term : 'attr I.t - ; defs : 'attr I.t 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 @@ -32,12 +27,17 @@ 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 * 'attr def_elt list + + and 'attr def_elt = + { term : 'attr I.t + ; defs : 'attr block list 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..f22a511b 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,12 +26,15 @@ 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 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" @@ -69,13 +77,30 @@ 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 deflist -> + let def = finish deflist.state in + let defs = def :: deflist.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 = deflist.term; defs = List.rev defs } ]) + :: blocks | Rindented_code l -> (* TODO: trim from the right *) let rec loop = function @@ -116,10 +141,19 @@ 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) -> *) + | Rparagraph [ h ], Ldef_list (indent, def) -> + { blocks + ; next = + (* 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) + } + } | 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,10 +181,34 @@ 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 ({ empty_line_seen = false; _ } as deflist) + , Ldef_list (indent, def) ) -> { blocks - ; next = Rdef_list (term, (d ^ "\n" ^ Sub.to_string s) :: defs) + ; next = + Rdef_list + { deflist with + indent + ; defs = finish deflist.state :: deflist.defs + ; state = process empty (Sub.of_string def) + } + } + | Rdef_list deflist, Lempty -> + { blocks + ; next = + Rdef_list + { 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 { 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 { deflist with 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 c5bd6212..a2d9f3f2 100644 --- a/src/html.ml +++ b/src/html.ml @@ -196,11 +196,19 @@ 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..7142b7d3 100644 --- a/src/omd.mli +++ b/src/omd.mli @@ -28,20 +28,20 @@ and 'attr inline = | Image of 'attr * 'attr link | Html of 'attr * string -type 'attr def_elt = - { term : 'attr inline - ; defs : 'attr inline list - } - 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 * 'attr def_elt list + +and 'attr def_elt = + { term : 'attr inline + ; defs : 'attr block list 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..6d87f839 100644 --- a/src/sexp.ml +++ b/src/sexp.ml @@ -39,13 +39,19 @@ 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..ab0386f0 100644 --- a/tests/def_list.md +++ b/tests/def_list.md @@ -8,15 +8,158 @@ Second Term : This is one definition of the second term. : This is another definition of the second term. which is multiline +. +
This is the definition of the first term.
+This is one paragraph of the second term.
+This is another paragraph of the second 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 paragraph
+Note that, currently, subsequent paragraphs cannot lazy wrapped. For example,
+: This is not a correct definition list
+this line will be part of a new paragraph outside of the definition list +entirely.
+++There is a block quote in the 1st definition of the fourth term.
+
Definition 1
+Definition 2
+Level one definition.
+L2 Definition
+with some code
+
+Level one definition for second root.
+: Followed by a line beginning with a :
is not a definition.