Skip to content

Commit 056bf57

Browse files
committed
mrkdwn: handle lists and paragraphs
use fields `fst_p_in_li` and `is_in_list` to output multi-level lists properly
1 parent 180590a commit 056bf57

File tree

1 file changed

+92
-38
lines changed

1 file changed

+92
-38
lines changed

lib/mrkdwn.ml

Lines changed: 92 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,24 @@ and transform = function
7070
let mrkdwn_of_md md =
7171
let b = Buffer.create 128 in
7272
let references : ref_container option ref = ref None in
73-
let rec f tl = function
74-
| X _ -> loop tl
75-
| Blockquote _q -> (* todo *) loop tl
73+
let nl b = Buffer.add_char b '\n' in
74+
let nl_if_needed_above b =
75+
if Buffer.length b > 0 && (not @@ Char.equal '\n' (Buffer.nth b (Buffer.length b - 1))) then nl b
76+
in
77+
let add_spaces n =
78+
for _i = 1 to n do
79+
Buffer.add_char b ' '
80+
done
81+
in
82+
let rec f ?(fst_p_in_li = true) ?(is_in_list = false) list_indent tl = function
83+
(* [list_indent: int] is the indentation level in number of spaces.
84+
[fst_p_in_li: bool] is used to apply different indentation to the first
85+
paragraph in a list items.
86+
[is_in_list: bool] is necessary to know if we are inside a paragraph
87+
which is inside a list item because those need to be indented!
88+
*)
89+
| X _ -> loop list_indent tl
90+
| Blockquote _q -> (* todo *) loop list_indent tl
7691
| Ref (rc, _name, _text, fallback) | Img_ref (rc, _name, _text, fallback) ->
7792
(* [rc] stores refs from whole document, so it's enough to record just the
7893
first encounter
@@ -83,71 +98,110 @@ let mrkdwn_of_md md =
8398
and
8499
![<text>][<name>] for Img_ref, e.g., ![image of cat][1]
85100
*)
86-
loop (Raw fallback#to_string :: tl)
87-
| Paragraph _md -> (* todo *) loop tl
88-
| Img (alt, src, title) -> loop (Url (src, [ Text alt ], title) :: tl)
101+
loop list_indent (Raw fallback#to_string :: tl)
102+
| Paragraph [] -> loop list_indent tl
103+
| Paragraph md ->
104+
(* indent if inside a list (Olp or Ulp) *)
105+
if is_in_list then if fst_p_in_li then add_spaces (list_indent - 2) else add_spaces list_indent;
106+
(* paragraph body + skip line *)
107+
loop ~fst_p_in_li:false list_indent md;
108+
nl b;
109+
nl b;
110+
loop ~fst_p_in_li:false list_indent tl
111+
| Img (alt, src, title) -> loop list_indent (Url (src, [ Text alt ], title) :: tl)
89112
| Text t ->
90113
Buffer.add_string b @@ escape_mrkdwn t;
91-
loop tl
114+
loop list_indent tl
92115
| Raw s ->
93116
Buffer.add_string b s;
94-
loop tl
117+
loop list_indent tl
95118
| Raw_block s ->
96-
Buffer.add_char b '\n';
119+
nl b;
97120
Buffer.add_string b s;
98-
Buffer.add_char b '\n';
99-
loop tl
121+
nl b;
122+
loop list_indent tl
100123
| Emph md' ->
101124
Buffer.add_string b "_";
102-
loop md';
125+
loop list_indent md';
103126
Buffer.add_string b "_";
104-
loop tl
127+
loop list_indent tl
105128
| Bold md' ->
106129
Buffer.add_string b "*";
107-
loop md';
130+
loop list_indent md';
108131
Buffer.add_string b "*";
109-
loop tl
110-
| Ul _l -> (* todo *) loop tl
111-
| Ol _l -> (* todo *) loop tl
112-
| Ulp _l -> (* todo *) loop tl
113-
| Olp _l -> (* todo *) loop tl
114-
| Code_block (_lang, _c) -> (* todo *) loop tl
132+
loop list_indent tl
133+
| Ul l ->
134+
nl_if_needed_above b;
135+
List.iter l ~f:(fun li ->
136+
add_spaces list_indent;
137+
Buffer.add_string b "- ";
138+
loop ~is_in_list:true (list_indent + 4) li;
139+
nl_if_needed_above b);
140+
if list_indent = 0 then nl b;
141+
loop list_indent tl
142+
| Ol l ->
143+
nl_if_needed_above b;
144+
List.iteri l ~f:(fun i li ->
145+
add_spaces list_indent;
146+
Printf.bprintf b "%d. " (i + 1);
147+
loop ~is_in_list:true (list_indent + 4) li;
148+
nl_if_needed_above b);
149+
if list_indent = 0 then nl b;
150+
loop list_indent tl
151+
| Ulp l ->
152+
List.iter l ~f:(fun li ->
153+
nl_if_needed_above b;
154+
add_spaces list_indent;
155+
Buffer.add_string b "- ";
156+
loop ~is_in_list:true (list_indent + 4) li (* Paragraphs => No need of '\n' *));
157+
loop list_indent tl
158+
| Olp l ->
159+
List.iteri l ~f:(fun i li ->
160+
nl_if_needed_above b;
161+
add_spaces list_indent;
162+
Printf.bprintf b "%d. " i;
163+
loop ~is_in_list:true (list_indent + 4) li (* Paragraphs => No need of '\n' *));
164+
loop list_indent tl
165+
| Code_block (_lang, _c) -> (* todo *) loop list_indent tl
115166
| Code (_lang, c) ->
116167
Buffer.add_char b '`';
117168
Buffer.add_string b (escape_mrkdwn c);
118169
Buffer.add_char b '`';
119-
loop tl
170+
loop list_indent tl
120171
| Hr ->
121172
Buffer.add_string b "* * *\n";
122-
loop tl
173+
loop list_indent tl
123174
| Html (tagname, _attrs, body) ->
124175
Printf.bprintf b "`&lt;%s&gt;" tagname;
125176
Buffer.add_string b (escape_mrkdwn @@ to_html body);
126177
Printf.bprintf b "&lt;/%s&gt;`" tagname;
127-
loop tl
128-
| Html_block (_tagname, _attrs, _body) -> (* todo *) loop tl
129-
| Html_comment _s -> loop tl
178+
loop list_indent tl
179+
| Html_block (_tagname, _attrs, _body) -> (* todo *) loop list_indent tl
180+
| Html_comment _s -> loop list_indent tl
130181
| Url (href, s, title) ->
131182
Buffer.add_char b '<';
132183
Buffer.add_string b href;
133184
Buffer.add_char b '|';
134185
if String.length title > 0 then Printf.bprintf b "%s - " @@ escape_mrkdwn title;
135-
loop s;
186+
loop list_indent s;
136187
Buffer.add_char b '>';
137-
loop tl
138-
| H1 md' | H2 md' | H3 md' | H4 md' | H5 md' | H6 md' -> loop (Paragraph (transform_list [ Bold md' ]) :: tl)
139-
| NL | Br ->
140-
(* the string "\n" renders NL
141-
the string "\\n" (backslash-newline) renders Br
142-
*)
143-
Buffer.add_char b '\n';
144-
loop tl
145-
and loop = function
146-
| hd :: tl -> f tl hd
188+
loop list_indent tl
189+
| H1 md' | H2 md' | H3 md' | H4 md' | H5 md' | H6 md' ->
190+
loop list_indent (Paragraph (transform_list [ Bold md' ]) :: tl)
191+
| Br ->
192+
(* the string "\\n" (backslash-newline) or end of line double-space renders Br *)
193+
nl b;
194+
loop list_indent tl
195+
| NL ->
196+
(* the string "\n" renders NL *)
197+
nl_if_needed_above b;
198+
loop list_indent tl
199+
and loop ?(fst_p_in_li = true) ?(is_in_list = false) list_indent = function
200+
| hd :: tl -> f ~fst_p_in_li ~is_in_list list_indent tl hd
147201
| [] -> ()
148202
in
149203
(* print the document *)
150-
loop md;
204+
loop 0 md;
151205
(* print any references *)
152206
begin
153207
match !references with
@@ -157,7 +211,7 @@ let mrkdwn_of_md md =
157211
if String.equal title "" then Printf.bprintf b "[%s]: %s \n" name url
158212
else Printf.bprintf b "[%s]: %s \"%s\"\n" name url title
159213
in
160-
Buffer.add_char b '\n';
214+
nl b;
161215
List.iter ~f:print_ref r#get_all
162216
end;
163217
Buffer.contents b

0 commit comments

Comments
 (0)