From abf78d51c49fabcfe65041e23a242ab61241eae3 Mon Sep 17 00:00:00 2001 From: Anil Madhavapeddy Date: Fri, 20 Dec 2013 15:57:24 +0000 Subject: [PATCH] Remove all the Markdown variants except `Omd`, which now claims the `Cow.Markdown` module name. Bump version to 0.9.0 --- CHANGES | 3 + _vars | 2 +- lib/cow.mlpack | 4 - lib/enum.ml | 397 -------------------------------- lib/markdown.ml | 497 ++-------------------------------------- lib/markdown.mli | 88 ++----- lib/markdown_github.ml | 371 ------------------------------ lib/markdown_github.mli | 29 --- lib/markdown_omd.ml | 24 -- lib/markdown_omd.mli | 21 -- 10 files changed, 47 insertions(+), 1389 deletions(-) delete mode 100644 lib/enum.ml delete mode 100644 lib/markdown_github.ml delete mode 100644 lib/markdown_github.mli delete mode 100644 lib/markdown_omd.ml delete mode 100644 lib/markdown_omd.mli diff --git a/CHANGES b/CHANGES index 643c4ad..3231755 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,6 @@ +0.9.0 (trunk): +* Remove all the Markdown variants except `Omd`, which now claims the `Cow.Markdown` module name. + 0.8.1 (15-Dec-2013): * Fix META file to include `omd`. * Improve ocamldoc in CSS module and document quotations in README. diff --git a/_vars b/_vars index 6294ce8..0702d70 100644 --- a/_vars +++ b/_vars @@ -1,5 +1,5 @@ NAME=cow -VERSION=0.8.1 +VERSION=0.9.0 LIB=cow SYNTAX="pa_cow" DEPS="dyntype.syntax dyntype re ulex uri xmlm omd" diff --git a/lib/cow.mlpack b/lib/cow.mlpack index f7bdeba..ad5fb72 100644 --- a/lib/cow.mlpack +++ b/lib/cow.mlpack @@ -1,6 +1,4 @@ Xml -# XXX: remove Enum -Enum Xhtml Html Css @@ -8,5 +6,3 @@ Code Atom Markdown Json -Markdown_github -Markdown_omd diff --git a/lib/enum.ml b/lib/enum.ml deleted file mode 100644 index c136ce6..0000000 --- a/lib/enum.ml +++ /dev/null @@ -1,397 +0,0 @@ -(* - * Enum - Enumeration over abstract collection of elements. - * Copyright (C) 2003 Nicolas Cannasse - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version, - * with the special exception on linking described in file LICENSE. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - *) - -type 'a t = { - mutable count : unit -> int; - mutable next : unit -> 'a; - mutable clone : unit -> 'a t; - mutable fast : bool; -} - -(* raised by 'next' functions, should NOT go outside the API *) -exception No_more_elements - -let _dummy () = assert false - -let make ~next ~count ~clone = - { - count = count; - next = next; - clone = clone; - fast = true; - } - -let rec init n f = - if n < 0 then invalid_arg "Enum.init"; - let count = ref n in - { - count = (fun () -> !count); - next = (fun () -> - match !count with - | 0 -> raise No_more_elements - | _ -> - decr count; - f (n - 1 - !count)); - clone = (fun () -> init !count f); - fast = true; - } - -let rec empty () = - { - count = (fun () -> 0); - next = (fun () -> raise No_more_elements); - clone = (fun () -> empty()); - fast = true; - } - -type 'a _mut_list = { - hd : 'a; - mutable tl : 'a _mut_list; -} - -let force t = - let rec clone enum count = - let enum = ref !enum - and count = ref !count in - { - count = (fun () -> !count); - next = (fun () -> - match !enum with - | [] -> raise No_more_elements - | h :: t -> decr count; enum := t; h); - clone = (fun () -> - let enum = ref !enum - and count = ref !count in - clone enum count); - fast = true; - } - in - let count = ref 0 in - let _empty = Obj.magic [] in - let rec loop dst = - let x = { hd = t.next(); tl = _empty } in - incr count; - dst.tl <- x; - loop x - in - let enum = ref _empty in - (try - enum := { hd = t.next(); tl = _empty }; - incr count; - loop !enum; - with No_more_elements -> ()); - let tc = clone (Obj.magic enum) count in - t.clone <- tc.clone; - t.next <- tc.next; - t.count <- tc.count; - t.fast <- true - -let from f = - let e = { - next = f; - count = _dummy; - clone = _dummy; - fast = false; - } in - e.count <- (fun () -> force e; e.count()); - e.clone <- (fun () -> force e; e.clone()); - e - -let from2 next clone = - let e = { - next = next; - count = _dummy; - clone = clone; - fast = false; - } in - e.count <- (fun () -> force e; e.count()); - e - -let get t = - try - Some (t.next()) - with - No_more_elements -> None - -let push t e = - let rec make t = - let fnext = t.next in - let fcount = t.count in - let fclone = t.clone in - let next_called = ref false in - t.next <- (fun () -> - next_called := true; - t.next <- fnext; - t.count <- fcount; - t.clone <- fclone; - e); - t.count <- (fun () -> - let n = fcount() in - if !next_called then n else n+1); - t.clone <- (fun () -> - let tc = fclone() in - if not !next_called then make tc; - tc); - in - make t - -let peek t = - match get t with - | None -> None - | Some x -> - push t x; - Some x - -let junk t = - try - ignore(t.next()) - with - No_more_elements -> () - -let is_empty t = - if t.fast then - t.count() = 0 - else - peek t = None - -let count t = - t.count() - -let fast_count t = - t.fast - -let clone t = - t.clone() - -let iter f t = - let rec loop () = - f (t.next()); - loop(); - in - try - loop(); - with - No_more_elements -> () - -let iteri f t = - let rec loop idx = - f idx (t.next()); - loop (idx+1); - in - try - loop 0; - with - No_more_elements -> () - -let iter2 f t u = - let push_t = ref None in - let rec loop () = - push_t := None; - let e = t.next() in - push_t := Some e; - f e (u.next()); - loop () - in - try - loop () - with - No_more_elements -> - match !push_t with - | None -> () - | Some e -> - push t e - -let iter2i f t u = - let push_t = ref None in - let rec loop idx = - push_t := None; - let e = t.next() in - push_t := Some e; - f idx e (u.next()); - loop (idx + 1) - in - try - loop 0 - with - No_more_elements -> - match !push_t with - | None -> () - | Some e -> push t e - -let fold f init t = - let acc = ref init in - let rec loop() = - acc := f (t.next()) !acc; - loop() - in - try - loop() - with - No_more_elements -> !acc - -let foldi f init t = - let acc = ref init in - let rec loop idx = - acc := f idx (t.next()) !acc; - loop (idx + 1) - in - try - loop 0 - with - No_more_elements -> !acc - -let fold2 f init t u = - let acc = ref init in - let push_t = ref None in - let rec loop() = - push_t := None; - let e = t.next() in - push_t := Some e; - acc := f e (u.next()) !acc; - loop() - in - try - loop() - with - No_more_elements -> - match !push_t with - | None -> !acc - | Some e -> - push t e; - !acc - -let fold2i f init t u = - let acc = ref init in - let push_t = ref None in - let rec loop idx = - push_t := None; - let e = t.next() in - push_t := Some e; - acc := f idx e (u.next()) !acc; - loop (idx + 1) - in - try - loop 0 - with - No_more_elements -> - match !push_t with - | None -> !acc - | Some e -> - push t e; - !acc - -let find f t = - let rec loop () = - let x = t.next() in - if f x then x else loop() - in - try - loop() - with - No_more_elements -> raise Not_found - -let rec map f t = - { - count = t.count; - next = (fun () -> f (t.next())); - clone = (fun () -> map f (t.clone())); - fast = t.fast; - } - -let rec mapi f t = - let idx = ref (-1) in - { - count = t.count; - next = (fun () -> incr idx; f !idx (t.next())); - clone = (fun () -> mapi f (t.clone())); - fast = t.fast; - } - -let rec filter f t = - let rec next() = - let x = t.next() in - if f x then x else next() - in - from2 next (fun () -> filter f (t.clone())) - -let rec filter_map f t = - let rec next () = - match f (t.next()) with - | None -> next() - | Some x -> x - in - from2 next (fun () -> filter_map f (t.clone())) - -let rec append ta tb = - let t = { - count = (fun () -> ta.count() + tb.count()); - next = _dummy; - clone = (fun () -> append (ta.clone()) (tb.clone())); - fast = ta.fast && tb.fast; - } in - t.next <- (fun () -> - try - ta.next() - with - No_more_elements -> - (* add one indirection because tb can mute *) - t.next <- (fun () -> tb.next()); - t.count <- (fun () -> tb.count()); - t.clone <- (fun () -> tb.clone()); - t.fast <- tb.fast; - t.next() - ); - t - -let rec concat t = - let concat_ref = ref _dummy in - let rec concat_next() = - let tn = t.next() in - concat_ref := (fun () -> - try - tn.next() - with - No_more_elements -> - concat_next()); - !concat_ref () - in - concat_ref := concat_next; - from2 (fun () -> !concat_ref ()) (fun () -> concat (t.clone())) - -let of_list l = - let rec make' lr count = - make - ~next:(fun () -> - match !lr with - | [] -> raise No_more_elements - | h :: t -> - decr count; - lr := t; - h - ) - ~count:(fun () -> - if !count < 0 then count := List.length !lr; - !count - ) - ~clone:(fun () -> - make' (ref !lr) (ref !count) - ) - in - make' (ref l) (ref (-1)) diff --git a/lib/markdown.ml b/lib/markdown.ml index 802227c..cc471e5 100644 --- a/lib/markdown.ml +++ b/lib/markdown.ml @@ -1,475 +1,24 @@ (* - Copyright (c) 2009 Mauricio Fernández - Copyright (c) 2009-2010 Anil Madhavapeddy - Copyright (c) 2010 Thomas Gazagnaire - - Permission is hereby granted, free of charge, to any person - obtaining a copy of this software and associated documentation - files (the "Software"), to deal in the Software without - restriction, including without limitation the rights to use, - copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following - conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - OTHER DEALINGS IN THE SOFTWARE. -*) - -open Printf - -type ref = { src : string; desc : string } - -type paragraph = - Normal of par_text - | Html of Html.t - | Pre of string * string option - | Heading of int * par_text - | Quote of paragraph list - | Ulist of paragraph list * paragraph list list - | Olist of paragraph list * paragraph list list - -and par_text = text list - -and text = - Text of string - | Emph of string - | Bold of string - | Struck of par_text - | Code of string - | Link of href - | Anchor of string - | Image of img_ref - -and href = { href_target : string; href_desc : string; } - -and img_ref = { img_src : string; img_alt : string; } - -type t = paragraph list - -type parse_state = { max : int; current : Buffer.t; fragments : text list; } - -let indentation ?(ts=8) s = - let rec loop n indent max = - if n >= max then indent - else match s.[n] with - ' ' -> loop (n + 1) (indent + 1) max - | '\t' -> loop (n + 1) (indent + 8) max - | _ -> indent - in loop 0 0 (String.length s) - -let unescape s = - let b = Buffer.create (String.length s) in - let len = String.length s in - let rec loop i = - if i >= len then Buffer.contents b - else match s.[i] with - '\\' when i < len - 1 -> Buffer.add_char b s.[i+1]; loop (i + 2) - | c -> Buffer.add_char b c; loop (i + 1) - in loop 0 - -let slice ?(first=0) ?(last=Sys.max_string_length) s = - let clip _min _max x = max _min (min _max x) in - let i = clip 0 (String.length s) - (if (first<0) then (String.length s) + first else first) - and j = clip 0 (String.length s) - (if (last<0) then (String.length s) + last else last) - in - if i>=j || i=String.length s then - String.create 0 - else - String.sub s i (j-i) - -let strip ?(chars=" \t\r\n") s = - let p = ref 0 in - let l = String.length s in - while !p < l && String.contains chars (String.get s !p) do - incr p; - done; - let p = !p in - let l = ref (l - 1) in - while !l >= p && String.contains chars (String.get s !l) do - decr l; - done; - String.sub s p (!l - p + 1) - -let unescape_slice s ~first ~last = - unescape (strip (slice ~first ~last s)) - -let snd_is s c = String.length s > 1 && s.[1] = c -let snd_is_space s = snd_is s ' ' || snd_is s '\t' - -let collect f x = - let rec loop acc = match f x with - None -> List.rev acc - | Some y -> loop (y :: acc) - in loop [] - -let push_remainder ?(first=2) indent s e = - let s = slice ~first s in - let s' = strip s in - Enum.push e (indent + first + indentation s, s', s' = "") - -let adds = Buffer.add_string - -let addc = Buffer.add_char - -let new_fragment () = Buffer.create 8 - -let push_current st = - if Buffer.length st.current > 0 then - Text (Buffer.contents st.current) :: st.fragments - else st.fragments - -let rec read_paragraph ?(skip_blank=true) indent e = match Enum.peek e with - None -> None - | Some (indentation, line, isblank) -> match isblank with - true -> - Enum.junk e; - if skip_blank then read_paragraph indent e else None - | false -> - if indentation < indent then - None - else begin - Enum.junk e; - read_nonempty indentation e line - end - -and skip_blank_line e = match Enum.peek e with - None | Some (_, _, false) -> () - | Some (_, _, true) -> Enum.junk e; skip_blank_line e - -and read_nonempty indent e s = match s.[0] with - '!' -> read_heading s - | '<' -> read_html e s - | '*' when snd_is_space s -> push_remainder indent s e; read_ul indent e - | '#' when snd_is_space s -> push_remainder indent s e; read_ol indent e - | '{' when snd_is s '{' -> read_pre (slice s ~first:2) e - | '>' when snd_is_space s || s = ">" -> - (* last check needed because "> " becomes ">" *) - Enum.push e (indent, s, false); read_quote indent e - | _ -> Enum.push e (indent, s, false); read_normal e - -and read_heading s = - let s' = strip ~chars:"!" s in - let level = String.length s - (String.length s') in - Some (Heading (level, parse_text s')) - -and read_html e s = - let is_closing_tag e s = (* ends by '>' and next line is blank *) - String.length s > 2 && s.[String.length s - 1] = '>' && - match Enum.peek e with Some (_,_,x) -> x | _ -> true in - let make_html ls = - Some (Html (Html.of_string (String.concat "" (List.rev ls)))) in - let rec read_all accu = match Enum.get e with - | Some (_, s, _) - when is_closing_tag e s -> make_html (s::accu) - | Some (_,s,_) -> read_all (s::accu) - | None -> make_html accu in - if is_closing_tag e s then - make_html [s] - else - read_all [s] - -and read_ul indent e = - read_list - (fun fst others -> Ulist (fst, others)) - (fun s -> snd_is_space s && s.[0] = '*') - indent e - -and read_ol indent e = - read_list - (fun fst others -> Olist (fst, others)) - (fun s -> snd_is_space s && s.[0] = '#') - indent e - -and read_list f is_item indent e = - let read_item indent ps = collect (read_paragraph (indent + 1)) e in - let rec read_all fst others = - skip_blank_line e; - match Enum.peek e with - | Some (indentation, s, _) when indentation >= indent && is_item s -> - Enum.junk e; - push_remainder indentation s e; - read_all fst (read_item indentation [] :: others) - | None | Some _ -> f fst (List.rev others) - in Some (read_all (read_item indent []) []) - -and read_pre kind e = - let kind = match kind with "" -> None | s -> Some s in - let re = Re_str.regexp_string "^\\\\+}}$" in - let unescape = function - s when Re_str.string_match re s 0 -> slice ~first:1 s - | s -> s in - (* don't forget the last \n *) - let ret ls = Some (Pre (String.concat "\n" (List.rev ("" :: ls)), kind)) in - let rec read_until_end fstindent ls = match Enum.get e with - None | Some (_, "}}", _) -> ret ls - | Some (indentation, s, _) -> - let spaces = String.make (max 0 (indentation - fstindent)) ' ' in - read_until_end fstindent ((spaces ^ unescape s) :: ls) - in match Enum.get e with - None | Some (_, "}}", _) -> ret [] - | Some (indentation, s, _) -> read_until_end indentation [s] - -and read_quote indent e = - let push_and_finish e elm = Enum.push e elm; raise Enum.No_more_elements in - let next_without_lt e = function - | (_, _, true) as line -> push_and_finish e line - | (n, s, false) as line -> - if n < indent || s.[0] <> '>' then - push_and_finish e line - else - let s = slice ~first:1 s in - let s' = strip s in - (String.length s - String.length s', s', s' = "") - - in match collect (read_paragraph 0) (Enum.map (next_without_lt e) e) with - [] -> None - | ps -> Some (Quote ps) - -and read_normal e = - let rec gettxt ls = - let return () = String.concat " " (List.rev ls) in - match Enum.peek e with - None | Some (_, _, true) -> return () - | Some (_, l, _) -> match l.[0] with - '!' | '*' | '#' | '>' when snd_is_space l -> return () - | '{' when snd_is l '{' -> return () - | _ -> Enum.junk e; gettxt (l :: ls) in - let txt = gettxt [] in - Some (Normal (parse_text txt)) - -and parse_text s = - scan - s - { max = String.length s; - fragments = []; - current = new_fragment (); } - 0 - - (* scan s starting from n, upto max (exclusive) *) -and scan s st n = - let max = st.max in - if n >= max then List.rev (push_current st) - - else match s.[n] with - | '`' -> - delimited (fun ~first ~last -> Code (unescape_slice s ~first ~last)) "`" - s st n - | '*' -> - delimited (fun ~first ~last -> Bold (unescape_slice s ~first ~last)) "*" - s st n - | '_' -> - delimited (fun ~first ~last -> Emph (unescape_slice s ~first ~last)) "__" - s st n - | '=' -> - delimited - (fun ~first ~last -> - Struck (scan s - { max = last; fragments = []; current = new_fragment (); } - first)) - "==" s st n - | '!' when matches_at s ~max n "![" -> - maybe_link - "![" (fun ref -> Image { img_src = ref.src; img_alt = ref.desc }) - s st (n + 2) - | '[' -> - maybe_link "[" - (fun ref -> match ref.src, ref.desc with - "", "" -> Text "" - | "", desc -> Link { href_target = desc; href_desc = desc } - | src, "" when src.[0] = '#' -> Anchor (slice ~first:1 src) - | src, desc -> Link { href_target = ref.src; href_desc = ref.desc}) - s st (n + 1) - | '\\' when (n + 1) < max -> addc st.current s.[n+1]; scan s st (n + 2) - | c -> addc st.current c; scan s st (n + 1) - -(* [delimited f delim first] tries to match [delim] starting from [first], - * returns Some (offset of char after closing delim) or None *) -and delimited f delim s st first = - let max = st.max in - let delim_len = String.length delim in - let scan_from_next_char () = - addc st.current s.[first]; - scan s st (first + 1) - in - if not (matches_at s ~max first delim) then scan_from_next_char () - else match scan_past ~delim s ~max (first + String.length delim) with - Some n -> - let chunk = f ~first:(first + delim_len) - ~last:(n - String.length delim) - in scan s - { st with fragments = chunk :: push_current st; - current = new_fragment () } - n - | None -> scan_from_next_char () - -and maybe_link delim f s st n = match scan_link s ~max:st.max n with - None -> adds st.current delim; scan s st n - | Some (ref, n) -> - scan s - { st with fragments = f ref :: push_current st; - current = (new_fragment ()) } - n - -(* return None if delim not found, else Some (offset of char *after* delim) *) -and scan_past ~delim s ~max n = - let re = Re_str.regexp_string delim in - let rec loop m ~max = - if m >= max then None else - try begin - match Re_str.search_forward re s m with - (*what if m = 0 ?*) - | m when m < max && s.[m-1] <> '\\' - -> Some (m + String.length delim) - | m when m < max - -> loop (m + 1) ~max - | _ -> None (* >= max *) - end with Not_found -> None (* no match *) - in loop n ~max - -(* returns None or offset of char after the reference - * (i.e. after closing ')'). *) -and scan_link s ~max n = match scan_past ~delim:"]" s ~max n with - None -> None - | Some end_of_desc -> - if end_of_desc >= max then None - else match s.[end_of_desc] with - '(' -> - begin match scan_past ~delim:")" s ~max (end_of_desc + 1) with - None -> None - | Some end_of_uri -> - let ref = - { - desc = unescape_slice s ~first:n ~last:(end_of_desc - 1); - src = unescape_slice s - ~first:(end_of_desc + 1) - ~last:(end_of_uri - 1) - } - in Some (ref, end_of_uri) - end - | _ -> None - -and matches_at s ~max n delim = - let len = String.length delim in - if n + len > max then false - else - let rec loop n m k = - if k = 0 then true - else if s.[n] = delim.[m] then loop (n + 1) (m + 1) (k - 1) - else false - in loop n 0 len - -let parse_enum e = - collect (read_paragraph 0) - (Enum.map (fun l -> let l' = strip l in (indentation l, l', l' = "")) e) - -let parse_lines ls = parse_enum (Enum.of_list ls) -let parse_text s = parse_lines ((Re_str.split_delim (Re_str.regexp_string "\n") s)) - -(* Create a suitable ID given a Header element *) -let id_of_heading (h: paragraph): string = - let rec str_of_pt pt = - let replace s = Re_str.global_replace (Re_str.regexp "[ ]+") "" s in - String.concat "-" (List.map (fun t -> replace (str_of_text t)) pt) - and str_of_text = function - | Text s | Emph s | Bold s | Code s | Anchor s -> s - | Struck pt -> str_of_pt pt - | Link href -> href.href_desc - | Image ref -> ref.img_alt - in - match h with - | Heading (n,pt) -> - let pt_str = str_of_pt pt in - Printf.sprintf "h%d-%s" n pt_str - | _ -> failwith "id_of_heading: input element is not a heading!" - -let rec text = function - Text t -> <:html<$str:t$&>> - | Emph t -> <:html<$str:t$&>> - | Bold t -> <:html<$str:t$&>> - | Struck pt -> <:html<$par_text pt$&>> - | Code t -> <:html<$str:t$&>> - | Link href -> <:html<$str:href.href_desc$&>> - | Anchor a -> <:html<&>> - | Image img -> <:html<$str:img.img_alt$/&>> - -and para p = - let heading_content h pt = - <:html<$par_text pt$&>> - in - match p with - Normal pt -> <:html<

