|
Packit Service |
d2f85f |
{-
|
|
Packit Service |
d2f85f |
Copyright (C) 2010-2012 Paul Rivier <paul*rivier#demotera*com> | tr '*#' '.@'
|
|
Packit Service |
d2f85f |
2010-2017 John MacFarlane
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
This program is free software; you can redistribute it and/or modify
|
|
Packit Service |
d2f85f |
it under the terms of the GNU General Public License as published by
|
|
Packit Service |
d2f85f |
the Free Software Foundation; either version 2 of the License, or
|
|
Packit Service |
d2f85f |
(at your option) any later version.
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
This program is distributed in the hope that it will be useful,
|
|
Packit Service |
d2f85f |
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit Service |
d2f85f |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
Packit Service |
d2f85f |
GNU General Public License for more details.
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
You should have received a copy of the GNU General Public License
|
|
Packit Service |
d2f85f |
along with this program; if not, write to the Free Software
|
|
Packit Service |
d2f85f |
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
Packit Service |
d2f85f |
-}
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
{- |
|
|
Packit Service |
d2f85f |
Module : Text.Pandoc.Readers.Textile
|
|
Packit Service |
d2f85f |
Copyright : Copyright (C) 2010-2012 Paul Rivier
|
|
Packit Service |
d2f85f |
2010-2017 John MacFarlane
|
|
Packit Service |
d2f85f |
License : GNU GPL, version 2 or above
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
Maintainer : Paul Rivier <paul*rivier#demotera*com>
|
|
Packit Service |
d2f85f |
Stability : alpha
|
|
Packit Service |
d2f85f |
Portability : portable
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
Conversion from Textile to 'Pandoc' document, based on the spec
|
|
Packit Service |
d2f85f |
available at http://redcloth.org/textile.
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
Implemented and parsed:
|
|
Packit Service |
d2f85f |
- Paragraphs
|
|
Packit Service |
d2f85f |
- Code blocks
|
|
Packit Service |
d2f85f |
- Lists
|
|
Packit Service |
d2f85f |
- blockquote
|
|
Packit Service |
d2f85f |
- Inlines : strong, emph, cite, code, deleted, superscript,
|
|
Packit Service |
d2f85f |
subscript, links
|
|
Packit Service |
d2f85f |
- footnotes
|
|
Packit Service |
d2f85f |
- HTML-specific and CSS-specific attributes on headers
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
Left to be implemented:
|
|
Packit Service |
d2f85f |
- dimension sign
|
|
Packit Service |
d2f85f |
- all caps
|
|
Packit Service |
d2f85f |
- continued blocks (ex bq..)
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
TODO : refactor common patterns across readers :
|
|
Packit Service |
d2f85f |
- more ...
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
-}
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
module Text.Pandoc.Readers.Textile ( readTextile) where
|
|
Packit Service |
d2f85f |
import Control.Monad (guard, liftM)
|
|
Packit Service |
d2f85f |
import Control.Monad.Except (throwError)
|
|
Packit Service |
d2f85f |
import Data.Char (digitToInt, isUpper)
|
|
Packit Service |
d2f85f |
import Data.List (intercalate, intersperse, transpose)
|
|
Packit Service |
d2f85f |
import Data.Monoid ((<>))
|
|
Packit Service |
d2f85f |
import Data.Text (Text)
|
|
Packit Service |
d2f85f |
import qualified Data.Text as T
|
|
Packit Service |
d2f85f |
import Text.HTML.TagSoup (Tag (..), fromAttrib)
|
|
Packit Service |
d2f85f |
import Text.HTML.TagSoup.Match
|
|
Packit Service |
d2f85f |
import Text.Pandoc.Builder (Blocks, Inlines, trimInlines)
|
|
Packit Service |
d2f85f |
import qualified Text.Pandoc.Builder as B
|
|
Packit Service |
d2f85f |
import Text.Pandoc.Class (PandocMonad (..))
|
|
Packit Service |
d2f85f |
import Text.Pandoc.CSS
|
|
Packit Service |
d2f85f |
import Text.Pandoc.Definition
|
|
Packit Service |
d2f85f |
import Text.Pandoc.Options
|
|
Packit Service |
d2f85f |
import Text.Pandoc.Parsing
|
|
Packit Service |
d2f85f |
import Text.Pandoc.Readers.HTML (htmlTag, isBlockTag, isInlineTag)
|
|
Packit Service |
d2f85f |
import Text.Pandoc.Readers.LaTeX (rawLaTeXBlock, rawLaTeXInline)
|
|
Packit Service |
d2f85f |
import Text.Pandoc.Shared (crFilter, trim, underlineSpan)
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
-- | Parse a Textile text and return a Pandoc document.
|
|
Packit Service |
d2f85f |
readTextile :: PandocMonad m
|
|
Packit Service |
d2f85f |
=> ReaderOptions -- ^ Reader options
|
|
Packit Service |
d2f85f |
-> Text -- ^ String to parse (assuming @'\n'@ line endings)
|
|
Packit Service |
d2f85f |
-> m Pandoc
|
|
Packit Service |
d2f85f |
readTextile opts s = do
|
|
Packit Service |
d2f85f |
parsed <- readWithM parseTextile def{ stateOptions = opts }
|
|
Packit Service |
d2f85f |
(T.unpack (crFilter s) ++ "\n\n")
|
|
Packit Service |
d2f85f |
case parsed of
|
|
Packit Service |
d2f85f |
Right result -> return result
|
|
Packit Service |
d2f85f |
Left e -> throwError e
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
-- | Generate a Pandoc ADT from a textile document
|
|
Packit Service |
d2f85f |
parseTextile :: PandocMonad m => ParserT [Char] ParserState m Pandoc
|
|
Packit Service |
d2f85f |
parseTextile = do
|
|
Packit Service |
d2f85f |
many blankline
|
|
Packit Service |
d2f85f |
startPos <- getPosition
|
|
Packit Service |
d2f85f |
-- go through once just to get list of reference keys and notes
|
|
Packit Service |
d2f85f |
-- docMinusKeys is the raw document with blanks where the keys/notes were...
|
|
Packit Service |
d2f85f |
let firstPassParser = noteBlock <|> lineClump
|
|
Packit Service |
d2f85f |
manyTill firstPassParser eof >>= setInput . concat
|
|
Packit Service |
d2f85f |
setPosition startPos
|
|
Packit Service |
d2f85f |
st' <- getState
|
|
Packit Service |
d2f85f |
let reversedNotes = stateNotes st'
|
|
Packit Service |
d2f85f |
updateState $ \s -> s { stateNotes = reverse reversedNotes }
|
|
Packit Service |
d2f85f |
-- now parse it for real...
|
|
Packit Service |
d2f85f |
blocks <- parseBlocks
|
|
Packit Service |
d2f85f |
return $ Pandoc nullMeta (B.toList blocks) -- FIXME
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
noteMarker :: PandocMonad m => ParserT [Char] ParserState m [Char]
|
|
Packit Service |
d2f85f |
noteMarker = skipMany spaceChar >> string "fn" >> manyTill digit (char '.')
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
noteBlock :: PandocMonad m => ParserT [Char] ParserState m [Char]
|
|
Packit Service |
d2f85f |
noteBlock = try $ do
|
|
Packit Service |
d2f85f |
startPos <- getPosition
|
|
Packit Service |
d2f85f |
ref <- noteMarker
|
|
Packit Service |
d2f85f |
optional blankline
|
|
Packit Service |
d2f85f |
contents <- fmap unlines $ many1Till anyLine (blanklines <|> noteBlock)
|
|
Packit Service |
d2f85f |
endPos <- getPosition
|
|
Packit Service |
d2f85f |
let newnote = (ref, contents ++ "\n")
|
|
Packit Service |
d2f85f |
st <- getState
|
|
Packit Service |
d2f85f |
let oldnotes = stateNotes st
|
|
Packit Service |
d2f85f |
updateState $ \s -> s { stateNotes = newnote : oldnotes }
|
|
Packit Service |
d2f85f |
-- return blanks so line count isn't affected
|
|
Packit Service |
d2f85f |
return $ replicate (sourceLine endPos - sourceLine startPos) '\n'
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
-- | Parse document blocks
|
|
Packit Service |
d2f85f |
parseBlocks :: PandocMonad m => ParserT [Char] ParserState m Blocks
|
|
Packit Service |
d2f85f |
parseBlocks = mconcat <$> manyTill block eof
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
-- | Block parsers list tried in definition order
|
|
Packit Service |
d2f85f |
blockParsers :: PandocMonad m => [ParserT [Char] ParserState m Blocks]
|
|
Packit Service |
d2f85f |
blockParsers = [ codeBlock
|
|
Packit Service |
d2f85f |
, header
|
|
Packit Service |
d2f85f |
, blockQuote
|
|
Packit Service |
d2f85f |
, hrule
|
|
Packit Service |
d2f85f |
, commentBlock
|
|
Packit Service |
d2f85f |
, anyList
|
|
Packit Service |
d2f85f |
, rawHtmlBlock
|
|
Packit Service |
d2f85f |
, rawLaTeXBlock'
|
|
Packit Service |
d2f85f |
, table
|
|
Packit Service |
d2f85f |
, maybeExplicitBlock "p" para
|
|
Packit Service |
d2f85f |
, mempty <$ blanklines
|
|
Packit Service |
d2f85f |
]
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
-- | Any block in the order of definition of blockParsers
|
|
Packit Service |
d2f85f |
block :: PandocMonad m => ParserT [Char] ParserState m Blocks
|
|
Packit Service |
d2f85f |
block = do
|
|
Packit Service |
d2f85f |
res <- choice blockParsers "block"
|
|
Packit Service |
d2f85f |
trace (take 60 $ show $ B.toList res)
|
|
Packit Service |
d2f85f |
return res
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
commentBlock :: PandocMonad m => ParserT [Char] ParserState m Blocks
|
|
Packit Service |
d2f85f |
commentBlock = try $ do
|
|
Packit Service |
d2f85f |
string "###."
|
|
Packit Service |
d2f85f |
manyTill anyLine blanklines
|
|
Packit Service |
d2f85f |
return mempty
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
codeBlock :: PandocMonad m => ParserT [Char] ParserState m Blocks
|
|
Packit Service |
d2f85f |
codeBlock = codeBlockBc <|> codeBlockPre
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
codeBlockBc :: PandocMonad m => ParserT [Char] ParserState m Blocks
|
|
Packit Service |
d2f85f |
codeBlockBc = try $ do
|
|
Packit Service |
d2f85f |
string "bc."
|
|
Packit Service |
d2f85f |
extended <- option False (True <$ char '.')
|
|
Packit Service |
d2f85f |
char ' '
|
|
Packit Service |
d2f85f |
let starts = ["p", "table", "bq", "bc", "h1", "h2", "h3",
|
|
Packit Service |
d2f85f |
"h4", "h5", "h6", "pre", "###", "notextile"]
|
|
Packit Service |
d2f85f |
let ender = choice $ map explicitBlockStart starts
|
|
Packit Service |
d2f85f |
contents <- if extended
|
|
Packit Service |
d2f85f |
then do
|
|
Packit Service |
d2f85f |
f <- anyLine
|
|
Packit Service |
d2f85f |
rest <- many (notFollowedBy ender *> anyLine)
|
|
Packit Service |
d2f85f |
return (f:rest)
|
|
Packit Service |
d2f85f |
else manyTill anyLine blanklines
|
|
Packit Service |
d2f85f |
return $ B.codeBlock (trimTrailingNewlines (unlines contents))
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
trimTrailingNewlines :: String -> String
|
|
Packit Service |
d2f85f |
trimTrailingNewlines = reverse . dropWhile (=='\n') . reverse
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
-- | Code Blocks in Textile are between and
|
|
Packit Service |
d2f85f |
codeBlockPre :: PandocMonad m => ParserT [Char] ParserState m Blocks
|
|
Packit Service |
d2f85f |
codeBlockPre = try $ do
|
|
Packit Service |
d2f85f |
(t@(TagOpen _ attrs),_) <- htmlTag (tagOpen (=="pre") (const True))
|
|
Packit Service |
d2f85f |
result' <- manyTill anyChar (htmlTag (tagClose (=="pre")))
|
|
Packit Service |
d2f85f |
-- drop leading newline if any
|
|
Packit Service |
d2f85f |
let result'' = case result' of
|
|
Packit Service |
d2f85f |
'\n':xs -> xs
|
|
Packit Service |
d2f85f |
_ -> result'
|
|
Packit Service |
d2f85f |
-- drop trailing newline if any
|
|
Packit Service |
d2f85f |
let result''' = case reverse result'' of
|
|
Packit Service |
d2f85f |
'\n':_ -> init result''
|
|
Packit Service |
d2f85f |
_ -> result''
|
|
Packit Service |
d2f85f |
let classes = words $ fromAttrib "class" t
|
|
Packit Service |
d2f85f |
let ident = fromAttrib "id" t
|
|
Packit Service |
d2f85f |
let kvs = [(k,v) | (k,v) <- attrs, k /= "id" && k /= "class"]
|
|
Packit Service |
d2f85f |
return $ B.codeBlockWith (ident,classes,kvs) result'''
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
-- | Header of the form "hN. content" with N in 1..6
|
|
Packit Service |
d2f85f |
header :: PandocMonad m => ParserT [Char] ParserState m Blocks
|
|
Packit Service |
d2f85f |
header = try $ do
|
|
Packit Service |
d2f85f |
char 'h'
|
|
Packit Service |
d2f85f |
level <- digitToInt <$> oneOf "123456"
|
|
Packit Service |
d2f85f |
attr <- attributes
|
|
Packit Service |
d2f85f |
char '.'
|
|
Packit Service |
d2f85f |
lookAhead whitespace
|
|
Packit Service |
d2f85f |
name <- trimInlines . mconcat <$> many inline
|
|
Packit Service |
d2f85f |
attr' <- registerHeader attr name
|
|
Packit Service |
d2f85f |
return $ B.headerWith attr' level name
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
-- | Blockquote of the form "bq. content"
|
|
Packit Service |
d2f85f |
blockQuote :: PandocMonad m => ParserT [Char] ParserState m Blocks
|
|
Packit Service |
d2f85f |
blockQuote = try $ do
|
|
Packit Service |
d2f85f |
string "bq" >> attributes >> char '.' >> whitespace
|
|
Packit Service |
d2f85f |
B.blockQuote <$> para
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
-- Horizontal rule
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
hrule :: PandocMonad m => ParserT [Char] st m Blocks
|
|
Packit Service |
d2f85f |
hrule = try $ do
|
|
Packit Service |
d2f85f |
skipSpaces
|
|
Packit Service |
d2f85f |
start <- oneOf "-*"
|
|
Packit Service |
d2f85f |
count 2 (skipSpaces >> char start)
|
|
Packit Service |
d2f85f |
skipMany (spaceChar <|> char start)
|
|
Packit Service |
d2f85f |
newline
|
|
Packit Service |
d2f85f |
optional blanklines
|
|
Packit Service |
d2f85f |
return B.horizontalRule
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
-- Lists handling
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
-- | Can be a bullet list or an ordered list. This implementation is
|
|
Packit Service |
d2f85f |
-- strict in the nesting, sublist must start at exactly "parent depth
|
|
Packit Service |
d2f85f |
-- plus one"
|
|
Packit Service |
d2f85f |
anyList :: PandocMonad m => ParserT [Char] ParserState m Blocks
|
|
Packit Service |
d2f85f |
anyList = try $ anyListAtDepth 1 <* blanklines
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
-- | This allow one type of list to be nested into an other type,
|
|
Packit Service |
d2f85f |
-- provided correct nesting
|
|
Packit Service |
d2f85f |
anyListAtDepth :: PandocMonad m => Int -> ParserT [Char] ParserState m Blocks
|
|
Packit Service |
d2f85f |
anyListAtDepth depth = choice [ bulletListAtDepth depth,
|
|
Packit Service |
d2f85f |
orderedListAtDepth depth,
|
|
Packit Service |
d2f85f |
definitionList ]
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
-- | Bullet List of given depth, depth being the number of leading '*'
|
|
Packit Service |
d2f85f |
bulletListAtDepth :: PandocMonad m => Int -> ParserT [Char] ParserState m Blocks
|
|
Packit Service |
d2f85f |
bulletListAtDepth depth = try $ B.bulletList <$> many1 (bulletListItemAtDepth depth)
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
-- | Bullet List Item of given depth, depth being the number of
|
|
Packit Service |
d2f85f |
-- leading '*'
|
|
Packit Service |
d2f85f |
bulletListItemAtDepth :: PandocMonad m => Int -> ParserT [Char] ParserState m Blocks
|
|
Packit Service |
d2f85f |
bulletListItemAtDepth = genericListItemAtDepth '*'
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
-- | Ordered List of given depth, depth being the number of
|
|
Packit Service |
d2f85f |
-- leading '#'
|
|
Packit Service |
d2f85f |
orderedListAtDepth :: PandocMonad m => Int -> ParserT [Char] ParserState m Blocks
|
|
Packit Service |
d2f85f |
orderedListAtDepth depth = try $ do
|
|
Packit Service |
d2f85f |
items <- many1 (orderedListItemAtDepth depth)
|
|
Packit Service |
d2f85f |
return $ B.orderedList items
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
-- | Ordered List Item of given depth, depth being the number of
|
|
Packit Service |
d2f85f |
-- leading '#'
|
|
Packit Service |
d2f85f |
orderedListItemAtDepth :: PandocMonad m => Int -> ParserT [Char] ParserState m Blocks
|
|
Packit Service |
d2f85f |
orderedListItemAtDepth = genericListItemAtDepth '#'
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
-- | Common implementation of list items
|
|
Packit Service |
d2f85f |
genericListItemAtDepth :: PandocMonad m => Char -> Int -> ParserT [Char] ParserState m Blocks
|
|
Packit Service |
d2f85f |
genericListItemAtDepth c depth = try $ do
|
|
Packit Service |
d2f85f |
count depth (char c) >> attributes >> whitespace
|
|
Packit Service |
d2f85f |
contents <- mconcat <$> many ((B.plain . mconcat <$> many1 inline) <|>
|
|
Packit Service |
d2f85f |
try (newline >> codeBlockPre))
|
|
Packit Service |
d2f85f |
newline
|
|
Packit Service |
d2f85f |
sublist <- option mempty (anyListAtDepth (depth + 1))
|
|
Packit Service |
d2f85f |
return $ contents <> sublist
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
-- | A definition list is a set of consecutive definition items
|
|
Packit Service |
d2f85f |
definitionList :: PandocMonad m => ParserT [Char] ParserState m Blocks
|
|
Packit Service |
d2f85f |
definitionList = try $ B.definitionList <$> many1 definitionListItem
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
-- | List start character.
|
|
Packit Service |
d2f85f |
listStart :: PandocMonad m => ParserT [Char] ParserState m ()
|
|
Packit Service |
d2f85f |
listStart = genericListStart '*'
|
|
Packit Service |
d2f85f |
<|> () <$ genericListStart '#'
|
|
Packit Service |
d2f85f |
<|> () <$ definitionListStart
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
genericListStart :: PandocMonad m => Char -> ParserT [Char] st m ()
|
|
Packit Service |
d2f85f |
genericListStart c = () <$ try (many1 (char c) >> whitespace)
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
basicDLStart :: PandocMonad m => ParserT [Char] ParserState m ()
|
|
Packit Service |
d2f85f |
basicDLStart = do
|
|
Packit Service |
d2f85f |
char '-'
|
|
Packit Service |
d2f85f |
whitespace
|
|
Packit Service |
d2f85f |
notFollowedBy newline
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
definitionListStart :: PandocMonad m => ParserT [Char] ParserState m Inlines
|
|
Packit Service |
d2f85f |
definitionListStart = try $ do
|
|
Packit Service |
d2f85f |
basicDLStart
|
|
Packit Service |
d2f85f |
trimInlines . mconcat <$>
|
|
Packit Service |
d2f85f |
many1Till inline
|
|
Packit Service |
d2f85f |
( try (newline *> lookAhead basicDLStart)
|
|
Packit Service |
d2f85f |
<|> try (lookAhead (() <$ string ":="))
|
|
Packit Service |
d2f85f |
)
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
-- | A definition list item in textile begins with '- ', followed by
|
|
Packit Service |
d2f85f |
-- the term defined, then spaces and ":=". The definition follows, on
|
|
Packit Service |
d2f85f |
-- the same single line, or spaned on multiple line, after a line
|
|
Packit Service |
d2f85f |
-- break.
|
|
Packit Service |
d2f85f |
definitionListItem :: PandocMonad m => ParserT [Char] ParserState m (Inlines, [Blocks])
|
|
Packit Service |
d2f85f |
definitionListItem = try $ do
|
|
Packit Service |
d2f85f |
term <- (mconcat . intersperse B.linebreak) <$> many1 definitionListStart
|
|
Packit Service |
d2f85f |
def' <- string ":=" *> optional whitespace *> (multilineDef <|> inlineDef)
|
|
Packit Service |
d2f85f |
return (term, def')
|
|
Packit Service |
d2f85f |
where inlineDef :: PandocMonad m => ParserT [Char] ParserState m [Blocks]
|
|
Packit Service |
d2f85f |
inlineDef = liftM (\d -> [B.plain d])
|
|
Packit Service |
d2f85f |
$ optional whitespace >> (trimInlines . mconcat <$> many inline) <* newline
|
|
Packit Service |
d2f85f |
multilineDef :: PandocMonad m => ParserT [Char] ParserState m [Blocks]
|
|
Packit Service |
d2f85f |
multilineDef = try $ do
|
|
Packit Service |
d2f85f |
optional whitespace >> newline
|
|
Packit Service |
d2f85f |
s <- many1Till anyChar (try (string "=:" >> newline))
|
|
Packit Service |
d2f85f |
-- this ++ "\n\n" does not look very good
|
|
Packit Service |
d2f85f |
ds <- parseFromString' parseBlocks (s ++ "\n\n")
|
|
Packit Service |
d2f85f |
return [ds]
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
-- raw content
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
-- | A raw Html Block, optionally followed by blanklines
|
|
Packit Service |
d2f85f |
rawHtmlBlock :: PandocMonad m => ParserT [Char] ParserState m Blocks
|
|
Packit Service |
d2f85f |
rawHtmlBlock = try $ do
|
|
Packit Service |
d2f85f |
skipMany spaceChar
|
|
Packit Service |
d2f85f |
(_,b) <- htmlTag isBlockTag
|
|
Packit Service |
d2f85f |
optional blanklines
|
|
Packit Service |
d2f85f |
return $ B.rawBlock "html" b
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
-- | Raw block of LaTeX content
|
|
Packit Service |
d2f85f |
rawLaTeXBlock' :: PandocMonad m => ParserT [Char] ParserState m Blocks
|
|
Packit Service |
d2f85f |
rawLaTeXBlock' = do
|
|
Packit Service |
d2f85f |
guardEnabled Ext_raw_tex
|
|
Packit Service |
d2f85f |
B.rawBlock "latex" <$> (rawLaTeXBlock <* spaces)
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
-- | In textile, paragraphs are separated by blank lines.
|
|
Packit Service |
d2f85f |
para :: PandocMonad m => ParserT [Char] ParserState m Blocks
|
|
Packit Service |
d2f85f |
para = B.para . trimInlines . mconcat <$> many1 inline
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
-- Tables
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
toAlignment :: Char -> Alignment
|
|
Packit Service |
d2f85f |
toAlignment '<' = AlignLeft
|
|
Packit Service |
d2f85f |
toAlignment '>' = AlignRight
|
|
Packit Service |
d2f85f |
toAlignment '=' = AlignCenter
|
|
Packit Service |
d2f85f |
toAlignment _ = AlignDefault
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
cellAttributes :: PandocMonad m => ParserT [Char] ParserState m (Bool, Alignment)
|
|
Packit Service |
d2f85f |
cellAttributes = try $ do
|
|
Packit Service |
d2f85f |
isHeader <- option False (True <$ char '_')
|
|
Packit Service |
d2f85f |
-- we just ignore colspan and rowspan markers:
|
|
Packit Service |
d2f85f |
optional $ try $ oneOf "/\\" >> many1 digit
|
|
Packit Service |
d2f85f |
-- we pay attention to alignments:
|
|
Packit Service |
d2f85f |
alignment <- option AlignDefault $ toAlignment <$> oneOf "<>="
|
|
Packit Service |
d2f85f |
-- ignore other attributes for now:
|
|
Packit Service |
d2f85f |
_ <- attributes
|
|
Packit Service |
d2f85f |
char '.'
|
|
Packit Service |
d2f85f |
return (isHeader, alignment)
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
-- | A table cell spans until a pipe |
|
|
Packit Service |
d2f85f |
tableCell :: PandocMonad m => ParserT [Char] ParserState m ((Bool, Alignment), Blocks)
|
|
Packit Service |
d2f85f |
tableCell = try $ do
|
|
Packit Service |
d2f85f |
char '|'
|
|
Packit Service |
d2f85f |
(isHeader, alignment) <- option (False, AlignDefault) $ cellAttributes
|
|
Packit Service |
d2f85f |
notFollowedBy blankline
|
|
Packit Service |
d2f85f |
raw <- trim <$>
|
|
Packit Service |
d2f85f |
many (noneOf "|\n" <|> try (char '\n' <* notFollowedBy blankline))
|
|
Packit Service |
d2f85f |
content <- mconcat <$> parseFromString' (many inline) raw
|
|
Packit Service |
d2f85f |
return ((isHeader, alignment), B.plain content)
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
-- | A table row is made of many table cells
|
|
Packit Service |
d2f85f |
tableRow :: PandocMonad m => ParserT [Char] ParserState m [((Bool, Alignment), Blocks)]
|
|
Packit Service |
d2f85f |
tableRow = try $ do
|
|
Packit Service |
d2f85f |
-- skip optional row attributes
|
|
Packit Service |
d2f85f |
optional $ try $ do
|
|
Packit Service |
d2f85f |
_ <- attributes
|
|
Packit Service |
d2f85f |
char '.'
|
|
Packit Service |
d2f85f |
many1 spaceChar
|
|
Packit Service |
d2f85f |
many1 tableCell <* char '|' <* blankline
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
-- | A table with an optional header.
|
|
Packit Service |
d2f85f |
table :: PandocMonad m => ParserT [Char] ParserState m Blocks
|
|
Packit Service |
d2f85f |
table = try $ do
|
|
Packit Service |
d2f85f |
-- ignore table attributes
|
|
Packit Service |
d2f85f |
caption <- option mempty $ try $ do
|
|
Packit Service |
d2f85f |
string "table"
|
|
Packit Service |
d2f85f |
_ <- attributes
|
|
Packit Service |
d2f85f |
char '.'
|
|
Packit Service |
d2f85f |
rawcapt <- trim <$> anyLine
|
|
Packit Service |
d2f85f |
parseFromString' (mconcat <$> many inline) rawcapt
|
|
Packit Service |
d2f85f |
rawrows <- many1 $ skipMany ignorableRow >> tableRow
|
|
Packit Service |
d2f85f |
skipMany ignorableRow
|
|
Packit Service |
d2f85f |
blanklines
|
|
Packit Service |
d2f85f |
let (headers, rows) = case rawrows of
|
|
Packit Service |
d2f85f |
(toprow:rest) | any (fst . fst) toprow ->
|
|
Packit Service |
d2f85f |
(toprow, rest)
|
|
Packit Service |
d2f85f |
_ -> (mempty, rawrows)
|
|
Packit Service |
d2f85f |
let nbOfCols = max (length headers) (length $ head rows)
|
|
Packit Service |
d2f85f |
let aligns = map minimum $ transpose $ map (map (snd . fst)) (headers:rows)
|
|
Packit Service |
d2f85f |
return $ B.table caption
|
|
Packit Service |
d2f85f |
(zip aligns (replicate nbOfCols 0.0))
|
|
Packit Service |
d2f85f |
(map snd headers)
|
|
Packit Service |
d2f85f |
(map (map snd) rows)
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
-- | Ignore markers for cols, thead, tfoot.
|
|
Packit Service |
d2f85f |
ignorableRow :: PandocMonad m => ParserT [Char] ParserState m ()
|
|
Packit Service |
d2f85f |
ignorableRow = try $ do
|
|
Packit Service |
d2f85f |
char '|'
|
|
Packit Service |
d2f85f |
oneOf ":^-~"
|
|
Packit Service |
d2f85f |
_ <- attributes
|
|
Packit Service |
d2f85f |
char '.'
|
|
Packit Service |
d2f85f |
_ <- anyLine
|
|
Packit Service |
d2f85f |
return ()
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
explicitBlockStart :: PandocMonad m => String -> ParserT [Char] ParserState m ()
|
|
Packit Service |
d2f85f |
explicitBlockStart name = try $ do
|
|
Packit Service |
d2f85f |
string name
|
|
Packit Service |
d2f85f |
attributes
|
|
Packit Service |
d2f85f |
char '.'
|
|
Packit Service |
d2f85f |
optional whitespace
|
|
Packit Service |
d2f85f |
optional endline
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
-- | Blocks like 'p' and 'table' do not need explicit block tag.
|
|
Packit Service |
d2f85f |
-- However, they can be used to set HTML/CSS attributes when needed.
|
|
Packit Service |
d2f85f |
maybeExplicitBlock :: PandocMonad m
|
|
Packit Service |
d2f85f |
=> String -- ^ block tag name
|
|
Packit Service |
d2f85f |
-> ParserT [Char] ParserState m Blocks -- ^ implicit block
|
|
Packit Service |
d2f85f |
-> ParserT [Char] ParserState m Blocks
|
|
Packit Service |
d2f85f |
maybeExplicitBlock name blk = try $ do
|
|
Packit Service |
d2f85f |
optional $ explicitBlockStart name
|
|
Packit Service |
d2f85f |
blk
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
----------
|
|
Packit Service |
d2f85f |
-- Inlines
|
|
Packit Service |
d2f85f |
----------
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
-- | Any inline element
|
|
Packit Service |
d2f85f |
inline :: PandocMonad m => ParserT [Char] ParserState m Inlines
|
|
Packit Service |
d2f85f |
inline = choice inlineParsers "inline"
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
-- | Inline parsers tried in order
|
|
Packit Service |
d2f85f |
inlineParsers :: PandocMonad m => [ParserT [Char] ParserState m Inlines]
|
|
Packit Service |
d2f85f |
inlineParsers = [ str
|
|
Packit Service |
d2f85f |
, whitespace
|
|
Packit Service |
d2f85f |
, endline
|
|
Packit Service |
d2f85f |
, code
|
|
Packit Service |
d2f85f |
, escapedInline
|
|
Packit Service |
d2f85f |
, inlineMarkup
|
|
Packit Service |
d2f85f |
, groupedInlineMarkup
|
|
Packit Service |
d2f85f |
, rawHtmlInline
|
|
Packit Service |
d2f85f |
, rawLaTeXInline'
|
|
Packit Service |
d2f85f |
, note
|
|
Packit Service |
d2f85f |
, link
|
|
Packit Service |
d2f85f |
, image
|
|
Packit Service |
d2f85f |
, mark
|
|
Packit Service |
d2f85f |
, (B.str . (:[])) <$> characterReference
|
|
Packit Service |
d2f85f |
, smartPunctuation inline
|
|
Packit Service |
d2f85f |
, symbol
|
|
Packit Service |
d2f85f |
]
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
-- | Inline markups
|
|
Packit Service |
d2f85f |
inlineMarkup :: PandocMonad m => ParserT [Char] ParserState m Inlines
|
|
Packit Service |
d2f85f |
inlineMarkup = choice [ simpleInline (string "??") (B.cite [])
|
|
Packit Service |
d2f85f |
, simpleInline (string "**") B.strong
|
|
Packit Service |
d2f85f |
, simpleInline (string "__") B.emph
|
|
Packit Service |
d2f85f |
, simpleInline (char '*') B.strong
|
|
Packit Service |
d2f85f |
, simpleInline (char '_') B.emph
|
|
Packit Service |
d2f85f |
, simpleInline (char '+') underlineSpan
|
|
Packit Service |
d2f85f |
, simpleInline (char '-' <* notFollowedBy (char '-')) B.strikeout
|
|
Packit Service |
d2f85f |
, simpleInline (char '^') B.superscript
|
|
Packit Service |
d2f85f |
, simpleInline (char '~') B.subscript
|
|
Packit Service |
d2f85f |
, simpleInline (char '%') id
|
|
Packit Service |
d2f85f |
]
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
-- | Trademark, registered, copyright
|
|
Packit Service |
d2f85f |
mark :: PandocMonad m => ParserT [Char] st m Inlines
|
|
Packit Service |
d2f85f |
mark = try $ char '(' >> (try tm <|> try reg <|> copy)
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
reg :: PandocMonad m => ParserT [Char] st m Inlines
|
|
Packit Service |
d2f85f |
reg = do
|
|
Packit Service |
d2f85f |
oneOf "Rr"
|
|
Packit Service |
d2f85f |
char ')'
|
|
Packit Service |
d2f85f |
return $ B.str "\174"
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
tm :: PandocMonad m => ParserT [Char] st m Inlines
|
|
Packit Service |
d2f85f |
tm = do
|
|
Packit Service |
d2f85f |
oneOf "Tt"
|
|
Packit Service |
d2f85f |
oneOf "Mm"
|
|
Packit Service |
d2f85f |
char ')'
|
|
Packit Service |
d2f85f |
return $ B.str "\8482"
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
copy :: PandocMonad m => ParserT [Char] st m Inlines
|
|
Packit Service |
d2f85f |
copy = do
|
|
Packit Service |
d2f85f |
oneOf "Cc"
|
|
Packit Service |
d2f85f |
char ')'
|
|
Packit Service |
d2f85f |
return $ B.str "\169"
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
note :: PandocMonad m => ParserT [Char] ParserState m Inlines
|
|
Packit Service |
d2f85f |
note = try $ do
|
|
Packit Service |
d2f85f |
ref <- (char '[' *> many1 digit <* char ']')
|
|
Packit Service |
d2f85f |
notes <- stateNotes <$> getState
|
|
Packit Service |
d2f85f |
case lookup ref notes of
|
|
Packit Service |
d2f85f |
Nothing -> fail "note not found"
|
|
Packit Service |
d2f85f |
Just raw -> B.note <$> parseFromString' parseBlocks raw
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
-- | Special chars
|
|
Packit Service |
d2f85f |
markupChars :: [Char]
|
|
Packit Service |
d2f85f |
markupChars = "\\*#_@~-+^|%=[]&"
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
-- | Break strings on following chars. Space tab and newline break for
|
|
Packit Service |
d2f85f |
-- inlines breaking. Open paren breaks for mark. Quote, dash and dot
|
|
Packit Service |
d2f85f |
-- break for smart punctuation. Punctuation breaks for regular
|
|
Packit Service |
d2f85f |
-- punctuation. Double quote breaks for named links. > and < break
|
|
Packit Service |
d2f85f |
-- for inline html.
|
|
Packit Service |
d2f85f |
stringBreakers :: [Char]
|
|
Packit Service |
d2f85f |
stringBreakers = " \t\n\r.,\"'?!;:<>«»„“”‚‘’()[]"
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
wordBoundaries :: [Char]
|
|
Packit Service |
d2f85f |
wordBoundaries = markupChars ++ stringBreakers
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
-- | Parse a hyphened sequence of words
|
|
Packit Service |
d2f85f |
hyphenedWords :: PandocMonad m => ParserT [Char] ParserState m String
|
|
Packit Service |
d2f85f |
hyphenedWords = do
|
|
Packit Service |
d2f85f |
x <- wordChunk
|
|
Packit Service |
d2f85f |
xs <- many (try $ char '-' >> wordChunk)
|
|
Packit Service |
d2f85f |
return $ intercalate "-" (x:xs)
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
wordChunk :: PandocMonad m => ParserT [Char] ParserState m String
|
|
Packit Service |
d2f85f |
wordChunk = try $ do
|
|
Packit Service |
d2f85f |
hd <- noneOf wordBoundaries
|
|
Packit Service |
d2f85f |
tl <- many ( (noneOf wordBoundaries) <|>
|
|
Packit Service |
d2f85f |
try (notFollowedBy' note *> oneOf markupChars
|
|
Packit Service |
d2f85f |
<* lookAhead (noneOf wordBoundaries) ) )
|
|
Packit Service |
d2f85f |
return $ hd:tl
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
-- | Any string
|
|
Packit Service |
d2f85f |
str :: PandocMonad m => ParserT [Char] ParserState m Inlines
|
|
Packit Service |
d2f85f |
str = do
|
|
Packit Service |
d2f85f |
baseStr <- hyphenedWords
|
|
Packit Service |
d2f85f |
-- RedCloth compliance : if parsed word is uppercase and immediatly
|
|
Packit Service |
d2f85f |
-- followed by parens, parens content is unconditionally word acronym
|
|
Packit Service |
d2f85f |
fullStr <- option baseStr $ try $ do
|
|
Packit Service |
d2f85f |
guard $ all isUpper baseStr
|
|
Packit Service |
d2f85f |
acro <- enclosed (char '(') (char ')') anyChar'
|
|
Packit Service |
d2f85f |
return $ concat [baseStr, " (", acro, ")"]
|
|
Packit Service |
d2f85f |
updateLastStrPos
|
|
Packit Service |
d2f85f |
return $ B.str fullStr
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
-- | Some number of space chars
|
|
Packit Service |
d2f85f |
whitespace :: PandocMonad m => ParserT [Char] st m Inlines
|
|
Packit Service |
d2f85f |
whitespace = many1 spaceChar >> return B.space "whitespace"
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
-- | In Textile, an isolated endline character is a line break
|
|
Packit Service |
d2f85f |
endline :: PandocMonad m => ParserT [Char] ParserState m Inlines
|
|
Packit Service |
d2f85f |
endline = try $ do
|
|
Packit Service |
d2f85f |
newline
|
|
Packit Service |
d2f85f |
notFollowedBy blankline
|
|
Packit Service |
d2f85f |
notFollowedBy listStart
|
|
Packit Service |
d2f85f |
notFollowedBy rawHtmlBlock
|
|
Packit Service |
d2f85f |
return B.linebreak
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
rawHtmlInline :: PandocMonad m => ParserT [Char] ParserState m Inlines
|
|
Packit Service |
d2f85f |
rawHtmlInline = B.rawInline "html" . snd <$> htmlTag isInlineTag
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
-- | Raw LaTeX Inline
|
|
Packit Service |
d2f85f |
rawLaTeXInline' :: PandocMonad m => ParserT [Char] ParserState m Inlines
|
|
Packit Service |
d2f85f |
rawLaTeXInline' = try $ do
|
|
Packit Service |
d2f85f |
guardEnabled Ext_raw_tex
|
|
Packit Service |
d2f85f |
B.rawInline "latex" <$> rawLaTeXInline
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
-- | Textile standard link syntax is "label":target. But we
|
|
Packit Service |
d2f85f |
-- can also have ["label":target].
|
|
Packit Service |
d2f85f |
link :: PandocMonad m => ParserT [Char] ParserState m Inlines
|
|
Packit Service |
d2f85f |
link = try $ do
|
|
Packit Service |
d2f85f |
bracketed <- (True <$ char '[') <|> return False
|
|
Packit Service |
d2f85f |
char '"' *> notFollowedBy (oneOf " \t\n\r")
|
|
Packit Service |
d2f85f |
attr <- attributes
|
|
Packit Service |
d2f85f |
name <- trimInlines . mconcat <$>
|
|
Packit Service |
d2f85f |
withQuoteContext InDoubleQuote (many1Till inline (char '"'))
|
|
Packit Service |
d2f85f |
char ':'
|
|
Packit Service |
d2f85f |
let stop = if bracketed
|
|
Packit Service |
d2f85f |
then char ']'
|
|
Packit Service |
d2f85f |
else lookAhead $ space <|> eof' <|>
|
|
Packit Service |
d2f85f |
try (oneOf "!.,;:" *>
|
|
Packit Service |
d2f85f |
(space <|> newline <|> eof'))
|
|
Packit Service |
d2f85f |
url <- many1Till nonspaceChar stop
|
|
Packit Service |
d2f85f |
let name' = if B.toList name == [Str "$"] then B.str url else name
|
|
Packit Service |
d2f85f |
return $ if attr == nullAttr
|
|
Packit Service |
d2f85f |
then B.link url "" name'
|
|
Packit Service |
d2f85f |
else B.spanWith attr $ B.link url "" name'
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
-- | image embedding
|
|
Packit Service |
d2f85f |
image :: PandocMonad m => ParserT [Char] ParserState m Inlines
|
|
Packit Service |
d2f85f |
image = try $ do
|
|
Packit Service |
d2f85f |
char '!' >> notFollowedBy space
|
|
Packit Service |
d2f85f |
(ident, cls, kvs) <- attributes
|
|
Packit Service |
d2f85f |
let attr = case lookup "style" kvs of
|
|
Packit Service |
d2f85f |
Just stls -> (ident, cls, pickStylesToKVs ["width", "height"] stls)
|
|
Packit Service |
d2f85f |
Nothing -> (ident, cls, kvs)
|
|
Packit Service |
d2f85f |
src <- many1 (noneOf " \t\n\r!(")
|
|
Packit Service |
d2f85f |
alt <- option "" $ try $ char '(' *> manyTill anyChar (char ')')
|
|
Packit Service |
d2f85f |
char '!'
|
|
Packit Service |
d2f85f |
return $ B.imageWith attr src alt (B.str alt)
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
escapedInline :: PandocMonad m => ParserT [Char] ParserState m Inlines
|
|
Packit Service |
d2f85f |
escapedInline = escapedEqs <|> escapedTag
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
escapedEqs :: PandocMonad m => ParserT [Char] ParserState m Inlines
|
|
Packit Service |
d2f85f |
escapedEqs = B.str <$>
|
|
Packit Service |
d2f85f |
try (string "==" *> manyTill anyChar' (try $ string "=="))
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
-- | literal text escaped btw <notextile> tags
|
|
Packit Service |
d2f85f |
escapedTag :: PandocMonad m => ParserT [Char] ParserState m Inlines
|
|
Packit Service |
d2f85f |
escapedTag = B.str <$>
|
|
Packit Service |
d2f85f |
(try $ string "<notextile>" *>
|
|
Packit Service |
d2f85f |
manyTill anyChar' (try $ string "</notextile>"))
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
-- | Any special symbol defined in wordBoundaries
|
|
Packit Service |
d2f85f |
symbol :: PandocMonad m => ParserT [Char] ParserState m Inlines
|
|
Packit Service |
d2f85f |
symbol = B.str . singleton <$> (notFollowedBy newline *>
|
|
Packit Service |
d2f85f |
notFollowedBy rawHtmlBlock *>
|
|
Packit Service |
d2f85f |
oneOf wordBoundaries)
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
-- | Inline code
|
|
Packit Service |
d2f85f |
code :: PandocMonad m => ParserT [Char] ParserState m Inlines
|
|
Packit Service |
d2f85f |
code = code1 <|> code2
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
-- any character except a newline before a blank line
|
|
Packit Service |
d2f85f |
anyChar' :: PandocMonad m => ParserT [Char] ParserState m Char
|
|
Packit Service |
d2f85f |
anyChar' =
|
|
Packit Service |
d2f85f |
satisfy (/='\n') <|> (try $ char '\n' <* notFollowedBy blankline)
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
code1 :: PandocMonad m => ParserT [Char] ParserState m Inlines
|
|
Packit Service |
d2f85f |
code1 = B.code <$> surrounded (char '@') anyChar'
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
code2 :: PandocMonad m => ParserT [Char] ParserState m Inlines
|
|
Packit Service |
d2f85f |
code2 = do
|
|
Packit Service |
d2f85f |
htmlTag (tagOpen (=="tt") null)
|
|
Packit Service |
d2f85f |
B.code <$> manyTill anyChar' (try $ htmlTag $ tagClose (=="tt"))
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
-- | Html / CSS attributes
|
|
Packit Service |
d2f85f |
attributes :: PandocMonad m => ParserT [Char] ParserState m Attr
|
|
Packit Service |
d2f85f |
attributes = foldl (flip ($)) ("",[],[]) <$>
|
|
Packit Service |
d2f85f |
try (do special <- option id specialAttribute
|
|
Packit Service |
d2f85f |
attrs <- many attribute
|
|
Packit Service |
d2f85f |
return (special : attrs))
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
specialAttribute :: PandocMonad m => ParserT [Char] ParserState m (Attr -> Attr)
|
|
Packit Service |
d2f85f |
specialAttribute = do
|
|
Packit Service |
d2f85f |
alignStr <- ("center" <$ char '=') <|>
|
|
Packit Service |
d2f85f |
("justify" <$ try (string "<>")) <|>
|
|
Packit Service |
d2f85f |
("right" <$ char '>') <|>
|
|
Packit Service |
d2f85f |
("left" <$ char '<')
|
|
Packit Service |
d2f85f |
notFollowedBy spaceChar
|
|
Packit Service |
d2f85f |
return $ addStyle ("text-align:" ++ alignStr)
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
attribute :: PandocMonad m => ParserT [Char] ParserState m (Attr -> Attr)
|
|
Packit Service |
d2f85f |
attribute = try $
|
|
Packit Service |
d2f85f |
(classIdAttr <|> styleAttr <|> langAttr) <* notFollowedBy spaceChar
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
classIdAttr :: PandocMonad m => ParserT [Char] ParserState m (Attr -> Attr)
|
|
Packit Service |
d2f85f |
classIdAttr = try $ do -- (class class #id)
|
|
Packit Service |
d2f85f |
char '('
|
|
Packit Service |
d2f85f |
ws <- words `fmap` manyTill anyChar' (char ')')
|
|
Packit Service |
d2f85f |
case reverse ws of
|
|
Packit Service |
d2f85f |
[] -> return $ \(_,_,keyvals) -> ("",[],keyvals)
|
|
Packit Service |
d2f85f |
(('#':ident'):classes') -> return $ \(_,_,keyvals) ->
|
|
Packit Service |
d2f85f |
(ident',classes',keyvals)
|
|
Packit Service |
d2f85f |
classes' -> return $ \(_,_,keyvals) ->
|
|
Packit Service |
d2f85f |
("",classes',keyvals)
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
styleAttr :: PandocMonad m => ParserT [Char] ParserState m (Attr -> Attr)
|
|
Packit Service |
d2f85f |
styleAttr = do
|
|
Packit Service |
d2f85f |
style <- try $ enclosed (char '{') (char '}') anyChar'
|
|
Packit Service |
d2f85f |
return $ addStyle style
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
addStyle :: String -> Attr -> Attr
|
|
Packit Service |
d2f85f |
addStyle style (id',classes,keyvals) =
|
|
Packit Service |
d2f85f |
(id',classes,keyvals')
|
|
Packit Service |
d2f85f |
where keyvals' = ("style", style') : [(k,v) | (k,v) <- keyvals, k /= "style"]
|
|
Packit Service |
d2f85f |
style' = style ++ ";" ++ concat [v | ("style",v) <- keyvals]
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
langAttr :: PandocMonad m => ParserT [Char] ParserState m (Attr -> Attr)
|
|
Packit Service |
d2f85f |
langAttr = do
|
|
Packit Service |
d2f85f |
lang <- try $ enclosed (char '[') (char ']') alphaNum
|
|
Packit Service |
d2f85f |
return $ \(id',classes,keyvals) -> (id',classes,("lang",lang):keyvals)
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
-- | Parses material surrounded by a parser.
|
|
Packit Service |
d2f85f |
surrounded :: (PandocMonad m, Show t)
|
|
Packit Service |
d2f85f |
=> ParserT [Char] st m t -- ^ surrounding parser
|
|
Packit Service |
d2f85f |
-> ParserT [Char] st m a -- ^ content parser (to be used repeatedly)
|
|
Packit Service |
d2f85f |
-> ParserT [Char] st m [a]
|
|
Packit Service |
d2f85f |
surrounded border =
|
|
Packit Service |
d2f85f |
enclosed (border *> notFollowedBy (oneOf " \t\n\r")) (try border)
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
simpleInline :: PandocMonad m
|
|
Packit Service |
d2f85f |
=> ParserT [Char] ParserState m t -- ^ surrounding parser
|
|
Packit Service |
d2f85f |
-> (Inlines -> Inlines) -- ^ Inline constructor
|
|
Packit Service |
d2f85f |
-> ParserT [Char] ParserState m Inlines -- ^ content parser (to be used repeatedly)
|
|
Packit Service |
d2f85f |
simpleInline border construct = try $ do
|
|
Packit Service |
d2f85f |
notAfterString
|
|
Packit Service |
d2f85f |
border *> notFollowedBy (oneOf " \t\n\r")
|
|
Packit Service |
d2f85f |
attr <- attributes
|
|
Packit Service |
d2f85f |
body <- trimInlines . mconcat <$>
|
|
Packit Service |
d2f85f |
withQuoteContext InSingleQuote
|
|
Packit Service |
d2f85f |
(manyTill (notFollowedBy newline >> inline)
|
|
Packit Service |
d2f85f |
(try border <* notFollowedBy alphaNum))
|
|
Packit Service |
d2f85f |
return $ construct $
|
|
Packit Service |
d2f85f |
if attr == nullAttr
|
|
Packit Service |
d2f85f |
then body
|
|
Packit Service |
d2f85f |
else B.spanWith attr body
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
groupedInlineMarkup :: PandocMonad m => ParserT [Char] ParserState m Inlines
|
|
Packit Service |
d2f85f |
groupedInlineMarkup = try $ do
|
|
Packit Service |
d2f85f |
char '['
|
|
Packit Service |
d2f85f |
sp1 <- option mempty $ B.space <$ whitespace
|
|
Packit Service |
d2f85f |
result <- withQuoteContext InSingleQuote inlineMarkup
|
|
Packit Service |
d2f85f |
sp2 <- option mempty $ B.space <$ whitespace
|
|
Packit Service |
d2f85f |
char ']'
|
|
Packit Service |
d2f85f |
return $ sp1 <> result <> sp2
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
-- | Create a singleton list
|
|
Packit Service |
d2f85f |
singleton :: a -> [a]
|
|
Packit Service |
d2f85f |
singleton x = [x]
|
|
Packit Service |
d2f85f |
|
|
Packit Service |
d2f85f |
eof' :: Monad m => ParserT [Char] s m Char
|
|
Packit Service |
d2f85f |
eof' = '\n' <$ eof
|