content: {-# LANGUAGE OverloadedStrings #-}
  {- |
     Module      : Text.Pandoc.Highlighting
     Copyright   : Copyright (C) 2008-2025 John MacFarlane
     License     : GNU GPL, version 2 or above
  
     Maintainer  : John MacFarlane <jgm@berkeley.edu>
     Stability   : alpha
     Portability : portable
  
  Exports functions for syntax highlighting.
  -}
  
  module Text.Pandoc.Highlighting ( highlightingStyles
                                  , languages
                                  , languagesByExtension
                                  , highlight
                                  -- * Formats
                                  -- ** LaTeX
                                  , formatLaTeXInline
                                  , formatLaTeXBlock
                                  , styleToLaTeX
                                  -- ** HTML
                                  , formatHtmlInline
                                  , formatHtmlBlock
                                  , formatHtml4Block
                                  , styleToCss
                                  -- ** ConTeXt
                                  , formatConTeXtInline
                                  , formatConTeXtBlock
                                  , styleToConTeXt
                                  , formatANSI
                                  -- ** Typst
                                  , formatTypstBlock
                                  , formatTypstInline
                                  , styleToTypst
                                  -- * Styles
                                  , defaultStyle
                                  , pygments
                                  , espresso
                                  , zenburn
                                  , tango
                                  , kate
                                  , monochrome
                                  , breezeDark
                                  , haddock
                                  , Style
                                  , lookupHighlightingStyle
                                  , fromListingsLanguage
                                  , toListingsLanguage
                                  ) where
  import Control.Monad ( msum )
  import qualified Data.Map as M
  import Data.Maybe (fromMaybe)
  import qualified Data.Text as T
  import Skylighting
      ( SyntaxMap,
        breezeDark,
        espresso,
        haddock,
        kate,
        monochrome,
        pygments,
        tango,
        zenburn,
        formatHtml4Block,
        formatHtmlBlock,
        formatHtmlInline,
        styleToCss,
        formatConTeXtBlock,
        formatConTeXtInline,
        styleToConTeXt,
        formatLaTeXBlock,
        formatLaTeXInline,
        styleToLaTeX,
        formatTypstBlock,
        formatTypstInline,
        styleToTypst,
        formatANSI,
        Style,
        FormatOptions(containerClasses, startNumber, lineAnchors,
                      numberLines, lineIdPrefix, codeClasses),
        SourceLine,
        lookupSyntax,
        syntaxesByExtension,
        parseTheme,
        tokenize,
        defaultFormatOpts,
        TokenizerConfig(traceOutput, TokenizerConfig, syntaxMap),
        Syntax(sShortname, sName),
        TokenType(NormalTok) )
  import Text.Pandoc.Definition ( Attr )
  import Text.Pandoc.Class (PandocMonad, readFileLazy)
  import Text.Pandoc.Error (PandocError(..))
  import Control.Monad.Except (throwError)
  import System.FilePath (takeExtension)
  import Text.Pandoc.Shared (safeRead)
  
  -- | The default highlighting style used by pandoc (pygments).
  defaultStyle :: Style
  defaultStyle = pygments
  
  highlightingStyles :: [(T.Text, Style)]
  highlightingStyles =
    [("pygments", pygments),
     ("tango", tango),
     ("espresso", espresso),
     ("zenburn", zenburn),
     ("kate", kate),
     ("monochrome", monochrome),
     ("breezedark", breezeDark),
     ("haddock", haddock)]
  
  languages :: SyntaxMap -> [T.Text]
  languages syntaxmap = [T.toLower (sName s) | s <- M.elems syntaxmap]
  
  languagesByExtension :: SyntaxMap -> T.Text -> [T.Text]
  languagesByExtension syntaxmap ext =
    [T.toLower (sName s) | s <- syntaxesByExtension syntaxmap (T.unpack ext)]
  
  highlight :: SyntaxMap
            -> (FormatOptions -> [SourceLine] -> a) -- ^ Formatter
            -> Attr   -- ^ Attributes of the CodeBlock
            -> T.Text -- ^ Raw contents of the CodeBlock
            -> Either T.Text a
  highlight syntaxmap formatter (ident, classes, keyvals) rawCode =
    let firstNum = fromMaybe 1 (safeRead (fromMaybe "1" $ lookup "startFrom" keyvals))
        fmtOpts = defaultFormatOpts{
                    startNumber = firstNum,
                    lineAnchors = any (`elem`
                          ["line-anchors", "lineAnchors"]) classes,
                    numberLines = any (`elem`
                          ["number","numberLines", "number-lines"]) classes,
                    lineIdPrefix = if T.null ident
                                      then mempty
                                      else ident <> "-" }
        tokenizeOpts = TokenizerConfig{ syntaxMap = syntaxmap
                                      , traceOutput = False }
    in  case msum (map (`lookupSyntax` syntaxmap) classes) of
              Nothing
                | numberLines fmtOpts -> Right
                                $ formatter fmtOpts{ codeClasses = [],
                                                     containerClasses = classes }
                                $ map (\ln -> [(NormalTok, ln)])
                                $ T.lines rawCode
                | otherwise  -> Left ""
              Just syntax  -> either (Left . T.pack) Right $
                formatter fmtOpts{ codeClasses =
                                     [T.toLower (sShortname syntax)],
                                    containerClasses = classes } <$>
                  tokenize tokenizeOpts syntax rawCode
  
  -- Functions for correlating latex listings package's language names
  -- with skylighting language names:
  
  langToListingsMap :: M.Map T.Text T.Text
  langToListingsMap = M.fromList langsList
  
  listingsToLangMap :: M.Map T.Text T.Text
  listingsToLangMap = M.fromList $ map switch langsList
    where switch (a,b) = (b,a)
  
  langsList :: [(T.Text, T.Text)]
  langsList =
    [("abap","ABAP"),
    ("acm","ACM"),
    ("acmscript","ACMscript"),
    ("acsl","ACSL"),
    ("ada","Ada"),
    ("algol","Algol"),
    ("ant","Ant"),
    ("assembler","Assembler"),
    ("gnuassembler","Assembler"),
    ("awk","Awk"),
    ("bash","bash"),
    ("monobasic","Basic"),
    ("purebasic","Basic"),
    ("c","C"),
    ("cs","C"),
    ("objectivec","C"),
    ("cpp","C++"),
    ("c++","C++"),
    ("ocaml","Caml"),
    ("cil","CIL"),
    ("clean","Clean"),
    ("cobol","Cobol"),
    ("comal80","Comal80"),
    ("command.com","command.com"),
    ("comsol","Comsol"),
    ("csh","csh"),
    ("delphi","Delphi"),
    ("eiffel","Eiffel"),
    ("elan","Elan"),
    ("elisp","elisp"),
    ("erlang","erlang"),
    ("euphoria","Euphoria"),
    ("fortran","Fortran"),
    ("gap","GAP"),
    ("gcl","GCL"),
    ("gnuplot","Gnuplot"),
    ("go","Go"),
    ("hansl","hansl"),
    ("haskell","Haskell"),
    ("html","HTML"),
    ("idl","IDL"),
    ("inform","inform"),
    ("java","Java"),
    ("jvmis","JVMIS"),
    ("ksh","ksh"),
    ("lingo","Lingo"),
    ("lisp","Lisp"),
    ("commonlisp","Lisp"),
    ("llvm","LLVM"),
    ("logo","Logo"),
    ("lua","Lua"),
    ("make","make"),
    ("makefile","make"),
    ("mathematica","Mathematica"),
    ("matlab","Matlab"),
    ("mercury","Mercury"),
    ("metapost","MetaPost"),
    ("miranda","Miranda"),
    ("mizar","Mizar"),
    ("ml","ML"),
    ("modula2","Modula-2"),
    ("mupad","MuPAD"),
    ("nastran","NASTRAN"),
    ("oberon2","Oberon-2"),
    ("ocl","OCL"),
    ("octave","Octave"),
    ("oorexx","OORexx"),
    ("oz","Oz"),
    ("pascal","Pascal"),
    ("perl","Perl"),
    ("php","PHP"),
    ("pli","PL/I"),
    ("plasm","Plasm"),
    ("postscript","PostScript"),
    ("pov","POV"),
    ("prolog","Prolog"),
    ("promela","Promela"),
    ("pstricks","PSTricks"),
    ("python","Python"),
    ("r","R"),
    ("reduce","Reduce"),
    ("rexx","Rexx"),
    ("rsl","RSL"),
    ("ruby","Ruby"),
    ("s","S"),
    ("sas","SAS"),
    ("scala","Scala"),
    ("scilab","Scilab"),
    ("sh","sh"),
    ("shelxl","SHELXL"),
    ("simula","Simula"),
    ("sparql","SPARQL"),
    ("sql","SQL"),
    ("swift","Swift"),
    ("tcl","tcl"),
    ("tex","TeX"),
    ("latex","TeX"),
    ("vbscript","VBScript"),
    ("verilog","Verilog"),
    ("vhdl","VHDL"),
    ("vrml","VRML"),
    ("xml","XML"),
    ("xslt","XSLT")]
  
  -- | Determine listings language name from skylighting language name.
  toListingsLanguage :: T.Text -> Maybe T.Text
  toListingsLanguage lang = M.lookup (T.toLower lang) langToListingsMap
  
  -- | Determine skylighting language name from listings language name.
  fromListingsLanguage :: T.Text -> Maybe T.Text
  fromListingsLanguage lang = M.lookup lang listingsToLangMap
  
  -- | Lookup style from a name. If the name is a standard style,
  -- load it; if it ends in ".theme", attempt to load a KDE theme
  -- from the file path specified.
  lookupHighlightingStyle :: PandocMonad m => String -> m Style
  lookupHighlightingStyle s
    | takeExtension s == ".theme" = -- attempt to load KDE theme
      do contents <- readFileLazy s
         case parseTheme contents of
              Left _    -> throwError $ PandocOptionError $ T.pack $
                             "Could not read highlighting theme " ++ s
              Right sty -> return sty
    | otherwise =
    case lookup (T.toLower $ T.pack s) highlightingStyles of
         Just sty -> return sty
         Nothing  -> throwError $ PandocOptionError $ T.pack $
                        "Unknown highlight-style " ++ s
  