(** A message for the user *)

(** User messages are styled document that can be printed to the console or in
    the log file. *)

(** Symbolic styles that can be used inside messages. These styles are later
    converted to actual concrete styles depending on the output device. For
    instance, when printed to the terminal they are converted to ansi terminal
    styles ([Ansi_color.Style.t list] values). *)
module Style : sig
  type t =
    | Loc
    | Error
    | Warning
    | Kwd
    | Id
    | Prompt
    | Hint
    | Details
    | Ok
    | Debug
    | Success
    | Ansi_styles of Ansi_color.Style.t list

  val to_dyn : t -> Dyn.t
  val compare : t -> t -> Ordering.t
end

module Annots : sig
  module Key : sig
    type 'a t

    val create : name:string -> ('a -> Dyn.t) -> 'a t
  end

  type t

  val to_dyn : t -> Dyn.t
  val find : t -> 'a Key.t -> 'a option
  val set : t -> 'a Key.t -> 'a -> t
  val empty : t
  val is_empty : t -> bool
  val singleton : 'a Key.t -> 'a -> t

  (** The message has a location embed in the text. *)
  val has_embedded_location : unit Key.t

  (** The message needs a stack trace for clarity. *)
  val needs_stack_trace : unit Key.t
end

(** A user message.contents composed of an optional file location and a list of
    paragraphs.

    The various paragraphs will be printed one after the other and will all
    start at the beginning of a line. They are all wrapped inside a [Pp.box].

    When hints are provided, they are printed as last paragraphs and prefixed
    with "Hint:". Hints should give indication to the user for how to fix the
    issue.

    The [annots] field is intended to carry extra context for other,
    non-user-facing purposes (such as data for the RPC). *)
type t =
  { loc : Loc0.t option
  ; paragraphs : Style.t Pp.t list
  ; hints : Style.t Pp.t list
  ; annots : Annots.t
  ; context : string option
  ; dir : string option
  }

val compare : t -> t -> Ordering.t
val equal : t -> t -> bool
val pp : t -> Style.t Pp.t

module Print_config : sig
  (** Associate ANSI terminal styles to symbolic styles *)
  type t = Style.t -> Ansi_color.Style.t list

  (** The default configuration *)
  val default : t
end

(** Construct a user message from a list of paragraphs.

    The first paragraph is prefixed with [prefix] inside the box. [prefix]
    should not end with a space as a space is automatically inserted by [make]
    if necessary. *)
val make
  :  ?loc:Loc0.t
  -> ?prefix:Style.t Pp.t
  -> ?hints:Style.t Pp.t list
  -> ?annots:Annots.t
  -> ?context:string
  -> ?dir:string
  -> Style.t Pp.t list
  -> t

(** Print to [stdout] (not thread safe) *)
val print : ?config:Print_config.t -> t -> unit

(** Print to [stderr] (not thread safe) *)
val prerr : ?config:Print_config.t -> t -> unit

(** Produces a "Did you mean ...?" hint *)
val did_you_mean : string -> candidates:string list -> Style.t Pp.t list

(** Produces a plain text representation of the error message, without the
    "Error: " prefix. *)
val to_string : t -> string

(** Returns [true] if the message has an explicit location or one embedded in
    the text. *)
val has_location : t -> bool

(** Returns [true] if the message's annotations contains
    [Annot.Has_embedded_location]. *)
val has_embedded_location : t -> bool

(** Returns [true] if the message's annotations contains
    [Annot.Needs_stack_trace]. *)
val needs_stack_trace : t -> bool

(** Formatting of shell commands *)
val command : string -> Style.t Pp.t

(** A message with a left and right component separated by a
    space. The left component is padded to 12 characters so that the space
    separating the left and right side of all messages are aligned. *)
val aligned_message : left:Style.t * string -> right:Style.t Pp.t -> Style.t Pp.t