$par_text pt$

&>> - | Html html -> <:html<

$html$

&>> - (* XXX: we assume that this is ocaml code *) - | Pre (t,kind) -> <:html<$ Code.ocaml t$>> - | Heading (1,pt) as h -> <:html<

$heading_content h pt$

&>> - | Heading (2,pt) as h -> <:html<

$heading_content h pt$

&>> - | Heading (3,pt) as h -> <:html<

$heading_content h pt$

&>> - | Heading (4,pt) as h -> <:html<

$heading_content h pt$

&>> - | Heading (5,pt) as h -> <:html<
$heading_content h pt$
&>> - | Heading (_,pt) as h -> <:html<
$heading_content h pt$
&>> - | Quote pl -> <:html<
$paras pl$
&>> - | Ulist (pl,pll) -> let l = pl :: pll in <:html<
    $li l$
&>> - | Olist (pl,pll) -> let l = pl :: pll in <:html<
    $li l$
&>> - -and par_text pt = <:html<$list:List.map text pt$>> - -and li pl = - let aux p = <:html<
  • $paras p$
  • &>> in - <:html< $list:List.map aux pl$ >> - -and paras ps = - let aux p = <:html<$para p$&>> in - <:html< $list:List.map aux ps$ >> - -let to_html ps = paras ps - -let of_string = parse_text - -(* Default html creation functions for table of contents *) -let wrap_li ~depth c = - <:html<
  • $c$
  • &>> - -let wrap_a ~depth ~heading c = - let href = "#" ^ id_of_heading heading in - wrap_li ~depth <:html<$c$&>> - -let wrap_ul ~depth l = - if depth = 0 then - <:html<&>> - else - wrap_li ~depth <:html<
      $l$
    &>> - -(* Extract a HTML table of contents from markdown elements. Depth can be - modified with the corresponding optional argument. *) -let to_html_toc ?(wrap_list=wrap_ul) ?(wrap_item=wrap_a) ?(depth=2) ps = - let rec aux level ps = match ps with - | [] -> [], [] - | h :: t -> match h with - | Heading (n,pt) -> - begin - match n with - | n when n = level -> - let acc, r = aux level t in - wrap_item ~depth:level ~heading:h (par_text pt) :: acc, r - | n when n > level && n <= depth -> - let acc, r = aux (level+1) ps in - let racc, rr = aux level r in - wrap_list ~depth:level <:html< $list: acc$ >> :: racc, rr - | n when n < level -> [], ps - | _ -> aux level t - end - | _ -> aux level t - in - wrap_list ~depth:0 <:html< $list: fst (aux 1 ps)$ >> + * Copyright (c) 2013 Richard Mortier + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + *) + +type t = Html.t + +let to_string = Html.to_string + +let of_string s = + let omd = Omd.of_string s in + let html = Omd.to_html omd in + Html.of_string html diff --git a/lib/markdown.mli b/lib/markdown.mli index d746b54..2e24257 100644 --- a/lib/markdown.mli +++ b/lib/markdown.mli @@ -1,69 +1,21 @@ (* - Copyright (c) 2009 Mauricio Fernández - Copyright (c) 2009-2010 Anil Madhavapeddy - Copyright (c) 2010 Thomas Gazagnaire - - Permission is hereby granted, free of charge, to any person - obtaining a copy of this software and associated documentation - files (the "Software"), to deal in the Software without - restriction, including without limitation the rights to use, - copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following - conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - OTHER DEALINGS IN THE SOFTWARE. -*) - -type ref = { src : string; desc : string } - -type paragraph = - Normal of par_text - | Html of Html.t - | Pre of string * string option - | Heading of int * par_text - | Quote of paragraph list - | Ulist of paragraph list * paragraph list list - | Olist of paragraph list * paragraph list list - -and par_text = text list - -and text = - Text of string - | Emph of string - | Bold of string - | Struck of par_text - | Code of string - | Link of href - | Anchor of string - | Image of img_ref - -and href = { href_target : string; href_desc : string; } - -and img_ref = { img_src : string; img_alt : string; } - -and t = paragraph list - -val to_html : t -> Html.t - -val of_string : string -> t - -(** Create a heading *) -val id_of_heading: paragraph -> string - -(** Extract a HTML table of contents from markdown elements. Depth can - be modified with the corresponding optional argument. *) -val to_html_toc: - ?wrap_list:(depth:int -> Html.t -> Html.t) -> - ?wrap_item:(depth:int -> heading:paragraph -> Html.t -> Html.t) -> - ?depth:int -> t -> Html.t + * Copyright (c) 2013 Richard Mortier + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + *) + +type t = Html.t + +val to_string: t -> string + +val of_string: string -> t diff --git a/lib/markdown_github.ml b/lib/markdown_github.ml deleted file mode 100644 index 264fa72..0000000 --- a/lib/markdown_github.ml +++ /dev/null @@ -1,371 +0,0 @@ -(* - Copyright (c) 2009 Mauricio Fernández - Copyright (c) 2009-2010 Anil Madhavapeddy - Copyright (c) 2010-2013 Thomas Gazagnaire - Copyright (c) 2012 Guillem Rieu - - Permission is hereby granted, free of charge, to any person - obtaining a copy of this software and associated documentation - files (the "Software"), to deal in the Software without - restriction, including without limitation the rights to use, - copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following - conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - OTHER DEALINGS IN THE SOFTWARE. -*) - -open Printf -open Markdown - -type parse_state = { max : int; current : Buffer.t; fragments : text list; } - -let indentation ?(ts=8) s = - let rec loop n indent max = - if n >= max then indent - else match s.[n] with - ' ' -> loop (n + 1) (indent + 1) max - | '\t' -> loop (n + 1) (indent + 8) max - | _ -> indent - in loop 0 0 (String.length s) - -let unescape s = - let b = Buffer.create (String.length s) in - let len = String.length s in - let rec loop i = - if i >= len then Buffer.contents b - else match s.[i] with - '\\' when i < len - 1 -> Buffer.add_char b s.[i+1]; loop (i + 2) - | c -> Buffer.add_char b c; loop (i + 1) - in loop 0 - -let slice ?(first=0) ?(last=Sys.max_string_length) s = - let clip _min _max x = max _min (min _max x) in - let i = clip 0 (String.length s) - (if (first<0) then (String.length s) + first else first) - and j = clip 0 (String.length s) - (if (last<0) then (String.length s) + last else last) - in - if i>=j || i=String.length s then - String.create 0 - else - String.sub s i (j-i) - -let strip ?(chars=" \t\r\n") s = - let p = ref 0 in - let l = String.length s in - while !p < l && String.contains chars (String.get s !p) do - incr p; - done; - let p = !p in - let l = ref (l - 1) in - while !l >= p && String.contains chars (String.get s !l) do - decr l; - done; - String.sub s p (!l - p + 1) - -let unescape_slice s ~first ~last = - unescape (strip (slice ~first ~last s)) - -let snd_is s c = String.length s > 1 && s.[1] = c -let snd_is_space s = snd_is s ' ' || snd_is s '\t' - -(* let range i j = *) -(* let rec aux acc n = *) -(* if n < i then acc else aux (n :: acc) (n-1) *) -(* in aux [] j *) - -let n_is n s c = String.length s > n && s.[n] = c - -let range_is (i, j) s c = String.length s > j && - let rec aux n = - if n > j then true else s.[n] = c && aux (n+1) - in aux i - -let collect f x = - let rec loop acc = match f x with - None -> List.rev acc - | Some y -> loop (y :: acc) - in loop [] - -let push_remainder ?(first=2) indent s e = - let s = slice ~first s in - let s' = strip s in - Enum.push e (indent + first + indentation s, s', s' = "") - -let adds = Buffer.add_string - -let addc = Buffer.add_char - -let new_fragment () = Buffer.create 8 - -let push_current st = - if Buffer.length st.current > 0 then - Text (Buffer.contents st.current) :: st.fragments - else st.fragments - -let rec read_paragraph ?(skip_blank=true) indent e = match Enum.peek e with - None -> None - | Some (indentation, line, isblank) -> match isblank with - true -> - Enum.junk e; - if skip_blank then read_paragraph indent e else None - | false -> - if indentation < indent then - None - else begin - Enum.junk e; - read_nonempty indentation e line - end - -and skip_blank_line e = match Enum.peek e with - None | Some (_, _, false) -> () - | Some (_, _, true) -> Enum.junk e; skip_blank_line e - -and read_nonempty indent e s = match s.[0] with - '#' -> read_heading s - | '<' -> read_html e s - | '*' | '+' | '-' when snd_is_space s -> - push_remainder indent s e; read_ul indent e - (* | '#' when snd_is_space s -> push_remainder indent s e; read_ol indent e *) - | '{' when snd_is s '{' -> read_pre ~delim:"}}" (slice s ~first:2) e - | '`' when range_is (1,2) s '`' -> read_pre ~delim:"```" (slice s ~first:3) e - | '>' when snd_is_space s || s = ">" -> - (* last check needed because "> " becomes ">" *) - Enum.push e (indent, s, false); read_quote indent e - | _ -> Enum.push e (indent, s, false); read_normal e - -and read_heading s = - let s' = strip ~chars:"#" s in - let level = String.length s - (String.length s') in - Some (Heading (level, parse_text s')) - -and read_html e s = - let is_closing_tag e s = (* ends by '>' and next line is blank *) - String.length s > 2 && s.[String.length s - 1] = '>' && - match Enum.peek e with Some (_,_,x) -> x | _ -> true in - let make_html ls = - Some (Html (Html.of_string (String.concat "" (List.rev ls)))) in - let rec read_all accu = match Enum.get e with - | Some (_, s, _) - when is_closing_tag e s -> make_html (s::accu) - | Some (_,s,_) -> read_all (s::accu) - | None -> make_html accu in - if is_closing_tag e s then - make_html [s] - else - read_all [s] - -and read_ul indent e = - read_list - (fun fst others -> Ulist (fst, others)) - (fun s -> snd_is_space s && s.[0] = '*') - indent e - -and read_ol indent e = - read_list - (fun fst others -> Olist (fst, others)) - (fun s -> snd_is_space s && s.[0] = '#') - indent e - -and read_list f is_item indent e = - let read_item indent ps = collect (read_paragraph (indent + 1)) e in - let rec read_all fst others = - skip_blank_line e; - match Enum.peek e with - | Some (indentation, s, _) when indentation >= indent && is_item s -> - Enum.junk e; - push_remainder indentation s e; - read_all fst (read_item indentation [] :: others) - | None | Some _ -> f fst (List.rev others) - in Some (read_all (read_item indent []) []) - -and read_pre ~delim kind e = - let kind = match kind with "" -> None | s -> Some s in - let re_str = Printf.sprintf "^\\\\+%s$" delim in - let re = Re_str.regexp_string re_str in - let unescape = function - s when Re_str.string_match re s 0 -> slice ~first:1 s - | s -> s in - (* don't forget the last \n *) - let ret ls = Some (Pre (String.concat "\n" (List.rev ("" :: ls)), kind)) in - let rec read_until_end fstindent ls = match Enum.get e with - | None -> ret ls - | Some (indentation, s, _) -> if s = delim then ret ls else - let spaces = String.make (max 0 (indentation - fstindent)) ' ' in - read_until_end fstindent ((spaces ^ unescape s) :: ls) - in match Enum.get e with - None | Some (_, "}}", _) | Some (_, "```", _) -> ret [] - | Some (indentation, s, _) -> read_until_end indentation [s] - -and read_quote indent e = - let push_and_finish e elm = Enum.push e elm; raise Enum.No_more_elements in - let next_without_lt e = function - | (_, _, true) as line -> push_and_finish e line - | (n, s, false) as line -> - if n < indent || s.[0] <> '>' then - push_and_finish e line - else - let s = slice ~first:1 s in - let s' = strip s in - (String.length s - String.length s', s', s' = "") - - in match collect (read_paragraph 0) (Enum.map (next_without_lt e) e) with - [] -> None - | ps -> Some (Quote ps) - -and read_normal e = - let rec gettxt ls = - let return () = String.concat " " (List.rev ls) in - match Enum.peek e with - None | Some (_, _, true) -> return () - | Some (_, l, _) -> match l.[0] with - '!' | '*' | '#' | '>' when snd_is_space l -> return () - | '{' when snd_is l '{' -> return () - | _ -> Enum.junk e; gettxt (l :: ls) in - let txt = gettxt [] in - Some (Normal (parse_text txt)) - -and parse_text s = - scan - s - { max = String.length s; - fragments = []; - current = new_fragment (); } - 0 - - (* scan s starting from n, upto max (exclusive) *) -and scan s st n = - let max = st.max in - if n >= max then List.rev (push_current st) - - else match s.[n] with - | '`' when n_is (n+1) s '`' -> - delimited (fun ~first ~last -> Code (unescape_slice s ~first ~last)) "``" - s st n - | '`' -> - delimited (fun ~first ~last -> Code (unescape_slice s ~first ~last)) "`" - s st n - | '*' -> - delimited (fun ~first ~last -> Bold (unescape_slice s ~first ~last)) "*" - s st n - | '_' -> - delimited (fun ~first ~last -> Emph (unescape_slice s ~first ~last)) "__" - s st n - | '=' -> - delimited - (fun ~first ~last -> - Struck (scan s - { max = last; fragments = []; current = new_fragment (); } - first)) - "==" s st n - | '!' when matches_at s ~max n "![" -> - maybe_link - "![" (fun ref -> Image { img_src = ref.src; img_alt = ref.desc }) - s st (n + 2) - | '[' -> - maybe_link "[" - (fun ref -> match ref.src, ref.desc with - "", "" -> Text "" - | "", desc -> Link { href_target = desc; href_desc = desc } - | src, "" when src.[0] = '#' -> Anchor (slice ~first:1 src) - | src, desc -> Link { href_target = ref.src; href_desc = ref.desc}) - s st (n + 1) - | '\\' when (n + 1) < max -> addc st.current s.[n+1]; scan s st (n + 2) - | c -> addc st.current c; scan s st (n + 1) - -(* [delimited f delim first] tries to match [delim] starting from [first], - * returns Some (offset of char after closing delim) or None *) -and delimited f delim s st first = - let max = st.max in - let delim_len = String.length delim in - let scan_from_next_char () = - addc st.current s.[first]; - scan s st (first + 1) - in - if not (matches_at s ~max first delim) then scan_from_next_char () - else match scan_past ~delim s ~max (first + String.length delim) with - Some n -> - let chunk = f ~first:(first + delim_len) - ~last:(n - String.length delim) - in scan s - { st with fragments = chunk :: push_current st; - current = new_fragment () } - n - | None -> scan_from_next_char () - -and maybe_link delim f s st n = match scan_link s ~max:st.max n with - None -> adds st.current delim; scan s st n - | Some (ref, n) -> - scan s - { st with fragments = f ref :: push_current st; - current = (new_fragment ()) } - n - -(* return None if delim not found, else Some (offset of char *after* delim) *) -and scan_past ~delim s ~max n = - let re = Re_str.regexp_string delim in - let rec loop m ~max = - if m >= max then None else - try begin - match Re_str.search_forward re s m with - (*what if m = 0 ?*) - | m when m < max && s.[m-1] <> '\\' - -> Some (m + String.length delim) - | m when m < max - -> loop (m + 1) ~max - | _ -> None (* >= max *) - end with Not_found -> None (* no match *) - in loop n ~max - -(* returns None or offset of char after the reference - * (i.e. after closing ')'). *) -and scan_link s ~max n = match scan_past ~delim:"]" s ~max n with - None -> None - | Some end_of_desc -> - if end_of_desc >= max then None - else match s.[end_of_desc] with - '(' -> - begin match scan_past ~delim:")" s ~max (end_of_desc + 1) with - None -> None - | Some end_of_uri -> - let ref = - { - desc = unescape_slice s ~first:n ~last:(end_of_desc - 1); - src = unescape_slice s - ~first:(end_of_desc + 1) - ~last:(end_of_uri - 1) - } - in Some (ref, end_of_uri) - end - | _ -> None - -and matches_at s ~max n delim = - let len = String.length delim in - if n + len > max then false - else - let rec loop n m k = - if k = 0 then true - else if s.[n] = delim.[m] then loop (n + 1) (m + 1) (k - 1) - else false - in loop n 0 len - -let parse_enum e = - collect (read_paragraph 0) - (Enum.map (fun l -> let l' = strip l in (indentation l, l', l' = "")) e) - -let parse_lines ls = parse_enum (Enum.of_list ls) -let parse_text s = parse_lines ((Re_str.split_delim (Re_str.regexp_string "\n") s)) - -let of_string = parse_text diff --git a/lib/markdown_github.mli b/lib/markdown_github.mli deleted file mode 100644 index d869135..0000000 --- a/lib/markdown_github.mli +++ /dev/null @@ -1,29 +0,0 @@ -(* - Copyright (c) 2009 Mauricio Fernández - Copyright (c) 2009-2010 Anil Madhavapeddy - Copyright (c) 2010-2013 Thomas Gazagnaire - Copyright (c) 2012 Guillem Rieu - - Permission is hereby granted, free of charge, to any person - obtaining a copy of this software and associated documentation - files (the "Software"), to deal in the Software without - restriction, including without limitation the rights to use, - copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following - conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - OTHER DEALINGS IN THE SOFTWARE. -*) - -val of_string : string -> Markdown.t diff --git a/lib/markdown_omd.ml b/lib/markdown_omd.ml deleted file mode 100644 index cc471e5..0000000 --- a/lib/markdown_omd.ml +++ /dev/null @@ -1,24 +0,0 @@ -(* - * Copyright (c) 2013 Richard Mortier - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - *) - -type t = Html.t - -let to_string = Html.to_string - -let of_string s = - let omd = Omd.of_string s in - let html = Omd.to_html omd in - Html.of_string html diff --git a/lib/markdown_omd.mli b/lib/markdown_omd.mli deleted file mode 100644 index 2e24257..0000000 --- a/lib/markdown_omd.mli +++ /dev/null @@ -1,21 +0,0 @@ -(* - * Copyright (c) 2013 Richard Mortier - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - *) - -type t = Html.t - -val to_string: t -> string - -val of_string: string -> t