1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
(* src/tdoc/tdoc_markdown.ml *)
(* Markdown generator for T-Doc *)

open Tdoc_types

let percent_encode_for_link (name : string) : string =
  let buf = Buffer.create (String.length name) in
  String.iter (fun c ->
    match c with
    | 'A' .. 'Z'
    | 'a' .. 'z'
    | '0' .. '9'
    | '-'
    | '_'
    | '.'
    | '~' -> Buffer.add_char buf c
    | _ -> Printf.bprintf buf "%%%02X" (Char.code c)
  ) name;
  Buffer.contents buf

let normalize_named_argument_syntax (line : string) : string =
  let len = String.length line in
  let buf = Buffer.create len in
  let is_ident_start = function
    | 'a' .. 'z' | 'A' .. 'Z' | '_' -> true
    | _ -> false
  in
  let is_ident_char = function
    | 'a' .. 'z' | 'A' .. 'Z' | '0' .. '9' | '_' -> true
    | _ -> false
  in
  let rec copy_ident j =
    if j < len && is_ident_char line.[j] then copy_ident (j + 1) else j
  in
  let rec skip_spaces j =
    if j < len && line.[j] = ' ' then skip_spaces (j + 1) else j
  in
  let rec prev_non_space j =
    if j < 0 then None
    else if line.[j] = ' ' then prev_non_space (j - 1)
    else Some line.[j]
  in
  let rec loop i =
    if i >= len then ()
    else if is_ident_start line.[i] then begin
      let j = copy_ident (i + 1) in
      let k = skip_spaces j in
      let prev = prev_non_space (i - 1) in
      if k < len && line.[k] = ':' && (prev = Some '(' || prev = Some ',') then begin
        Buffer.add_substring buf line i (j - i);
        Buffer.add_string buf " = ";
        loop (skip_spaces (k + 1))
      end else begin
        Buffer.add_substring buf line i (j - i);
        loop j
      end
    end else begin
      Buffer.add_char buf line.[i];
      loop (i + 1)
    end
  in
  loop 0;
  Buffer.contents buf

let generate_function_doc entry =
  let buf = Buffer.create 1024 in
  Printf.bprintf buf "# %s\n\n" entry.name;
  
  if entry.description_brief <> "" then
    Printf.bprintf buf "%s\n\n" entry.description_brief;
    
  if entry.description_full <> "" then
    Printf.bprintf buf "%s\n\n" entry.description_full;
    
  if entry.params <> [] then begin
    Printf.bprintf buf "## Parameters\n\n";
    List.iter (fun (p : param_doc) ->
      let type_str = match p.type_info with Some t -> " (`" ^ t ^ "`)" | None -> "" in
      Printf.bprintf buf "- **%s**%s: %s\n\n" p.name type_str p.description
    ) entry.params;
    Buffer.add_string buf "\n";
  end;
  
  begin match entry.return_value with
  | Some r ->
      Printf.bprintf buf "## Returns\n\n";
      Printf.bprintf buf "%s\n\n" r.description
  | None -> ()
  end;
  
  if entry.examples <> [] then begin
    Printf.bprintf buf "## Examples\n\n```t\n";
    List.iter (fun e -> Printf.bprintf buf "%s\n" (normalize_named_argument_syntax e)) entry.examples;
    Printf.bprintf buf "```\n\n";
  end;
  
  if entry.see_also <> [] then begin
    Printf.bprintf buf "## See Also\n\n";
    let links =
      List.map
        (fun s -> Printf.sprintf "[%s](%s.html)" s (percent_encode_for_link s))
        entry.see_also
    in
    Printf.bprintf buf "%s\n\n" (String.concat ", " links);
  end;
  
  Buffer.contents buf

let generate_index entries =
  let buf = Buffer.create 1024 in
  Printf.bprintf buf "# Function Reference\n\n";
  
  Printf.bprintf buf "| Function | Description |\n";
  Printf.bprintf buf "| --- | --- |\n";
  
  List.iter (fun e ->
    if e.is_export then
      Printf.bprintf buf "| [%s](%s.html) | %s |\n"
        e.name
        (percent_encode_for_link e.name)
        e.description_brief
  ) (List.sort (fun a b -> String.compare a.name b.name) entries);
  
  Buffer.contents buf