package odoc
Source file extract_code.ml
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 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238# 2 "src/odoc/extract_code.cppo.ml" open Odoc_utils open Odoc_parser let names = List.exists (function | `Binding ({ Loc.value = "name"; _ }, { Loc.value = n; _ }) when List.exists (String.equal n) names -> true | _ -> false) tags let needs_extraction names meta = let check_language () = match meta with | None -> true | Some { Ast.language; _ } -> String.equal "ocaml" language.Loc.value in let check_name () = match meta with | Some { ; _ } -> tags_included_in_names names tags | _ -> false in match names with [] -> check_language () | _ :: _ -> check_name () let print line_directives oc location value = if line_directives then ( Printf.fprintf oc "#%d \"%s\"\n" location.Loc.start.line location.file; Printf.fprintf oc "%s%s\n" (String.v ~len:location.start.column (fun _ -> ' ')) value) else Printf.fprintf oc "%s" value let rec nestable_block_element line_directives oc names v = match v.Loc.value with | `Verbatim _ | `Modules _ | `Math_block _ | `Media _ | `Paragraph _ -> () | `Code_block { Ast.content = { value; location }; meta; _ } when needs_extraction names meta -> print line_directives oc location value | `Code_block _ -> () | `List (_, _, l) -> List.iter (List.iter (nestable_block_element line_directives oc names)) l | `Table ((table, _), _) -> List.iter (List.iter (fun (x, _) -> List.iter (nestable_block_element line_directives oc names) x)) table let block_element line_directives oc names v = match v.Loc.value with | `Tag ( `Deprecated l | `Param (_, l) | `Raise (_, l) | `Return l | `See (_, _, l) | `Before (_, l) ) -> List.iter (nestable_block_element line_directives oc names) l | `Tag ( `Author _ | `Since _ | `Version _ | `Canonical _ | `Inline | `Open | `Children_order _ | `Toc_status _ | `Order_category _ | `Short_title _ | `Closed | `Hidden ) | `Heading _ -> () | #Ast.nestable_block_element as value -> nestable_block_element line_directives oc names { v with value } let pad_loc loc = { loc.Location.loc_start with pos_cnum = loc.loc_start.pos_cnum + 3 } let iterator line_directives oc names = let default_iterator = Tast_iterator.default_iterator in let attribute _ attr = match Odoc_loader.parse_attribute attr with | None | Some (`Stop _ | `Alert _) -> () | Some (`Text (doc, loc) | `Doc (doc, loc)) -> let ast_docs = Odoc_parser.parse_comment ~location:(pad_loc loc) ~text:doc in let ast = Odoc_parser.ast ast_docs in List.iter (block_element line_directives oc names) ast in let attributes sub attrs = List.iter (attribute sub) attrs in (* For some reason, Tast_iterator.default_iterator does not recurse on Tsig_attribute and on attributes... *) let signature_item sub sig_ = match sig_.Typedtree.sig_desc with | Tsig_attribute attr -> attribute sub attr | Tsig_include incl -> attributes sub incl.incl_attributes | Tsig_open o -> attributes sub o.open_attributes | _ -> default_iterator.signature_item sub sig_ in let row_field sub rf = attributes sub rf.Typedtree.rf_attributes; default_iterator.row_field sub rf in let value_description sub vd = attributes sub vd.Typedtree.val_attributes; default_iterator.value_description sub vd in let label_declaration sub lbls = List.iter (fun ld -> attributes sub ld.Typedtree.ld_attributes) lbls in let constructor_declaration sub cd = (match cd.Typedtree.cd_args with | Cstr_record lds -> label_declaration sub lds | _ -> ()); attributes sub cd.cd_attributes in let type_kind sub tk = (match tk with | Typedtree.Ttype_record lbls -> label_declaration sub lbls | Ttype_variant cstrs -> List.iter (constructor_declaration sub) cstrs | _ -> ()); default_iterator.type_kind sub tk in let type_declaration sub decl = attributes sub decl.Typedtree.typ_attributes; default_iterator.type_declaration sub decl in let extension_constructor sub ext = attributes sub ext.Typedtree.ext_attributes; default_iterator.extension_constructor sub ext in let class_type_field sub ctf = attributes sub ctf.Typedtree.ctf_attributes; (match ctf.ctf_desc with | Tctf_attribute attr -> attribute sub attr | _ -> ()); default_iterator.class_type_field sub ctf in let class_type_declaration sub ctd = attributes sub ctd.Typedtree.ci_attributes; default_iterator.class_type_declaration sub ctd in let class_description sub cd = attributes sub cd.Typedtree.ci_attributes; default_iterator.class_description sub cd in (* let type_exception sub exc = *) (* attributes sub ext.Typedtree.ext_attributes; *) (* default_iterator.extension_constructor sub ext *) (* in *) let type_extension sub ext = attributes sub ext.Typedtree.tyext_attributes; default_iterator.type_extension sub ext in let module_type_declaration sub mtd = attributes sub mtd.Typedtree.mtd_attributes; default_iterator.module_type_declaration sub mtd in let module_declaration sub md = attributes sub md.Typedtree.md_attributes; default_iterator.module_declaration sub md in let module_expr sub me = attributes sub me.Typedtree.mod_attributes; default_iterator.module_expr sub me in let module_substitution sub ms = attributes sub ms.Typedtree.ms_attributes; default_iterator.module_substitution sub ms in (* let module_type_substitution sub mtd = *) (* attributes sub mtd.Typedtree.mtd_attributes; *) (* default_iterator.module_type_substitution sub ms *) (* in *) { default_iterator with row_field (* ; attribute *) (* ; attributes *); value_description; signature_item; type_kind; type_declaration; extension_constructor; type_extension; class_type_field; class_type_declaration; class_description; module_type_declaration; module_declaration; module_substitution; module_expr; } let load_cmti line_directives oc names input ~warnings_options = try let res = Odoc_loader.wrap_errors ~filename:input @@ fun () -> let cmt_info = Cmt_format.read_cmt input in match cmt_info.cmt_annots with | Interface intf -> let iterator = iterator line_directives oc names in iterator.signature iterator intf; Ok () | _ -> Error (`Msg (Format.sprintf "Provided file %s is not an interface" input)) in Odoc_model.Error.handle_errors_and_warnings ~warnings_options res |> Result.join with exn -> Error (`Msg (Format.sprintf "Error while unmarshalling input file %s:\n\ %s\n\ Check that the input file is a valid cmti file" input (Printexc.to_string exn))) let load_mld line_directives oc names input = let location = { Lexing.pos_fname = input; pos_lnum = 1; pos_bol = 0; pos_cnum = 0 } in let c = Io_utils.read_lines input |> String.concat ~sep:"\n" in let parsed = parse_comment ~location ~text:c in let ast = ast parsed in List.iter (block_element line_directives oc names) ast; Ok () let extract ~dst ~input ~names ~line_directives ~warnings_options = let ( let* ) = Result.bind in let* loader = match input |> Fpath.v |> Fpath.get_ext with | ".mld" -> Ok load_mld | ".cmti" -> Ok (load_cmti ~warnings_options) | _ -> Error (`Msg "Input must have either mld or cmti as extension") in match dst with | None -> loader line_directives stdout names input | Some dst -> Io_utils.with_open_out dst @@ fun oc -> loader line_directives oc names input