<commit_before>(***********************************************************************)
(*                             OCamldoc                                *)
(*                                                                     *)
(*            <NAME>, <NAME>, 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 Q Public License version 1.0.               *)
(*                                                                     *)
(***********************************************************************)


(** Generation of html documentation. *)

let print_DEBUG s = print_string s ; print_newline ()

open Odoc_info 
open Parameter
open Value
open Type
open Exception
open Class 
open Module


(** The functions used for naming files and html marks.*)
module Naming =
  struct
    (** The prefix for types marks. *)
    let mark_type = "TYPE"

    (** The prefix for functions marks. *)
    let mark_function = "FUN"

    (** The prefix for exceptions marks. *)
    let mark_exception = "EXCEPTION"

    (** The prefix for values marks. *)
    let mark_value = "VAL"

    (** The prefix for attributes marks. *)
    let mark_attribute = "ATT"

    (** The prefix for methods marks. *)
    let mark_method = "METHOD"

    (** The prefix for code files.. *)
    let code_prefix = "code_"

    (** The prefix for type files.. *)
    let type_prefix = "type_"

    (** Return the two html files names for the given module or class name.*)
    let html_files name =
      let html_file = name^".html" in
      let html_frame_file = name^"-frame.html" in
      (html_file, html_frame_file)

    (** Return the target for the given prefix and simple name. *)
    let target pref simple_name = pref^simple_name

    (** Return the complete link target (file#target) for the given prefix string and complete name.*)
    let complete_target pref complete_name = 
      let simple_name = Name.simple complete_name in
      let module_name = 
        let s = Name.father complete_name in
        if s = "" then simple_name else s
      in
      let (html_file, _) = html_files module_name in
      html_file^"#"^(target pref simple_name)

    (** Return the link target for the given type. *)
    let type_target t = target mark_type (Name.simple t.ty_name)

    (** Return the complete link target for the given type. *)
    let complete_type_target t = complete_target mark_type t.ty_name

    (** Return the link target for the given exception. *)
    let exception_target e = target mark_exception (Name.simple e.ex_name)

    (** Return the complete link target for the given exception. *)
    let complete_exception_target e = complete_target mark_exception e.ex_name

	
    
    (** Return the link target for the given value. *)
    let value_target v = target mark_value (Name.simple v.val_name)

    (** Return the given value name where symbols accepted in infix values
       are replaced by strings, to avoid clashes with the filesystem.*)
    let subst_infix_symbols name =
      let len = String.length name in
      let buf = Buffer.create len in
      let ch c = Buffer.add_char buf c in
      let st s = Buffer.add_string buf s in
      for i = 0 to len - 1 do
	match name.[i] with
	| '|' -> st "_pipe_"
        | '<' -> st "_lt_"
        | '>' -> st "_gt_"
        | '@' -> st "_at_"
        | '^' -> st "_exp_"
        | '&' -> st "_amp_"
        | '+' -> st "_plus_"
        | '-' -> st "_minus_"
        | '*' -> st "_star_"
        | '/' -> st "_slash_"
        | '$' -> st "_dollar_"
        | '%' -> st "_percent_"
        | '=' -> st "_equal_"
        | ':' -> st "_column_"
        | '~' -> st "_tilde_"
        | '!' -> st "_bang_"
	| c -> ch c
      done;
      Buffer.contents buf

    (** Return the complete link target for the given value. *)
    let complete_value_target v = complete_target mark_value v.val_name

    (** Return the complete filename for the code of the given value. *)
    let file_code_value_complete_target v = 
      let f = code_prefix^mark_value^(subst_infix_symbols v.val_name)^".html" in
      f

    (** Return the link target for the given attribute. *)
    let attribute_target a = target mark_attribute (Name.simple a.att_value.val_name)

    (** Return the complete link target for the given attribute. *)
    let complete_attribute_target a = complete_target mark_attribute a.att_value.val_name

    (** Return the complete filename for the code of the given attribute. *)
    let file_code_attribute_complete_target a = 
      let f = code_prefix^mark_attribute^a.att_value.val_name^".html" in
      f

    (** Return the link target for the given method. *)
    let method_target m = target mark_method (Name.simple m.met_value.val_name)

    (** Return the complete link target for the given method. *)
    let complete_method_target m = complete_target mark_method m.met_value.val_name

    (** Return the complete filename for the code of the given method. *)
    let file_code_method_complete_target m = 
      let f = code_prefix^mark_method^m.met_value.val_name^".html" in
      f

    (** Return the link target for the given label section. *)
    let label_target l = target "" l

    (** Return the complete link target for the given section label. *)
    let complete_label_target l = complete_target "" l

    (** Return the complete filename for the code of the type of the 
       given module or module type name. *)
    let file_type_module_complete_target name = 
      let f = type_prefix^name^".html" in
      f

    (** Return the complete filename for the code of the type of the 
       given class or class type name. *)
    let file_type_class_complete_target name = 
      let f = type_prefix^name^".html" in
      f
  end

(** A class with a method to colorize a string which represents OCaml code. *)
class ocaml_code =
  object(self)
    method html_of_code ?(with_pre=true) code =
      let html_code = Odoc_ocamlhtml.html_of_code ~with_pre: with_pre code in
      html_code
  end

(** Generation of html code from text structures. *)
class text =
  object (self)
    (** We want to display colorized code. *)
    inherit ocaml_code 

    (** Escape the strings which would clash with html syntax, and
       make some replacements (double newlines replaced by <br>). *)
    method escape s = Odoc_ocamlhtml.escape_base s


    method keep_alpha_num s =
      let len = String.length s in
      let buf = Buffer.create len in
      for i = 0 to len - 1 do
        match s.[i] with
          'a'..'z' | 'A'..'Z' | '0'..'9' -> Buffer.add_char buf s.[i]
        | _ -> ()
      done;
      Buffer.contents buf

    (** Return a label created from the first sentence of a text. *)
    method label_of_text t=
      let t2 = Odoc_info.first_sentence_of_text t in
      let s = Odoc_info.string_of_text t2 in
      let s2 = self#keep_alpha_num s in
      s2

    (** Create a label for the associated title. 
       Return the label specified by the user or a label created
       from the title level and the first sentence of the title. *)
    method create_title_label (n,label_opt,t) =
      match label_opt with
        Some s -> s
      | None -> Printf.sprintf "%d_%s" n (self#label_of_text t)

    (** Return the html code corresponding to the [text] parameter. *)
    method html_of_text t = String.concat "" (List.map self#html_of_text_element t)

    (** Return the html code for the [text_element] in parameter. *)
    method html_of_text_element te =
      print_DEBUG "text::html_of_text_element";
      match te with
      | Odoc_info.Raw s -> self#html_of_Raw s
      | Odoc_info.Code s -> self#html_of_Code s
      | Odoc_info.CodePre s -> self#html_of_CodePre s
      | Odoc_info.Verbatim s -> self#html_of_Verbatim s
      | Odoc_info.Bold t -> self#html_of_Bold t
      | Odoc_info.Italic t -> self#html_of_Italic t 
      | Odoc_info.Emphasize t -> self#html_of_Emphasize t
      | Odoc_info.Center t -> self#html_of_Center t
      | Odoc_info.Left t -> self#html_of_Left t
      | Odoc_info.Right t -> self#html_of_Right t
      | Odoc_info.List tl -> self#html_of_List tl
      | Odoc_info.Enum tl -> self#html_of_Enum tl
      | Odoc_info.Newline -> self#html_of_Newline
      | Odoc_info.Block t -> self#html_of_Block t
      | Odoc_info.Title (n, l_opt, t) -> self#html_of_Title n l_opt t
      | Odoc_info.Latex s -> self#html_of_Latex s
      | Odoc_info.Link (s, t) -> self#html_of_Link s t
      | Odoc_info.Ref (name, ref_opt) -> self#html_of_Ref name ref_opt
      | Odoc_info.Superscript t -> self#html_of_Superscript t
      | Odoc_info.Subscript t -> self#html_of_Subscript t

    method html_of_Raw s = self#escape s

    method html_of_Code s =
      if !Args.colorize_code then
        self#html_of_code ~with_pre: false s
      else
        "<code class=\""^Odoc_ocamlhtml.code_class^"\">"^(self#escape s)^"</code>"

    method html_of_CodePre s =
      if !Args.colorize_code then
        "<pre></pre>"^(self#html_of_code s)^"<pre></pre>"
      else
        "<pre><code class=\""^Odoc_ocamlhtml.code_class^"\">"^(self#escape s)^"</code></pre>"

    method html_of_Verbatim s = "<pre>"^(self#escape s)^"</pre>"
    method html_of_Bold t = "<b>"^(self#html_of_text t)^"</b>"
    method html_of_Italic t = "<i>"^(self#html_of_text t)^"</i>"
    method html_of_Emphasize t = "<em>"^(self#html_of_text t)^"</em>"
    method html_of_Center t = "<center>"^(self#html_of_text t)^"</center>"
    method html_of_Left t = "<div align=left>"^(self#html_of_text t)^"</div>"
    method html_of_Right t = "<div align=right>"^(self#html_of_text t)^"</div>"

    method html_of_List tl = 
      "<ul>\n"^
      (String.concat ""
         (List.map (fun t -> "<li>"^(self#html_of_text t)^"</li>\n") tl))^
      "</ul>\n"

    method html_of_Enum tl =
      "<OL>\n"^
      (String.concat ""
         (List.map (fun t -> "<li>"^(self#html_of_text t)^"</li>\n") tl))^
      "</OL>\n"

    method html_of_Newline = "\n<p>\n"

    method html_of_Block t =
      "<blockquote>\n"^(self#html_of_text t)^"</blockquote>\n"

    method html_of_Title n label_opt t =
      let css_class = "title"^(string_of_int n) in
      let label1 = self#create_title_label (n, label_opt, t) in
      "<br>\n"^
      "<a name=\""^(Naming.label_target label1)^"\"></a>\n"^
      "<table cellpadding=5 cellspacing=5 width=\"100%\">\n"^
      "<tr class=\""^css_class^"\"><td><div align=center>\n"^
      "<span class=\""^css_class^"\">"^(self#html_of_text t)^"</span>\n"^
      "</div>\n</td>\n</tr>\n</table>\n"

    method html_of_Latex _ = ""
      (* don't care about LaTeX stuff in HTML. *)

    method html_of_Link s t =
      "<a href=\""^s^"\">"^(self#html_of_text t)^"</a>"

    method html_of_Ref name ref_opt =
      match ref_opt with
        None -> 
          self#html_of_text_element (Odoc_info.Code name)
      | Some kind ->
	  let h name = Odoc_info.Code (Odoc_info.use_hidden_modules name) in
          let (target, text) = 
            match kind with
              Odoc_info.RK_module 
            | Odoc_info.RK_module_type
            | Odoc_info.RK_class
            | Odoc_info.RK_class_type ->
                let (html_file, _) = Naming.html_files name in
                (html_file, h name)
            | Odoc_info.RK_value -> (Naming.complete_target Naming.mark_value name, h name)
            | Odoc_info.RK_type -> (Naming.complete_target Naming.mark_type name, h name)
            | Odoc_info.RK_exception -> (Naming.complete_target Naming.mark_exception name, h name)
            | Odoc_info.RK_attribute -> (Naming.complete_target Naming.mark_attribute name, h name)
            | Odoc_info.RK_method -> (Naming.complete_target Naming.mark_method name, h name)
            | Odoc_info.RK_section t -> (Naming.complete_label_target name,
					 Odoc_info.Italic [Raw (Odoc_info.string_of_text t)])
          in
          "<a href=\""^target^"\">"^
          (self#html_of_text_element text)^
	  "</a>"

    method html_of_Superscript t =
      "<sup class=\"superscript\">"^(self#html_of_text t)^"</sup>"

    method html_of_Subscript t =
      "<sub class=\"subscript\">"^(self#html_of_text t)^"</sub>"

  end

(** A class used to generate html code 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 html code. 
       Add a pair here to handle a tag.*)
    val mutable tag_functions = ([] : (string * (Odoc_info.text -> string)) list)

    (** The method used to get html code from a [text]. *)
    method virtual html_of_text : Odoc_info.text -> string

    (** Return html for an author list. *)
    method html_of_author_list l =
      match l with
        [] ->
          ""
      | _ ->
          "<b>"^Odoc_messages.authors^": </b>"^
          (String.concat ", " l)^
          "<br>\n"

    (** Return html code for the given optional version information.*)
    method html_of_version_opt v_opt =
      match v_opt with
        None -> ""
      | Some v -> "<b>"^Odoc_messages.version^": </b>"^v^"<br>\n"

    (** Return html code for the given optional since information.*)
    method html_of_since_opt s_opt =
      match s_opt with
        None -> ""
      | Some s -> "<b>"^Odoc_messages.since^"</b> "^s^"<br>\n"

    (** Return html code for the given list of raised exceptions.*)
    method html_of_raised_exceptions l =
      match l with
        [] -> ""
      | (s, t) :: [] -> "<b>"^Odoc_messages.raises^"</b> <code>"^s^"</code> "^(self#html_of_text t)^"<br>\n"
      | _ ->
          "<b>"^Odoc_messages.raises^"</b><ul>"^
          (String.concat ""
             (List.map
                (fun (ex, desc) -> "<li><code>"^ex^"</code> "^(self#html_of_text desc)^"</li>\n")
                l
             )
          )^"</ul>\n"

    (** Return html code for the given "see also" reference. *)
    method html_of_see (see_ref, t)  =
      let t_ref = 
        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
      in
      self#html_of_text t_ref

    (** Return html code for the given list of "see also" references.*)
    method html_of_sees l =
      match l with
        [] -> ""
      | see :: [] -> "<b>"^Odoc_messages.see_also^"</b> "^(self#html_of_see see)^"<br>\n"
      | _ ->
          "<b>"^Odoc_messages.see_also^"</b><ul>"^
          (String.concat ""
             (List.map
                (fun see -> "<li>"^(self#html_of_see see)^"</li>\n")
                l
             )
          )^"</ul>\n"

    (** Return html code for the given optional return information.*)
    method html_of_return_opt return_opt =
      match return_opt with
        None -> ""
      | Some s -> "<b>"^Odoc_messages.returns^"</b> "^(self#html_of_text s)^"<br>\n"

    (** Return html code for the given list of custom tagged texts. *)
    method html_of_custom l =
      let buf = Buffer.create 50 in
      List.iter
        (fun (tag, text) ->
          try
            let f = List.assoc tag tag_functions in
            Buffer.add_string buf (f text)
          with
            Not_found ->
              Odoc_info.warning (Odoc_messages.tag_not_handled tag)
        )
        l;
      Buffer.contents buf

    (** Return html code for a description, except for the [i_params] field. *)
    method html_of_info info_opt =
      match info_opt with
        None ->
          ""
      | Some info ->
          let module M = Odoc_info in
          "<div class=\"info\">\n"^
          (match info.M.i_deprecated with
            None -> ""
          | Some d -> 
              "<span class=\"warning\">"^Odoc_messages.deprecated^"</span> "^
              (self#html_of_text d)^
              "<br>\n"
          )^
          (match info.M.i_desc with
            None -> "" 
          | Some d when d = [Odoc_info.Raw ""] -> ""
          | Some d -> (self#html_of_text d)^"<br>\n"
          )^
          (self#html_of_author_list info.M.i_authors)^
          (self#html_of_version_opt info.M.i_version)^
          (self#html_of_since_opt info.M.i_since)^
          (self#html_of_raised_exceptions info.M.i_raised_exceptions)^
          (self#html_of_return_opt info.M.i_return_value)^
          (self#html_of_sees info.M.i_sees)^
          (self#html_of_custom info.M.i_custom)^
          "</div>\n"

    (** Return html code for the first sentence of a description. 
       The titles and lists in this first sentence has been removed.*)
    method html_of_info_first_sentence info_opt =
      match info_opt with
        None -> ""
      | Some info ->
          let module M = Odoc_info in
          let dep = info.M.i_deprecated <> None in
          "<div class=\"info\">\n"^
          (if dep then "<font color=\"#CCCCCC\">" else "") ^
          (match info.M.i_desc with
            None -> "" 
          | Some d when d = [Odoc_info.Raw ""] -> ""
          | Some d -> (self#html_of_text
                         (Odoc_info.text_no_title_no_list
                            (Odoc_info.first_sentence_of_text d)))^"\n"
          )^
          (if dep then "</font>" else "") ^
          "</div>\n"

  end



let opt = Odoc_info.apply_opt

(** This class is used to create objects which can generate a simple html documentation. *)
class html =
  object (self)
    inherit text
    inherit info

    (** The default style options. *)
    val mutable default_style_options =
      ["a:visited {color : #416DFF; text-decoration : none; }" ;
        "a:link {color : #416DFF; text-decoration : none;}" ;
        "a:hover {color : Red; text-decoration : none; background-color: #5FFF88}" ;
        "a:active {color : Red; text-decoration : underline; }" ;
        ".keyword { font-weight : bold ; color : Red }" ;
        ".keywordsign { color : #C04600 }" ; 
        ".superscript { font-size : 4 }" ;
        ".subscript { font-size : 4 }" ;
        ".comment { color : Green }" ;
        ".constructor { color : Blue }" ;
        ".type { color : #5C6585 }" ;
        ".string { color : Maroon }" ;
        ".warning { color : Red ; font-weight : bold }" ;
        ".info { margin-left : 3em; margin-right : 3em }" ;
        ".code { color : #465F91 ; }" ;
        ".title1 { font-size : 20pt ; background-color : #909DFF }" ;
        ".title2 { font-size : 20pt ; background-color : #90BDFF }" ;
        ".title3 { font-size : 20pt ; background-color : #90DDFF }" ;
        ".title4 { font-size : 20pt ; background-color : #90EDFF }" ;
        ".title5 { font-size : 20pt ; background-color : #90FDFF }" ;
        ".title6 { font-size : 20pt ; background-color : #C0FFFF }" ;
	".typetable { border-style : hidden }" ;
	".indextable { border-style : hidden }" ;
	".paramstable { border-style : hidden ; padding: 5pt 5pt}" ;
        "body { background-color : White }" ;
        "tr { background-color : White }" ;
	"td.typefieldcomment { background-color : #FFFFFF }" ;
      ] 
      
    (** The style file for all pages. *)
    val mutable style_file = "style.css"

    (** The code to import the style. Initialized in [init_style]. *)
    val mutable style = ""

    (** The known types names. 
       Used to know if we must create a link to a type
       when printing a type. *)
    val mutable known_types_names = []

    (** The known class and class type names. 
       Used to know if we must create a link to a class 
       or class type or not when printing a type. *)
    val mutable known_classes_names = []

    (** The known modules and module types names. 
       Used to know if we must create a link to a type or not
       when printing a module type. *)
    val mutable known_modules_names = []

    (** The main file. *)
    val mutable index = "index.html"
    (** The file for the index of values. *)
    val mutable index_values = "index_values.html"
    (** The file for the index of types. *)
    val mutable index_types = "index_types.html"
    (** The file for the index of exceptions. *)
    val mutable index_exceptions = "index_exceptions.html"
    (** The file for the index of attributes. *)
    val mutable index_attributes = "index_attributes.html"
    (** The file for the index of methods. *)
    val mutable index_methods = "index_methods.html"
    (** The file for the index of classes. *)
    val mutable index_classes = "index_classes.html"
    (** The file for the index of class types. *)
    val mutable index_class_types = "index_class_types.html"
    (** The file for the index of modules. *)
    val mutable index_modules = "index_modules.html"
    (** The file for the index of module types. *)
    val mutable index_module_types = "index_module_types.html"


    (** The list of attributes. Filled in the [generate] method. *)
    val mutable list_attributes = []
    (** The list of methods. Filled in the [generate] method. *)
    val mutable list_methods = []
    (** The list of values. Filled in the [generate] method. *)
    val mutable list_values = []
    (** The list of exceptions. Filled in the [generate] method. *)
    val mutable list_exceptions = []
    (** The list of types. Filled in the [generate] method. *)
    val mutable list_types = []
    (** The list of modules. Filled in the [generate] method. *)
    val mutable list_modules = []
    (** The list of module types. Filled in the [generate] method. *)
    val mutable list_module_types = []
    (** The list of classes. Filled in the [generate] method. *)
    val mutable list_classes = []
    (** The list of class types. Filled in the [generate] method. *)
    val mutable list_class_types = []

    (** The header of pages. Must be prepared by the [prepare_header] method.*)
    val mutable header = fun ?(nav=None) -> fun ?(comments=[]) -> fun _ -> ""

    (** Init the style. *)
    method init_style =
      (match !Args.css_style with
        None -> 
          let default_style = String.concat "\n" default_style_options in
          (
           try
             let chanout = open_out (Filename.concat !Args.target_dir style_file) in
             output_string chanout default_style ;
             flush chanout ;
             close_out chanout
           with
             Sys_error s ->
               prerr_endline s ;
               incr Odoc_info.errors ;
          )
      | Some f ->
          style_file <- f 
      );
      style <- "<link rel=\"stylesheet\" href=\""^style_file^"\" type=\"text/css\">\n"

    (** Get the title given by the user *)
    method title = match !Args.title with None -> "" | Some t -> self#escape t

    (** Get the title given by the user completed with the given subtitle. *)
    method inner_title s = 
      (match self#title with "" -> "" | t -> t^" : ")^
      (self#escape s)

    (** Get the page header. *)
    method header ?nav ?comments title = header ?nav ?comments title

    (** A function to build the header of pages. *)
    method prepare_header module_list =
      let f ?(nav=None) ?(comments=[]) t  = 
        let link_if_not_empty l m url =
          match l with
            [] -> ""
          | _ -> "<link title=\""^m^"\" rel=Appendix href=\""^url^"\">\n"
        in
        "<head>\n"^
        style^
        "<link rel=\"Start\" href=\""^index^"\">\n"^
        (
         match nav with
           None -> ""
         | Some (pre_opt, post_opt, name) ->
             (match pre_opt with
               None -> ""
             | Some name -> 
                 "<link rel=\"previous\" href=\""^(fst (Naming.html_files name))^"\">\n"
             )^
             (match post_opt with
               None -> ""
             | Some name -> 
                 "<link rel=\"next\" href=\""^(fst (Naming.html_files name))^"\">\n"
             )^
             (
              let father = Name.father name in
              let href = if father = "" then index else fst (Naming.html_files father) in
              "<link rel=\"Up\" href=\""^href^"\">\n"
             )
        )^
        (link_if_not_empty list_types Odoc_messages.index_of_types index_types)^
        (link_if_not_empty list_exceptions Odoc_messages.index_of_exceptions index_exceptions)^
        (link_if_not_empty list_values Odoc_messages.index_of_values index_values)^
        (link_if_not_empty list_attributes Odoc_messages.index_of_attributes index_attributes)^
        (link_if_not_empty list_methods Odoc_messages.index_of_methods index_methods)^
        (link_if_not_empty list_classes Odoc_messages.index_of_classes index_classes)^
        (link_if_not_empty list_class_types Odoc_messages.index_of_class_types index_class_types)^
        (link_if_not_empty list_modules Odoc_messages.index_of_modules index_modules)^
        (link_if_not_empty list_module_types Odoc_messages.index_of_module_types index_module_types)^
        (String.concat "\n"
           (List.map
              (fun m -> 
                let html_file = fst (Naming.html_files m.m_name) in
                "<link title=\""^m.m_name^"\" rel=\"Chapter\" href=\""^html_file^"\">"
              )
              module_list
           )
        )^
        (self#html_sections_links comments)^
        "<title>"^
        t^
        "</title>\n</head>\n"
      in
      header <- f

    (** Build the html code for the link tags in the header, defining section and
       subsections for the titles found in the given comments.*)
    method html_sections_links comments =
      let titles = List.flatten (List.map Odoc_info.get_titles_in_text comments) in
      let levels = 
        let rec iter acc l =
          match l with
            [] -> acc
          | (n,_,_) :: q ->
              if List.mem n acc 
              then iter acc q
              else iter (n::acc) q
        in
        iter [] titles
      in
      let sorted_levels = List.sort compare levels in
      let (section_level, subsection_level) =
        match sorted_levels with
          [] -> (None, None)
        | [n] -> (Some n, None)
        | n :: m :: _ -> (Some n, Some m)
      in
      let titles_per_level level_opt =
        match level_opt with
          None -> []
        | Some n -> List.filter (fun (m,_,_) -> m = n) titles
      in
      let section_titles = titles_per_level section_level in
      let subsection_titles = titles_per_level subsection_level in
      let create_lines s_rel titles =
        List.map
          (fun (n,lopt,t) -> 
            let s = Odoc_info.string_of_text t in
            let label = self#create_title_label (n,lopt,t) in
            Printf.sprintf "<link title=\"%s\" rel=\"%s\" href=\"#%s\">\n" s s_rel label)
          titles
      in
      let section_lines = create_lines "Section" section_titles in
      let subsection_lines = create_lines "Subsection" subsection_titles in
      String.concat "" (section_lines @ subsection_lines)

    (** Html code for navigation bar. 
       @param pre optional name for optional previous module/class 
       @param post optional name for optional next module/class
       @param name name of current module/class *)
    method navbar pre post name =
      "<div class=\"navbar\">"^
      (match pre with
        None -> ""
      | Some name -> 
          "<a href=\""^(fst (Naming.html_files name))^"\">"^Odoc_messages.previous^"</a>\n"
      )^
      "&nbsp;"^
      (
       let father = Name.father name in
       let href = if father = "" then index else fst (Naming.html_files father) in
       "<a href=\""^href^"\">"^Odoc_messages.up^"</a>\n"
      )^
      "&nbsp;"^
      (match post with
        None -> ""
      | Some name -> 
          "<a href=\""^(fst (Naming.html_files name))^"\">"^Odoc_messages.next^"</a>\n"
      )^
      "</div>\n"

    (** Return html code with the given string in the keyword style.*)
    method keyword s = 
      "<span class=\"keyword\">"^s^"</span>"

    (** Return html code with the given string in the constructor style. *)
    method constructor s = "<span class=\"constructor\">"^s^"</span>"

    (** Output the given ocaml code to the given file name. *)
    method private output_code in_title file code =
      try
        let chanout = open_out file in
        let html_code = self#html_of_code code in
        output_string chanout ("<html>"^(self#header (self#inner_title in_title))^"<body>\n");
        output_string chanout html_code;
        output_string chanout "</body></html>";
        close_out chanout
      with
        Sys_error s -> 
          incr Odoc_info.errors ;
          prerr_endline s

    (** Take a string and return the string where fully qualified 
       type (or class or class type) idents 
       have been replaced by links to the type referenced by the ident.*)
    method create_fully_qualified_idents_links 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
        let s_final = Odoc_info.apply_if_equal 
            Odoc_info.use_hidden_modules 
            match_s
            rel
        in
        if List.mem match_s known_types_names then
           "<a href=\""^(Naming.complete_target Naming.mark_type match_s)^"\">"^
           s_final^
           "</a>"
        else
          if List.mem match_s known_classes_names then
            let (html_file, _) = Naming.html_files match_s in
            "<a href=\""^html_file^"\">"^s_final^"</a>"
          else
            s_final
      in
      let s2 = Str.global_substitute
          (Str.regexp "\\([A-Z]\\([a-zA-Z_'0-9]\\)*\\.\\)+\\([a-z][a-zA-Z_'0-9]*\\)")
          f
          s
      in
      s2

    (** Take a string and return the string where fully qualified module idents 
       have been replaced by links to the module referenced by the ident.*)
    method create_fully_qualified_module_idents_links m_name s =
      let f str_t = 
        let match_s = Str.matched_string str_t in
        if List.mem match_s known_modules_names then
          let (html_file, _) = Naming.html_files match_s in
          "<a href=\""^html_file^"\">"^(Name.get_relative m_name match_s)^"</a>"
        else
          match_s
      in
      let s2 = Str.global_substitute
          (Str.regexp "\\([A-Z]\\([a-zA-Z_'0-9]\\)*\\.\\)+\\([A-Z][a-zA-Z_'0-9]*\\)")
          f
          s
      in
      s2

    (** Return html code to display a [Types.type_expr].*)
    method html_of_type_expr m_name t =
      let s = String.concat "\n"
          (Str.split (Str.regexp "\n") (Odoc_info.string_of_type_expr t))
      in
      let s2 = Str.global_replace (Str.regexp "\n") "<br>       " s in
      "<code class=\"type\">"^(self#create_fully_qualified_idents_links m_name s2)^"</code>"


    (** Return html code to display a [Types.class_type].*)
    method html_of_class_type_expr m_name t =
      let s = String.concat "\n"
          (Str.split (Str.regexp "\n") (Odoc_info.string_of_class_type t))
      in
      let s2 = Str.global_replace (Str.regexp "\n") "<br>       " s in
      "<code class=\"type\">"^(self#create_fully_qualified_idents_links m_name s2)^"</code>"

    (** Return html code to display a [Types.type_expr list].*)
    method html_of_type_expr_list m_name sep l =
      print_DEBUG "html#html_of_type_expr_list";
      let s = Odoc_info.string_of_type_list sep l in
      print_DEBUG "html#html_of_type_expr_list: 1";
      let s2 = Str.global_replace (Str.regexp "\n") "<br>       " s in
      print_DEBUG "html#html_of_type_expr_list: 2";
      "<code class=\"type\">"^(self#create_fully_qualified_idents_links m_name s2)^"</code>"

    (** Return html code to display a [Types.module_type]. *)
    method html_of_module_type m_name t =
      let s = String.concat "\n"
          (Str.split (Str.regexp "\n") (Odoc_info.string_of_module_type t))
      in
      let s2 = Str.global_replace (Str.regexp "\n") "<br>       " s in
      "<code class=\"type\">"^(self#create_fully_qualified_module_idents_links m_name s2)^"</code>"
        
    (** Generate a file containing the module type in the given file name. *)
    method output_module_type in_title file mtyp =
      let s = String.concat "\n"
          (Str.split (Str.regexp "\n") (Odoc_info.string_of_module_type ~complete: true mtyp))
      in
      self#output_code in_title file s

    (** Generate a file containing the class type in the given file name. *)
    method output_class_type in_title file ctyp =
      let s = String.concat "\n"
          (Str.split (Str.regexp "\n") (Odoc_info.string_of_class_type ~complete: true ctyp))
      in
      self#output_code in_title file s


    (** Return html code for a value. *)
    method html_of_value v =
      Odoc_info.reset_type_names ();
      "<pre>"^(self#keyword "val")^" "^
      (* html mark *)
      "<a name=\""^(Naming.value_target v)^"\"></a>"^
      (match v.val_code with 
        None -> Name.simple v.val_name
      | Some c -> 
          let file = Naming.file_code_value_complete_target v in
          self#output_code v.val_name (Filename.concat !Args.target_dir file) c;
          "<a href=\""^file^"\">"^(Name.simple v.val_name)^"</a>"
      )^" : "^
      (self#html_of_type_expr (Name.father v.val_name) v.val_type)^"</pre>"^
      (self#html_of_info v.val_info)^
      (if !Args.with_parameter_list then
        self#html_of_parameter_list (Name.father v.val_name) v.val_parameters
      else
        self#html_of_described_parameter_list (Name.father v.val_name) v.val_parameters
      )

    (** Return html code for an exception. *)
    method html_of_exception e =
      Odoc_info.reset_type_names ();
      "<pre>"^(self#keyword "exception")^" "^
      (* html mark *)
      "<a name=\""^(Naming.exception_target e)^"\"></a>"^
      (Name.simple e.ex_name)^
      (match e.ex_args with
        [] -> ""
      | _ -> 
          " "^(self#keyword "of")^" "^
          (self#html_of_type_expr_list (Name.father e.ex_name) " * " e.ex_args)
      )^
      (match e.ex_alias with
        None -> ""
      | Some ea -> " = "^
          (
           match ea.ea_ex with
             None -> ea.ea_name
           | Some e -> "<a href=\""^(Naming.complete_exception_target e)^"\">"^e.ex_name^"</a>"
          )
      )^
      "</pre>\n"^
      (self#html_of_info e.ex_info)

    (** Return html code for a type. *)
    method html_of_type t =
      Odoc_info.reset_type_names ();
      let father = Name.father t.ty_name in
      "<br><code>"^(self#keyword "type")^" "^
      (* html mark *)
      "<a name=\""^(Naming.type_target t)^"\"></a>"^
      (match t.ty_parameters with
        [] -> ""
      | tp :: [] -> (self#html_of_type_expr father tp)^" "
      | l -> "("^(self#html_of_type_expr_list father ", " l)^") "
      )^
      (Name.simple t.ty_name)^" "^
      (match t.ty_manifest with None -> "" | Some typ -> "= "^(self#html_of_type_expr father typ)^" ")^
      (match t.ty_kind with
        Type_abstract -> "</code>"
      | Type_variant l ->
          "=<br>"^
          "</code><table class=\"typetable\">\n"^ 
          (String.concat "\n"
             (List.map 
                (fun constr ->
                  "<tr>\n"^
                  "<td align=\"left\" valign=\"top\" >\n"^
                  "<code>"^
                  (self#keyword "|")^
                  "</code></td>\n"^
                  "<td align=\"left\" valign=\"top\" >\n"^
                  "<code>"^
                  (self#constructor constr.vc_name)^
                  (match constr.vc_args with
                    [] -> "" 
                  | l -> 
                      " "^(self#keyword "of")^" "^
                      (self#html_of_type_expr_list father " * " l)
                  )^
                  "</code></td>\n"^
                  (match constr.vc_text with
                    None -> ""
                  | Some t ->
                      "<td class=\"typefieldcomment\" align=\"left\" valign=\"top\" >"^
                      "<code>"^
                      "(*"^
                      "</code></td>"^
                      "<td class=\"typefieldcomment\" align=\"left\" valign=\"top\" >"^
                      "<code>"^
                      (self#html_of_text t)^
                      "</code></td>"^
                      "<td class=\"typefieldcomment\" align=\"left\" valign=\"bottom\" >"^
                      "<code>"^
                      "*)"^
                      "</code></td>"
                  )^
                  "\n</tr>"
                )
                l
             )
          )^
          "</table>\n"

      | Type_record l ->
          "= {<br>"^
          "</code><table class=\"typetable\">\n"^ 
          (String.concat "\n"
             (List.map 
                (fun r ->
                  "<tr>\n"^
                  "<td align=\"left\" valign=\"top\" >\n"^
                  "<code>&nbsp;&nbsp;</code>"^
                  "</td>\n"^
                  "<td align=\"left\" valign=\"top\" >\n"^
                  "<code>"^(if r.rf_mutable then self#keyword "mutable&nbsp;" else "")^
                  r.rf_name^"&nbsp;: "^(self#html_of_type_expr father r.rf_type)^";"^
                  "</code></td>\n"^
                  (match r.rf_text with
                    None -> ""
                  | Some t ->
                      "<td class=\"typefieldcomment\" align=\"left\" valign=\"top\" >"^
                      "<code>"^
                      "(*"^
                      "</code></td>"^
                      "<td class=\"typefieldcomment\" align=\"left\" valign=\"top\" >"^
                      "<code>"^
                      (self#html_of_text t)^
                      "</code></td>"^
                      "<td class=\"typefieldcomment\" align=\"left\" valign=\"bottom\" >"^
                      "<code>"^
                      "*)"^
                      "</code></td>"
                  )^
                  "\n</tr>"
                )
                l
             )
          )^
          "</table>\n"^
          "}\n"
      )^"\n"^
      (self#html_of_info t.ty_info)^
      "<br>\n"

    (** Return html code for a class attribute. *)
    method html_of_attribute a =
      let module_name = Name.father (Name.father a.att_value.val_name) in
      "<pre>"^(self#keyword "val")^" "^
      (* html mark *)
      "<a name=\""^(Naming.attribute_target a)^"\"></a>"^
      (if a.att_mutable then (self#keyword Odoc_messages.mutab)^" " else "")^
      (match a.att_value.val_code with 
        None -> Name.simple a.att_value.val_name
      | Some c -> 
          let file = Naming.file_code_attribute_complete_target a in
          self#output_code a.att_value.val_name (Filename.concat !Args.target_dir file) c;
          "<a href=\""^file^"\">"^(Name.simple a.att_value.val_name)^"</a>"
      )^" : "^
      (self#html_of_type_expr module_name  a.att_value.val_type)^"</pre>"^
      (self#html_of_info a.att_value.val_info)

    (** Return html code for a class method. *)
    method html_of_method m =<commit_msg>use <pre> instead of <code> for types definitions, to make long type definitions indented, and set a smaller bottom margin for pre in the default style<commit_after>(***********************************************************************)
(*                             OCamldoc                                *)
(*                                                                     *)
(*            <NAME>, <NAME>, 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 Q Public License version 1.0.               *)
(*                                                                     *)
(***********************************************************************)


(** Generation of html documentation. *)

let print_DEBUG s = print_string s ; print_newline ()

open Odoc_info 
open Parameter
open Value
open Type
open Exception
open Class 
open Module


(** The functions used for naming files and html marks.*)
module Naming =
  struct
    (** The prefix for types marks. *)
    let mark_type = "TYPE"

    (** The prefix for functions marks. *)
    let mark_function = "FUN"

    (** The prefix for exceptions marks. *)
    let mark_exception = "EXCEPTION"

    (** The prefix for values marks. *)
    let mark_value = "VAL"

    (** The prefix for attributes marks. *)
    let mark_attribute = "ATT"

    (** The prefix for methods marks. *)
    let mark_method = "METHOD"

    (** The prefix for code files.. *)
    let code_prefix = "code_"

    (** The prefix for type files.. *)
    let type_prefix = "type_"

    (** Return the two html files names for the given module or class name.*)
    let html_files name =
      let html_file = name^".html" in
      let html_frame_file = name^"-frame.html" in
      (html_file, html_frame_file)

    (** Return the target for the given prefix and simple name. *)
    let target pref simple_name = pref^simple_name

    (** Return the complete link target (file#target) for the given prefix string and complete name.*)
    let complete_target pref complete_name = 
      let simple_name = Name.simple complete_name in
      let module_name = 
        let s = Name.father complete_name in
        if s = "" then simple_name else s
      in
      let (html_file, _) = html_files module_name in
      html_file^"#"^(target pref simple_name)

    (** Return the link target for the given type. *)
    let type_target t = target mark_type (Name.simple t.ty_name)

    (** Return the complete link target for the given type. *)
    let complete_type_target t = complete_target mark_type t.ty_name

    (** Return the link target for the given exception. *)
    let exception_target e = target mark_exception (Name.simple e.ex_name)

    (** Return the complete link target for the given exception. *)
    let complete_exception_target e = complete_target mark_exception e.ex_name

	
    
    (** Return the link target for the given value. *)
    let value_target v = target mark_value (Name.simple v.val_name)

    (** Return the given value name where symbols accepted in infix values
       are replaced by strings, to avoid clashes with the filesystem.*)
    let subst_infix_symbols name =
      let len = String.length name in
      let buf = Buffer.create len in
      let ch c = Buffer.add_char buf c in
      let st s = Buffer.add_string buf s in
      for i = 0 to len - 1 do
	match name.[i] with
	| '|' -> st "_pipe_"
        | '<' -> st "_lt_"
        | '>' -> st "_gt_"
        | '@' -> st "_at_"
        | '^' -> st "_exp_"
        | '&' -> st "_amp_"
        | '+' -> st "_plus_"
        | '-' -> st "_minus_"
        | '*' -> st "_star_"
        | '/' -> st "_slash_"
        | '$' -> st "_dollar_"
        | '%' -> st "_percent_"
        | '=' -> st "_equal_"
        | ':' -> st "_column_"
        | '~' -> st "_tilde_"
        | '!' -> st "_bang_"
	| c -> ch c
      done;
      Buffer.contents buf

    (** Return the complete link target for the given value. *)
    let complete_value_target v = complete_target mark_value v.val_name

    (** Return the complete filename for the code of the given value. *)
    let file_code_value_complete_target v = 
      let f = code_prefix^mark_value^(subst_infix_symbols v.val_name)^".html" in
      f

    (** Return the link target for the given attribute. *)
    let attribute_target a = target mark_attribute (Name.simple a.att_value.val_name)

    (** Return the complete link target for the given attribute. *)
    let complete_attribute_target a = complete_target mark_attribute a.att_value.val_name

    (** Return the complete filename for the code of the given attribute. *)
    let file_code_attribute_complete_target a = 
      let f = code_prefix^mark_attribute^a.att_value.val_name^".html" in
      f

    (** Return the link target for the given method. *)
    let method_target m = target mark_method (Name.simple m.met_value.val_name)

    (** Return the complete link target for the given method. *)
    let complete_method_target m = complete_target mark_method m.met_value.val_name

    (** Return the complete filename for the code of the given method. *)
    let file_code_method_complete_target m = 
      let f = code_prefix^mark_method^m.met_value.val_name^".html" in
      f

    (** Return the link target for the given label section. *)
    let label_target l = target "" l

    (** Return the complete link target for the given section label. *)
    let complete_label_target l = complete_target "" l

    (** Return the complete filename for the code of the type of the 
       given module or module type name. *)
    let file_type_module_complete_target name = 
      let f = type_prefix^name^".html" in
      f

    (** Return the complete filename for the code of the type of the 
       given class or class type name. *)
    let file_type_class_complete_target name = 
      let f = type_prefix^name^".html" in
      f
  end

(** A class with a method to colorize a string which represents OCaml code. *)
class ocaml_code =
  object(self)
    method html_of_code ?(with_pre=true) code =
      let html_code = Odoc_ocamlhtml.html_of_code ~with_pre: with_pre code in
      html_code
  end

(** Generation of html code from text structures. *)
class text =
  object (self)
    (** We want to display colorized code. *)
    inherit ocaml_code 

    (** Escape the strings which would clash with html syntax, and
       make some replacements (double newlines replaced by <br>). *)
    method escape s = Odoc_ocamlhtml.escape_base s


    method keep_alpha_num s =
      let len = String.length s in
      let buf = Buffer.create len in
      for i = 0 to len - 1 do
        match s.[i] with
          'a'..'z' | 'A'..'Z' | '0'..'9' -> Buffer.add_char buf s.[i]
        | _ -> ()
      done;
      Buffer.contents buf

    (** Return a label created from the first sentence of a text. *)
    method label_of_text t=
      let t2 = Odoc_info.first_sentence_of_text t in
      let s = Odoc_info.string_of_text t2 in
      let s2 = self#keep_alpha_num s in
      s2

    (** Create a label for the associated title. 
       Return the label specified by the user or a label created
       from the title level and the first sentence of the title. *)
    method create_title_label (n,label_opt,t) =
      match label_opt with
        Some s -> s
      | None -> Printf.sprintf "%d_%s" n (self#label_of_text t)

    (** Return the html code corresponding to the [text] parameter. *)
    method html_of_text t = String.concat "" (List.map self#html_of_text_element t)

    (** Return the html code for the [text_element] in parameter. *)
    method html_of_text_element te =
      print_DEBUG "text::html_of_text_element";
      match te with
      | Odoc_info.Raw s -> self#html_of_Raw s
      | Odoc_info.Code s -> self#html_of_Code s
      | Odoc_info.CodePre s -> self#html_of_CodePre s
      | Odoc_info.Verbatim s -> self#html_of_Verbatim s
      | Odoc_info.Bold t -> self#html_of_Bold t
      | Odoc_info.Italic t -> self#html_of_Italic t 
      | Odoc_info.Emphasize t -> self#html_of_Emphasize t
      | Odoc_info.Center t -> self#html_of_Center t
      | Odoc_info.Left t -> self#html_of_Left t
      | Odoc_info.Right t -> self#html_of_Right t
      | Odoc_info.List tl -> self#html_of_List tl
      | Odoc_info.Enum tl -> self#html_of_Enum tl
      | Odoc_info.Newline -> self#html_of_Newline
      | Odoc_info.Block t -> self#html_of_Block t
      | Odoc_info.Title (n, l_opt, t) -> self#html_of_Title n l_opt t
      | Odoc_info.Latex s -> self#html_of_Latex s
      | Odoc_info.Link (s, t) -> self#html_of_Link s t
      | Odoc_info.Ref (name, ref_opt) -> self#html_of_Ref name ref_opt
      | Odoc_info.Superscript t -> self#html_of_Superscript t
      | Odoc_info.Subscript t -> self#html_of_Subscript t

    method html_of_Raw s = self#escape s

    method html_of_Code s =
      if !Args.colorize_code then
        self#html_of_code ~with_pre: false s
      else
        "<code class=\""^Odoc_ocamlhtml.code_class^"\">"^(self#escape s)^"</code>"

    method html_of_CodePre s =
      if !Args.colorize_code then
        "<pre></pre>"^(self#html_of_code s)^"<pre></pre>"
      else
        "<pre><code class=\""^Odoc_ocamlhtml.code_class^"\">"^(self#escape s)^"</code></pre>"

    method html_of_Verbatim s = "<pre>"^(self#escape s)^"</pre>"
    method html_of_Bold t = "<b>"^(self#html_of_text t)^"</b>"
    method html_of_Italic t = "<i>"^(self#html_of_text t)^"</i>"
    method html_of_Emphasize t = "<em>"^(self#html_of_text t)^"</em>"
    method html_of_Center t = "<center>"^(self#html_of_text t)^"</center>"
    method html_of_Left t = "<div align=left>"^(self#html_of_text t)^"</div>"
    method html_of_Right t = "<div align=right>"^(self#html_of_text t)^"</div>"

    method html_of_List tl = 
      "<ul>\n"^
      (String.concat ""
         (List.map (fun t -> "<li>"^(self#html_of_text t)^"</li>\n") tl))^
      "</ul>\n"

    method html_of_Enum tl =
      "<OL>\n"^
      (String.concat ""
         (List.map (fun t -> "<li>"^(self#html_of_text t)^"</li>\n") tl))^
      "</OL>\n"

    method html_of_Newline = "\n<p>\n"

    method html_of_Block t =
      "<blockquote>\n"^(self#html_of_text t)^"</blockquote>\n"

    method html_of_Title n label_opt t =
      let css_class = "title"^(string_of_int n) in
      let label1 = self#create_title_label (n, label_opt, t) in
      "<br>\n"^
      "<a name=\""^(Naming.label_target label1)^"\"></a>\n"^
      "<table cellpadding=5 cellspacing=5 width=\"100%\">\n"^
      "<tr class=\""^css_class^"\"><td><div align=center>\n"^
      "<span class=\""^css_class^"\">"^(self#html_of_text t)^"</span>\n"^
      "</div>\n</td>\n</tr>\n</table>\n"

    method html_of_Latex _ = ""
      (* don't care about LaTeX stuff in HTML. *)

    method html_of_Link s t =
      "<a href=\""^s^"\">"^(self#html_of_text t)^"</a>"

    method html_of_Ref name ref_opt =
      match ref_opt with
        None -> 
          self#html_of_text_element (Odoc_info.Code name)
      | Some kind ->
	  let h name = Odoc_info.Code (Odoc_info.use_hidden_modules name) in
          let (target, text) = 
            match kind with
              Odoc_info.RK_module 
            | Odoc_info.RK_module_type
            | Odoc_info.RK_class
            | Odoc_info.RK_class_type ->
                let (html_file, _) = Naming.html_files name in
                (html_file, h name)
            | Odoc_info.RK_value -> (Naming.complete_target Naming.mark_value name, h name)
            | Odoc_info.RK_type -> (Naming.complete_target Naming.mark_type name, h name)
            | Odoc_info.RK_exception -> (Naming.complete_target Naming.mark_exception name, h name)
            | Odoc_info.RK_attribute -> (Naming.complete_target Naming.mark_attribute name, h name)
            | Odoc_info.RK_method -> (Naming.complete_target Naming.mark_method name, h name)
            | Odoc_info.RK_section t -> (Naming.complete_label_target name,
					 Odoc_info.Italic [Raw (Odoc_info.string_of_text t)])
          in
          "<a href=\""^target^"\">"^
          (self#html_of_text_element text)^
	  "</a>"

    method html_of_Superscript t =
      "<sup class=\"superscript\">"^(self#html_of_text t)^"</sup>"

    method html_of_Subscript t =
      "<sub class=\"subscript\">"^(self#html_of_text t)^"</sub>"

  end

(** A class used to generate html code 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 html code. 
       Add a pair here to handle a tag.*)
    val mutable tag_functions = ([] : (string * (Odoc_info.text -> string)) list)

    (** The method used to get html code from a [text]. *)
    method virtual html_of_text : Odoc_info.text -> string

    (** Return html for an author list. *)
    method html_of_author_list l =
      match l with
        [] ->
          ""
      | _ ->
          "<b>"^Odoc_messages.authors^": </b>"^
          (String.concat ", " l)^
          "<br>\n"

    (** Return html code for the given optional version information.*)
    method html_of_version_opt v_opt =
      match v_opt with
        None -> ""
      | Some v -> "<b>"^Odoc_messages.version^": </b>"^v^"<br>\n"

    (** Return html code for the given optional since information.*)
    method html_of_since_opt s_opt =
      match s_opt with
        None -> ""
      | Some s -> "<b>"^Odoc_messages.since^"</b> "^s^"<br>\n"

    (** Return html code for the given list of raised exceptions.*)
    method html_of_raised_exceptions l =
      match l with
        [] -> ""
      | (s, t) :: [] -> "<b>"^Odoc_messages.raises^"</b> <code>"^s^"</code> "^(self#html_of_text t)^"<br>\n"
      | _ ->
          "<b>"^Odoc_messages.raises^"</b><ul>"^
          (String.concat ""
             (List.map
                (fun (ex, desc) -> "<li><code>"^ex^"</code> "^(self#html_of_text desc)^"</li>\n")
                l
             )
          )^"</ul>\n"

    (** Return html code for the given "see also" reference. *)
    method html_of_see (see_ref, t)  =
      let t_ref = 
        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
      in
      self#html_of_text t_ref

    (** Return html code for the given list of "see also" references.*)
    method html_of_sees l =
      match l with
        [] -> ""
      | see :: [] -> "<b>"^Odoc_messages.see_also^"</b> "^(self#html_of_see see)^"<br>\n"
      | _ ->
          "<b>"^Odoc_messages.see_also^"</b><ul>"^
          (String.concat ""
             (List.map
                (fun see -> "<li>"^(self#html_of_see see)^"</li>\n")
                l
             )
          )^"</ul>\n"

    (** Return html code for the given optional return information.*)
    method html_of_return_opt return_opt =
      match return_opt with
        None -> ""
      | Some s -> "<b>"^Odoc_messages.returns^"</b> "^(self#html_of_text s)^"<br>\n"

    (** Return html code for the given list of custom tagged texts. *)
    method html_of_custom l =
      let buf = Buffer.create 50 in
      List.iter
        (fun (tag, text) ->
          try
            let f = List.assoc tag tag_functions in
            Buffer.add_string buf (f text)
          with
            Not_found ->
              Odoc_info.warning (Odoc_messages.tag_not_handled tag)
        )
        l;
      Buffer.contents buf

    (** Return html code for a description, except for the [i_params] field. *)
    method html_of_info info_opt =
      match info_opt with
        None ->
          ""
      | Some info ->
          let module M = Odoc_info in
          "<div class=\"info\">\n"^
          (match info.M.i_deprecated with
            None -> ""
          | Some d -> 
              "<span class=\"warning\">"^Odoc_messages.deprecated^"</span> "^
              (self#html_of_text d)^
              "<br>\n"
          )^
          (match info.M.i_desc with
            None -> "" 
          | Some d when d = [Odoc_info.Raw ""] -> ""
          | Some d -> (self#html_of_text d)^"<br>\n"
          )^
          (self#html_of_author_list info.M.i_authors)^
          (self#html_of_version_opt info.M.i_version)^
          (self#html_of_since_opt info.M.i_since)^
          (self#html_of_raised_exceptions info.M.i_raised_exceptions)^
          (self#html_of_return_opt info.M.i_return_value)^
          (self#html_of_sees info.M.i_sees)^
          (self#html_of_custom info.M.i_custom)^
          "</div>\n"

    (** Return html code for the first sentence of a description. 
       The titles and lists in this first sentence has been removed.*)
    method html_of_info_first_sentence info_opt =
      match info_opt with
        None -> ""
      | Some info ->
          let module M = Odoc_info in
          let dep = info.M.i_deprecated <> None in
          "<div class=\"info\">\n"^
          (if dep then "<font color=\"#CCCCCC\">" else "") ^
          (match info.M.i_desc with
            None -> "" 
          | Some d when d = [Odoc_info.Raw ""] -> ""
          | Some d -> (self#html_of_text
                         (Odoc_info.text_no_title_no_list
                            (Odoc_info.first_sentence_of_text d)))^"\n"
          )^
          (if dep then "</font>" else "") ^
          "</div>\n"

  end



let opt = Odoc_info.apply_opt

(** This class is used to create objects which can generate a simple html documentation. *)
class html =
  object (self)
    inherit text
    inherit info

    (** The default style options. *)
    val mutable default_style_options =
      ["a:visited {color : #416DFF; text-decoration : none; }" ;
        "a:link {color : #416DFF; text-decoration : none;}" ;
        "a:hover {color : Red; text-decoration : none; background-color: #5FFF88}" ;
        "a:active {color : Red; text-decoration : underline; }" ;
        ".keyword { font-weight : bold ; color : Red }" ;
        ".keywordsign { color : #C04600 }" ; 
        ".superscript { font-size : 4 }" ;
        ".subscript { font-size : 4 }" ;
        ".comment { color : Green }" ;
        ".constructor { color : Blue }" ;
        ".type { color : #5C6585 }" ;
        ".string { color : Maroon }" ;
        ".warning { color : Red ; font-weight : bold }" ;
        ".info { margin-left : 3em; margin-right : 3em }" ;
        ".code { color : #465F91 ; }" ;
        ".title1 { font-size : 20pt ; background-color : #909DFF }" ;
        ".title2 { font-size : 20pt ; background-color : #90BDFF }" ;
        ".title3 { font-size : 20pt ; background-color : #90DDFF }" ;
        ".title4 { font-size : 20pt ; background-color : #90EDFF }" ;
        ".title5 { font-size : 20pt ; background-color : #90FDFF }" ;
        ".title6 { font-size : 20pt ; background-color : #C0FFFF }" ;
	".typetable { border-style : hidden }" ;
	".indextable { border-style : hidden }" ;
	".paramstable { border-style : hidden ; padding: 5pt 5pt}" ;
        "body { background-color : White }" ;
        "tr { background-color : White }" ;
	"td.typefieldcomment { background-color : #FFFFFF }" ;
	"pre { margin-bottom: 4px }" ;
      ] 
      
    (** The style file for all pages. *)
    val mutable style_file = "style.css"

    (** The code to import the style. Initialized in [init_style]. *)
    val mutable style = ""

    (** The known types names. 
       Used to know if we must create a link to a type
       when printing a type. *)
    val mutable known_types_names = []

    (** The known class and class type names. 
       Used to know if we must create a link to a class 
       or class type or not when printing a type. *)
    val mutable known_classes_names = []

    (** The known modules and module types names. 
       Used to know if we must create a link to a type or not
       when printing a module type. *)
    val mutable known_modules_names = []

    (** The main file. *)
    val mutable index = "index.html"
    (** The file for the index of values. *)
    val mutable index_values = "index_values.html"
    (** The file for the index of types. *)
    val mutable index_types = "index_types.html"
    (** The file for the index of exceptions. *)
    val mutable index_exceptions = "index_exceptions.html"
    (** The file for the index of attributes. *)
    val mutable index_attributes = "index_attributes.html"
    (** The file for the index of methods. *)
    val mutable index_methods = "index_methods.html"
    (** The file for the index of classes. *)
    val mutable index_classes = "index_classes.html"
    (** The file for the index of class types. *)
    val mutable index_class_types = "index_class_types.html"
    (** The file for the index of modules. *)
    val mutable index_modules = "index_modules.html"
    (** The file for the index of module types. *)
    val mutable index_module_types = "index_module_types.html"


    (** The list of attributes. Filled in the [generate] method. *)
    val mutable list_attributes = []
    (** The list of methods. Filled in the [generate] method. *)
    val mutable list_methods = []
    (** The list of values. Filled in the [generate] method. *)
    val mutable list_values = []
    (** The list of exceptions. Filled in the [generate] method. *)
    val mutable list_exceptions = []
    (** The list of types. Filled in the [generate] method. *)
    val mutable list_types = []
    (** The list of modules. Filled in the [generate] method. *)
    val mutable list_modules = []
    (** The list of module types. Filled in the [generate] method. *)
    val mutable list_module_types = []
    (** The list of classes. Filled in the [generate] method. *)
    val mutable list_classes = []
    (** The list of class types. Filled in the [generate] method. *)
    val mutable list_class_types = []

    (** The header of pages. Must be prepared by the [prepare_header] method.*)
    val mutable header = fun ?(nav=None) -> fun ?(comments=[]) -> fun _ -> ""

    (** Init the style. *)
    method init_style =
      (match !Args.css_style with
        None -> 
          let default_style = String.concat "\n" default_style_options in
          (
           try
             let chanout = open_out (Filename.concat !Args.target_dir style_file) in
             output_string chanout default_style ;
             flush chanout ;
             close_out chanout
           with
             Sys_error s ->
               prerr_endline s ;
               incr Odoc_info.errors ;
          )
      | Some f ->
          style_file <- f 
      );
      style <- "<link rel=\"stylesheet\" href=\""^style_file^"\" type=\"text/css\">\n"

    (** Get the title given by the user *)
    method title = match !Args.title with None -> "" | Some t -> self#escape t

    (** Get the title given by the user completed with the given subtitle. *)
    method inner_title s = 
      (match self#title with "" -> "" | t -> t^" : ")^
      (self#escape s)

    (** Get the page header. *)
    method header ?nav ?comments title = header ?nav ?comments title

    (** A function to build the header of pages. *)
    method prepare_header module_list =
      let f ?(nav=None) ?(comments=[]) t  = 
        let link_if_not_empty l m url =
          match l with
            [] -> ""
          | _ -> "<link title=\""^m^"\" rel=Appendix href=\""^url^"\">\n"
        in
        "<head>\n"^
        style^
        "<link rel=\"Start\" href=\""^index^"\">\n"^
        (
         match nav with
           None -> ""
         | Some (pre_opt, post_opt, name) ->
             (match pre_opt with
               None -> ""
             | Some name -> 
                 "<link rel=\"previous\" href=\""^(fst (Naming.html_files name))^"\">\n"
             )^
             (match post_opt with
               None -> ""
             | Some name -> 
                 "<link rel=\"next\" href=\""^(fst (Naming.html_files name))^"\">\n"
             )^
             (
              let father = Name.father name in
              let href = if father = "" then index else fst (Naming.html_files father) in
              "<link rel=\"Up\" href=\""^href^"\">\n"
             )
        )^
        (link_if_not_empty list_types Odoc_messages.index_of_types index_types)^
        (link_if_not_empty list_exceptions Odoc_messages.index_of_exceptions index_exceptions)^
        (link_if_not_empty list_values Odoc_messages.index_of_values index_values)^
        (link_if_not_empty list_attributes Odoc_messages.index_of_attributes index_attributes)^
        (link_if_not_empty list_methods Odoc_messages.index_of_methods index_methods)^
        (link_if_not_empty list_classes Odoc_messages.index_of_classes index_classes)^
        (link_if_not_empty list_class_types Odoc_messages.index_of_class_types index_class_types)^
        (link_if_not_empty list_modules Odoc_messages.index_of_modules index_modules)^
        (link_if_not_empty list_module_types Odoc_messages.index_of_module_types index_module_types)^
        (String.concat "\n"
           (List.map
              (fun m -> 
                let html_file = fst (Naming.html_files m.m_name) in
                "<link title=\""^m.m_name^"\" rel=\"Chapter\" href=\""^html_file^"\">"
              )
              module_list
           )
        )^
        (self#html_sections_links comments)^
        "<title>"^
        t^
        "</title>\n</head>\n"
      in
      header <- f

    (** Build the html code for the link tags in the header, defining section and
       subsections for the titles found in the given comments.*)
    method html_sections_links comments =
      let titles = List.flatten (List.map Odoc_info.get_titles_in_text comments) in
      let levels = 
        let rec iter acc l =
          match l with
            [] -> acc
          | (n,_,_) :: q ->
              if List.mem n acc 
              then iter acc q
              else iter (n::acc) q
        in
        iter [] titles
      in
      let sorted_levels = List.sort compare levels in
      let (section_level, subsection_level) =
        match sorted_levels with
          [] -> (None, None)
        | [n] -> (Some n, None)
        | n :: m :: _ -> (Some n, Some m)
      in
      let titles_per_level level_opt =
        match level_opt with
          None -> []
        | Some n -> List.filter (fun (m,_,_) -> m = n) titles
      in
      let section_titles = titles_per_level section_level in
      let subsection_titles = titles_per_level subsection_level in
      let create_lines s_rel titles =
        List.map
          (fun (n,lopt,t) -> 
            let s = Odoc_info.string_of_text t in
            let label = self#create_title_label (n,lopt,t) in
            Printf.sprintf "<link title=\"%s\" rel=\"%s\" href=\"#%s\">\n" s s_rel label)
          titles
      in
      let section_lines = create_lines "Section" section_titles in
      let subsection_lines = create_lines "Subsection" subsection_titles in
      String.concat "" (section_lines @ subsection_lines)

    (** Html code for navigation bar. 
       @param pre optional name for optional previous module/class 
       @param post optional name for optional next module/class
       @param name name of current module/class *)
    method navbar pre post name =
      "<div class=\"navbar\">"^
      (match pre with
        None -> ""
      | Some name -> 
          "<a href=\""^(fst (Naming.html_files name))^"\">"^Odoc_messages.previous^"</a>\n"
      )^
      "&nbsp;"^
      (
       let father = Name.father name in
       let href = if father = "" then index else fst (Naming.html_files father) in
       "<a href=\""^href^"\">"^Odoc_messages.up^"</a>\n"
      )^
      "&nbsp;"^
      (match post with
        None -> ""
      | Some name -> 
          "<a href=\""^(fst (Naming.html_files name))^"\">"^Odoc_messages.next^"</a>\n"
      )^
      "</div>\n"

    (** Return html code with the given string in the keyword style.*)
    method keyword s = 
      "<span class=\"keyword\">"^s^"</span>"

    (** Return html code with the given string in the constructor style. *)
    method constructor s = "<span class=\"constructor\">"^s^"</span>"

    (** Output the given ocaml code to the given file name. *)
    method private output_code in_title file code =
      try
        let chanout = open_out file in
        let html_code = self#html_of_code code in
        output_string chanout ("<html>"^(self#header (self#inner_title in_title))^"<body>\n");
        output_string chanout html_code;
        output_string chanout "</body></html>";
        close_out chanout
      with
        Sys_error s -> 
          incr Odoc_info.errors ;
          prerr_endline s

    (** Take a string and return the string where fully qualified 
       type (or class or class type) idents 
       have been replaced by links to the type referenced by the ident.*)
    method create_fully_qualified_idents_links 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
        let s_final = Odoc_info.apply_if_equal 
            Odoc_info.use_hidden_modules 
            match_s
            rel
        in
        if List.mem match_s known_types_names then
           "<a href=\""^(Naming.complete_target Naming.mark_type match_s)^"\">"^
           s_final^
           "</a>"
        else
          if List.mem match_s known_classes_names then
            let (html_file, _) = Naming.html_files match_s in
            "<a href=\""^html_file^"\">"^s_final^"</a>"
          else
            s_final
      in
      let s2 = Str.global_substitute
          (Str.regexp "\\([A-Z]\\([a-zA-Z_'0-9]\\)*\\.\\)+\\([a-z][a-zA-Z_'0-9]*\\)")
          f
          s
      in
      s2

    (** Take a string and return the string where fully qualified module idents 
       have been replaced by links to the module referenced by the ident.*)
    method create_fully_qualified_module_idents_links m_name s =
      let f str_t = 
        let match_s = Str.matched_string str_t in
        if List.mem match_s known_modules_names then
          let (html_file, _) = Naming.html_files match_s in
          "<a href=\""^html_file^"\">"^(Name.get_relative m_name match_s)^"</a>"
        else
          match_s
      in
      let s2 = Str.global_substitute
          (Str.regexp "\\([A-Z]\\([a-zA-Z_'0-9]\\)*\\.\\)+\\([A-Z][a-zA-Z_'0-9]*\\)")
          f
          s
      in
      s2

    (** Return html code to display a [Types.type_expr].*)
    method html_of_type_expr m_name t =
      let s = String.concat "\n"
          (Str.split (Str.regexp "\n") (Odoc_info.string_of_type_expr t))
      in
      let s2 = Str.global_replace (Str.regexp "\n") "<br>       " s in
      "<code class=\"type\">"^(self#create_fully_qualified_idents_links m_name s2)^"</code>"


    (** Return html code to display a [Types.class_type].*)
    method html_of_class_type_expr m_name t =
      let s = String.concat "\n"
          (Str.split (Str.regexp "\n") (Odoc_info.string_of_class_type t))
      in
      let s2 = Str.global_replace (Str.regexp "\n") "<br>       " s in
      "<code class=\"type\">"^(self#create_fully_qualified_idents_links m_name s2)^"</code>"

    (** Return html code to display a [Types.type_expr list].*)
    method html_of_type_expr_list m_name sep l =
      print_DEBUG "html#html_of_type_expr_list";
      let s = Odoc_info.string_of_type_list sep l in
      print_DEBUG "html#html_of_type_expr_list: 1";
      let s2 = Str.global_replace (Str.regexp "\n") "<br>       " s in
      print_DEBUG "html#html_of_type_expr_list: 2";
      "<code class=\"type\">"^(self#create_fully_qualified_idents_links m_name s2)^"</code>"

    (** Return html code to display a [Types.module_type]. *)
    method html_of_module_type m_name t =
      let s = String.concat "\n"
          (Str.split (Str.regexp "\n") (Odoc_info.string_of_module_type t))
      in
      let s2 = Str.global_replace (Str.regexp "\n") "<br>       " s in
      "<code class=\"type\">"^(self#create_fully_qualified_module_idents_links m_name s2)^"</code>"
        
    (** Generate a file containing the module type in the given file name. *)
    method output_module_type in_title file mtyp =
      let s = String.concat "\n"
          (Str.split (Str.regexp "\n") (Odoc_info.string_of_module_type ~complete: true mtyp))
      in
      self#output_code in_title file s

    (** Generate a file containing the class type in the given file name. *)
    method output_class_type in_title file ctyp =
      let s = String.concat "\n"
          (Str.split (Str.regexp "\n") (Odoc_info.string_of_class_type ~complete: true ctyp))
      in
      self#output_code in_title file s


    (** Return html code for a value. *)
    method html_of_value v =
      Odoc_info.reset_type_names ();
      "<pre>"^(self#keyword "val")^" "^
      (* html mark *)
      "<a name=\""^(Naming.value_target v)^"\"></a>"^
      (match v.val_code with 
        None -> Name.simple v.val_name
      | Some c -> 
          let file = Naming.file_code_value_complete_target v in
          self#output_code v.val_name (Filename.concat !Args.target_dir file) c;
          "<a href=\""^file^"\">"^(Name.simple v.val_name)^"</a>"
      )^" : "^
      (self#html_of_type_expr (Name.father v.val_name) v.val_type)^"</pre>"^
      (self#html_of_info v.val_info)^
      (if !Args.with_parameter_list then
        self#html_of_parameter_list (Name.father v.val_name) v.val_parameters
      else
        self#html_of_described_parameter_list (Name.father v.val_name) v.val_parameters
      )

    (** Return html code for an exception. *)
    method html_of_exception e =
      Odoc_info.reset_type_names ();
      "<pre>"^(self#keyword "exception")^" "^
      (* html mark *)
      "<a name=\""^(Naming.exception_target e)^"\"></a>"^
      (Name.simple e.ex_name)^
      (match e.ex_args with
        [] -> ""
      | _ -> 
          " "^(self#keyword "of")^" "^
          (self#html_of_type_expr_list (Name.father e.ex_name) " * " e.ex_args)
      )^
      (match e.ex_alias with
        None -> ""
      | Some ea -> " = "^
          (
           match ea.ea_ex with
             None -> ea.ea_name
           | Some e -> "<a href=\""^(Naming.complete_exception_target e)^"\">"^e.ex_name^"</a>"
          )
      )^
      "</pre>\n"^
      (self#html_of_info e.ex_info)

    (** Return html code for a type. *)
    method html_of_type t =
      Odoc_info.reset_type_names ();
      let father = Name.father t.ty_name in
      "<pre>"^
      (self#keyword "type")^" "^
      (* html mark *)
      "<a name=\""^(Naming.type_target t)^"\"></a>"^
      (match t.ty_parameters with
        [] -> ""
      | tp :: [] -> (self#html_of_type_expr father tp)^" "
      | l -> "("^(self#html_of_type_expr_list father ", " l)^") "
      )^
      (Name.simple t.ty_name)^" "^
      (match t.ty_manifest with None -> "" | Some typ -> "= "^(self#html_of_type_expr father typ)^" ")^
      (match t.ty_kind with
        Type_abstract -> "</pre>"
      | Type_variant l ->
          "="^
          "</pre><table class=\"typetable\">\n"^ 
          (String.concat "\n"
             (List.map 
                (fun constr ->
                  "<tr>\n"^
                  "<td align=\"left\" valign=\"top\" >\n"^
                  "<code>"^
                  (self#keyword "|")^
                  "</code></td>\n"^
                  "<td align=\"left\" valign=\"top\" >\n"^
                  "<code>"^
                  (self#constructor constr.vc_name)^
                  (match constr.vc_args with
                    [] -> "" 
                  | l -> 
                      " "^(self#keyword "of")^" "^
                      (self#html_of_type_expr_list father " * " l)
                  )^
                  "</code></td>\n"^
                  (match constr.vc_text with
                    None -> ""
                  | Some t ->
                      "<td class=\"typefieldcomment\" align=\"left\" valign=\"top\" >"^
                      "<code>"^
                      "(*"^
                      "</code></td>"^
                      "<td class=\"typefieldcomment\" align=\"left\" valign=\"top\" >"^
                      "<code>"^
                      (self#html_of_text t)^
                      "</code></td>"^
                      "<td class=\"typefieldcomment\" align=\"left\" valign=\"bottom\" >"^
                      "<code>"^
                      "*)"^
                      "</code></td>"
                  )^
                  "\n</tr>"
                )
                l
             )
          )^
          "</table>\n"

      | Type_record l ->
          "= {"^
          "</pre><table class=\"typetable\">\n"^ 
          (String.concat "\n"
             (List.map 
                (fun r ->
                  "<tr>\n"^
                  "<td align=\"left\" valign=\"top\" >\n"^
                  "<code>&nbsp;&nbsp;</code>"^
                  "</td>\n"^
                  "<td align=\"left\" valign=\"top\" >\n"^
                  "<code>"^(if r.rf_mutable then self#keyword "mutable&nbsp;" else "")^
                  r.rf_name^"&nbsp;: "^(self#html_of_type_expr father r.rf_type)^";"^
                  "</code></td>\n"^
                  (match r.rf_text with
                    None -> ""
                  | Some t ->
                      "<td class=\"typefieldcomment\" align=\"left\" valign=\"top\" >"^
                      "<code>"^
                      "(*"^
                      "</code></td>"^
                      "<td class=\"typefieldcomment\" align=\"left\" valign=\"top\" >"^
                      "<code>"^
                      (self#html_of_text t)^
                      "</code></td>"^
                      "<td class=\"typefieldcomment\" align=\"left\" valign=\"bottom\" >"^
                      "<code>"^
                      "*)"^
                      "</code></td>"
                  )^
                  "\n</tr>"
                )
                l
             )
          )^
          "</table>\n"^
          "}\n"
      )^"\n"^
      (self#html_of_info t.ty_info)^
      "\n"

    (** Return html code for a class attribute. *)
    method html_of_attribute a =
      let module_name = Name.father (Name.father a.att_value.val_name) in
      "<pre>"^(self#keyword "val")^" "^
      (* html mark *)
      "<a name=\""^(Naming.attribute_target a)^"\"></a>"^
      (if a.att_mutable then (self#keyword Odoc_messages.mutab)^" " else "")^
      (match a.att_value.val_code with 
        None -> Name.simple a.att_value.val_name
      | Some c -> 
          let file = Naming.file_code_attribute_complete_target a in
          self#output_code a.att_value.val_name (Filename.concat !Args.target_dir file) c;
          "<a href=\""^file^"\">"^(Name.simple a.att_value.val_name)^"</a>"
      )^" : "^
      (self#html_of_type_expr module_name  a.att_value.val_type)^"</pre>"^
      (self#html_of_info a.att_value.val_info)

    (** Return html code for a class method. *)
    method html_of_method m =
{'fraction_non_alphanumeric': 0.13513169128046992, 'fraction_numerical': 0.0027395863861670003, 'mean_word_length': 2.0502545765478644, 'pattern_counts': {'":': 4, '<': 461, '<?xml version=': 0, '>': 747, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 12, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1032749', 'n_tokens_mistral': 25110, 'n_tokens_neox': 24144, 'n_words': 6647}