content: module Error.Diagnose
    ( -- $header
  
      -- * How to use this module
      -- $usage
  
      -- ** Generating a report
      -- $generate_report
  
      -- ** Creating diagnostics from reports
      -- $create_diagnostic
  
      -- *** Pretty-printing a diagnostic onto a file 'System.IO.Handle'
      -- $diagnostic_pretty
  
      -- *** Pretty-printing a diagnostic as a document
      -- $diagnostic_to_doc
  
      -- *** Exporting a diagnostic to JSON
      -- $diagnostic_json
  
      -- ** Compatibility layers for popular parsing libraries
      -- $compatibility_layers
  
      -- *** megaparsec >= 9.0.0 ("Error.Diagnose.Compat.Megaparsec")
      -- $compatibility_megaparsec
  
      -- *** parsec >= 3.1.14.0 ("Error.Diagnose.Compat.Parsec")
      -- $compatibility_parsec
  
      -- *** Common errors
      -- $compatibility_errors
  
      -- * Re-exports
      module Export,
    )
  where
  
  import Error.Diagnose.Diagnostic as Export
  import Error.Diagnose.Position as Export
  import Error.Diagnose.Pretty as Export
  import Error.Diagnose.Report as Export
  import Error.Diagnose.Style as Export
  
  -- $header
  --
  --   This module exports all the needed data types to use this library.
  --   It should be sufficient to only @import "Error.Diagnose"@.
  
  -- $usage
  --
  --   This library is intended to provide a very simple way of creating beautiful errors, by exposing
  --   a small yet simple API to the user.
  --
  --   The basic idea is that a diagnostic is a collection of reports (which embody errors or warnings) along
  --   with the files which can be referenced in those reports.
  
  -- $generate_report
  --
  --   A report contains:
  --
  --   - A message, to be shown at the top
  --
  --   - A list of located markers, used to underline parts of the source code and to emphasize it with a message
  --
  --   - A list of hints, shown at the very bottom
  --
  --   __Note__: The message type contained in a report is abstracted by a type variable.
  --             In order to render the report, the message must also be able to be rendered in some way
  --             (that we'll see later).
  --
  --   This library allows defining two kinds of reports:
  --
  --   - Errors, using 'Err'
  --
  --   - Warnings, using 'Warn'
  --
  --   Both take an optional error code, a message, a list of located markers and a list of hints.
  --
  --   A very simple example is:
  --
  --   > exampleReport :: Report String
  --   > exampleReport =
  --   >   Err
  --   >     -- vv  OPTIONAL ERROR CODE
  --   >     Nothing
  --   >     -- vv  ERROR MESSAGE
  --   >     "This is my first error report"
  --   >     -- vv  MARKERS
  --   >     [ (Position (1, 3) (1, 8) "some_test.txt", This "Some text under the marker") ]
  --   >     -- vv  HINTS
  --   >     []
  --
  --   In general, 'Position's are returned by either a lexer or a parser, so that you never have to construct them
  --   directly in the code.
  --
  --   __Note__: If using any parser library, you will have to convert from the internal positioning system to a 'Position'
  --             to be able to use this library.
  --
  --   Markers put in the report can be one of (the colors specified are used only when pretty-printing):
  --
  --   - A 'Error.Diagnose.Report.This' marker, which is the primary marker of the report.
  --     While it is allowed to have multiple of these inside one report, it is encouraged not to, because the position at the top of
  --     the report will only be the one of the /first/ 'Error.Diagnose.Report.This' marker, and because the resulting report may be harder to understand.
  --
  --         This marker is output in red in an error report, and yellow in a warning report.
  --
  --   - A 'Error.Diagnose.Report.Where' marker contains additional information\/provides context to the error\/warning report.
  --     For example, it may underline where a given variable @x@ is bound to emphasize it.
  --
  --         This marker is output in blue.
  --
  --   - A 'Error.Diagnose.Report.Maybe' marker may contain possible fixes (if the text is short, else hints are recommended for this use).
  --
  --         This marker is output in magenta.
  --
  --   - A 'Error.Diagnose.Report.Blank' marker is useful only to output additional lines of code in the report.
  --
  --         This marker is not output and has no color.
  
  -- $create_diagnostic
  --
  --   To create a new diagnostic, you need to use its 'Data.Default.Default' instance (which exposes a 'def' function, returning a new empty 'Diagnostic').
  --   Once the 'Diagnostic' is created, you can use either 'addReport' (which takes a 'Diagnostic' and a 'Report', abstract by the same message type,
  --   and returns a 'Diagnostic') to insert a new report inside the diagnostic, or 'addFile' (which takes a 'Diagnostic', a 'FilePath' and a @['String']@,
  --   and returns a 'Diagnostic') to insert a new file reference in the diagnostic.
  --
  --   You can then either pretty-print the diagnostic obtained (which requires all messages to be instances of the 'Prettyprinter.Pretty')
  --   -- directly onto a file handle or as a plain 'Prettyprinter.Doc'ument --
  --   or export it to a lazy JSON 'Data.Bytestring.Lazy.ByteString' (e.g. in a LSP context).
  
  -- $diagnostic_pretty
  --
  --   'Diagnostic's can be output to any 'System.IO.Handle' using the 'printDiagnostic' function.
  --   This function takes several parameters:
  --
  --   - The 'System.IO.Handle' onto which to output the 'Diagnostic'.
  --     It __must__ be a 'System.IO.Handle' capable of outputting data.
  --
  --   - A 'Bool' used to indicate whether you want to output the 'Diagnostic' with unicode characters, or simple ASCII characters.
  --
  --         Here are two examples of the same diagnostic, the first output with unicode characters, and the second output with ASCII characters:
  --
  --         > [error]: Error with one marker in bounds
  --         >      ╭──▶ test.zc@1:25-1:30
  --         >      │
  --         >    1 │ let id<a>(x : a) : a := x + 1
  --         >      •                         ┬────
  --         >      •                         ╰╸ Required here
  --         > ─────╯
  --
  --         > [error]: Error with one marker in bounds
  --         >      +--> test.zc@1:25-1:30
  --         >      |
  --         >    1 | let id<a>(x : a) : a := x + 1
  --         >      :                         ^----
  --         >      :                         `- Required here
  --         > -----+
  --
  --   - A 'Bool' set to 'False' if you don't want colors in the end result.
  --
  --   - A 'Int' describing the number of spaces with which to output a TAB character.
  --
  --   - The 'Style' describing colors of the report.
  --     See the module "Error.Diagnose.Style" for how to define new styles.
  --
  --   - And finally the 'Diagnostic' to output.
  
  -- $diagnostic_to_doc
  --
  -- 'Diagnostic's can be “output” (at least ready to be rendered) to a 'Prettyprinter.Doc' using 'prettyDiagnostic', which allows it to be easily added to other 'Prettyprinter.Doc' outputs.
  -- This makes it easy to customize the error messages further (though not the internal parts, only adding to it).
  -- As a 'Prettyprinter.Doc', there is also the possibility of altering internal annotations (styles) much easier (although this is already possible when printing the diagnostic).
  --
  -- The arguments of the function mostly follow the ones from 'printDiagnostic'.
  -- The style is not one, as it can be applied by simply applying the styling function to the resulting function (if wanted).
  
  -- $diagnostic_json
  --
  --   'Diagnostic's can be exported to a JSON record of the following type, using the 'diagnosticToJson' function:
  --
  --   > { files:
  --   >     { name: string
  --   >     , content: string[]
  --   >     }[]
  --   > , reports:
  --   >     { kind: 'error' | 'warning'
  --   >     , code: string?
  --   >     , message: string
  --   >     , markers:
  --   >         { kind: 'this' | 'where' | 'maybe'
  --   >         , position:
  --   >             { beginning: { line: int, column: int }
  --   >             , end: { line: int, column: int }
  --   >             , file: string
  --   >             }
  --   >         , message: string
  --   >         }[]
  --   >     , hints: ({ note: string } | { hint: string })[]
  --   >     }[]
  --   > }
  --
  --   This is particularly useful in the context of a LSP server, where outputting or parsing a raw error yields strange results or is unnecessarily complicated.
  --
  --   Please note that this requires the flag @diagnose:json@ to be enabled (it is disabled by default in order not to include @aeson@, which is a heavy library).
  
  -- $compatibility_layers
  --
  --   There are many parsing libraries available in the Haskell ecosystem, each coming with its own way of handling errors.
  --   Eventually, one needs to be able to map errors from these libraries to 'Diagnostic's, without having to include additional code for doing so.
  --   This is where compatibility layers come in handy.
  --
  --   As of now, there are compatibility layers for these libraries:
  
  -- $compatibility_megaparsec
  --
  --   This needs the flag @diagnose:megaparsec-compat@ to be enabled.
  --
  --   Using the compatibility layer is very easy, as it is designed to be as simple as possible.
  --   One simply needs to convert the 'Text.Megaparsec.ParseErrorBundle' which is returned by running a parser into a 'Diagnostic' by using 'Error.Diagnose.Compat.Megaparsec.diagnosticFromBundle'.
  --   Several wrappers are included for easy creation of kinds (error, warning) of diagnostics.
  --
  --   __Note:__ the returned diagnostic does not include file contents, which needs to be added manually afterwards.
  --
  --   As a quick example:
  --
  --   > import qualified Text.Megaparsec as MP
  --   > import qualified Text.Megaparsec.Char as MP
  --   > import qualified Text.Megaparsec.Char.Lexer as MP
  --   >
  --   > let filename = "<interactive>"
  --   >     content  = "00000a2223266"
  --   >
  --   > let myParser = MP.some MP.decimal <* MP.eof
  --   >
  --   > let res      = MP.runParser myParser filename content
  --   >
  --   > case res of
  --   >   Left bundle ->
  --   >     let diag  = errorDiagnosticFromBundle Nothing "Parse error on input" Nothing bundle
  --   >            --   Creates a new diagnostic with no default hints from the bundle returned by megaparsec
  --   >         diag' = addFile diag filename content
  --   >            --   Add the file used when parsing with the same filename given to 'MP.runParser'
  --   >     in printDiagnostic stderr True 4 diag'
  --   >   Right res   -> print res
  --
  --   This example will return the following error message (assuming default instances for @'Error.Diagnose.Compat.Megaparsec.HasHints' 'Data.Void.Void' msg@):
  --
  --   > [error]: Parse error on input
  --   >      ╭──▶ <interactive>@1:6-1:7
  --   >      │
  --   >    1 │ 00000a2223266
  --   >      •      ┬
  --   >      •      ├╸ unexpected 'a'
  --   >      •      ╰╸ expecting digit, end of input, or integer
  --   > ─────╯
  
  -- $compatibility_parsec
  --
  --   This needs the flag @diagnose:parsec-compat@ to be enabled.
  --
  --   This compatibility layer allows easily converting 'Text.Parsec.Error.ParseError's into a single-report diagnostic containing all available information such
  --   as unexpected/expected tokens or error messages.
  --   The function 'Error.Diagnose.Compat.Parsec.diagnosticFromParseError' is used to perform the conversion between a 'Text.Parsec.Error.ParseError' and a 'Diagnostic'.
  --
  --   __Note:__ the returned diagnostic does not include file contents, which needs to be added manually afterwards.
  --
  --   Quick example:
  --
  --   > import qualified Text.Parsec as P
  --   >
  --   > let filename = "<interactive>"
  --   >     content  = "00000a2223266"
  --   >
  --   > let myParser = P.many1 P.digit <* P.eof
  --   >
  --   > let res      = P.parse myParser filename content
  --   >
  --   > case res of
  --   >   Left error ->
  --   >     let diag  = errorDiagnosticFromParseError Nothing "Parse error on input" Nothing error
  --   >            --   Creates a new diagnostic with no default hints from the bundle returned by megaparsec
  --   >         diag' = addFile diag filename content
  --   >            --   Add the file used when parsing with the same filename given to 'MP.runParser'
  --   >     in printDiagnostic stderr True 4 diag'
  --   >   Right res  -> print res
  --
  --   This will output the following error on @stderr@:
  --
  --   > [error]: Parse error on input
  --   >      ╭──▶ <interactive>@1:6-1:7
  --   >      │
  --   >    1 │ 00000a2223266
  --   >      •      ┬
  --   >      •      ├╸ unexpected 'a'
  --   >      •      ╰╸ expecting any of digit, end of input
  --   > ─────╯
  
  -- $compatibility_errors
  --
  --   - @No instance for (HasHints ??? msg) arising from a use of ‘errorDiagnosticFromBundle’@ (@???@ is any type, depending on your parser's custom error type):
  --
  --       The typeclass 'Error.Diagnose.Compat.Megaparsec.HasHints' does not have any default instances, because treatments of custom errors is highly dependent on who is using the library.
  --       As such, you will need to create orphan instances for your parser's error type.
  --
  --       Note that the message type @msg@ can be left abstract if the implements of 'Error.Diagnose.Compat.Hints.hints' is @hints _ = mempty@.
  