aboutsummaryrefslogtreecommitdiff
path: root/lib/merge_style.ml
diff options
context:
space:
mode:
Diffstat (limited to 'lib/merge_style.ml')
-rwxr-xr-xlib/merge_style.ml131
1 files changed, 131 insertions, 0 deletions
diff --git a/lib/merge_style.ml b/lib/merge_style.ml
new file mode 100755
index 0000000..1e8be7d
--- /dev/null
+++ b/lib/merge_style.ml
@@ -0,0 +1,131 @@
+open StdLabels
+open Css.Types
+
+let delim_coma = ( Component_value.Delim ","
+ , Common.location_none )
+
+module MapRule = Map.Make(struct
+ type t = Component_value.t list
+ let compare =
+ Comparator.compare_list (fun l1 l2 ->
+ Comparator.component_value
+ (l1, Common.location_none)
+ (l2, Common.location_none)
+ )
+ end)
+
+(** The type of the map contains both :
+
+ - The declaration inside the selector
+ - The selector Location
+
+*)
+type t = (Declaration_list.t * Location.t) list MapRule.t
+
+type acc = Component_value.t list * Component_value.t list list
+(** Group all the selectors together, using a given delimiter *)
+let group_selector
+ : string -> Component_value.t with_loc list with_loc -> Component_value.t list list
+ = fun delim elems ->
+
+ let add_element
+ : acc -> Component_value.t with_loc -> acc
+ = fun (acc, prev) elem ->
+ match (fst elem) with
+ | Delim s when String.equal s delim -> [], (List.rev acc)::prev
+ | other -> other::acc, prev
+ in
+ let last, prev = List.fold_left
+ (fst elems)
+ ~init:([], [])
+ ~f:add_element in
+ (List.rev last)::prev
+
+(** Add a new style in the map. *)
+let add_style
+ : Style_rule.t -> t -> t
+ = fun {prelude; block; loc} map ->
+ List.fold_left (group_selector "," prelude)
+ ~init:map
+ ~f:(fun map group ->
+ MapRule.update group
+ (function
+ | None ->
+ (* There is no declaration yet, just add this one *)
+ Some [(block, loc)]
+ | Some tl ->
+
+ (* The declaration is already present.
+
+ For each of them, we check if the declaration is overriden
+ by the new one, and update the list.
+
+ The news declarations are added in a new block (a second
+ pass may be necessary to join all the remaining elements
+ together.
+ *)
+ Some (Common.update_declarations (block, loc) tl))
+ map)
+
+module ReversedMapRule = Map.Make(struct
+ type t = Declaration_list.t * Location.t
+
+ (* Use a custom comparaison without the location *)
+ let compare l1 l2 =
+ Comparator.declaration_list
+ (fst l1)
+ (fst l2)
+ end)
+type splitted_rules' = (Component_value.t list list) ReversedMapRule.t
+
+(** Extract all the styles, and return them as a Rule.t sequence *)
+let extract_style
+ : t -> Rule.t Seq.t
+ = fun map ->
+ (* First, iterate all the values and match the identical one together *)
+
+ let table:splitted_rules' =
+ MapRule.fold
+ (fun k values map' ->
+
+ (* Each element may be present multiple times in the declaration. We
+ have te extract each of them *)
+ List.fold_left values
+ ~init:map'
+ ~f:(fun map' (v, loc) ->
+
+ ReversedMapRule.update (v, loc)
+ (function
+ | None -> Some [k]
+ | Some tl -> Some (k::tl))
+ map' ))
+ map
+ ReversedMapRule.empty in
+
+ (* The rebuild the rules *)
+ ReversedMapRule.to_seq table
+ |> Seq.map (fun ((block, loc), k) ->
+
+ let selectors =
+ List.fold_left k
+ ~init:[]
+ ~f:(fun acc v ->
+ let selectors = List.map
+ v
+ ~f:(fun x -> x , Common.location_none) in
+ let tail = List.append selectors acc in
+ delim_coma::tail) in
+
+ let prelude =
+ match selectors with
+ | (Component_value.Delim ",", _)::tl ->
+ (* Remove the first delimiter element *)
+ ( tl
+ , Common.location_none)
+ | _->
+ ( selectors
+ , Common.location_none )
+ in
+ Rule.Style_rule (Style_rule.{prelude; block; loc}))
+
+let empty = MapRule.empty