2009-12-31 16:48:14 +00:00
|
|
|
{-# LANGUAGE DeriveDataTypeable #-}
|
2007-11-03 23:27:58 +00:00
|
|
|
{-
|
2010-03-23 13:31:09 -07:00
|
|
|
Copyright (C) 2006-2010 John MacFarlane <jgm@berkeley.edu>
|
2007-11-03 23:27:58 +00:00
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
-}
|
|
|
|
|
|
|
|
{- |
|
|
|
|
Module : Text.Pandoc.Shared
|
2010-03-23 13:31:09 -07:00
|
|
|
Copyright : Copyright (C) 2006-2010 John MacFarlane
|
2007-11-03 23:27:58 +00:00
|
|
|
License : GNU GPL, version 2 or above
|
|
|
|
|
|
|
|
Maintainer : John MacFarlane <jgm@berkeley.edu>
|
|
|
|
Stability : alpha
|
|
|
|
Portability : portable
|
|
|
|
|
|
|
|
Utility functions and definitions used by the various Pandoc modules.
|
|
|
|
-}
|
2010-03-23 15:05:33 -07:00
|
|
|
module Text.Pandoc.Shared (
|
2007-11-03 23:27:58 +00:00
|
|
|
-- * List processing
|
|
|
|
splitBy,
|
|
|
|
splitByIndices,
|
|
|
|
substitute,
|
|
|
|
-- * Text processing
|
|
|
|
backslashEscapes,
|
|
|
|
escapeStringUsing,
|
|
|
|
stripTrailingNewlines,
|
|
|
|
removeLeadingTrailingSpace,
|
|
|
|
removeLeadingSpace,
|
|
|
|
removeTrailingSpace,
|
|
|
|
stripFirstAndLast,
|
|
|
|
camelCaseToHyphenated,
|
|
|
|
toRomanNumeral,
|
2010-03-23 15:05:33 -07:00
|
|
|
escapeURI,
|
2010-07-06 23:17:06 -07:00
|
|
|
tabFilter,
|
2007-11-03 23:27:58 +00:00
|
|
|
-- * Pandoc block and inline list processing
|
|
|
|
orderedListMarkers,
|
|
|
|
normalizeSpaces,
|
2010-12-14 20:04:37 -08:00
|
|
|
normalize,
|
2010-11-27 07:08:06 -08:00
|
|
|
stringify,
|
2007-11-03 23:27:58 +00:00
|
|
|
compactify,
|
|
|
|
Element (..),
|
|
|
|
hierarchicalize,
|
2010-03-16 06:45:52 +00:00
|
|
|
uniqueIdent,
|
2007-11-03 23:27:58 +00:00
|
|
|
isHeaderBlock,
|
2010-07-11 20:03:55 -07:00
|
|
|
headerShift,
|
2007-11-03 23:27:58 +00:00
|
|
|
-- * Writer options
|
2007-12-01 03:11:44 +00:00
|
|
|
HTMLMathMethod (..),
|
2010-12-13 21:18:01 +01:00
|
|
|
CiteMethod (..),
|
2009-01-24 19:58:48 +00:00
|
|
|
ObfuscationMethod (..),
|
2010-07-13 20:44:56 -07:00
|
|
|
HTMLSlideVariant (..),
|
2007-11-03 23:27:58 +00:00
|
|
|
WriterOptions (..),
|
2008-07-31 23:16:02 +00:00
|
|
|
defaultWriterOptions,
|
|
|
|
-- * File handling
|
2009-12-31 01:11:23 +00:00
|
|
|
inDirectory,
|
2010-11-19 22:13:30 -08:00
|
|
|
findDataFile,
|
2011-07-17 19:33:52 -07:00
|
|
|
readDataFile,
|
2007-11-03 23:27:58 +00:00
|
|
|
) where
|
|
|
|
|
|
|
|
import Text.Pandoc.Definition
|
2010-12-24 13:39:27 -08:00
|
|
|
import Text.Pandoc.Generic
|
2010-07-04 13:43:45 -07:00
|
|
|
import qualified Text.Pandoc.UTF8 as UTF8 (readFile)
|
2011-12-02 19:39:30 -08:00
|
|
|
import Data.Char ( toLower, isLower, isUpper, isAlpha,
|
|
|
|
isLetter, isDigit, isSpace )
|
2008-09-08 06:36:28 +00:00
|
|
|
import Data.List ( find, isPrefixOf, intercalate )
|
2011-12-02 19:39:30 -08:00
|
|
|
import Network.URI ( escapeURIString )
|
2008-07-31 23:16:02 +00:00
|
|
|
import System.Directory
|
2011-07-19 12:01:01 -07:00
|
|
|
import System.FilePath ( (</>) )
|
2010-12-24 13:39:27 -08:00
|
|
|
import Data.Generics (Typeable, Data)
|
2009-04-25 00:29:58 +00:00
|
|
|
import qualified Control.Monad.State as S
|
2009-12-31 01:11:23 +00:00
|
|
|
import Paths_pandoc (getDataFileName)
|
2011-12-27 23:46:23 -08:00
|
|
|
import Text.Pandoc.Highlighting (Style, pygments)
|
2010-03-22 19:29:37 -07:00
|
|
|
|
2007-11-03 23:27:58 +00:00
|
|
|
--
|
|
|
|
-- List processing
|
|
|
|
--
|
|
|
|
|
|
|
|
-- | Split list by groups of one or more sep.
|
2010-12-21 08:41:24 -08:00
|
|
|
splitBy :: (a -> Bool) -> [a] -> [[a]]
|
2007-11-03 23:27:58 +00:00
|
|
|
splitBy _ [] = []
|
2010-12-21 08:41:24 -08:00
|
|
|
splitBy isSep lst =
|
|
|
|
let (first, rest) = break isSep lst
|
|
|
|
rest' = dropWhile isSep rest
|
|
|
|
in first:(splitBy isSep rest')
|
2007-11-03 23:27:58 +00:00
|
|
|
|
|
|
|
-- | Split list into chunks divided at specified indices.
|
|
|
|
splitByIndices :: [Int] -> [a] -> [[a]]
|
|
|
|
splitByIndices [] lst = [lst]
|
|
|
|
splitByIndices (x:xs) lst =
|
|
|
|
let (first, rest) = splitAt x lst in
|
|
|
|
first:(splitByIndices (map (\y -> y - x) xs) rest)
|
|
|
|
|
|
|
|
-- | Replace each occurrence of one sublist in a list with another.
|
|
|
|
substitute :: (Eq a) => [a] -> [a] -> [a] -> [a]
|
|
|
|
substitute _ _ [] = []
|
2010-07-11 12:22:18 -07:00
|
|
|
substitute [] _ xs = xs
|
|
|
|
substitute target replacement lst@(x:xs) =
|
2007-11-03 23:27:58 +00:00
|
|
|
if target `isPrefixOf` lst
|
2010-07-11 12:22:18 -07:00
|
|
|
then replacement ++ substitute target replacement (drop (length target) lst)
|
|
|
|
else x : substitute target replacement xs
|
2007-11-03 23:27:58 +00:00
|
|
|
|
|
|
|
--
|
|
|
|
-- Text processing
|
|
|
|
--
|
|
|
|
|
|
|
|
-- | Returns an association list of backslash escapes for the
|
|
|
|
-- designated characters.
|
|
|
|
backslashEscapes :: [Char] -- ^ list of special characters to escape
|
|
|
|
-> [(Char, String)]
|
|
|
|
backslashEscapes = map (\ch -> (ch, ['\\',ch]))
|
|
|
|
|
|
|
|
-- | Escape a string of characters, using an association list of
|
|
|
|
-- characters and strings.
|
|
|
|
escapeStringUsing :: [(Char, String)] -> String -> String
|
|
|
|
escapeStringUsing _ [] = ""
|
|
|
|
escapeStringUsing escapeTable (x:xs) =
|
|
|
|
case (lookup x escapeTable) of
|
|
|
|
Just str -> str ++ rest
|
|
|
|
Nothing -> x:rest
|
|
|
|
where rest = escapeStringUsing escapeTable xs
|
|
|
|
|
|
|
|
-- | Strip trailing newlines from string.
|
|
|
|
stripTrailingNewlines :: String -> String
|
|
|
|
stripTrailingNewlines = reverse . dropWhile (== '\n') . reverse
|
|
|
|
|
|
|
|
-- | Remove leading and trailing space (including newlines) from string.
|
|
|
|
removeLeadingTrailingSpace :: String -> String
|
|
|
|
removeLeadingTrailingSpace = removeLeadingSpace . removeTrailingSpace
|
|
|
|
|
|
|
|
-- | Remove leading space (including newlines) from string.
|
|
|
|
removeLeadingSpace :: String -> String
|
|
|
|
removeLeadingSpace = dropWhile (`elem` " \n\t")
|
|
|
|
|
|
|
|
-- | Remove trailing space (including newlines) from string.
|
|
|
|
removeTrailingSpace :: String -> String
|
|
|
|
removeTrailingSpace = reverse . removeLeadingSpace . reverse
|
|
|
|
|
|
|
|
-- | Strip leading and trailing characters from string
|
|
|
|
stripFirstAndLast :: String -> String
|
|
|
|
stripFirstAndLast str =
|
|
|
|
drop 1 $ take ((length str) - 1) str
|
|
|
|
|
|
|
|
-- | Change CamelCase word to hyphenated lowercase (e.g., camel-case).
|
|
|
|
camelCaseToHyphenated :: String -> String
|
|
|
|
camelCaseToHyphenated [] = ""
|
|
|
|
camelCaseToHyphenated (a:b:rest) | isLower a && isUpper b =
|
|
|
|
a:'-':(toLower b):(camelCaseToHyphenated rest)
|
|
|
|
camelCaseToHyphenated (a:rest) = (toLower a):(camelCaseToHyphenated rest)
|
|
|
|
|
|
|
|
-- | Convert number < 4000 to uppercase roman numeral.
|
|
|
|
toRomanNumeral :: Int -> String
|
|
|
|
toRomanNumeral x =
|
|
|
|
if x >= 4000 || x < 0
|
|
|
|
then "?"
|
|
|
|
else case x of
|
|
|
|
_ | x >= 1000 -> "M" ++ toRomanNumeral (x - 1000)
|
|
|
|
_ | x >= 900 -> "CM" ++ toRomanNumeral (x - 900)
|
|
|
|
_ | x >= 500 -> "D" ++ toRomanNumeral (x - 500)
|
|
|
|
_ | x >= 400 -> "CD" ++ toRomanNumeral (x - 400)
|
|
|
|
_ | x >= 100 -> "C" ++ toRomanNumeral (x - 100)
|
|
|
|
_ | x >= 90 -> "XC" ++ toRomanNumeral (x - 90)
|
|
|
|
_ | x >= 50 -> "L" ++ toRomanNumeral (x - 50)
|
|
|
|
_ | x >= 40 -> "XL" ++ toRomanNumeral (x - 40)
|
|
|
|
_ | x >= 10 -> "X" ++ toRomanNumeral (x - 10)
|
|
|
|
_ | x >= 9 -> "IX" ++ toRomanNumeral (x - 5)
|
|
|
|
_ | x >= 5 -> "V" ++ toRomanNumeral (x - 5)
|
|
|
|
_ | x >= 4 -> "IV" ++ toRomanNumeral (x - 4)
|
|
|
|
_ | x >= 1 -> "I" ++ toRomanNumeral (x - 1)
|
|
|
|
_ -> ""
|
|
|
|
|
2011-12-02 19:39:30 -08:00
|
|
|
-- | Escape whitespace in URI.
|
2010-03-23 15:05:33 -07:00
|
|
|
escapeURI :: String -> String
|
2011-12-02 19:39:30 -08:00
|
|
|
escapeURI = escapeURIString (not . isSpace)
|
2010-03-23 15:34:53 -07:00
|
|
|
|
2010-07-06 23:17:06 -07:00
|
|
|
-- | Convert tabs to spaces and filter out DOS line endings.
|
|
|
|
-- Tabs will be preserved if tab stop is set to 0.
|
|
|
|
tabFilter :: Int -- ^ Tab stop
|
|
|
|
-> String -- ^ Input
|
|
|
|
-> String
|
|
|
|
tabFilter tabStop =
|
|
|
|
let go _ [] = ""
|
|
|
|
go _ ('\n':xs) = '\n' : go tabStop xs
|
|
|
|
go _ ('\r':'\n':xs) = '\n' : go tabStop xs
|
|
|
|
go _ ('\r':xs) = '\n' : go tabStop xs
|
|
|
|
go spsToNextStop ('\t':xs) =
|
|
|
|
if tabStop == 0
|
|
|
|
then '\t' : go tabStop xs
|
|
|
|
else replicate spsToNextStop ' ' ++ go tabStop xs
|
|
|
|
go 1 (x:xs) =
|
|
|
|
x : go tabStop xs
|
|
|
|
go spsToNextStop (x:xs) =
|
|
|
|
x : go (spsToNextStop - 1) xs
|
|
|
|
in go tabStop
|
|
|
|
|
2007-11-03 23:27:58 +00:00
|
|
|
--
|
|
|
|
-- Pandoc block and inline list processing
|
|
|
|
--
|
|
|
|
|
|
|
|
-- | Generate infinite lazy list of markers for an ordered list,
|
|
|
|
-- depending on list attributes.
|
|
|
|
orderedListMarkers :: (Int, ListNumberStyle, ListNumberDelim) -> [String]
|
|
|
|
orderedListMarkers (start, numstyle, numdelim) =
|
|
|
|
let singleton c = [c]
|
|
|
|
nums = case numstyle of
|
|
|
|
DefaultStyle -> map show [start..]
|
2010-07-11 22:47:52 -07:00
|
|
|
Example -> map show [start..]
|
2007-11-03 23:27:58 +00:00
|
|
|
Decimal -> map show [start..]
|
|
|
|
UpperAlpha -> drop (start - 1) $ cycle $
|
|
|
|
map singleton ['A'..'Z']
|
|
|
|
LowerAlpha -> drop (start - 1) $ cycle $
|
|
|
|
map singleton ['a'..'z']
|
|
|
|
UpperRoman -> map toRomanNumeral [start..]
|
|
|
|
LowerRoman -> map (map toLower . toRomanNumeral) [start..]
|
|
|
|
inDelim str = case numdelim of
|
|
|
|
DefaultDelim -> str ++ "."
|
|
|
|
Period -> str ++ "."
|
|
|
|
OneParen -> str ++ ")"
|
|
|
|
TwoParens -> "(" ++ str ++ ")"
|
|
|
|
in map inDelim nums
|
|
|
|
|
|
|
|
-- | Normalize a list of inline elements: remove leading and trailing
|
|
|
|
-- @Space@ elements, collapse double @Space@s into singles, and
|
|
|
|
-- remove empty Str elements.
|
|
|
|
normalizeSpaces :: [Inline] -> [Inline]
|
2010-12-07 20:10:21 -08:00
|
|
|
normalizeSpaces = cleanup . dropWhile isSpaceOrEmpty
|
2011-02-04 13:22:31 -08:00
|
|
|
where cleanup [] = []
|
2010-12-07 20:10:21 -08:00
|
|
|
cleanup (Space:rest) = let rest' = dropWhile isSpaceOrEmpty rest
|
|
|
|
in case rest' of
|
|
|
|
[] -> []
|
|
|
|
_ -> Space : cleanup rest'
|
|
|
|
cleanup ((Str ""):rest) = cleanup rest
|
|
|
|
cleanup (x:rest) = x : cleanup rest
|
2007-11-03 23:27:58 +00:00
|
|
|
|
2011-02-04 13:22:31 -08:00
|
|
|
isSpaceOrEmpty :: Inline -> Bool
|
|
|
|
isSpaceOrEmpty Space = True
|
|
|
|
isSpaceOrEmpty (Str "") = True
|
|
|
|
isSpaceOrEmpty _ = False
|
|
|
|
|
2010-12-14 20:04:37 -08:00
|
|
|
-- | Normalize @Pandoc@ document, consolidating doubled 'Space's,
|
|
|
|
-- combining adjacent 'Str's and 'Emph's, remove 'Null's and
|
|
|
|
-- empty elements, etc.
|
2011-01-29 10:03:31 -08:00
|
|
|
normalize :: (Eq a, Data a) => a -> a
|
|
|
|
normalize = topDown removeEmptyBlocks .
|
|
|
|
topDown consolidateInlines .
|
2011-02-04 13:22:31 -08:00
|
|
|
bottomUp (removeEmptyInlines . removeTrailingInlineSpaces)
|
2010-12-26 10:24:15 -08:00
|
|
|
|
|
|
|
removeEmptyBlocks :: [Block] -> [Block]
|
|
|
|
removeEmptyBlocks (Null : xs) = removeEmptyBlocks xs
|
|
|
|
removeEmptyBlocks (BulletList [] : xs) = removeEmptyBlocks xs
|
|
|
|
removeEmptyBlocks (OrderedList _ [] : xs) = removeEmptyBlocks xs
|
|
|
|
removeEmptyBlocks (DefinitionList [] : xs) = removeEmptyBlocks xs
|
2011-01-23 10:55:56 -08:00
|
|
|
removeEmptyBlocks (RawBlock _ [] : xs) = removeEmptyBlocks xs
|
2010-12-26 10:24:15 -08:00
|
|
|
removeEmptyBlocks (x:xs) = x : removeEmptyBlocks xs
|
|
|
|
removeEmptyBlocks [] = []
|
|
|
|
|
|
|
|
removeEmptyInlines :: [Inline] -> [Inline]
|
|
|
|
removeEmptyInlines (Emph [] : zs) = removeEmptyInlines zs
|
|
|
|
removeEmptyInlines (Strong [] : zs) = removeEmptyInlines zs
|
|
|
|
removeEmptyInlines (Subscript [] : zs) = removeEmptyInlines zs
|
|
|
|
removeEmptyInlines (Superscript [] : zs) = removeEmptyInlines zs
|
|
|
|
removeEmptyInlines (SmallCaps [] : zs) = removeEmptyInlines zs
|
|
|
|
removeEmptyInlines (Strikeout [] : zs) = removeEmptyInlines zs
|
2011-01-23 10:55:56 -08:00
|
|
|
removeEmptyInlines (RawInline _ [] : zs) = removeEmptyInlines zs
|
2011-01-26 20:44:25 -08:00
|
|
|
removeEmptyInlines (Code _ [] : zs) = removeEmptyInlines zs
|
2011-01-29 10:03:31 -08:00
|
|
|
removeEmptyInlines (Str "" : zs) = removeEmptyInlines zs
|
2010-12-26 10:24:15 -08:00
|
|
|
removeEmptyInlines (x : xs) = x : removeEmptyInlines xs
|
|
|
|
removeEmptyInlines [] = []
|
|
|
|
|
2011-02-04 13:22:31 -08:00
|
|
|
removeTrailingInlineSpaces :: [Inline] -> [Inline]
|
2011-02-04 18:32:54 -08:00
|
|
|
removeTrailingInlineSpaces = reverse . removeLeadingInlineSpaces . reverse
|
|
|
|
|
|
|
|
removeLeadingInlineSpaces :: [Inline] -> [Inline]
|
|
|
|
removeLeadingInlineSpaces = dropWhile isSpaceOrEmpty
|
2011-02-04 13:22:31 -08:00
|
|
|
|
2010-12-26 10:24:15 -08:00
|
|
|
consolidateInlines :: [Inline] -> [Inline]
|
|
|
|
consolidateInlines (Str x : ys) =
|
2010-12-14 20:04:37 -08:00
|
|
|
case concat (x : map fromStr strs) of
|
2010-12-26 10:24:15 -08:00
|
|
|
"" -> consolidateInlines rest
|
|
|
|
n -> Str n : consolidateInlines rest
|
2010-12-14 20:04:37 -08:00
|
|
|
where
|
|
|
|
(strs, rest) = span isStr ys
|
|
|
|
isStr (Str _) = True
|
|
|
|
isStr _ = False
|
|
|
|
fromStr (Str z) = z
|
2010-12-26 10:24:15 -08:00
|
|
|
fromStr _ = error "consolidateInlines - fromStr - not a Str"
|
2010-12-26 12:01:33 -08:00
|
|
|
consolidateInlines (Space : ys) = Space : rest
|
2011-12-02 19:39:30 -08:00
|
|
|
where isSp Space = True
|
|
|
|
isSp _ = False
|
|
|
|
rest = consolidateInlines $ dropWhile isSp ys
|
2010-12-26 10:24:15 -08:00
|
|
|
consolidateInlines (Emph xs : Emph ys : zs) = consolidateInlines $
|
2010-12-14 20:04:37 -08:00
|
|
|
Emph (xs ++ ys) : zs
|
2010-12-26 10:24:15 -08:00
|
|
|
consolidateInlines (Strong xs : Strong ys : zs) = consolidateInlines $
|
2010-12-14 20:04:37 -08:00
|
|
|
Strong (xs ++ ys) : zs
|
2010-12-26 10:24:15 -08:00
|
|
|
consolidateInlines (Subscript xs : Subscript ys : zs) = consolidateInlines $
|
2010-12-14 20:04:37 -08:00
|
|
|
Subscript (xs ++ ys) : zs
|
2010-12-26 10:24:15 -08:00
|
|
|
consolidateInlines (Superscript xs : Superscript ys : zs) = consolidateInlines $
|
2010-12-14 20:04:37 -08:00
|
|
|
Superscript (xs ++ ys) : zs
|
2010-12-26 10:24:15 -08:00
|
|
|
consolidateInlines (SmallCaps xs : SmallCaps ys : zs) = consolidateInlines $
|
2010-12-14 20:04:37 -08:00
|
|
|
SmallCaps (xs ++ ys) : zs
|
2010-12-26 10:24:15 -08:00
|
|
|
consolidateInlines (Strikeout xs : Strikeout ys : zs) = consolidateInlines $
|
2010-12-14 20:04:37 -08:00
|
|
|
Strikeout (xs ++ ys) : zs
|
2011-01-23 10:55:56 -08:00
|
|
|
consolidateInlines (RawInline f x : RawInline f' y : zs) | f == f' =
|
|
|
|
consolidateInlines $ RawInline f (x ++ y) : zs
|
2011-01-26 20:44:25 -08:00
|
|
|
consolidateInlines (Code a1 x : Code a2 y : zs) | a1 == a2 =
|
|
|
|
consolidateInlines $ Code a1 (x ++ y) : zs
|
2010-12-26 10:24:15 -08:00
|
|
|
consolidateInlines (x : xs) = x : consolidateInlines xs
|
|
|
|
consolidateInlines [] = []
|
2010-12-14 20:04:37 -08:00
|
|
|
|
2010-11-27 07:08:06 -08:00
|
|
|
-- | Convert list of inlines to a string with formatting removed.
|
|
|
|
stringify :: [Inline] -> String
|
|
|
|
stringify = queryWith go
|
|
|
|
where go :: Inline -> [Char]
|
|
|
|
go Space = " "
|
|
|
|
go (Str x) = x
|
2011-01-26 20:44:25 -08:00
|
|
|
go (Code _ x) = x
|
2010-12-12 20:09:14 -08:00
|
|
|
go (Math _ x) = x
|
2010-12-19 10:13:36 -08:00
|
|
|
go LineBreak = " "
|
2010-11-27 07:08:06 -08:00
|
|
|
go _ = ""
|
|
|
|
|
2009-11-01 02:38:18 +00:00
|
|
|
-- | Change final list item from @Para@ to @Plain@ if the list contains
|
|
|
|
-- no other @Para@ blocks.
|
2007-11-03 23:27:58 +00:00
|
|
|
compactify :: [[Block]] -- ^ List of list items (each a list of blocks)
|
|
|
|
-> [[Block]]
|
|
|
|
compactify [] = []
|
|
|
|
compactify items =
|
2009-11-01 02:38:18 +00:00
|
|
|
case (init items, last items) of
|
|
|
|
(_,[]) -> items
|
|
|
|
(others, final) ->
|
|
|
|
case last final of
|
|
|
|
Para a -> case (filter isPara $ concat items) of
|
|
|
|
-- if this is only Para, change to Plain
|
|
|
|
[_] -> others ++ [init final ++ [Plain a]]
|
|
|
|
_ -> items
|
|
|
|
_ -> items
|
|
|
|
|
|
|
|
isPara :: Block -> Bool
|
|
|
|
isPara (Para _) = True
|
|
|
|
isPara _ = False
|
2007-11-03 23:27:58 +00:00
|
|
|
|
|
|
|
-- | Data structure for defining hierarchical Pandoc documents
|
|
|
|
data Element = Blk Block
|
2009-12-08 02:36:16 +00:00
|
|
|
| Sec Int [Int] String [Inline] [Element]
|
|
|
|
-- lvl num ident label contents
|
2009-04-25 00:29:58 +00:00
|
|
|
deriving (Eq, Read, Show, Typeable, Data)
|
|
|
|
|
2010-03-28 22:29:31 -07:00
|
|
|
-- | Convert Pandoc inline list to plain text identifier. HTML
|
|
|
|
-- identifiers must start with a letter, and may contain only
|
2010-07-04 23:26:04 -07:00
|
|
|
-- letters, digits, and the characters _-.
|
2009-04-25 00:29:58 +00:00
|
|
|
inlineListToIdentifier :: [Inline] -> String
|
2010-03-28 22:29:31 -07:00
|
|
|
inlineListToIdentifier =
|
2010-12-19 10:13:36 -08:00
|
|
|
dropWhile (not . isAlpha) . intercalate "-" . words .
|
|
|
|
map (nbspToSp . toLower) .
|
|
|
|
filter (\c -> isLetter c || isDigit c || c `elem` "_-. ") .
|
|
|
|
stringify
|
|
|
|
where nbspToSp '\160' = ' '
|
|
|
|
nbspToSp x = x
|
2007-11-03 23:27:58 +00:00
|
|
|
|
|
|
|
-- | Convert list of Pandoc blocks into (hierarchical) list of Elements
|
|
|
|
hierarchicalize :: [Block] -> [Element]
|
2009-12-08 02:36:16 +00:00
|
|
|
hierarchicalize blocks = S.evalState (hierarchicalizeWithIds blocks) ([],[])
|
2009-04-25 00:29:58 +00:00
|
|
|
|
2009-12-08 02:36:16 +00:00
|
|
|
hierarchicalizeWithIds :: [Block] -> S.State ([Int],[String]) [Element]
|
2009-04-25 00:29:58 +00:00
|
|
|
hierarchicalizeWithIds [] = return []
|
|
|
|
hierarchicalizeWithIds ((Header level title'):xs) = do
|
2009-12-08 02:36:16 +00:00
|
|
|
(lastnum, usedIdents) <- S.get
|
2009-04-25 00:29:58 +00:00
|
|
|
let ident = uniqueIdent title' usedIdents
|
2009-12-08 02:36:16 +00:00
|
|
|
let lastnum' = take level lastnum
|
|
|
|
let newnum = if length lastnum' >= level
|
|
|
|
then init lastnum' ++ [last lastnum' + 1]
|
|
|
|
else lastnum ++ replicate (level - length lastnum - 1) 0 ++ [1]
|
|
|
|
S.put (newnum, (ident : usedIdents))
|
2009-04-25 00:29:58 +00:00
|
|
|
let (sectionContents, rest) = break (headerLtEq level) xs
|
|
|
|
sectionContents' <- hierarchicalizeWithIds sectionContents
|
|
|
|
rest' <- hierarchicalizeWithIds rest
|
2009-12-08 02:36:16 +00:00
|
|
|
return $ Sec level newnum ident title' sectionContents' : rest'
|
2009-04-25 00:29:58 +00:00
|
|
|
hierarchicalizeWithIds (x:rest) = do
|
|
|
|
rest' <- hierarchicalizeWithIds rest
|
|
|
|
return $ (Blk x) : rest'
|
|
|
|
|
|
|
|
headerLtEq :: Int -> Block -> Bool
|
|
|
|
headerLtEq level (Header l _) = l <= level
|
|
|
|
headerLtEq _ _ = False
|
|
|
|
|
2010-03-16 06:45:52 +00:00
|
|
|
-- | Generate a unique identifier from a list of inlines.
|
|
|
|
-- Second argument is a list of already used identifiers.
|
2009-04-25 00:29:58 +00:00
|
|
|
uniqueIdent :: [Inline] -> [String] -> String
|
|
|
|
uniqueIdent title' usedIdents =
|
2010-03-28 22:29:31 -07:00
|
|
|
let baseIdent = case inlineListToIdentifier title' of
|
|
|
|
"" -> "section"
|
|
|
|
x -> x
|
2009-04-25 00:29:58 +00:00
|
|
|
numIdent n = baseIdent ++ "-" ++ show n
|
|
|
|
in if baseIdent `elem` usedIdents
|
|
|
|
then case find (\x -> numIdent x `notElem` usedIdents) ([1..60000] :: [Int]) of
|
|
|
|
Just x -> numIdent x
|
|
|
|
Nothing -> baseIdent -- if we have more than 60,000, allow repeats
|
|
|
|
else baseIdent
|
2007-11-03 23:27:58 +00:00
|
|
|
|
|
|
|
-- | True if block is a Header block.
|
|
|
|
isHeaderBlock :: Block -> Bool
|
|
|
|
isHeaderBlock (Header _ _) = True
|
|
|
|
isHeaderBlock _ = False
|
|
|
|
|
2010-07-11 20:03:55 -07:00
|
|
|
-- | Shift header levels up or down.
|
|
|
|
headerShift :: Int -> Pandoc -> Pandoc
|
2010-12-24 13:39:27 -08:00
|
|
|
headerShift n = bottomUp shift
|
2010-07-11 20:03:55 -07:00
|
|
|
where shift :: Block -> Block
|
|
|
|
shift (Header level inner) = Header (level + n) inner
|
|
|
|
shift x = x
|
|
|
|
|
2007-11-03 23:27:58 +00:00
|
|
|
--
|
|
|
|
-- Writer options
|
|
|
|
--
|
|
|
|
|
2007-12-01 03:11:44 +00:00
|
|
|
data HTMLMathMethod = PlainMath
|
2008-08-13 03:02:42 +00:00
|
|
|
| LaTeXMathML (Maybe String) -- url of LaTeXMathML.js
|
2008-10-28 21:54:50 +00:00
|
|
|
| JsMath (Maybe String) -- url of jsMath load script
|
2007-12-01 03:11:44 +00:00
|
|
|
| GladTeX
|
2010-07-15 19:01:00 -07:00
|
|
|
| WebTeX String -- url of TeX->image script.
|
2010-03-18 06:45:56 +00:00
|
|
|
| MathML (Maybe String) -- url of MathMLinHTML.js
|
2010-10-26 21:06:51 -07:00
|
|
|
| MathJax String -- url of MathJax.js
|
2007-12-01 03:11:44 +00:00
|
|
|
deriving (Show, Read, Eq)
|
|
|
|
|
2010-12-13 21:18:01 +01:00
|
|
|
data CiteMethod = Citeproc -- use citeproc to render them
|
|
|
|
| Natbib -- output natbib cite commands
|
|
|
|
| Biblatex -- output biblatex cite commands
|
|
|
|
deriving (Show, Read, Eq)
|
|
|
|
|
2009-01-24 19:58:48 +00:00
|
|
|
-- | Methods for obfuscating email addresses in HTML.
|
|
|
|
data ObfuscationMethod = NoObfuscation
|
|
|
|
| ReferenceObfuscation
|
|
|
|
| JavascriptObfuscation
|
|
|
|
deriving (Show, Read, Eq)
|
|
|
|
|
2010-07-13 20:44:56 -07:00
|
|
|
-- | Varieties of HTML slide shows.
|
|
|
|
data HTMLSlideVariant = S5Slides
|
|
|
|
| SlidySlides
|
2011-10-01 22:57:03 -07:00
|
|
|
| DZSlides
|
2010-07-13 20:44:56 -07:00
|
|
|
| NoSlides
|
|
|
|
deriving (Show, Read, Eq)
|
|
|
|
|
2007-11-03 23:27:58 +00:00
|
|
|
-- | Options for writers
|
|
|
|
data WriterOptions = WriterOptions
|
2009-01-24 19:58:48 +00:00
|
|
|
{ writerStandalone :: Bool -- ^ Include header and footer
|
2009-12-31 01:09:28 +00:00
|
|
|
, writerTemplate :: String -- ^ Template to use in standalone mode
|
|
|
|
, writerVariables :: [(String, String)] -- ^ Variables to set in template
|
2010-07-04 16:56:17 -07:00
|
|
|
, writerEPUBMetadata :: String -- ^ Metadata to include in EPUB
|
2009-01-24 19:58:48 +00:00
|
|
|
, writerTabStop :: Int -- ^ Tabstop for conversion btw spaces and tabs
|
|
|
|
, writerTableOfContents :: Bool -- ^ Include table of contents
|
2010-07-13 20:44:56 -07:00
|
|
|
, writerSlideVariant :: HTMLSlideVariant -- ^ Are we writing S5 or Slidy?
|
|
|
|
, writerIncremental :: Bool -- ^ True if lists should be incremental
|
2009-12-31 01:18:06 +00:00
|
|
|
, writerXeTeX :: Bool -- ^ Create latex suitable for use by xetex
|
2009-01-24 19:58:48 +00:00
|
|
|
, writerHTMLMathMethod :: HTMLMathMethod -- ^ How to print math in HTML
|
|
|
|
, writerIgnoreNotes :: Bool -- ^ Ignore footnotes (used in making toc)
|
|
|
|
, writerNumberSections :: Bool -- ^ Number sections in LaTeX
|
2010-07-15 19:01:00 -07:00
|
|
|
, writerSectionDivs :: Bool -- ^ Put sections in div tags in HTML
|
2009-01-24 19:58:48 +00:00
|
|
|
, writerStrictMarkdown :: Bool -- ^ Use strict markdown syntax
|
|
|
|
, writerReferenceLinks :: Bool -- ^ Use reference links in writing markdown, rst
|
|
|
|
, writerWrapText :: Bool -- ^ Wrap text to line length
|
2010-12-12 20:09:14 -08:00
|
|
|
, writerColumns :: Int -- ^ Characters in a line (for text wrapping)
|
2009-01-24 19:58:48 +00:00
|
|
|
, writerLiterateHaskell :: Bool -- ^ Write as literate haskell
|
|
|
|
, writerEmailObfuscation :: ObfuscationMethod -- ^ How to obfuscate emails
|
2009-12-05 17:56:02 +00:00
|
|
|
, writerIdentifierPrefix :: String -- ^ Prefix for section & note ids in HTML
|
2010-07-08 17:14:03 -07:00
|
|
|
, writerSourceDirectory :: FilePath -- ^ Directory path of 1st source file
|
2010-07-08 17:31:55 -07:00
|
|
|
, writerUserDataDir :: Maybe FilePath -- ^ Path of user data directory
|
2010-12-13 21:18:01 +01:00
|
|
|
, writerCiteMethod :: CiteMethod -- ^ How to print cites
|
2010-12-15 11:40:53 +01:00
|
|
|
, writerBiblioFiles :: [FilePath] -- ^ Biblio files to use for citations
|
2011-01-11 20:37:06 -08:00
|
|
|
, writerHtml5 :: Bool -- ^ Produce HTML5
|
2011-12-29 13:24:05 -08:00
|
|
|
, writerBeamer :: Bool -- ^ Produce beamer LaTeX slide show
|
2012-01-25 17:50:03 -08:00
|
|
|
, writerSlideLevel :: Maybe Int -- ^ Force header level of slides
|
2011-01-16 08:57:32 -08:00
|
|
|
, writerChapters :: Bool -- ^ Use "chapter" for top-level sects
|
2011-01-17 23:54:51 +01:00
|
|
|
, writerListings :: Bool -- ^ Use listings package for code
|
2011-12-22 13:12:08 -08:00
|
|
|
, writerHighlight :: Bool -- ^ Highlight source code
|
2011-12-27 23:46:23 -08:00
|
|
|
, writerHighlightStyle :: Style -- ^ Style to use for highlighting
|
2012-01-26 23:55:37 -08:00
|
|
|
, writerSetextHeaders :: Bool -- ^ Use setext headers for levels 1-2 in markdown
|
2007-11-03 23:27:58 +00:00
|
|
|
} deriving Show
|
|
|
|
|
2011-07-22 13:11:46 -07:00
|
|
|
{-# DEPRECATED writerXeTeX "writerXeTeX no longer does anything" #-}
|
2007-11-03 23:27:58 +00:00
|
|
|
-- | Default writer options.
|
|
|
|
defaultWriterOptions :: WriterOptions
|
|
|
|
defaultWriterOptions =
|
2009-01-24 19:58:48 +00:00
|
|
|
WriterOptions { writerStandalone = False
|
2009-12-31 01:09:28 +00:00
|
|
|
, writerTemplate = ""
|
|
|
|
, writerVariables = []
|
2010-07-04 16:56:17 -07:00
|
|
|
, writerEPUBMetadata = ""
|
2009-01-24 19:58:48 +00:00
|
|
|
, writerTabStop = 4
|
|
|
|
, writerTableOfContents = False
|
2010-07-13 20:44:56 -07:00
|
|
|
, writerSlideVariant = NoSlides
|
|
|
|
, writerIncremental = False
|
2010-01-02 21:09:29 +00:00
|
|
|
, writerXeTeX = False
|
2009-01-24 19:58:48 +00:00
|
|
|
, writerHTMLMathMethod = PlainMath
|
|
|
|
, writerIgnoreNotes = False
|
|
|
|
, writerNumberSections = False
|
2011-02-05 11:21:10 -08:00
|
|
|
, writerSectionDivs = False
|
2009-01-24 19:58:48 +00:00
|
|
|
, writerStrictMarkdown = False
|
|
|
|
, writerReferenceLinks = False
|
|
|
|
, writerWrapText = True
|
2010-12-12 20:09:14 -08:00
|
|
|
, writerColumns = 72
|
2009-01-24 19:58:48 +00:00
|
|
|
, writerLiterateHaskell = False
|
|
|
|
, writerEmailObfuscation = JavascriptObfuscation
|
2009-12-05 17:56:02 +00:00
|
|
|
, writerIdentifierPrefix = ""
|
2010-07-08 17:14:03 -07:00
|
|
|
, writerSourceDirectory = "."
|
2010-07-08 17:31:55 -07:00
|
|
|
, writerUserDataDir = Nothing
|
2010-12-13 21:18:01 +01:00
|
|
|
, writerCiteMethod = Citeproc
|
2010-12-15 11:40:53 +01:00
|
|
|
, writerBiblioFiles = []
|
2011-01-11 20:37:06 -08:00
|
|
|
, writerHtml5 = False
|
2011-12-29 13:24:05 -08:00
|
|
|
, writerBeamer = False
|
2012-01-25 17:50:03 -08:00
|
|
|
, writerSlideLevel = Nothing
|
2011-01-16 08:57:32 -08:00
|
|
|
, writerChapters = False
|
2011-01-17 23:54:51 +01:00
|
|
|
, writerListings = False
|
2011-12-22 13:12:08 -08:00
|
|
|
, writerHighlight = False
|
2011-12-27 23:46:23 -08:00
|
|
|
, writerHighlightStyle = pygments
|
2012-01-26 23:55:37 -08:00
|
|
|
, writerSetextHeaders = True
|
2007-11-03 23:27:58 +00:00
|
|
|
}
|
2008-07-31 23:16:02 +00:00
|
|
|
|
2008-08-02 17:22:55 +00:00
|
|
|
--
|
|
|
|
-- File handling
|
|
|
|
--
|
|
|
|
|
2008-09-04 02:51:28 +00:00
|
|
|
-- | Perform an IO action in a directory, returning to starting directory.
|
|
|
|
inDirectory :: FilePath -> IO a -> IO a
|
|
|
|
inDirectory path action = do
|
|
|
|
oldDir <- getCurrentDirectory
|
|
|
|
setCurrentDirectory path
|
|
|
|
result <- action
|
|
|
|
setCurrentDirectory oldDir
|
|
|
|
return result
|
2009-12-31 01:11:23 +00:00
|
|
|
|
2010-11-19 22:13:30 -08:00
|
|
|
-- | Get file path for data file, either from specified user data directory,
|
|
|
|
-- or, if not found there, from Cabal data directory.
|
|
|
|
findDataFile :: Maybe FilePath -> FilePath -> IO FilePath
|
|
|
|
findDataFile Nothing f = getDataFileName f
|
|
|
|
findDataFile (Just u) f = do
|
|
|
|
ex <- doesFileExist (u </> f)
|
|
|
|
if ex
|
|
|
|
then return (u </> f)
|
|
|
|
else getDataFileName f
|
|
|
|
|
2010-01-14 05:54:38 +00:00
|
|
|
-- | Read file from specified user data directory or, if not found there, from
|
|
|
|
-- Cabal data directory.
|
2010-01-18 07:01:29 +00:00
|
|
|
readDataFile :: Maybe FilePath -> FilePath -> IO String
|
2010-11-19 22:13:30 -08:00
|
|
|
readDataFile userDir fname = findDataFile userDir fname >>= UTF8.readFile
|