open! Compat open Types type out = Source.t module State = struct type t = { context : (out * Source.tag) Stack.t; mutable current : out; mutable ignore_all : int; } let create () = { context = Stack.create (); current = []; ignore_all = 0 } let push state elt = if state.ignore_all = 0 then state.current <- elt :: state.current let push_ignore state = state.ignore_all <- state.ignore_all + 1 let pop_ignore state = state.ignore_all <- (if state.ignore_all > 0 then state.ignore_all - 1 else 0) let enter state tag = if state.ignore_all = 0 then ( let previous_elt = state.current in Stack.push (previous_elt, tag) state.context; state.current <- []; ()) let leave state = if state.ignore_all = 0 then ( let current_elt = List.rev state.current in let previous_elt, tag = Stack.pop state.context in state.current <- Tag (tag, current_elt) :: previous_elt; ()) let rec flush state = if Stack.is_empty state.context then List.rev state.current else ( leave state; flush state) end let rec compute_length_source (t : Types.Source.t) : int = let f (acc : int) = function | Types.Source.Elt t -> acc + compute_length_inline t | Types.Source.Tag (_, t) -> acc + compute_length_source t in List.fold_left f 0 t and compute_length_inline (t : Types.Inline.t) : int = let f (acc : int) { Types.Inline.desc; _ } = match desc with | Text s -> acc + String.length s | Entity _e -> acc + 1 | Linebreak -> 0 (* TODO *) | Styled (_, t) | Link { content = t; _ } -> acc + compute_length_inline t | Source s -> acc + compute_length_source s | Math _ -> assert false | Raw_markup _ -> assert false (* TODO *) in List.fold_left f 0 t (** Modern implementation using semantic tags, Only for 4.08+ *) (* module Tag = struct type Format.stag += | Elt of Inline.t | Tag of Source.tag | Ignore let setup_tags formatter state0 = let stag_functions = let mark_open_stag = function | Elt elt -> State.push state0 (Elt elt); "" | Tag tag -> State.enter state0 tag; "" | Format.String_tag "" -> State.enter state0 None; "" | Format.String_tag tag -> State.enter state0 (Some tag); "" | Ignore -> State.push_ignore state0; "" | _ -> "" and mark_close_stag = function | Elt _ -> "" | Tag _ | Format.String_tag _ -> State.leave state0; "" | Ignore -> State.pop_ignore state0; "" | _ -> "" in {Format. print_open_stag = (fun _ -> ()); print_close_stag = (fun _ -> ()); mark_open_stag; mark_close_stag; } in Format.pp_set_tags formatter true; Format.pp_set_formatter_stag_functions formatter stag_functions; () let elt ppf elt = Format.pp_open_stag ppf (Elt elt); Format.pp_print_as ppf (compute_length_inline elt) ""; Format.pp_close_stag ppf () let ignore ppf txt = Format.pp_open_stag ppf Ignore; Format.fprintf ppf "%t" txt; Format.pp_close_stag ppf () end *) (** Ugly terrible implementation of Format Semantic tags for OCaml < 4.08. Please get rid of it as soon as possible. *) module Tag = struct let setup_tags formatter state0 = let tag_functions = let get_tag s = let prefix_tag = "tag:" and prefix_ignore = "ignore-tag" in let l = String.length prefix_tag in if String.length s > l && String.sub s 0 l = prefix_tag then let elt : Inline.t = Marshal.from_string s l in `Elt elt else if s = prefix_ignore then `Ignore else `String s in let mark_open_tag s = match get_tag s with | `Ignore -> State.push_ignore state0; "" | `Elt elt -> State.push state0 (Elt elt); "" | `String "" -> State.enter state0 None; "" | `String tag -> State.enter state0 (Some tag); "" and mark_close_tag s = match get_tag s with | `Ignore -> State.pop_ignore state0; "" | `Elt _ -> "" | `String _ -> State.leave state0; "" in { Format.print_open_tag = (fun _ -> ()); print_close_tag = (fun _ -> ()); mark_open_tag; mark_close_tag; } in Format.pp_set_tags formatter true; Format.pp_set_formatter_tag_functions formatter tag_functions; () let elt ppf (elt : Inline.t) = Format.fprintf ppf "@{%t@}" (Marshal.to_string elt []) (fun fmt -> Format.pp_print_as fmt (compute_length_inline elt) "") let ignore ppf txt = Format.fprintf ppf "@{%t@}" txt end [@@alert "-deprecated--deprecated"] type t = Format.formatter -> unit let make () = let open Inline in let state0 = State.create () in let push elt = State.push state0 (Elt elt) in let push_text s = if state0.ignore_all = 0 then push [ inline @@ Text s ] in let formatter = let out_string s i j = push_text (String.sub s i j) in let out_flush () = () in Format.make_formatter out_string out_flush in (* out_functions is only available in OCaml>=4.06 *) (* let out_functions = {Format. * out_string = (fun i j s -> push_text @@ String.sub i j s ); * out_flush = (fun () -> ()); * out_newline = (fun () -> push [inline @@ Linebreak]); * out_spaces = (fun n -> push_text (String.make n ' ')); * out_indent = (fun n -> push_text (String.make n ' ')) * } * in * let formatter = Format.formatter_of_out_functions out_functions in *) Tag.setup_tags formatter state0; Format.pp_set_margin formatter 80; ( (fun () -> Format.pp_print_flush formatter (); State.flush state0), formatter ) let spf fmt = let flush, ppf = make () in Format.kfprintf (fun _ -> flush ()) ppf fmt let pf = Format.fprintf let elt t ppf = Tag.elt ppf t let entity e ppf = elt [ inline @@ Inline.Entity e ] ppf let ignore t ppf = Tag.ignore ppf t let ( ++ ) f g ppf = f ppf; g ppf let span ?(attr = "") f ppf = pf ppf "@{<%s>%t@}" attr f let txt s ppf = Format.pp_print_string ppf s let noop (_ : Format.formatter) = () let break i j ppf = Format.pp_print_break ppf i j let cut = break 0 0 let sp = break 1 0 let rec list ?sep ~f = function | [] -> noop | [ x ] -> f x | x :: xs -> ( let hd = f x in let tl = list ?sep ~f xs in match sep with None -> hd ++ tl | Some sep -> hd ++ sep ++ tl) let box_hv t ppf = pf ppf "@[%t@]" t let box_hv_no_indent t ppf = pf ppf "@[%t@]" t let render f = spf "@[%t@]" (span f) let code ?attr f = [ inline ?attr @@ Inline.Source (render f) ] let documentedSrc f = [ DocumentedSrc.Code (render f) ] let codeblock ?attr f = [ block ?attr @@ Block.Source (Comment.default_lang_tag, [], [], render f, []) ] let keyword keyword ppf = pf ppf "@{%s@}" keyword let mode mode ppf = pf ppf "@{%s@}" mode module Infix = struct let ( ++ ) = ( ++ ) end