aboutsummaryrefslogtreecommitdiffhomepage
path: root/lib/richPp.ml
blob: 3f1b17d77343fb8326533c679a4de59304cb1ef8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
(************************************************************************)
(*  v      *   The Coq Proof Assistant  /  The Coq Development Team     *)
(* <O___,, *   INRIA - CNRS - LIX - LRI - PPS - Copyright 1999-2014     *)
(*   \VV/  **************************************************************)
(*    //   *      This file is distributed under the terms of the       *)
(*         *       GNU Lesser General Public License Version 2.1        *)
(************************************************************************)

open Xml_datatype

type index = Format.tag

type 'annotation located = {
  annotation : 'annotation;
  startpos   : int;
  endpos     : int
}

module Indexer (Annotation : sig type t end) = struct

  let annotations : Annotation.t list ref = ref []

  let index =
    let count = ref (-1) in
    fun annotation ->
      incr count;
      annotations := annotation :: !annotations;
      string_of_int !count

  let get_annotations () =
    let annotations = Array.of_list (List.rev !annotations) in
    fun index -> annotations.(int_of_string index)

end

let rich_pp get_annotations ppcmds =
  (** First, we use Format to introduce tags inside
      the pretty-printed document.

      Each inserted tag is an index to an annotation.
  *)
  let tagged_pp =
    let b = Buffer.create 13 in
    Buffer.add_string b "<pp>";
    Format.set_tags true;
    Pp.pp_with (Format.formatter_of_buffer b) ppcmds;
    Format.set_tags false;
    Buffer.add_string b "</pp>";
    Buffer.contents b
  in

  (** Second, we retrieve the final function that relates
      each tag to an annotation. *)
  let get = get_annotations () in

  (** Third, we parse the resulting string. It is a valid XML
      document (in the sense of Xml_parser). *)
  let xml_pp = Xml_parser.(parse (make (SString tagged_pp))) in

  (** Fourth, the low-level XML is turned into a high-level
      semi-structured document that contains a located annotation in
      every node. During the traversal of the low-level XML document,
      we build a raw string representation of the pretty-print. *)
  let rec node buffer = function
    | Element (index, [], cs) ->
      let startpos, endpos, cs = children buffer cs in
      let annotation = get index in
      (Element (index, { annotation; startpos; endpos }, cs), endpos)
    | PCData s ->
      Buffer.add_string buffer s;
      (PCData s, Buffer.length buffer)
    | _ ->
      assert false (* Because of the form of XML produced by Format. *)
  and children buffer cs =
    let startpos   = Buffer.length buffer in
    let cs, endpos =
      List.fold_left (fun (cs, endpos) c ->
        let c, endpos = node buffer c in
        (c :: cs, endpos)
      ) ([], startpos) cs
    in
    (startpos, endpos, List.rev cs)
  in
  let pp_buffer = Buffer.create 13 in
  let xml, _ = node pp_buffer xml_pp in

  (** That's it. We return the raw pretty-printing and its annotations
      tree. *)
  (Buffer.contents pp_buffer, xml)

let annotations_positions xml =
  let rec node accu = function
    | Element (_, { annotation; startpos; endpos }, cs) ->
      children ((annotation, (startpos, endpos)) :: accu) cs
    | _ ->
      accu
  and children accu cs =
    List.fold_left node accu cs
  in
  node [] xml

let xml_of_rich_pp tag_of_annotation attributes_of_annotation xml =
  let rec node = function
    | Element (_, { annotation; startpos; endpos }, cs) ->
      let attributes =
        [ "startpos", string_of_int startpos;
          "endpos", string_of_int endpos
        ]
        @ attributes_of_annotation annotation
      in
      Element (tag_of_annotation annotation,
               attributes,
               List.map node cs)
    | PCData s ->
      PCData s
  in
  node xml