ocaml/ocamldoc/odoc_to_text.ml

609 lines
21 KiB
OCaml

(**************************************************************************)
(* *)
(* OCaml *)
(* *)
(* Maxence Guesdon, projet Cristal, INRIA Rocquencourt *)
(* *)
(* Copyright 2001 Institut National de Recherche en Informatique et *)
(* en Automatique. *)
(* *)
(* All rights reserved. This file is distributed under the terms of *)
(* the GNU Lesser General Public License version 2.1, with the *)
(* special exception on linking described in the file LICENSE. *)
(* *)
(**************************************************************************)
(** Text generation.
This module contains the class [to_text] with methods used to transform
information about elements to a [text] structure.*)
open Odoc_info
open Exception
open Type
open Value
open Module
open Class
(** A class used to get a [text] for info structures. *)
class virtual info =
object (self)
(** The list of pairs [(tag, f)] where [f] is a function taking
the [text] associated to [tag] and returning a [text].
Add a pair here to handle a tag.*)
val mutable tag_functions = ([] : (string * (Odoc_info.text -> Odoc_info.text)) list)
(** @return [text] value for an authors list. *)
method text_of_author_list l =
match l with
[] ->
[]
| _ ->
[ Bold [Raw (Odoc_messages.authors^": ")] ;
Raw (String.concat ", " l) ;
Newline
]
(** @return [text] value for the given optional version information.*)
method text_of_version_opt v_opt =
match v_opt with
None -> []
| Some v -> [ Bold [Raw (Odoc_messages.version^": ")] ;
Raw v ;
Newline
]
(** @return [text] value for the given optional since information.*)
method text_of_since_opt s_opt =
match s_opt with
None -> []
| Some s -> [ Bold [Raw (Odoc_messages.since^": ")] ;
Raw s ;
Newline
]
(** @return [text] value to represent the list of "before" information. *)
method text_of_before = function
[] -> []
| l ->
let f (v, text) =
(Bold [Raw (Printf.sprintf "%s %s " Odoc_messages.before v) ]) ::
text @
[Newline]
in
List.flatten (List.map f l)
(** @return [text] value for the given list of raised exceptions.*)
method text_of_raised_exceptions l =
match l with
[] -> []
| (s, t) :: [] ->
[ Bold [ Raw Odoc_messages.raises ] ;
Raw " " ;
Code s ;
Raw " "
]
@ t
@ [ Newline ]
| _ ->
[ Bold [ Raw Odoc_messages.raises ] ;
Raw " " ;
List
(List.map
(fun (ex, desc) ->(Code ex) :: (Raw " ") :: desc )
l
) ;
Newline
]
(** Return [text] value for the given "see also" reference. *)
method text_of_see (see_ref, t) =
match see_ref with
Odoc_info.See_url s -> [ Odoc_info.Link (s, t) ]
| Odoc_info.See_file s -> (Odoc_info.Code s) :: (Odoc_info.Raw " ") :: t
| Odoc_info.See_doc s -> (Odoc_info.Italic [Odoc_info.Raw s]) :: (Odoc_info.Raw " ") :: t
(** Return [text] value for the given list of "see also" references.*)
method text_of_sees l =
match l with
[] -> []
| see :: [] ->
(Bold [ Raw Odoc_messages.see_also ]) ::
(Raw " ") ::
(self#text_of_see see) @ [ Newline ]
| _ ->
(Bold [ Raw Odoc_messages.see_also ]) ::
[ List
(List.map
(fun see -> self#text_of_see see)
l
);
Newline
]
(** @return [text] value for the given optional return information.*)
method text_of_return_opt return_opt =
match return_opt with
None -> []
| Some t -> (Bold [Raw (Odoc_messages.returns^" ")]) :: t @ [ Newline ]
(** Return a [text] for the given list of custom tagged texts. *)
method text_of_custom l =
List.fold_left
(fun acc -> fun (tag, text) ->
try
let f = List.assoc tag tag_functions in
match acc with
[] -> f text
| _ -> acc @ (Newline :: (f text))
with
Not_found ->
Odoc_info.warning (Odoc_messages.tag_not_handled tag) ;
acc
)
[]
l
(** @return [text] value for a description, except for the i_params field. *)
method text_of_info ?(block=true) info_opt =
match info_opt with
None ->
[]
| Some info ->
let t =
(match info.i_deprecated with
None -> []
| Some t -> ( Italic [Raw (Odoc_messages.deprecated^" ")] ) :: t
) @
(match info.i_desc with
None -> []
| Some t when t = [Odoc_info.Raw ""] -> []
| Some t -> t @ [ Newline ]
) @
(self#text_of_author_list info.i_authors) @
(self#text_of_version_opt info.i_version) @
(self#text_of_before info.i_before) @
(self#text_of_since_opt info.i_since) @
(self#text_of_raised_exceptions info.i_raised_exceptions) @
(self#text_of_return_opt info.i_return_value) @
(self#text_of_sees info.i_sees) @
(self#text_of_custom info.i_custom)
in
if block then
[Block t]
else
t
end
(** This class defines methods to generate a [text] structure from elements. *)
class virtual to_text =
object (self)
inherit info
method virtual label : ?no_: bool -> string -> string
(** Take a string and return the string where fully qualified idents
have been replaced by idents relative to the given module name.
Also remove the "hidden modules".*)
method relative_idents m_name s =
let f str_t =
let match_s = Str.matched_string str_t in
let rel = Name.get_relative m_name match_s in
Odoc_info.apply_if_equal Odoc_info.use_hidden_modules match_s rel
in
Str.global_substitute
(Str.regexp "\\([A-Z]\\([a-zA-Z_'0-9]\\)*\\.\\)+\\([a-z][a-zA-Z_'0-9]*\\)")
f
s
(** Take a string and return the string where fully qualified idents
have been replaced by idents relative to the given module name.
Also remove the "hidden modules".*)
method relative_module_idents m_name s =
let f str_t =
let match_s = Str.matched_string str_t in
let rel = Name.get_relative m_name match_s in
Odoc_info.apply_if_equal Odoc_info.use_hidden_modules match_s rel
in
Str.global_substitute
(Str.regexp "\\([A-Z]\\([a-zA-Z_'0-9]\\)*\\.\\)+\\([A-Z][a-zA-Z_'0-9]*\\)")
f
s
(** Get a string for a [Types.class_type] where all idents are relative. *)
method normal_class_type m_name t =
self#relative_idents m_name (Odoc_info.string_of_class_type t)
(** Get a string for a [Types.module_type] where all idents are relative. *)
method normal_module_type ?code m_name t =
self#relative_module_idents m_name (Odoc_info.string_of_module_type ?code t)
(** Get a string for a type where all idents are relative. *)
method normal_type m_name t =
self#relative_idents m_name (Odoc_info.string_of_type_expr t)
(** Get a string for a list of types where all idents are relative. *)
method normal_type_list ?par m_name sep t =
self#relative_idents m_name (Odoc_info.string_of_type_list ?par sep t)
method normal_cstr_args ?par m_name = function
| Cstr_tuple l -> self#normal_type_list ?par m_name " * " l
| Cstr_record r -> self#relative_idents m_name
(Odoc_str.string_of_record r)
(** Get a string for a list of class or class type type parameters
where all idents are relative. *)
method normal_class_type_param_list m_name t =
self#relative_idents m_name (Odoc_info.string_of_class_type_param_list t)
(** Get a string for the parameters of a class (with arrows) where all idents are relative. *)
method normal_class_params m_name c =
let s = Odoc_info.string_of_class_params c in
self#relative_idents m_name
(Odoc_info.remove_ending_newline s)
(** @return [text] value to represent a [Types.type_expr].*)
method text_of_type_expr module_name t =
List.flatten
(List.map
(fun s -> [Code s ; Newline ])
(Str.split (Str.regexp "\n")
(self#normal_type module_name t))
)
(** Return [text] value for a given short [Types.type_expr].*)
method text_of_short_type_expr module_name t =
[ Code (self#normal_type module_name t) ]
(** Return [text] value or the given list of [Types.type_expr], with
the given separator. *)
method text_of_type_expr_list module_name sep l =
[ Code (self#normal_type_list module_name sep l) ]
(** Return [text] value or the given list of [Types.type_expr],
as type parameters of a class of class type. *)
method text_of_class_type_param_expr_list module_name l =
[ Code (self#normal_class_type_param_list module_name l) ]
(** @return [text] value to represent parameters of a class (with arrows).*)
method text_of_class_params module_name c =
Odoc_info.text_concat
[Newline]
(List.map
(fun s -> [Code s])
(Str.split (Str.regexp "\n")
(self#normal_class_params module_name c))
)
(** @return [text] value to represent a [Types.module_type]. *)
method text_of_module_type t =
let s = String.concat "\n"
(Str.split (Str.regexp "\n") (Odoc_info.string_of_module_type t))
in
[ Code s ]
(** @return [text] value for a value. *)
method text_of_value v =
let name = v.val_name in
let s_name = Name.simple name in
let s =
Format.fprintf Format.str_formatter "@[<hov 2>val %s :@ %s"
s_name
(self#normal_type (Name.father v.val_name) v.val_type);
Format.flush_str_formatter ()
in
[ CodePre s ] @
[Latex ("\\index{"^(self#label s_name)^"@\\verb`"^(self#label ~no_:false s_name)^"`}\n")] @
(self#text_of_info v.val_info)
(** @return [text] value for a class attribute. *)
method text_of_attribute a =
let s_name = Name.simple a.att_value.val_name in
let mod_name = Name.father a.att_value.val_name in
let s =
Format.fprintf Format.str_formatter "@[<hov 2>val %s%s%s :@ %s"
(if a.att_virtual then "virtual " else "")
(if a.att_mutable then "mutable " else "")
s_name
(self#normal_type mod_name a.att_value.val_type);
Format.flush_str_formatter ()
in
(CodePre s) ::
[Latex ("\\index{"^(self#label s_name)^"@\\verb`"^(self#label ~no_:false s_name)^"`}\n")] @
(self#text_of_info a.att_value.val_info)
(** @return [text] value for a class method. *)
method text_of_method m =
let s_name = Name.simple m.met_value.val_name in
let mod_name = Name.father m.met_value.val_name in
let s =
Format.fprintf Format.str_formatter "@[<hov 2>method %s%s%s :@ %s"
(if m.met_private then "private " else "")
(if m.met_virtual then "virtual " else "")
s_name
(self#normal_type mod_name m.met_value.val_type);
Format.flush_str_formatter ()
in
(CodePre s) ::
[Latex ("\\index{"^(self#label s_name)^"@\\verb`"^(self#label ~no_:false s_name)^"`}\n")] @
(self#text_of_info m.met_value.val_info)
(** @return [text] value for an exception. *)
method text_of_exception e =
let s_name = Name.simple e.ex_name in
let father = Name.father e.ex_name in
Format.fprintf Format.str_formatter "@[<hov 2>exception %s" s_name ;
(match e.ex_args, e.ex_ret with
Cstr_tuple [], None -> ()
| Cstr_tuple [], Some r ->
Format.fprintf Format.str_formatter " %s@ %s"
":"
(self#normal_type father r)
| args, None ->
Format.fprintf Format.str_formatter " %s@ %s"
"of"
(self#normal_cstr_args ~par:false father args)
| args, Some r ->
Format.fprintf Format.str_formatter " %s@ %s@ %s@ %s"
":"
(self#normal_cstr_args ~par:false father args)
"->"
(self#normal_type father r)
);
(match e.ex_alias with
None -> ()
| Some ea ->
Format.fprintf Format.str_formatter " = %s"
(
match ea.ea_ex with
None -> ea.ea_name
| Some e -> e.ex_name
)
);
let s2 = Format.flush_str_formatter () in
[ CodePre s2 ] @
[Latex ("\\index{"^(self#label s_name)^"@\\verb`"^(self#label ~no_:false s_name)^"`}\n")] @
(self#text_of_info e.ex_info)
(** Return [text] value for the description of a function parameter. *)
method text_of_parameter_description p =
match Parameter.names p with
[] -> []
| name :: [] ->
(
(* Only one name, no need for label for the description. *)
match Parameter.desc_by_name p name with
None -> []
| Some t -> t
)
| l ->
(* A list of names, we display those with a description. *)
let l2 = List.filter (fun n -> (Parameter.desc_by_name p n) <> None) l in
match l2 with
[] -> []
| _ ->
[List
(List.map
(fun n ->
match Parameter.desc_by_name p n with
None -> [] (* should not occur *)
| Some t -> [Code (n^" ") ; Raw ": "] @ t
)
l2
)
]
(** Return [text] value for a list of parameters. *)
method text_of_parameter_list m_name l =
match l with
[] ->
[]
| _ ->
[ Bold [Raw Odoc_messages.parameters] ;
Raw ":" ;
List
(List.map
(fun p ->
(match Parameter.complete_name p with
"" -> Code "?"
| s -> Code s
) ::
[Code " : "] @
(self#text_of_short_type_expr m_name (Parameter.typ p)) @
[Newline] @
(self#text_of_parameter_description p)
)
l
)
]
(** Return [text] value for a list of module parameters. *)
method text_of_module_parameter_list l =
match l with
[] ->
[]
| _ ->
[ Newline ;
Bold [Raw Odoc_messages.parameters] ;
Raw ":" ;
List
(List.map
(fun (p, desc_opt) ->
begin match p.mp_type with None -> [Raw ""]
| Some mty ->
[Code (p.mp_name^" : ")] @
(self#text_of_module_type mty)
end @
(match desc_opt with
None -> []
| Some t -> (Raw " ") :: t)
)
l
)
]
(**/**)
(** Return [text] value for the given [class_kind].*)
method text_of_class_kind father ckind =
match ckind with
Class_structure _ ->
[Code Odoc_messages.object_end]
| Class_apply capp ->
[Code
(
(
match capp.capp_class with
None -> capp.capp_name
| Some cl -> cl.cl_name
)^
" "^
(String.concat " "
(List.map
(fun s -> "("^s^")")
capp.capp_params_code))
)
]
| Class_constr cco ->
(
match cco.cco_type_parameters with
[] -> []
| l ->
(Code "[")::
(self#text_of_type_expr_list father ", " l)@
[Code "] "]
)@
[Code (
match cco.cco_class with
None -> cco.cco_name
| Some (Cl cl) -> Name.get_relative father cl.cl_name
| Some (Cltype (clt,_)) -> Name.get_relative father clt.clt_name
)
]
| Class_constraint (ck, ctk) ->
[Code "( "] @
(self#text_of_class_kind father ck) @
[Code " : "] @
(self#text_of_class_type_kind father ctk) @
[Code " )"]
(** Return [text] value for the given [class_type_kind].*)
method text_of_class_type_kind father ctkind =
match ctkind with
Class_type cta ->
(
match cta.cta_type_parameters with
[] -> []
| l ->
(Code "[") ::
(self#text_of_class_type_param_expr_list father l) @
[Code "] "]
) @
(
match cta.cta_class with
None -> [ Code cta.cta_name ]
| Some (Cltype (clt, _)) ->
let rel = Name.get_relative father clt.clt_name in
[Code rel]
| Some (Cl cl) ->
let rel = Name.get_relative father cl.cl_name in
[Code rel]
)
| Class_signature _ ->
[Code Odoc_messages.object_end]
(** Return [text] value for a [module_kind]. *)
method text_of_module_kind ?(with_def_syntax=true) k =
match k with
Module_alias m_alias ->
(match m_alias.ma_module with
None ->
[Code ((if with_def_syntax then " = " else "")^m_alias.ma_name)]
| Some (Mod m) ->
[Code ((if with_def_syntax then " = " else "")^m.m_name)]
| Some (Modtype mt) ->
[Code ((if with_def_syntax then " = " else "")^mt.mt_name)]
)
| Module_apply (k1, k2) ->
(if with_def_syntax then [Code " = "] else []) @
(self#text_of_module_kind ~with_def_syntax: false k1) @
[Code " ( "] @
(self#text_of_module_kind ~with_def_syntax: false k2) @
[Code " ) "]
| Module_with (tk, code) ->
(if with_def_syntax then [Code " : "] else []) @
(self#text_of_module_type_kind ~with_def_syntax: false tk) @
[Code code]
| Module_constraint (k, tk) ->
(if with_def_syntax then [Code " : "] else []) @
[Code "( "] @
(self#text_of_module_kind ~with_def_syntax: false k) @
[Code " : "] @
(self#text_of_module_type_kind ~with_def_syntax: false tk) @
[Code " )"]
| Module_struct _ ->
[Code ((if with_def_syntax then " : " else "")^
Odoc_messages.struct_end^" ")]
| Module_functor (_, k) ->
(if with_def_syntax then [Code " : "] else []) @
[Code "functor ... "] @
[Code " -> "] @
(self#text_of_module_kind ~with_def_syntax: false k)
| Module_typeof s ->
let code = Printf.sprintf "%smodule type of %s"
(if with_def_syntax then " : " else "")
s
in
[Code code]
| Module_unpack (code, _) ->
let code = Printf.sprintf "%s%s"
(if with_def_syntax then " : " else "")
code
in
[Code code]
(** Return html code for a [module_type_kind].*)
method text_of_module_type_kind ?(with_def_syntax=true) tk =
match tk with
| Module_type_struct _ ->
[Code ((if with_def_syntax then " = " else "")^Odoc_messages.sig_end)]
| Module_type_functor (p, k) ->
let t1 =
[Code ("("^p.mp_name^" : ")] @
(self#text_of_module_type_kind p.mp_kind) @
[Code ") -> "]
in
let t2 = self#text_of_module_type_kind ~with_def_syntax: false k in
(if with_def_syntax then [Code " = "] else []) @ t1 @ t2
| Module_type_with (tk2, code) ->
let t = self#text_of_module_type_kind ~with_def_syntax: false tk2 in
(if with_def_syntax then [Code " = "] else []) @
t @ [Code code]
| Module_type_alias mt_alias ->
[Code ((if with_def_syntax then " = " else "")^
(match mt_alias.mta_module with
None -> mt_alias.mta_name
| Some mt -> mt.mt_name))
]
| Odoc_module.Module_type_typeof s ->
let code = Printf.sprintf "%smodule type of %s"
(if with_def_syntax then " = " else "") s
in
[ Code code ]
end