packages feed

skylighting-lucid-1.0.0: lib/Skylighting/Format/HTML/Lucid.hs

{-# LANGUAGE CPP               #-}
{-# LANGUAGE OverloadedStrings #-}

module Skylighting.Format.HTML.Lucid
  ( formatHtmlInline
  , formatHtmlBlock
  ) where

import           Data.Foldable (traverse_)
import qualified Data.List as L
import qualified Data.Text as T
import           Lucid
import           Skylighting.Types

#if !MIN_VERSION_base(4,11,0)
import           Data.Semigroup
#endif

-- | Format tokens using HTML spans inside @code@ tags. For example,
-- A @KeywordTok@ is rendered as a span with class @kw@.
-- Short class names correspond to 'TokenType's as follows:
-- 'KeywordTok'        = @kw@,
-- 'DataTypeTok'       = @dt@,
-- 'DecValTok'         = @dv@,
-- 'BaseNTok'          = @bn@,
-- 'FloatTok'          = @fl@,
-- 'CharTok'           = @ch@,
-- 'StringTok'         = @st@,
-- 'CommentTok'        = @co@,
-- 'OtherTok'          = @ot@,
-- 'AlertTok'          = @al@,
-- 'FunctionTok'       = @fu@,
-- 'RegionMarkerTok'   = @re@,
-- 'ErrorTok'          = @er@,
-- 'ConstantTok'       = @cn@,
-- 'SpecialCharTok'    = @sc@,
-- 'VerbatimStringTok' = @vs@,
-- 'SpecialStringTok'  = @ss@,
-- 'ImportTok'         = @im@,
-- 'DocumentationTok'  = @do@,
-- 'AnnotationTok'     = @an@,
-- 'CommentVarTok'     = @cv@,
-- 'VariableTok'       = @va@,
-- 'ControlFlowTok'    = @cf@,
-- 'OperatorTok'       = @op@,
-- 'BuiltInTok'        = @bu@,
-- 'ExtensionTok'      = @ex@,
-- 'PreprocessorTok'   = @pp@,
-- 'AttributeTok'      = @at@,
-- 'InformationTok'    = @in@,
-- 'WarningTok'        = @wa@.
-- A 'NormalTok' is not marked up at all.
formatHtmlInline :: FormatOptions -> [SourceLine] -> Html ()
formatHtmlInline opts =
  -- wrapCode opts . mconcat . L.intersperse (toHtml "\n") . map (traverse_ (tokenToHtml opts))
  wrapCode opts . mconcat . map (traverse_ (tokenToHtml opts))

-- | Format tokens as an HTML @pre@ block. Each line is wrapped in an a element
-- with the class ‘source-line’. If line numbering is selected, the surrounding
-- pre is given the class ‘numberSource’, and the resulting html will display
-- line numbers thanks to the included CSS. See the documentation for
-- 'formatHtmlInline' for information about how tokens are encoded.
formatHtmlBlock :: FormatOptions -> [SourceLine] -> Html ()
formatHtmlBlock opts ls =
  div_ [class_ "sourceCode"]
  $ pre_ [classes_ classes]
    $ wrapCode opts
    $ mconcat
    $ L.intersperse "\n"
    $ zipWith (sourceLineToHtml opts) [startNum..] ls
  where
    classes :: [T.Text]
    classes = "sourceCode"
      :  ["numberSource" | numberLines opts]
      ++ [x | x <- containerClasses opts, x /= "sourceCode"]

    startNum :: LineNo
    startNum = LineNo $ startNumber opts

wrapCode :: FormatOptions -> Html () -> Html ()
wrapCode opts h =
  code_ [classes_ $ "sourceCode" : codeClasses opts] h
                         -- !? (startZero /= 0, A.style (toValue counterOverride))
                         -- $ h
  -- where
  --   counterOverride :: String
  --   counterOverride = "counter-reset: source-line " <> show startZero <> ";"

  --   startZero :: Int
    -- startZero = startNumber opts - 1

-- | Each line of source is wrapped in an (inline-block) anchor that makes
-- subsequent per-line processing (e.g. adding line numnbers) possible.
sourceLineToHtml :: FormatOptions -> LineNo -> SourceLine -> Html ()
sourceLineToHtml opts lno cont = span_ [id_ prefixedLineNo] $ do
  a_ [href_ lineRef] ""
  traverse_ (tokenToHtml opts) cont
  where
    lineRef :: T.Text
    lineRef = T.cons '#' prefixedLineNo

    prefixedLineNo :: T.Text
    prefixedLineNo = lineIdPrefix opts <> T.pack (show $ lineNo lno)

tokenToHtml :: FormatOptions -> Token -> Html ()
tokenToHtml _ (NormalTok, txt) = toHtml txt
tokenToHtml opts (toktype, txt)
  | titleAttributes opts = sp -- ! A.title (toValue $ show toktype)
  | otherwise = sp
  where
    sp :: Html ()
    sp = span_ [class_ $ short toktype] $ toHtml txt

short :: TokenType -> T.Text
short KeywordTok        = "kw"
short DataTypeTok       = "dt"
short DecValTok         = "dv"
short BaseNTok          = "bn"
short FloatTok          = "fl"
short CharTok           = "ch"
short StringTok         = "st"
short CommentTok        = "co"
short OtherTok          = "ot"
short AlertTok          = "al"
short FunctionTok       = "fu"
short RegionMarkerTok   = "re"
short ErrorTok          = "er"
short ConstantTok       = "cn"
short SpecialCharTok    = "sc"
short VerbatimStringTok = "vs"
short SpecialStringTok  = "ss"
short ImportTok         = "im"
short DocumentationTok  = "do"
short AnnotationTok     = "an"
short CommentVarTok     = "cv"
short VariableTok       = "va"
short ControlFlowTok    = "cf"
short OperatorTok       = "op"
short BuiltInTok        = "bu"
short ExtensionTok      = "ex"
short PreprocessorTok   = "pp"
short AttributeTok      = "at"
short InformationTok    = "in"
short WarningTok        = "wa"
short NormalTok         = ""