pandoc-3.10.1: src/Text/Pandoc/Writers/MediaWiki.hs
{-# LANGUAGE OverloadedStrings #-}
{- |
Module : Text.Pandoc.Writers.MediaWiki
Copyright : Copyright (C) 2008-2024 John MacFarlane
License : GNU GPL, version 2 or above
Maintainer : John MacFarlane <jgm@berkeley.edu>
Stability : alpha
Portability : portable
Conversion of 'Pandoc' documents to MediaWiki markup.
MediaWiki: <http://www.mediawiki.org/wiki/MediaWiki>
-}
module Text.Pandoc.Writers.MediaWiki ( writeMediaWiki, highlightingLangs ) where
import Control.Monad.Reader
import Control.Monad.State.Strict
import Data.Maybe (fromMaybe)
import qualified Data.List as DL
import qualified Data.Set as Set
import Data.Text (Text)
import qualified Data.Text as T
import Data.List.NonEmpty (NonEmpty((:|)))
import Text.Pandoc.Class.PandocMonad (PandocMonad, report)
import Text.Pandoc.Definition
import Text.Pandoc.ImageSize
import Text.Pandoc.Logging
import Text.Pandoc.Options
import Text.DocLayout
import Text.Pandoc.Shared
import Text.Pandoc.URI
import Text.Pandoc.Templates (renderTemplate)
import qualified Text.Pandoc.Writers.AnnotatedTable as Ann
import Text.Pandoc.Writers.Shared
import Text.Pandoc.XML (escapeStringForXML)
data WriterState = WriterState {
stNotes :: Bool -- True if there are notes
, stOptions :: WriterOptions -- writer options
, stInDefLabel :: Bool -- True if in definition list label
}
data WriterReader = WriterReader {
options :: WriterOptions -- Writer options
, listLevel :: [Char] -- String at beginning of list items, e.g. "**"
, useTags :: Bool -- True if we should use HTML tags because we're in a complex list
}
type MediaWikiWriter m = ReaderT WriterReader (StateT WriterState m)
-- | Convert Pandoc to MediaWiki.
writeMediaWiki :: PandocMonad m => WriterOptions -> Pandoc -> m Text
writeMediaWiki opts document =
let initialState = WriterState {
stNotes = False, stOptions = opts, stInDefLabel = False }
env = WriterReader { options = opts, listLevel = [], useTags = False }
in evalStateT (runReaderT (pandocToMediaWiki document) env) initialState
-- | Return MediaWiki representation of document.
pandocToMediaWiki :: PandocMonad m => Pandoc -> MediaWikiWriter m Text
pandocToMediaWiki (Pandoc meta blocks) = do
opts <- asks options
metadata <- metaToContext opts
(fmap chomp . blockListToMediaWiki)
(fmap chomp . inlineListToMediaWiki)
meta
body <- blockListToMediaWiki blocks
notesExist <- gets stNotes
let notes = if notesExist
then cr <> literal "<references />"
else mempty
let main = body <> notes
let context = defField "body" main
$ defField "toc" (writerTableOfContents opts) metadata
return $ render Nothing $
case writerTemplate opts of
Nothing -> main
Just tpl -> renderTemplate tpl context
-- | Escape special characters for MediaWiki.
escapeText :: Text -> Text
escapeText = escapeStringForXML
-- | Convert Pandoc block element to MediaWiki.
blockToMediaWiki :: PandocMonad m
=> Block -- ^ Block element
-> MediaWikiWriter m (Doc Text)
blockToMediaWiki (Div attrs bs) = do
contents <- blockListToMediaWiki bs
return $ tagWithAttrs "div" attrs $$
contents $$
literal "</div>" $$
blankline
blockToMediaWiki (Plain inlines) =
inlineListToMediaWiki inlines
blockToMediaWiki (SimpleFigure attr txt (src, tit)) = do
capt <- inlineListToMediaWiki txt
img <- imageToMediaWiki attr
let capt' = render Nothing capt
let opt = if T.null tit
then
if T.null capt'
then ""
else "alt=" <> capt'
else "alt=" <> tit
let separator = "<nowiki></nowiki>"
-- External images (URIs) cannot use [[File:...]] syntax;
-- they must be rendered as bare URLs. MediaWiki will auto-embed
-- them when $wgAllowExternalImages is enabled.
return $ if isURI src
then literal (separator <> src <> separator) <> cr
else literal ("[[" <>
T.intercalate "|"
(filter (not . T.null) ["File:" <> src
, "thumb"
, "none"
, img
, opt
, capt'
]) <>
"]]") <> cr
blockToMediaWiki (Para inlines) = do
tags <- asks useTags
lev <- asks listLevel
contents <- inlineListToMediaWiki inlines
let contents' = render Nothing contents
let initEsc = if startsWithListMarker contents'
then literal "<nowiki></nowiki>"
else mempty
return $ if tags
then literal "<p>" <> contents <> literal "</p>"
else initEsc <> contents $$
if null lev then blankline else mempty
blockToMediaWiki (LineBlock lns) =
blockToMediaWiki $ linesToPara lns
blockToMediaWiki b@(RawBlock f str)
| f == Format "mediawiki" = return $ literal str
| f == Format "html" = return $ literal str
| otherwise = mempty <$ report (BlockNotRendered b)
blockToMediaWiki HorizontalRule = return $ blankline <> literal "-----" <> blankline
blockToMediaWiki (Header level (ident,_,_) inlines) = do
let autoId = T.replace " " "_" $ stringify inlines
contents <- inlineListToMediaWiki inlines
let eqs = literal $ T.replicate level "="
return $
(if T.null ident || autoId == ident
then mempty
else literal ("<span id=\"" <> ident <> "\"></span>") <> cr)
<> eqs <> space <> contents <> space <> eqs <> blankline
blockToMediaWiki (CodeBlock (_,classes,keyvals) str) = do
let at = Set.fromList classes `Set.intersection` highlightingLangs
let numberLines = any (`elem` ["number","numberLines", "number-lines"])
classes
let start = lookup "startFrom" keyvals
return $ literal $
case Set.toList at of
[] -> "<pre" <> (if null classes
then ">"
else " class=\"" <> T.unwords classes <> "\">") <>
escapeText str <> "</pre>"
(l:_) -> "<syntaxhighlight lang=\"" <> l <> "\"" <>
(if numberLines then " line" else "") <>
maybe "" (\x -> " start=\"" <> x <> "\"") start <>
">" <> str <>
"</syntaxhighlight>"
-- note: no escape! even for <!
blockToMediaWiki (BlockQuote blocks) = do
contents <- blockListToMediaWiki blocks
return $ literal "<blockquote>" <> chomp contents <> cr <> literal "</blockquote>"
blockToMediaWiki (Table attr capt colSpecs thead tbody tfoot) = do
tableToMediaWiki (Ann.toTable attr capt colSpecs thead tbody tfoot)
blockToMediaWiki x@(BulletList items) = do
tags <-
(|| not (isSimpleList x)) <$> asks useTags
if tags
then do
contents <- local (\ s -> s { useTags = True }) $ mapM listItemToMediaWiki items
return $ literal "<ul>" <> cr <> vcat contents <> literal "</ul>" <> blankline
else do
lev <- asks listLevel
contents <- local (\s -> s { listLevel = listLevel s <> "*" }) $ mapM listItemToMediaWiki items
return $ vcat contents <> if null lev then blankline else mempty
blockToMediaWiki x@(OrderedList attribs items) = do
tags <-
(|| not (isSimpleList x)) <$> asks useTags
if tags
then do
contents <- local (\s -> s { useTags = True }) $ mapM listItemToMediaWiki items
return $ literal ("<ol" <> listAttribsToText attribs <> ">") <> cr <> vcat contents <> literal "</ol>" <> blankline
else do
lev <- asks listLevel
contents <- local (\s -> s { listLevel = listLevel s <> "#" }) $ mapM listItemToMediaWiki items
return $ vcat contents <> if null lev then blankline else mempty
blockToMediaWiki x@(DefinitionList items) = do
tags <-
(|| not (isSimpleList x)) <$> asks useTags
if tags
then do
contents <- local (\s -> s { useTags = True }) $ mapM definitionListItemToMediaWiki items
return $ literal "<dl>" <> cr <> vcat contents <> literal "</dl>" <> blankline
else do
lev <- asks listLevel
contents <- local (\s -> s { listLevel = listLevel s <> ";" }) $ mapM definitionListItemToMediaWiki items
return $ vcat contents <> if null lev then blankline else mempty
blockToMediaWiki (Figure (ident, classes, kvs) _ body) =
blockToMediaWiki (Div (ident, ["figure"] `DL.union` classes, kvs) body)
-- Auxiliary functions for lists:
-- | Convert ordered list attributes to HTML attribute string
listAttribsToText :: ListAttributes -> Text
listAttribsToText (startnum, numstyle, _) =
let numstyle' = camelCaseToHyphenated $ tshow numstyle
in (if startnum /= 1
then " start=\"" <> tshow startnum <> "\""
else "") <>
(if numstyle /= DefaultStyle
then " style=\"list-style-type: " <> numstyle' <> ";\""
else "")
-- | Convert bullet or ordered list item (list of blocks) to MediaWiki.
listItemToMediaWiki :: PandocMonad m => [Block] -> MediaWikiWriter m (Doc Text)
listItemToMediaWiki items = do
contents <- blockListToMediaWiki items
tags <- asks useTags
if tags
then return $ literal "<li>" <> chomp contents <> literal "</li>"
else do
marker <- asks listLevel
return $ literal (T.pack marker) <> space <> chomp contents
-- | Convert definition list item (label, list of blocks) to MediaWiki.
definitionListItemToMediaWiki :: PandocMonad m
=> ([Inline],[[Block]])
-> MediaWikiWriter m (Doc Text)
definitionListItemToMediaWiki (label, items) = do
modify $ \st -> st{ stInDefLabel = True }
labelText <- inlineListToMediaWiki label
modify $ \st -> st{ stInDefLabel = False }
contents <- mapM blockListToMediaWiki items
tags <- asks useTags
if tags
then return $ literal "<dt>" <> chomp labelText <> literal "</dt>" <> cr <>
vcat (map (\d -> literal "<dd>" <> chomp d <> literal "</dd>") contents)
else do
marker <- asks listLevel
return $ literal (T.pack marker) <> space <> chomp labelText <> cr <>
vcat (map (\d -> literal (T.pack (init marker)) <> literal ": " <> chomp d) contents)
-- | True if the list can be handled by simple wiki markup, False if HTML tags will be needed.
isSimpleList :: Block -> Bool
isSimpleList x =
case x of
BulletList items -> all isSimpleListItem items
OrderedList (num, sty, _) items -> all isSimpleListItem items &&
num == 1 && sty `elem` [DefaultStyle, Decimal]
DefinitionList items -> all isSimpleListItem $ concatMap snd items
_ -> False
-- | True if list item can be handled with the simple wiki syntax. False if
-- HTML tags will be needed.
isSimpleListItem :: [Block] -> Bool
isSimpleListItem [] = True
isSimpleListItem [x] =
case x of
Plain _ -> True
Para _ -> True
BulletList _ -> isSimpleList x
OrderedList _ _ -> isSimpleList x
DefinitionList _ -> isSimpleList x
_ -> False
isSimpleListItem [x, y] | isPlainOrPara x =
case y of
BulletList _ -> isSimpleList y
OrderedList _ _ -> isSimpleList y
DefinitionList _ -> isSimpleList y
_ -> False
isSimpleListItem _ = False
isPlainOrPara :: Block -> Bool
isPlainOrPara (Plain _) = True
isPlainOrPara (Para _) = True
isPlainOrPara _ = False
-- Auxiliary functions for tables:
tableToMediaWiki :: PandocMonad m => Ann.Table -> MediaWikiWriter m (Doc Text)
tableToMediaWiki (Ann.Table attr capt _ thead tbodies tfoot) = do
let (ident,classes,kvs) = attr
caption <- case capt of
Caption _ [] -> return mempty
Caption _ longCapt -> do
c <- blockListToMediaWiki longCapt
return $ literal "|+ " <> chomp c <> cr
head' <- tableHeadToMW thead
bodies' <- mconcat <$> mapM tableBodyToMW tbodies
foot' <- tableFootToMW tfoot
return $ literal "{|" <> htmlAttrs (ident, "wikitable":classes, kvs) <> cr
<> caption <> head' <> bodies' <> foot'
<> literal "|}" <> blankline
tableHeadToMW :: PandocMonad m => Ann.TableHead -> MediaWikiWriter m (Doc Text)
tableHeadToMW (Ann.TableHead _ rows) = headerRowsToMW rows
tableFootToMW :: PandocMonad m => Ann.TableFoot -> MediaWikiWriter m (Doc Text)
tableFootToMW (Ann.TableFoot _ rows) = headerRowsToMW rows
tableBodyToMW :: PandocMonad m => Ann.TableBody -> MediaWikiWriter m (Doc Text)
tableBodyToMW (Ann.TableBody _ _ headerRows bodyRows) = do
headerRows' <- headerRowsToMW headerRows
bodyRows' <- bodyRowsToMW bodyRows
return $ headerRows' <> bodyRows'
headerRowsToMW :: PandocMonad m => [Ann.HeaderRow] -> MediaWikiWriter m (Doc Text)
headerRowsToMW rows = mconcat <$> mapM headerRowToMW rows
headerRowToMW :: PandocMonad m => Ann.HeaderRow -> MediaWikiWriter m (Doc Text)
headerRowToMW (Ann.HeaderRow attr _ cells) = do
cells' <- mconcat <$> mapM (cellToMW "!") cells
return $ literal "|-" <> htmlAttrs attr <> cr <> cells'
bodyRowsToMW :: PandocMonad m => [Ann.BodyRow] -> MediaWikiWriter m (Doc Text)
bodyRowsToMW rows = mconcat <$> mapM bodyRowToMW rows
bodyRowToMW :: PandocMonad m => Ann.BodyRow -> MediaWikiWriter m (Doc Text)
bodyRowToMW (Ann.BodyRow attr _ headCells bodyCells) = do
headCells' <- mconcat <$> mapM (cellToMW "!") headCells
bodyCells' <- mconcat <$> mapM (cellToMW "|") bodyCells
return $ literal "|-" <> htmlAttrs attr <> cr <> headCells' <> bodyCells'
cellToMW :: PandocMonad m => Text -> Ann.Cell -> MediaWikiWriter m (Doc Text)
cellToMW marker (Ann.Cell (colSpec :| _) _ (Cell attr align rowspan colspan content)) = do
content' <- blockListToMediaWiki content
let (ident,classes,keyVals) = attr
let align' = case align of
AlignDefault -> fst colSpec
_ -> align
let keyVals' = case (htmlAlignmentToString align') of
Nothing -> keyVals
Just alignStr -> htmlAddStyle ("text-align", alignStr) keyVals
let rowspan' = case rowspan of
RowSpan 1 -> mempty
RowSpan n -> [("rowspan", T.pack(show n))]
let colspan' = case colspan of
ColSpan 1 -> mempty
ColSpan n -> [("colspan", T.pack(show n))]
let attrs' = htmlAttrs (ident, classes, rowspan' <> colspan' <> keyVals')
let attrsRendered = render Nothing attrs'
let pipeAttr = if T.null attrsRendered then mempty else attrs' <> literal "|"
let contentRendered = render Nothing content'
let spaceContent = if T.null contentRendered then mempty else space <> chomp content'
return $ literal marker <> pipeAttr <> spaceContent <> cr
imageToMediaWiki :: PandocMonad m => Attr -> MediaWikiWriter m Text
imageToMediaWiki attr = do
opts <- gets stOptions
let (_, cls, _) = attr
toPx = fmap (showInPixel opts) . checkPct
checkPct (Just (Percent _)) = Nothing
checkPct maybeDim = maybeDim
go (Just w) Nothing = w <> "px"
go (Just w) (Just h) = w <> "x" <> h <> "px"
go Nothing (Just h) = "x" <> h <> "px"
go Nothing Nothing = ""
dims = go (toPx $ dimension Width attr) (toPx $ dimension Height attr)
classes = if null cls
then ""
else "class=" <> T.unwords cls
return $ T.intercalate "|" $ filter (not . T.null) [dims, classes]
-- | Convert list of Pandoc block elements to MediaWiki.
blockListToMediaWiki :: PandocMonad m
=> [Block] -- ^ List of block elements
-> MediaWikiWriter m (Doc Text)
blockListToMediaWiki blocks =
vcat <$> mapM blockToMediaWiki blocks
-- | Convert list of Pandoc inline elements to MediaWiki.
inlineListToMediaWiki :: PandocMonad m => [Inline] -> MediaWikiWriter m (Doc Text)
inlineListToMediaWiki lst =
hcat <$> mapM inlineToMediaWiki (fixup lst)
where
fixup [] = []
fixup (Str t : x : xs)
| not (T.null t) && T.last t == '['
, isLinkOrImage x =
Str t : RawInline (Format "mediawiki") "<nowiki/>" : x : fixup xs
fixup (x:xs) = x : fixup xs
isLinkOrImage Link{} = True
isLinkOrImage Image{} = True
isLinkOrImage _ = False
-- | Convert Pandoc inline element to MediaWiki.
inlineToMediaWiki :: PandocMonad m => Inline -> MediaWikiWriter m (Doc Text)
inlineToMediaWiki (Span attrs ils) = do
contents <- inlineListToMediaWiki ils
return $ tagWithAttrs "span" attrs <> contents <> literal "</span>"
inlineToMediaWiki (Emph lst) = do
contents <- inlineListToMediaWiki lst
return $ literal "''" <> contents <> literal "''"
inlineToMediaWiki (Underline lst) = do
contents <- inlineListToMediaWiki lst
return $ literal "<u>" <> contents <> literal "</u>"
inlineToMediaWiki (Strong lst) = do
contents <- inlineListToMediaWiki lst
return $ literal "'''" <> contents <> literal "'''"
inlineToMediaWiki (Strikeout lst) = do
contents <- inlineListToMediaWiki lst
return $ literal "<s>" <> contents <> literal "</s>"
inlineToMediaWiki (Superscript lst) = do
contents <- inlineListToMediaWiki lst
return $ literal "<sup>" <> contents <> literal "</sup>"
inlineToMediaWiki (Subscript lst) = do
contents <- inlineListToMediaWiki lst
return $ literal "<sub>" <> contents <> literal "</sub>"
inlineToMediaWiki (SmallCaps lst) = inlineListToMediaWiki lst
inlineToMediaWiki (Quoted SingleQuote lst) = do
contents <- inlineListToMediaWiki lst
return $ literal "\8216" <> contents <> literal "\8217"
inlineToMediaWiki (Quoted DoubleQuote lst) = do
contents <- inlineListToMediaWiki lst
return $ literal "\8220" <> contents <> literal "\8221"
inlineToMediaWiki (Cite _ lst) = inlineListToMediaWiki lst
inlineToMediaWiki (Code _ str) =
return $ literal $ "<code>" <> escapeText str <> "</code>"
inlineToMediaWiki (Str str) = do
inDefLabel <- gets stInDefLabel
return $ literal $
if inDefLabel
then T.intercalate "<nowiki>:</nowiki>" $
map escapeText $ T.splitOn ":" str
else escapeText str
inlineToMediaWiki (Math mt str) = return $ literal $
"<math display=\"" <>
(if mt == DisplayMath then "block" else "inline") <>
"\">" <> str <> "</math>"
-- note: str should NOT be escaped
inlineToMediaWiki il@(RawInline f str)
| f == Format "mediawiki" = return $ literal str
| f == Format "html" = return $ literal str
| otherwise = mempty <$ report (InlineNotRendered il)
inlineToMediaWiki LineBreak = return $ literal "<br />" <> cr
inlineToMediaWiki SoftBreak = do
wrapText <- gets (writerWrapText . stOptions)
listlevel <- asks listLevel
case wrapText of
WrapAuto -> return space
WrapNone -> return space
WrapPreserve -> if null listlevel
then return cr
else return space
inlineToMediaWiki Space = return space
inlineToMediaWiki (Link _ txt (src, _)) = do
-- We need to remove links from link text, because an <a> element is
-- not allowed inside another <a> element.
label <- inlineListToMediaWiki (removeLinks txt)
let label' = render Nothing label
case txt of
[Str s] | isURI src && escapeURI s == src -> return $ literal src
_ -> return $ literal $ if isURI (T.takeWhile (/= '#') src) -- see #11562
then "[" <> src <> " " <> label' <> "]"
else
if src == label'
then "[[" <> src' <> "]]"
else "[[" <> src' <> "|" <> label' <> "]]"
-- with leading / it's a link to a help page
where src' = fromMaybe src $ T.stripPrefix "/" src
inlineToMediaWiki (Image attr alt (source, tit)) = do
let separator = "<nowiki></nowiki>"
-- External images (URIs) cannot use [[File:...]] syntax;
-- they must be rendered as bare URLs. MediaWiki will auto-embed
-- them when $wgAllowExternalImages is enabled.
if isURI source
then return $ literal $ separator <> source <> separator
else do
img <- imageToMediaWiki attr
alt' <- inlineListToMediaWiki alt
let altText = render Nothing alt'
let txt = if T.null altText
then if T.null tit
then ""
else tit
else altText
return $ literal $ "[[" <>
T.intercalate "|"
(filter (not . T.null)
[ "File:" <> source
, img
, txt
]) <> "]]"
inlineToMediaWiki (Note contents) = do
contents' <- blockListToMediaWiki contents
modify (\s -> s { stNotes = True })
let rendered = render Nothing contents'
return $ literal $ "<ref>" <> stripTrailingNewlines rendered <> "</ref>"
-- note - does not work for notes with multiple blocks
highlightingLangs :: Set.Set Text
highlightingLangs = Set.fromList [
"abap",
"abl",
"abnf",
"aconf",
"actionscript",
"actionscript3",
"ada",
"ada2005",
"ada95",
"adl",
"agda",
"ahk",
"alloy",
"ambienttalk",
"ambienttalk/2",
"antlr",
"antlr-actionscript",
"antlr-as",
"antlr-c#",
"antlr-cpp",
"antlr-csharp",
"antlr-java",
"antlr-objc",
"antlr-perl",
"antlr-python",
"antlr-rb",
"antlr-ruby",
"apache",
"apacheconf",
"apl",
"applescript",
"arduino",
"arexx",
"as",
"as3",
"asm",
"aspectj",
"aspx-cs",
"aspx-vb",
"asy",
"asymptote",
"at",
"autohotkey",
"autoit",
"awk",
"b3d",
"basemake",
"bash",
"basic",
"bat",
"batch",
"bbcode",
"because",
"befunge",
"bf",
"blitzbasic",
"blitzmax",
"bmax",
"bnf",
"boo",
"boogie",
"bplus",
"brainfuck",
"bro",
"bsdmake",
"bugs",
"c",
"c#",
"c++",
"c++-objdumb",
"c-objdump",
"ca65",
"cadl",
"camkes",
"cbmbas",
"ceylon",
"cf3",
"cfc",
"cfengine3",
"cfg",
"cfm",
"cfs",
"chai",
"chaiscript",
"chapel",
"cheetah",
"chpl",
"cirru",
"cl",
"clay",
"clipper",
"clj",
"cljs",
"clojure",
"clojurescript",
"cmake",
"cobol",
"cobolfree",
"coffee",
"coffee-script",
"coffeescript",
"common-lisp",
"componentpascal",
"console",
"control",
"coq",
"cp",
"cpp",
"cpp-objdump",
"cpsa",
"crmsh",
"croc",
"cry",
"cryptol",
"csh",
"csharp",
"csound",
"csound-csd",
"csound-document",
"csound-orc",
"csound-sco",
"csound-score",
"css",
"css+django",
"css+erb",
"css+genshi",
"css+genshitext",
"css+jinja",
"css+lasso",
"css+mako",
"css+mozpreproc",
"css+myghty",
"css+php",
"css+ruby",
"css+smarty",
"cu",
"cucumber",
"cuda",
"cxx-objdump",
"cypher",
"cython",
"d",
"d-objdump",
"dart",
"debcontrol",
"debsources",
"delphi",
"dg",
"diff",
"django",
"docker",
"dockerfile",
"dosbatch",
"doscon",
"dosini",
"dpatch",
"dtd",
"duby",
"duel",
"dylan",
"dylan-console",
"dylan-lid",
"dylan-repl",
"earl-grey",
"earlgrey",
"easytrieve",
"ebnf",
"ec",
"ecl",
"eg",
"eiffel",
"elisp",
"elixir",
"elm",
"emacs",
"erb",
"erl",
"erlang",
"evoque",
"ex",
"exs",
"ezhil",
"f#",
"factor",
"fan",
"fancy",
"felix",
"fish",
"fishshell",
"flx",
"fortran",
"fortranfixed",
"foxpro",
"fsharp",
"fy",
"gap",
"gas",
"gawk",
"genshi",
"genshitext",
"gherkin",
"glsl",
"gnuplot",
"go",
"golo",
"gooddata-cl",
"gosu",
"groff",
"groovy",
"gst",
"haml",
"handlebars",
"haskell",
"haxe",
"haxeml",
"hexdump",
"hs",
"html",
"html+cheetah",
"html+django",
"html+erb",
"html+evoque",
"html+genshi",
"html+handlebars",
"html+jinja",
"html+kid",
"html+lasso",
"html+mako",
"html+myghty",
"html+php",
"html+ruby",
"html+smarty",
"html+spitfire",
"html+twig",
"html+velocity",
"htmlcheetah",
"htmldjango",
"http",
"hx",
"hxml",
"hxsl",
"hy",
"hybris",
"hylang",
"i6",
"i6t",
"i7",
"idl",
"idl4",
"idr",
"idris",
"iex",
"igor",
"igorpro",
"ik",
"inform6",
"inform7",
"ini",
"io",
"ioke",
"irb",
"irc",
"isabelle",
"j",
"jade",
"jags",
"jasmin",
"jasminxt",
"java",
"javascript",
"javascript+cheetah",
"javascript+django",
"javascript+erb",
"javascript+genshi",
"javascript+genshitext",
"javascript+jinja",
"javascript+lasso",
"javascript+mako",
"javascript+mozpreproc",
"javascript+myghty",
"javascript+php",
"javascript+ruby",
"javascript+smarty",
"javascript+spitfire",
"jbst",
"jcl",
"jinja",
"jl",
"jlcon",
"jproperties",
"js",
"js+cheetah",
"js+django",
"js+erb",
"js+genshi",
"js+genshitext",
"js+jinja",
"js+lasso",
"js+mako",
"js+myghty",
"js+php",
"js+ruby",
"js+smarty",
"js+spitfire",
"json",
"json-ld",
"jsonld",
"jsonml+bst",
"jsp",
"julia",
"kal",
"kconfig",
"kernel-config",
"kid",
"koka",
"kotlin",
"ksh",
"lagda",
"lasso",
"lassoscript",
"latex",
"lcry",
"lcryptol",
"lean",
"less",
"lhaskell",
"lhs",
"lid",
"lidr",
"lidris",
"lighttpd",
"lighty",
"limbo",
"linux-config",
"liquid",
"lisp",
"literate-agda",
"literate-cryptol",
"literate-haskell",
"literate-idris",
"live-script",
"livescript",
"llvm",
"logos",
"logtalk",
"lsl",
"lua",
"m2",
"make",
"makefile",
"mako",
"man",
"maql",
"mask",
"mason",
"mathematica",
"matlab",
"matlabsession",
"mawk",
"menuconfig",
"mf",
"minid",
"mma",
"modelica",
"modula2",
"moin",
"monkey",
"moo",
"moocode",
"moon",
"moonscript",
"mozhashpreproc",
"mozpercentpreproc",
"mq4",
"mq5",
"mql",
"mql4",
"mql5",
"msc",
"mscgen",
"mupad",
"mxml",
"myghty",
"mysql",
"nasm",
"nawk",
"nb",
"nemerle",
"nesc",
"newlisp",
"newspeak",
"nginx",
"nim",
"nimrod",
"nit",
"nix",
"nixos",
"nroff",
"nsh",
"nsi",
"nsis",
"numpy",
"obj-c",
"obj-c++",
"obj-j",
"objc",
"objc++",
"objdump",
"objdump-nasm",
"objective-c",
"objective-c++",
"objective-j",
"objectivec",
"objectivec++",
"objectivej",
"objectpascal",
"objj",
"ocaml",
"octave",
"odin",
"ooc",
"opa",
"openbugs",
"openedge",
"pacmanconf",
"pan",
"parasail",
"pas",
"pascal",
"pawn",
"pcmk",
"perl",
"perl6",
"php",
"php3",
"php4",
"php5",
"pig",
"pike",
"pkgconfig",
"pl",
"pl6",
"plpgsql",
"po",
"posh",
"postgres",
"postgres-console",
"postgresql",
"postgresql-console",
"postscr",
"postscript",
"pot",
"pov",
"powershell",
"praat",
"progress",
"prolog",
"properties",
"proto",
"protobuf",
"ps1",
"ps1con",
"psm1",
"psql",
"puppet",
"py",
"py3",
"py3tb",
"pycon",
"pypy",
"pypylog",
"pyrex",
"pytb",
"python",
"python3",
"pyx",
"qbasic",
"qbs",
"qml",
"qvt",
"qvto",
"r",
"racket",
"ragel",
"ragel-c",
"ragel-cpp",
"ragel-d",
"ragel-em",
"ragel-java",
"ragel-objc",
"ragel-rb",
"ragel-ruby",
"raw",
"rb",
"rbcon",
"rconsole",
"rd",
"rebol",
"red",
"red/system",
"redcode",
"registry",
"resource",
"resourcebundle",
"rest",
"restructuredtext",
"rexx",
"rhtml",
"rkt",
"roboconf-graph",
"roboconf-instances",
"robotframework",
"rout",
"rql",
"rsl",
"rst",
"rts",
"ruby",
"rust",
"s",
"sage",
"salt",
"sass",
"sc",
"scala",
"scaml",
"scheme",
"scilab",
"scm",
"scss",
"sh",
"shell",
"shell-session",
"shen",
"slim",
"sls",
"smali",
"smalltalk",
"smarty",
"sml",
"snobol",
"sources.list",
"sourceslist",
"sp",
"sparql",
"spec",
"spitfire",
"splus",
"sql",
"sqlite3",
"squeak",
"squid",
"squid.conf",
"squidconf",
"ssp",
"st",
"stan",
"supercollider",
"sv",
"swift",
"swig",
"systemverilog",
"tads3",
"tap",
"tcl",
"tcsh",
"tcshcon",
"tea",
"termcap",
"terminfo",
"terraform",
"tex",
"text",
"tf",
"thrift",
"todotxt",
"trac-wiki",
"trafficscript",
"treetop",
"ts",
"turtle",
"twig",
"typescript",
"udiff",
"urbiscript",
"v",
"vala",
"vapi",
"vb.net",
"vbnet",
"vctreestatus",
"velocity",
"verilog",
"vfp",
"vgl",
"vhdl",
"vim",
"winbatch",
"winbugs",
"x10",
"xbase",
"xml",
"xml+cheetah",
"xml+django",
"xml+erb",
"xml+evoque",
"xml+genshi",
"xml+jinja",
"xml+kid",
"xml+lasso",
"xml+mako",
"xml+myghty",
"xml+php",
"xml+ruby",
"xml+smarty",
"xml+spitfire",
"xml+velocity",
"xq",
"xql",
"xqm",
"xquery",
"xqy",
"xslt",
"xten",
"xtend",
"xul+mozpreproc",
"yaml",
"yaml+jinja",
"zephir" ]
startsWithListMarker :: Text -> Bool
startsWithListMarker t =
case T.uncons t of
Nothing -> False
Just (c,_) -> c == '#' || c == ':' || c == ';' || c == '*'