summaryrefslogtreecommitdiff
path: root/css/merge_lib/merge.ml
blob: af95298604ad1f8fdd0bc9ea0d6975013dadfdd8 (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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
open StdLabels
open Css.Types

module AtRule = Map.Make(struct
    type t  = string * (Component_value.t list)
    let compare at1 at2 =

      let cmp = String.compare (fst at1) (fst at2) in
      if cmp <> 0 then cmp
      else
        Comparator.compare_list (fun l1 l2 ->
            Comparator.component_value
              (l1, Common.location_none)
              (l2, Common.location_none) )
          (snd at1)
          (snd at2)
  end)

type at_type =
  | Empty
  | Declaration of (Declaration_list.t * Css.Location.t) list
  | Stylesheet of (Merge_style.t * ats)

and at_map_content = (Css.Location.t * at_type)

and ats = at_map_content AtRule.t

type t = Merge_style.t * ats

let rec add_brace_block
  : Brace_block.t -> Css.Location.t -> at_map_content option -> at_map_content option
  = fun block loc value ->

    begin match block, value with
      (* Empty element, update the existing one if any *)
      | Brace_block.Empty, _ ->
        Some
          ( loc
          , Empty )

      (* New declarationList, juste add it *)
      | Brace_block.Declaration_list decls, None ->
        Some
          ( loc
          , Declaration [(decls, loc)])

      | Brace_block.Declaration_list decls, (Some (loc, Declaration decl2)) ->
        Some
          ( loc
          , Declaration (Common.update_declarations
                           (decls, Common.location_none) decl2 ))

      | Brace_block.Stylesheet s, None ->
        let eval = add_css (Merge_style.empty, AtRule.empty) s in
        Some
          ( loc
          , Stylesheet eval )

      | Brace_block.Stylesheet s, Some (loc, Stylesheet css) ->
        let eval = add_css css s in
        Some
          ( loc
          , Stylesheet eval )

      (* Othe cases are not handled *)
      | _ -> None
    end

(** Add a new @ definition *)
and add_at
  : Css.Types.At_rule.t -> ats -> ats
  = fun {name; prelude; block; loc} map ->

    let prelude = List.map (fst prelude) ~f:fst in
    let key = (fst name), prelude in
    AtRule.update key
      (add_brace_block block loc)
      map

and add_css
  : t -> Stylesheet.t -> t
  = fun (styles, atrules) css ->
    List.fold_left (fst css)
      ~init:(styles, atrules)
      ~f:(fun (styles, ats)-> function
          | Rule.At_rule r -> (styles, add_at r ats)
          | Rule.Style_rule r -> (Merge_style.add_style r styles, ats))

(** Helper function for retrieving the location *)
let get_loc
  : Rule.t -> Css.Location.t
  = function
    | Rule.Style_rule t -> t.Style_rule.loc
    | Rule.At_rule t -> t.At_rule.loc

let rec extract_at
  : ats -> Css.Types.Rule.t Seq.t
  = fun map ->
    AtRule.to_seq map
    |> Seq.map (fun ((name, prelude), (loc, value)) ->

        let name = name, loc
        and prelude = List.map ~f:(fun x -> x, loc) prelude, loc in

        match value with
        | Stylesheet css ->

          let stylesheet = extract_css css in
          let block = Brace_block.Stylesheet stylesheet in
          (Rule.At_rule (At_rule.{name; prelude; block; loc}))
        | Empty ->
          let block = Brace_block.Empty in
          (Rule.At_rule (At_rule.{name; prelude; block; loc}))

        | Declaration decls ->
          let declarations = List.fold_left decls
              ~init:[]
              ~f:(fun acc (decl, _) ->
                  let elems = fst decl in
                  List.append elems acc) in
          let block = Brace_block.Declaration_list (declarations, loc) in
          (Rule.At_rule (At_rule.{name; prelude; block; loc})))

and extract_css
  : t -> Stylesheet.t
  = fun (styles, ats) ->
    let arr =
      Seq.append
        (extract_at ats)
        (Merge_style.extract_style styles)
      |> Array.of_seq in
    (* Sort the declaration in initial ordering (using the location attribute) *)
    Array.fast_sort ~cmp:(fun v1 v2 -> Stdlib.compare (get_loc v1) (get_loc v2) ) arr;
    (Array.to_list arr, Common.location_none)

let empty
  : t
  = Merge_style.empty, AtRule.empty