skylighting-format-blaze-html (empty) → 0.1
raw patch · 4 files changed
+288/−0 lines, 4 filesdep +basedep +blaze-htmldep +containers
Dependencies added: base, blaze-html, containers, skylighting-core, text
Files
- LICENSE +30/−0
- README.md +5/−0
- skylighting-format-blaze-html.cabal +37/−0
- src/Skylighting/Format/HTML.hs +216/−0
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2016-2018, John MacFarlane.+All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are+met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * The names of the contributors may not be used to endorse or+ promote products derived from this software without specific+ prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,5 @@+# skylighting-format-ansi++This package provides functions to render syntax-highlighting+as ANSI text.+
+ skylighting-format-blaze-html.cabal view
@@ -0,0 +1,37 @@+name: skylighting-format-blaze-html+version: 0.1+synopsis: HTML formatter for skylighting syntax highlighting library+description: This module allows tokens produced by skylighting-core+ to be rendered as HTML.+homepage: https://github.com/jgm/skylighting+license: BSD3+license-file: LICENSE+author: John MacFarlane+maintainer: jgm@berkeley.edu+copyright: (C) 2016-2022 John MacFarlane+category: Text+build-type: Simple+extra-source-files: README.md++cabal-version: >=1.10++source-repository head+ type: git+ location: https://github.com/jgm/skylighting.git++library+ exposed-modules: Skylighting.Format.HTML+ other-extensions: CPP+ build-depends: base >= 4.8 && < 5.0,+ skylighting-core,+ text,+ containers,+ blaze-html >= 0.5+ hs-source-dirs: src+ ghc-prof-options: -fprof-auto-exported+ default-language: Haskell2010+ ghc-options: -Wall+ if impl(ghc >= 8.4)+ ghc-options: -fhide-source-paths+ if impl(ghc >= 8.10)+ ghc-options: -Wunused-packages
+ src/Skylighting/Format/HTML.hs view
@@ -0,0 +1,216 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE NoOverloadedStrings #-}+module Skylighting.Format.HTML (+ formatHtmlInline+ , formatHtmlBlock+ , styleToCss+ ) where++import Data.List (intersperse, sort)+import qualified Data.Map as Map+import qualified Data.Text as Text+import Skylighting.Types+import Text.Blaze.Html+import qualified Text.Blaze.Html5 as H+import qualified Text.Blaze.Html5.Attributes as A+import Data.String (fromString)+#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 . intersperse (toHtml "\n")+ . map (mapM_ (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 =+ H.div ! A.class_ (toValue "sourceCode") $+ H.pre ! A.class_ (toValue $ Text.unwords classes)+ $ wrapCode opts+ $ mconcat . intersperse (toHtml "\n")+ $ zipWith (sourceLineToHtml opts) [startNum..] ls+ where classes = Text.pack "sourceCode" :+ [Text.pack "numberSource" | numberLines opts] +++ [x | x <- containerClasses opts+ , x /= Text.pack "sourceCode"]+ startNum = LineNo $ startNumber opts++wrapCode :: FormatOptions -> Html -> Html+wrapCode opts h = H.code ! A.class_ (toValue $ Text.unwords+ $ Text.pack "sourceCode"+ : codeClasses opts)+ !? (startZero /= 0, A.style (toValue counterOverride))+ $ h+ where counterOverride = "counter-reset: source-line " <> show startZero <> ";"+ 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 =+ H.span ! A.id lineNum+ $ do+ H.a ! A.href lineRef+ ! (if numberLines opts+ then mempty+ else customAttribute (fromString "aria-hidden")+ (fromString "true")) -- see jgm/pandoc#6352+ ! (if numberLines opts+ then mempty+ else customAttribute (fromString "tabindex")+ (fromString "-1"))+ $ mempty+ mapM_ (tokenToHtml opts) cont+ where lineNum = toValue prefixedLineNo+ lineRef = toValue ('#':prefixedLineNo)+ prefixedLineNo = Text.unpack (lineIdPrefix opts) <> show (lineNo lno)++tokenToHtml :: FormatOptions -> Token -> Html+tokenToHtml _ (NormalTok, txt) = toHtml txt+tokenToHtml opts (toktype, txt) =+ if titleAttributes opts+ then sp ! A.title (toValue $ show toktype)+ else sp+ where sp = H.span ! A.class_ (toValue $ short toktype) $ toHtml txt++short :: TokenType -> String+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 = ""++-- | Returns CSS for styling highlighted code according to the given style.+styleToCss :: Style -> String+styleToCss f = unlines $+ divspec ++ numberspec ++ colorspec ++ linkspec +++ sort (map toCss (Map.toList (tokenStyles f)))+ where colorspec = pure . unwords $ [+ "div.sourceCode\n {"+ , maybe "" (\c -> "color: " ++ fromColor c ++ ";") (defaultColor f)+ , maybe "" (\c -> "background-color: " ++ fromColor c ++ ";") (backgroundColor f)+ , "}"+ ]+ numberspec = [+ "pre.numberSource code"+ , " { counter-reset: source-line 0; }"+ , "pre.numberSource code > span"+ , " { position: relative; left: -4em; counter-increment: source-line; }"+ , "pre.numberSource code > span > a:first-child::before"+ , " { content: counter(source-line);"+ , " position: relative; left: -1em; text-align: right; vertical-align: baseline;"+ , " border: none; display: inline-block;"+ , " -webkit-touch-callout: none; -webkit-user-select: none;"+ , " -khtml-user-select: none; -moz-user-select: none;"+ , " -ms-user-select: none; user-select: none;"+ , " padding: 0 4px; width: 4em;"+ , maybe "" (\c -> " background-color: " ++ fromColor c ++ ";\n")+ (lineNumberBackgroundColor f) +++ maybe "" (\c -> " color: " ++ fromColor c ++ ";\n")+ (lineNumberColor f) +++ " }"+ , "pre.numberSource { margin-left: 3em; " +++ maybe "" (\c -> "border-left: 1px solid " ++ fromColor c ++ "; ") (lineNumberColor f) +++ " padding-left: 4px; }"+ ]+ divspec = [+ "pre > code.sourceCode { white-space: pre; position: relative; }" -- position relative needed for relative contents+ , "pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }"+ , "pre > code.sourceCode > span:empty { height: 1.2em; }" -- correct empty line height+ , ".sourceCode { overflow: visible; }" -- needed for line numbers+ , "code.sourceCode > span { color: inherit; text-decoration: inherit; }"+ , "div.sourceCode { margin: 1em 0; }" -- Collapse neighbours correctly+ , "pre.sourceCode { margin: 0; }" -- Collapse neighbours correctly+ , "@media screen {"+ , "div.sourceCode { overflow: auto; }" -- do not overflow on screen+ , "}"+ , "@media print {"+ , "pre > code.sourceCode { white-space: pre-wrap; }"+ , "pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }"+ , "}"+ ]+ linkspec = [ "@media screen {"+ , "pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }"+ , "}"+ ]++toCss :: (TokenType, TokenStyle) -> String+toCss (t,tf) = "code span" ++ (if null (short t) then "" else ('.' : short t)) ++ " { "+ ++ colorspec ++ backgroundspec ++ weightspec ++ stylespec+ ++ decorationspec ++ "} /* " ++ showTokenType t ++ " */"+ where colorspec = maybe "" (\col -> "color: " ++ fromColor col ++ "; ") $ tokenColor tf+ backgroundspec = maybe "" (\col -> "background-color: " ++ fromColor col ++ "; ") $ tokenBackground tf+ weightspec = if tokenBold tf then "font-weight: bold; " else ""+ stylespec = if tokenItalic tf then "font-style: italic; " else ""+ decorationspec = if tokenUnderline tf then "text-decoration: underline; " else ""+ showTokenType t' = case reverse (show t') of+ 'k':'o':'T':xs -> reverse xs+ _ -> ""+