git.iko.soy/typeable/pandoc
on master help sign in
Merge pull request #2962 from tarleb/org-more-export-settings

Org reader: support more export settings

commit2b2769975fea76337a5afd9e985a4de43c7276a0 authorJohn MacFarlane <jgm@berkeley.edu> dateFri 3 Jun 2016, 09:39 (10 years ago) parent9eb3dbaf567929
7 files changed+175−103
pandoc.cabal+1 src/Text/Pandoc/Readers/Org.hs+3−2 src/Text/Pandoc/Readers/Org/Blocks.hs+11−30 src/Text/Pandoc/Readers/Org/Inlines.hs+23−40 src/Text/Pandoc/Readers/Org/ParserState.hs+48−30 src/Text/Pandoc/Readers/Org/Parsing.hs+13−1 src/Text/Pandoc/Readers/Org/Shared.hs+76
pandoc.cabal+1
@@ -398,6 +398,7 @@ Library
398398 Text.Pandoc.Readers.Org.Inlines,
399399 Text.Pandoc.Readers.Org.ParserState,
400400 Text.Pandoc.Readers.Org.Parsing,
401+ Text.Pandoc.Readers.Org.Shared,
401402 Text.Pandoc.Writers.Shared,
402403 Text.Pandoc.Asciify,
403404 Text.Pandoc.MIME,
src/Text/Pandoc/Readers/Org.hs+3−2
@@ -29,7 +29,7 @@ module Text.Pandoc.Readers.Org ( readOrg ) where
2929
3030import Text.Pandoc.Readers.Org.Blocks ( blockList, meta )
3131import Text.Pandoc.Readers.Org.Parsing ( OrgParser, readWithM )
32−import Text.Pandoc.Readers.Org.ParserState ( OrgParserState (..) )
32+import Text.Pandoc.Readers.Org.ParserState ( optionsToParserState )
3333
3434import Text.Pandoc.Definition
3535import Text.Pandoc.Error
@@ -42,7 +42,8 @@ import Control.Monad.Reader ( runReader )
4242readOrg :: ReaderOptions -- ^ Reader options
4343 -> String -- ^ String to parse (assuming @'\n'@ line endings)
4444 -> Either PandocError Pandoc
45−readOrg opts s = flip runReader def $ readWithM parseOrg def{ orgStateOptions = opts } (s ++ "\n\n")
45+readOrg opts s = flip runReader def $
46+ readWithM parseOrg (optionsToParserState opts) (s ++ "\n\n")
4647
4748--
4849-- Parser
src/Text/Pandoc/Readers/Org/Blocks.hs+11−30
@@ -35,6 +35,9 @@ import Text.Pandoc.Readers.Org.BlockStarts
3535import Text.Pandoc.Readers.Org.Inlines
3636import Text.Pandoc.Readers.Org.ParserState
3737import Text.Pandoc.Readers.Org.Parsing
38+import Text.Pandoc.Readers.Org.Shared
39+ ( isImageFilename, rundocBlockClass, toRundocAttrib
40+ , translateLang )
3841
3942import qualified Text.Pandoc.Builder as B
4043import Text.Pandoc.Builder ( Inlines, Blocks )
@@ -43,7 +46,6 @@ import Text.Pandoc.Compat.Monoid ((<>))
4346import Text.Pandoc.Options
4447import Text.Pandoc.Shared ( compactify', compactify'DL )
4548
46−import Control.Arrow ( first )
4749import Control.Monad ( foldM, guard, mzero )
4850import Data.Char ( isSpace, toLower, toUpper)
4951import Data.List ( foldl', intersperse, isPrefixOf )
@@ -67,7 +69,7 @@ blockList = do
6769meta :: OrgParser Meta
6870meta = do
6971 st <- getState
70− return $ runF (orgStateMeta' st) st
72+ return $ runF (orgStateMeta st) st
7173
7274blocks :: OrgParser (F Blocks)
7375blocks = mconcat <$> manyTill block eof
@@ -314,7 +316,6 @@ codeHeaderArgs = try $ do
314316 else ([ pandocLang ], parameters)
315317 where
316318 hasRundocParameters = not . null
317− toRundocAttrib = first ("rundoc-" ++)
318319
319320switch :: OrgParser (Char, Maybe String)
320321switch = try $ simpleSwitch <|> lineNumbersSwitch
@@ -323,25 +324,6 @@ switch = try $ simpleSwitch <|> lineNumbersSwitch
323324 lineNumbersSwitch = (\ls -> ('l', Just ls)) <$>
324325 (string "-l \"" *> many1Till nonspaceChar (char '"'))
325326
326−translateLang :: String -> String
327−translateLang "C" = "c"
328−translateLang "C++" = "cpp"
329−translateLang "emacs-lisp" = "commonlisp" -- emacs lisp is not supported
330−translateLang "js" = "javascript"
331−translateLang "lisp" = "commonlisp"
332−translateLang "R" = "r"
333−translateLang "sh" = "bash"
334−translateLang "sqlite" = "sql"
335−translateLang cs = cs
336−
337−-- | Prefix used for Rundoc classes and arguments.
338−rundocPrefix :: String
339−rundocPrefix = "rundoc-"
340−
341−-- | The class-name used to mark rundoc blocks.
342−rundocBlockClass :: String
343−rundocBlockClass = rundocPrefix ++ "block"
344−
345327blockOption :: OrgParser (String, String)
346328blockOption = try $ do
347329 argKey <- orgArgKey
@@ -480,12 +462,11 @@ commentLine = commentLineStart *> anyLine *> pure mempty
480462
481463declarationLine :: OrgParser ()
482464declarationLine = try $ do
483− key <- metaKey
484− inlinesF <- metaInlines
465+ key <- metaKey
466+ value <- metaInlines
485467 updateState $ \st ->
486− let meta' = B.setMeta <$> pure key <*> inlinesF <*> pure nullMeta
487− in st { orgStateMeta' = orgStateMeta' st <> meta' }
488− return ()
468+ let meta' = B.setMeta key <$> value <*> pure nullMeta
469+ in st { orgStateMeta = orgStateMeta st <> meta' }
489470
490471metaInlines :: OrgParser (F MetaValue)
491472metaInlines = fmap (MetaInlines . B.toList) <$> inlinesTillNewline
@@ -519,9 +500,9 @@ addLinkFormat key formatter = updateState $ \s ->
519500exportSetting :: OrgParser ()
520501exportSetting = choice
521502 [ booleanSetting "^" setExportSubSuperscripts
522− , ignoredSetting "'"
523− , ignoredSetting "*"
524− , ignoredSetting "-"
503+ , booleanSetting "'" setExportSmartQuotes
504+ , booleanSetting "*" setExportEmphasizedText
505+ , booleanSetting "-" setExportSpecialStrings
525506 , ignoredSetting ":"
526507 , ignoredSetting "<"
527508 , ignoredSetting "\\n"
src/Text/Pandoc/Readers/Org/Inlines.hs+23−40
@@ -30,13 +30,15 @@ module Text.Pandoc.Readers.Org.Inlines
3030 ( inline
3131 , inlines
3232 , addToNotesTable
33− , isImageFilename
3433 , linkTarget
3534 ) where
3635
3736import Text.Pandoc.Readers.Org.BlockStarts
3837import Text.Pandoc.Readers.Org.ParserState
3938import Text.Pandoc.Readers.Org.Parsing
39+import Text.Pandoc.Readers.Org.Shared
40+ ( isImageFilename, rundocBlockClass, toRundocAttrib
41+ , translateLang )
4042
4143import qualified Text.Pandoc.Builder as B
4244import Text.Pandoc.Builder ( Inlines )
@@ -47,35 +49,12 @@ import Text.Pandoc.Readers.LaTeX ( inlineCommand, rawLaTeXInline )
4749import Text.TeXMath ( readTeX, writePandoc, DisplayType(..) )
4850import qualified Text.TeXMath.Readers.MathML.EntityMap as MathMLEntityMap
4951
50−import Control.Arrow ( first )
5152import Control.Monad ( guard, mplus, mzero, when )
5253import Data.Char ( isAlphaNum, isSpace )
53−import Data.List ( isPrefixOf, isSuffixOf )
54+import Data.List ( isPrefixOf )
5455import Data.Maybe ( fromMaybe )
5556import qualified Data.Map as M
5657
57−-- | Prefix used for Rundoc classes and arguments.
58−rundocPrefix :: String
59−rundocPrefix = "rundoc-"
60−
61−-- | The class-name used to mark rundoc blocks.
62−rundocBlockClass :: String
63−rundocBlockClass = rundocPrefix ++ "block"
64−
65−toRundocAttrib :: (String, String) -> (String, String)
66−toRundocAttrib = first ("rundoc-" ++)
67−
68−translateLang :: String -> String
69−translateLang "C" = "c"
70−translateLang "C++" = "cpp"
71−translateLang "emacs-lisp" = "commonlisp" -- emacs lisp is not supported
72−translateLang "js" = "javascript"
73−translateLang "lisp" = "commonlisp"
74−translateLang "R" = "r"
75−translateLang "sh" = "bash"
76−translateLang "sqlite" = "sql"
77−translateLang cs = cs
78−
7958--
8059-- Functions acting on the parser state
8160--
@@ -129,10 +108,7 @@ inline =
129108 , inlineCodeBlock
130109 , str
131110 , endline
132− , emph
133− , strong
134− , strikeout
135− , underline
111+ , emphasizedText
136112 , code
137113 , math
138114 , displayMath
@@ -405,15 +381,6 @@ cleanLinkString s =
405381 in all (\c -> isAlphaNum c || c `elem` (".-"::String)) scheme
406382 && not (null path)
407383
408−isImageFilename :: String -> Bool
409−isImageFilename filename =
410− any (\x -> ('.':x) `isSuffixOf` filename) imageExtensions &&
411− (any (\x -> (x++":") `isPrefixOf` filename) protocols ||
412− ':' `notElem` filename)
413− where
414− imageExtensions = [ "jpeg" , "jpg" , "png" , "gif" , "svg" ]
415− protocols = [ "file", "http", "https" ]
416−
417384internalLink :: String -> Inlines -> F Inlines
418385internalLink link title = do
419386 anchorB <- (link `elem`) <$> asksF orgStateAnchorIds
@@ -473,6 +440,16 @@ inlineCodeBlock = try $ do
473440 <* skipSpaces
474441
475442
443+emphasizedText :: OrgParser (F Inlines)
444+emphasizedText = do
445+ state <- getState
446+ guard . exportEmphasizedText . orgStateExportSettings $ state
447+ try $ choice
448+ [ emph
449+ , strong
450+ , strikeout
451+ , underline
452+ ]
476453
477454enclosedByPair :: Char -- ^ opening char
478455 -> Char -- ^ closing char
@@ -751,8 +728,12 @@ smart = do
751728 doubleQuoted <|> singleQuoted <|>
752729 choice (map (return <$>) [orgApostrophe, orgDash, orgEllipses])
753730 where
754− orgDash = dash <* updatePositions '-'
755− orgEllipses = ellipses <* updatePositions '.'
731+ orgDash = do
732+ guard =<< getExportSetting exportSpecialStrings
733+ dash <* updatePositions '-'
734+ orgEllipses = do
735+ guard =<< getExportSetting exportSpecialStrings
736+ ellipses <* updatePositions '.'
756737 orgApostrophe =
757738 (char '\'' <|> char '\8217') <* updateLastPreCharPos
758739 <* updateLastForbiddenCharPos
@@ -760,6 +741,7 @@ smart = do
760741
761742singleQuoted :: OrgParser (F Inlines)
762743singleQuoted = try $ do
744+ guard =<< getExportSetting exportSmartQuotes
763745 singleQuoteStart
764746 updatePositions '\''
765747 withQuoteContext InSingleQuote $
@@ -771,6 +753,7 @@ singleQuoted = try $ do
771753-- in the same paragraph.
772754doubleQuoted :: OrgParser (F Inlines)
773755doubleQuoted = try $ do
756+ guard =<< getExportSetting exportSmartQuotes
774757 doubleQuoteStart
775758 updatePositions '"'
776759 contents <- mconcat <$> many (try $ notFollowedBy doubleQuoteEnd >> inline)
src/Text/Pandoc/Readers/Org/ParserState.hs+48−30
@@ -42,9 +42,13 @@ module Text.Pandoc.Readers.Org.ParserState
4242 , returnF
4343 , ExportSettingSetter
4444 , ExportSettings (..)
45− , setExportSubSuperscripts
4645 , setExportDrawers
46+ , setExportEmphasizedText
47+ , setExportSmartQuotes
48+ , setExportSpecialStrings
49+ , setExportSubSuperscripts
4750 , modifyExportSettings
51+ , optionsToParserState
4852 ) where
4953
5054import Control.Monad (liftM, liftM2)
@@ -54,8 +58,7 @@ import Data.Default (Default(..))
5458import qualified Data.Map as M
5559import qualified Data.Set as Set
5660
57−import Text.Pandoc.Builder ( Inlines, Blocks, HasMeta(..),
58− trimInlines )
61+import Text.Pandoc.Builder ( Inlines, Blocks, trimInlines )
5962import Text.Pandoc.Definition ( Meta(..), nullMeta )
6063import Text.Pandoc.Options ( ReaderOptions(..) )
6164import Text.Pandoc.Parsing ( HasHeaderMap(..)
@@ -78,30 +81,32 @@ type OrgLinkFormatters = M.Map String (String -> String)
7881-- | Export settings <http://orgmode.org/manual/Export-settings.html>
7982-- These settings can be changed via OPTIONS statements.
8083data ExportSettings = ExportSettings
81− { exportSubSuperscripts :: Bool -- ^ TeX-like syntax for sub- and superscripts
82− , exportDrawers :: Either [String] [String]
84+ { exportDrawers :: Either [String] [String]
8385 -- ^ Specify drawer names which should be exported. @Left@ names are
8486 -- explicitly excluded from the resulting output while @Right@ means that
8587 -- only the listed drawer names should be included.
88+ , exportEmphasizedText :: Bool -- ^ Parse emphasized text
89+ , exportSmartQuotes :: Bool -- ^ Parse quotes smartly
90+ , exportSpecialStrings :: Bool -- ^ Parse ellipses and dashes smartly
91+ , exportSubSuperscripts :: Bool -- ^ TeX-like syntax for sub- and superscripts
8692 }
8793
8894-- | Org-mode parser state
8995data OrgParserState = OrgParserState
90− { orgStateOptions :: ReaderOptions
91− , orgStateAnchorIds :: [String]
96+ { orgStateAnchorIds :: [String]
9297 , orgStateEmphasisCharStack :: [Char]
9398 , orgStateEmphasisNewlines :: Maybe Int
9499 , orgStateExportSettings :: ExportSettings
100+ , orgStateHeaderMap :: M.Map Inlines String
101+ , orgStateIdentifiers :: Set.Set String
95102 , orgStateLastForbiddenCharPos :: Maybe SourcePos
96103 , orgStateLastPreCharPos :: Maybe SourcePos
97104 , orgStateLastStrPos :: Maybe SourcePos
98105 , orgStateLinkFormatters :: OrgLinkFormatters
99− , orgStateMeta :: Meta
100− , orgStateMeta' :: F Meta
106+ , orgStateMeta :: F Meta
101107 , orgStateNotes' :: OrgNoteTable
108+ , orgStateOptions :: ReaderOptions
102109 , orgStateParserContext :: ParserContext
103− , orgStateIdentifiers :: Set.Set String
104− , orgStateHeaderMap :: M.Map Inlines String
105110 }
106111
107112data OrgParserLocal = OrgParserLocal { orgLocalQuoteContext :: QuoteContext }
@@ -112,12 +117,6 @@ instance Default OrgParserLocal where
112117instance HasReaderOptions OrgParserState where
113118 extractReaderOptions = orgStateOptions
114119
115−instance HasMeta OrgParserState where
116− setMeta field val st =
117− st{ orgStateMeta = setMeta field val $ orgStateMeta st }
118− deleteMeta field st =
119− st{ orgStateMeta = deleteMeta field $ orgStateMeta st }
120−
121120instance HasLastStrPosition OrgParserState where
122121 getLastStrPos = orgStateLastStrPos
123122 setLastStrPos pos st = st{ orgStateLastStrPos = Just pos }
@@ -142,45 +141,64 @@ instance Default OrgParserState where
142141
143142defaultOrgParserState :: OrgParserState
144143defaultOrgParserState = OrgParserState
145− { orgStateOptions = def
146− , orgStateAnchorIds = []
144+ { orgStateAnchorIds = []
147145 , orgStateEmphasisCharStack = []
148146 , orgStateEmphasisNewlines = Nothing
149147 , orgStateExportSettings = def
148+ , orgStateHeaderMap = M.empty
149+ , orgStateIdentifiers = Set.empty
150150 , orgStateLastForbiddenCharPos = Nothing
151151 , orgStateLastPreCharPos = Nothing
152152 , orgStateLastStrPos = Nothing
153153 , orgStateLinkFormatters = M.empty
154− , orgStateMeta = nullMeta
155− , orgStateMeta' = return nullMeta
154+ , orgStateMeta = return nullMeta
156155 , orgStateNotes' = []
156+ , orgStateOptions = def
157157 , orgStateParserContext = NullState
158− , orgStateIdentifiers = Set.empty
159− , orgStateHeaderMap = M.empty
160158 }
161159
162160defaultExportSettings :: ExportSettings
163161defaultExportSettings = ExportSettings
164− { exportSubSuperscripts = True
165− , exportDrawers = Left ["LOGBOOK"]
162+ { exportDrawers = Left ["LOGBOOK"]
163+ , exportEmphasizedText = True
164+ , exportSmartQuotes = True
165+ , exportSpecialStrings = True
166+ , exportSubSuperscripts = True
166167 }
167168
169+optionsToParserState :: ReaderOptions -> OrgParserState
170+optionsToParserState opts =
171+ def { orgStateOptions = opts }
172+
168173
169174--
170175-- Setter for exporting options
171176--
172177type ExportSettingSetter a = a -> ExportSettings -> ExportSettings
173178
174−-- | Set export options for sub/superscript parsing. The short syntax will
175−-- not be parsed if this is set set to @False@.
176−setExportSubSuperscripts :: ExportSettingSetter Bool
177−setExportSubSuperscripts val es = es { exportSubSuperscripts = val }
178−
179179-- | Set export options for drawers. See the @exportDrawers@ in ADT
180180-- @ExportSettings@ for details.
181181setExportDrawers :: ExportSettingSetter (Either [String] [String])
182182setExportDrawers val es = es { exportDrawers = val }
183183
184+-- | Set export options for emphasis parsing.
185+setExportEmphasizedText :: ExportSettingSetter Bool
186+setExportEmphasizedText val es = es { exportEmphasizedText = val }
187+
188+-- | Set export options for parsing of smart quotes.
189+setExportSmartQuotes :: ExportSettingSetter Bool
190+setExportSmartQuotes val es = es { exportSmartQuotes = val }
191+
192+-- | Set export options for parsing of special strings (like em/en dashes or
193+-- ellipses).
194+setExportSpecialStrings :: ExportSettingSetter Bool
195+setExportSpecialStrings val es = es { exportSpecialStrings = val }
196+
197+-- | Set export options for sub/superscript parsing. The short syntax will
198+-- not be parsed if this is set set to @False@.
199+setExportSubSuperscripts :: ExportSettingSetter Bool
200+setExportSubSuperscripts val es = es { exportSubSuperscripts = val }
201+
184202-- | Modify a parser state
185203modifyExportSettings :: ExportSettingSetter a -> a -> OrgParserState -> OrgParserState
186204modifyExportSettings setter val state =
src/Text/Pandoc/Readers/Org/Parsing.hs+13−1
@@ -37,6 +37,7 @@ module Text.Pandoc.Readers.Org.Parsing
3737 , skipSpaces1
3838 , inList
3939 , withContext
40+ , getExportSetting
4041 , updateLastForbiddenCharPos
4142 , updateLastPreCharPos
4243 , orgArgKey
@@ -174,9 +175,13 @@ withContext context parser = do
174175 return result
175176
176177--
177−-- Parser state update functions
178+-- Parser state functions
178179--
179180
181+-- | Get an export setting.
182+getExportSetting :: (ExportSettings -> a) -> OrgParser a
183+getExportSetting s = s . orgStateExportSettings <$> getState
184+
180185-- | Set the current position as the last position at which a forbidden char
181186-- was found (i.e. a character which is not allowed at the inner border of
182187-- markup).
@@ -190,13 +195,20 @@ updateLastPreCharPos :: OrgParser ()
190195updateLastPreCharPos = getPosition >>= \p ->
191196 updateState $ \s -> s{ orgStateLastPreCharPos = Just p}
192197
198+--
199+-- Org key-value parsing
200+--
201+
202+-- | Read the key of a plist style key-value list.
193203orgArgKey :: OrgParser String
194204orgArgKey = try $
195205 skipSpaces *> char ':'
196206 *> many1 orgArgWordChar
197207
208+-- | Read the value of a plist style key-value list.
198209orgArgWord :: OrgParser String
199210orgArgWord = many1 orgArgWordChar
200211
212+-- | Chars treated as part of a word in plists.
201213orgArgWordChar :: OrgParser Char
202214orgArgWordChar = alphaNum <|> oneOf "-_"
src/Text/Pandoc/Readers/Org/Shared.hsnew file+76
@@ -0,0 +1,76 @@
1+{-# LANGUAGE OverloadedStrings #-}
2+{-
3+Copyright (C) 2014-2016 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de>
4+
5+This program is free software; you can redistribute it and/or modify
6+it under the terms of the GNU General Public License as published by
7+the Free Software Foundation; either version 2 of the License, or
8+(at your option) any later version.
9+
10+This program is distributed in the hope that it will be useful,
11+but WITHOUT ANY WARRANTY; without even the implied warranty of
12+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+GNU General Public License for more details.
14+
15+You should have received a copy of the GNU General Public License
16+along with this program; if not, write to the Free Software
17+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18+-}
19+
20+{- |
21+ Module : Text.Pandoc.Readers.Org.Options
22+ Copyright : Copyright (C) 2014-2016 Albert Krewinkel
23+ License : GNU GPL, version 2 or above
24+
25+ Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de>
26+
27+Utility functions used in other Pandoc Org modules.
28+-}
29+module Text.Pandoc.Readers.Org.Shared
30+ ( isImageFilename
31+ , rundocBlockClass
32+ , toRundocAttrib
33+ , translateLang
34+ ) where
35+
36+import Control.Arrow ( first )
37+import Data.List ( isPrefixOf, isSuffixOf )
38+
39+
40+-- | Check whether the given string looks like the path to of URL of an image.
41+isImageFilename :: String -> Bool
42+isImageFilename filename =
43+ any (\x -> ('.':x) `isSuffixOf` filename) imageExtensions &&
44+ (any (\x -> (x++":") `isPrefixOf` filename) protocols ||
45+ ':' `notElem` filename)
46+ where
47+ imageExtensions = [ "jpeg" , "jpg" , "png" , "gif" , "svg" ]
48+ protocols = [ "file", "http", "https" ]
49+
50+-- | Prefix used for Rundoc classes and arguments.
51+rundocPrefix :: String
52+rundocPrefix = "rundoc-"
53+
54+-- | The class-name used to mark rundoc blocks.
55+rundocBlockClass :: String
56+rundocBlockClass = rundocPrefix ++ "block"
57+
58+-- | Prefix the name of a attribute, marking it as a code execution parameter.
59+toRundocAttrib :: (String, String) -> (String, String)
60+toRundocAttrib = first (rundocPrefix ++)
61+
62+-- | Translate from Org-mode's programming language identifiers to those used
63+-- by Pandoc. This is useful to allow for proper syntax highlighting in
64+-- Pandoc output.
65+translateLang :: String -> String
66+translateLang cs =
67+ case cs of
68+ "C" -> "c"
69+ "C++" -> "cpp"
70+ "emacs-lisp" -> "commonlisp" -- emacs lisp is not supported
71+ "js" -> "javascript"
72+ "lisp" -> "commonlisp"
73+ "R" -> "r"
74+ "sh" -> "bash"
75+ "sqlite" -> "sql"
76+ _ -> cs