git.iko.soy/typeable/pandoc
on master help sign in
Merge pull request #2912 from tarleb/org-export-settings
3800cb3John MacFarlane
pandoc.cabal+1
@@ -393,6 +393,7 @@ Library
393 Text.Pandoc.Readers.Odt.Generic.XMLConverter,
393 Text.Pandoc.Readers.Odt.Generic.XMLConverter,
394 Text.Pandoc.Readers.Odt.Arrows.State,
394 Text.Pandoc.Readers.Odt.Arrows.State,
395 Text.Pandoc.Readers.Odt.Arrows.Utils,
395 Text.Pandoc.Readers.Odt.Arrows.Utils,
396+ Text.Pandoc.Readers.Org.ParserState,
396 Text.Pandoc.Writers.Shared,
397 Text.Pandoc.Writers.Shared,
397 Text.Pandoc.Asciify,
398 Text.Pandoc.Asciify,
398 Text.Pandoc.MIME,
399 Text.Pandoc.MIME,
src/Text/Pandoc/Readers/Org.hs+113−159
@@ -1,6 +1,5 @@
1+{-# LANGUAGE FlexibleContexts #-}
1{-# LANGUAGE OverloadedStrings #-}
2{-# LANGUAGE OverloadedStrings #-}
2−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
3−{-# LANGUAGE MultiParamTypeClasses, FlexibleContexts, FlexibleInstances #-}
4{-
3{-
5Copyright (C) 2014-2016 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de>
4Copyright (C) 2014-2016 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de>
6
5
@@ -31,8 +30,7 @@ Conversion of org-mode formatted plain text to 'Pandoc' document.
31module Text.Pandoc.Readers.Org ( readOrg ) where
30module Text.Pandoc.Readers.Org ( readOrg ) where
32
31
33import qualified Text.Pandoc.Builder as B
32import qualified Text.Pandoc.Builder as B
34−import Text.Pandoc.Builder ( Inlines, Blocks, HasMeta(..),
33+import Text.Pandoc.Builder ( Inlines, Blocks )
35− trimInlines )
36import Text.Pandoc.Definition
34import Text.Pandoc.Definition
37import Text.Pandoc.Compat.Monoid ((<>))
35import Text.Pandoc.Compat.Monoid ((<>))
38import Text.Pandoc.Error
36import Text.Pandoc.Error
@@ -43,153 +41,33 @@ import Text.Pandoc.Parsing hiding ( F, unF, askF, asksF, runF
43 , parseFromString, blanklines
41 , parseFromString, blanklines
44 )
42 )
45import Text.Pandoc.Readers.LaTeX (inlineCommand, rawLaTeXInline)
43import Text.Pandoc.Readers.LaTeX (inlineCommand, rawLaTeXInline)
44+import Text.Pandoc.Readers.Org.ParserState
46import Text.Pandoc.Shared (compactify', compactify'DL)
45import Text.Pandoc.Shared (compactify', compactify'DL)
47import Text.TeXMath (readTeX, writePandoc, DisplayType(..))
46import Text.TeXMath (readTeX, writePandoc, DisplayType(..))
48import qualified Text.TeXMath.Readers.MathML.EntityMap as MathMLEntityMap
47import qualified Text.TeXMath.Readers.MathML.EntityMap as MathMLEntityMap
49
48
50import Control.Arrow (first)
49import Control.Arrow (first)
51−import Control.Monad (foldM, guard, liftM, liftM2, mplus, mzero, when)
50+import Control.Monad (foldM, guard, mplus, mzero, when)
52−import Control.Monad.Reader (Reader, runReader, ask, asks, local)
51+import Control.Monad.Reader ( Reader, runReader )
53import Data.Char (isAlphaNum, isSpace, toLower)
52import Data.Char (isAlphaNum, isSpace, toLower)
54−import Data.Default
55import Data.List (intersperse, isPrefixOf, isSuffixOf)
53import Data.List (intersperse, isPrefixOf, isSuffixOf)
56import qualified Data.Map as M
54import qualified Data.Map as M
57−import qualified Data.Set as Set
58import Data.Maybe (fromMaybe, isJust)
55import Data.Maybe (fromMaybe, isJust)
59import Network.HTTP (urlEncode)
56import Network.HTTP (urlEncode)
60
57
58+
61-- | Parse org-mode string and return a Pandoc document.
59-- | Parse org-mode string and return a Pandoc document.
62readOrg :: ReaderOptions -- ^ Reader options
60readOrg :: ReaderOptions -- ^ Reader options
63 -> String -- ^ String to parse (assuming @'\n'@ line endings)
61 -> String -- ^ String to parse (assuming @'\n'@ line endings)
64 -> Either PandocError Pandoc
62 -> Either PandocError Pandoc
65readOrg opts s = flip runReader def $ readWithM parseOrg def{ orgStateOptions = opts } (s ++ "\n\n")
63readOrg opts s = flip runReader def $ readWithM parseOrg def{ orgStateOptions = opts } (s ++ "\n\n")
66
64
67−data OrgParserLocal = OrgParserLocal { orgLocalQuoteContext :: QuoteContext }
65+-- | The parser used to read org files.
68−
69type OrgParser = ParserT [Char] OrgParserState (Reader OrgParserLocal)
66type OrgParser = ParserT [Char] OrgParserState (Reader OrgParserLocal)
70
67
71−instance HasIdentifierList OrgParserState where
72− extractIdentifierList = orgStateIdentifiers
73− updateIdentifierList f s = s{ orgStateIdentifiers = f (orgStateIdentifiers s) }
74−
75−instance HasHeaderMap OrgParserState where
76− extractHeaderMap = orgStateHeaderMap
77− updateHeaderMap f s = s{ orgStateHeaderMap = f (orgStateHeaderMap s) }
78−
79−parseOrg :: OrgParser Pandoc
80−parseOrg = do
81− blocks' <- parseBlocks
82− st <- getState
83− let meta = runF (orgStateMeta' st) st
84− let removeUnwantedBlocks = dropCommentTrees . filter (/= Null)
85− return $ Pandoc meta $ removeUnwantedBlocks (B.toList $ runF blocks' st)
86−
87−-- | Drop COMMENT headers and the document tree below those headers.
88−dropCommentTrees :: [Block] -> [Block]
89−dropCommentTrees [] = []
90−dropCommentTrees (b:bs) =
91− maybe (b:dropCommentTrees bs)
92− (dropCommentTrees . flip dropUntilHeaderAboveLevel bs)
93− (commentHeaderLevel b)
94−
95−-- | Return the level of a header starting a comment or :noexport: tree and
96−-- Nothing otherwise.
97−commentHeaderLevel :: Block -> Maybe Int
98−commentHeaderLevel blk =
99− case blk of
100− (Header level _ ((Str "COMMENT"):_)) -> Just level
101− (Header level _ title) | hasNoExportTag title -> Just level
102− _ -> Nothing
103− where
104− hasNoExportTag :: [Inline] -> Bool
105− hasNoExportTag = any isNoExportTag
106−
107− isNoExportTag :: Inline -> Bool
108− isNoExportTag (Span ("", ["tag"], [("data-tag-name", "noexport")]) []) = True
109− isNoExportTag _ = False
110−
111−-- | Drop blocks until a header on or above the given level is seen
112−dropUntilHeaderAboveLevel :: Int -> [Block] -> [Block]
113−dropUntilHeaderAboveLevel n = dropWhile (not . isHeaderLevelLowerEq n)
114−
115−isHeaderLevelLowerEq :: Int -> Block -> Bool
116−isHeaderLevelLowerEq n blk =
117− case blk of
118− (Header level _ _) -> n >= level
119− _ -> False
120−
121--
68--
122−-- Parser State for Org
69+-- Functions acting on the parser state
123--
70--
124−
125−type OrgNoteRecord = (String, F Blocks)
126−type OrgNoteTable = [OrgNoteRecord]
127−
128−type OrgBlockAttributes = M.Map String String
129−
130−type OrgLinkFormatters = M.Map String (String -> String)
131−
132−-- | Org-mode parser state
133−data OrgParserState = OrgParserState
134− { orgStateOptions :: ReaderOptions
135− , orgStateAnchorIds :: [String]
136− , orgStateBlockAttributes :: OrgBlockAttributes
137− , orgStateEmphasisCharStack :: [Char]
138− , orgStateEmphasisNewlines :: Maybe Int
139− , orgStateLastForbiddenCharPos :: Maybe SourcePos
140− , orgStateLastPreCharPos :: Maybe SourcePos
141− , orgStateLastStrPos :: Maybe SourcePos
142− , orgStateLinkFormatters :: OrgLinkFormatters
143− , orgStateMeta :: Meta
144− , orgStateMeta' :: F Meta
145− , orgStateNotes' :: OrgNoteTable
146− , orgStateParserContext :: ParserContext
147− , orgStateIdentifiers :: Set.Set String
148− , orgStateHeaderMap :: M.Map Inlines String
149− }
150−
151−instance Default OrgParserLocal where
152− def = OrgParserLocal NoQuote
153−
154−instance HasReaderOptions OrgParserState where
155− extractReaderOptions = orgStateOptions
156−
157−instance HasMeta OrgParserState where
158− setMeta field val st =
159− st{ orgStateMeta = setMeta field val $ orgStateMeta st }
160− deleteMeta field st =
161− st{ orgStateMeta = deleteMeta field $ orgStateMeta st }
162−
163−instance HasLastStrPosition OrgParserState where
164− getLastStrPos = orgStateLastStrPos
165− setLastStrPos pos st = st{ orgStateLastStrPos = Just pos }
166−
167−instance HasQuoteContext st (Reader OrgParserLocal) where
168− getQuoteContext = asks orgLocalQuoteContext
169− withQuoteContext q = local (\s -> s{orgLocalQuoteContext = q})
170−
171−instance Default OrgParserState where
172− def = defaultOrgParserState
173−
174−defaultOrgParserState :: OrgParserState
175−defaultOrgParserState = OrgParserState
176− { orgStateOptions = def
177− , orgStateAnchorIds = []
178− , orgStateBlockAttributes = M.empty
179− , orgStateEmphasisCharStack = []
180− , orgStateEmphasisNewlines = Nothing
181− , orgStateLastForbiddenCharPos = Nothing
182− , orgStateLastPreCharPos = Nothing
183− , orgStateLastStrPos = Nothing
184− , orgStateLinkFormatters = M.empty
185− , orgStateMeta = nullMeta
186− , orgStateMeta' = return nullMeta
187− , orgStateNotes' = []
188− , orgStateParserContext = NullState
189− , orgStateIdentifiers = Set.empty
190− , orgStateHeaderMap = M.empty
191− }
192−
193recordAnchorId :: String -> OrgParser ()
71recordAnchorId :: String -> OrgParser ()
194recordAnchorId i = updateState $ \s ->
72recordAnchorId i = updateState $ \s ->
195 s{ orgStateAnchorIds = i : (orgStateAnchorIds s) }
73 s{ orgStateAnchorIds = i : (orgStateAnchorIds s) }
@@ -243,44 +121,117 @@ addToNotesTable note = do
243 oldnotes <- orgStateNotes' <$> getState
121 oldnotes <- orgStateNotes' <$> getState
244 updateState $ \s -> s{ orgStateNotes' = note:oldnotes }
122 updateState $ \s -> s{ orgStateNotes' = note:oldnotes }
245
123
246−-- The version Text.Pandoc.Parsing cannot be used, as we need additional parts
124+--
247−-- of the state saved and restored.
125+-- Export Settings
248−parseFromString :: OrgParser a -> String -> OrgParser a
126+--
249−parseFromString parser str' = do
127+exportSetting :: OrgParser ()
250− oldLastPreCharPos <- orgStateLastPreCharPos <$> getState
128+exportSetting = choice
251− updateState $ \s -> s{ orgStateLastPreCharPos = Nothing }
129+ [ booleanSetting "^" setExportSubSuperscripts
252− result <- P.parseFromString parser str'
130+ , ignoredSetting "'"
253− updateState $ \s -> s{ orgStateLastPreCharPos = oldLastPreCharPos }
131+ , ignoredSetting "*"
254− return result
132+ , ignoredSetting "-"
133+ , ignoredSetting ":"
134+ , ignoredSetting "<"
135+ , ignoredSetting "\\n"
136+ , ignoredSetting "arch"
137+ , ignoredSetting "author"
138+ , ignoredSetting "c"
139+ , ignoredSetting "creator"
140+ , ignoredSetting "d"
141+ , ignoredSetting "date"
142+ , ignoredSetting "e"
143+ , ignoredSetting "email"
144+ , ignoredSetting "f"
145+ , ignoredSetting "H"
146+ , ignoredSetting "inline"
147+ , ignoredSetting "num"
148+ , ignoredSetting "p"
149+ , ignoredSetting "pri"
150+ , ignoredSetting "prop"
151+ , ignoredSetting "stat"
152+ , ignoredSetting "tags"
153+ , ignoredSetting "tasks"
154+ , ignoredSetting "tex"
155+ , ignoredSetting "timestamp"
156+ , ignoredSetting "title"
157+ , ignoredSetting "toc"
158+ , ignoredSetting "todo"
159+ , ignoredSetting "|"
160+ ] <?> "export setting"
161+
162+booleanSetting :: String -> ExportSettingSetter Bool -> OrgParser ()
163+booleanSetting settingIdentifier setter = try $ do
164+ string settingIdentifier
165+ char ':'
166+ value <- many nonspaceChar
167+ let boolValue = case value of
168+ "nil" -> False
169+ "{}" -> False
170+ _ -> True
171+ updateState $ modifyExportSettings setter boolValue
255
172
173+ignoredSetting :: String -> OrgParser ()
174+ignoredSetting s = try (() <$ string s <* char ':' <* many nonspaceChar)
256
175
257--
176--
258−-- Adaptions and specializations of parsing utilities
177+-- Parser
259--
178--
179+parseOrg :: OrgParser Pandoc
180+parseOrg = do
181+ blocks' <- parseBlocks
182+ st <- getState
183+ let meta = runF (orgStateMeta' st) st
184+ let removeUnwantedBlocks = dropCommentTrees . filter (/= Null)
185+ return $ Pandoc meta $ removeUnwantedBlocks (B.toList $ runF blocks' st)
260
186
261−newtype F a = F { unF :: Reader OrgParserState a
187+-- | Drop COMMENT headers and the document tree below those headers.
262− } deriving (Monad, Applicative, Functor)
188+dropCommentTrees :: [Block] -> [Block]
189+dropCommentTrees [] = []
190+dropCommentTrees (b:bs) =
191+ maybe (b:dropCommentTrees bs)
192+ (dropCommentTrees . flip dropUntilHeaderAboveLevel bs)
193+ (commentHeaderLevel b)
263
194
264−runF :: F a -> OrgParserState -> a
195+-- | Return the level of a header starting a comment or :noexport: tree and
265−runF = runReader . unF
196+-- Nothing otherwise.
197+commentHeaderLevel :: Block -> Maybe Int
198+commentHeaderLevel blk =
199+ case blk of
200+ (Header level _ ((Str "COMMENT"):_)) -> Just level
201+ (Header level _ title) | hasNoExportTag title -> Just level
202+ _ -> Nothing
203+ where
204+ hasNoExportTag :: [Inline] -> Bool
205+ hasNoExportTag = any isNoExportTag
266
206
267−askF :: F OrgParserState
207+ isNoExportTag :: Inline -> Bool
268−askF = F ask
208+ isNoExportTag (Span ("", ["tag"], [("data-tag-name", "noexport")]) []) = True
209+ isNoExportTag _ = False
269
210
270−asksF :: (OrgParserState -> a) -> F a
211+-- | Drop blocks until a header on or above the given level is seen
271−asksF f = F $ asks f
212+dropUntilHeaderAboveLevel :: Int -> [Block] -> [Block]
213+dropUntilHeaderAboveLevel n = dropWhile (not . isHeaderLevelLowerEq n)
272
214
273−instance Monoid a => Monoid (F a) where
215+isHeaderLevelLowerEq :: Int -> Block -> Bool
274− mempty = return mempty
216+isHeaderLevelLowerEq n blk =
275− mappend = liftM2 mappend
217+ case blk of
276− mconcat = fmap mconcat . sequence
218+ (Header level _ _) -> n >= level
219+ _ -> False
277
220
278−trimInlinesF :: F Inlines -> F Inlines
279−trimInlinesF = liftM trimInlines
280
221
281−returnF :: a -> OrgParser (F a)
222+--
282−returnF = return . return
223+-- Adaptions and specializations of parsing utilities
224+--
283
225
226+-- The version Text.Pandoc.Parsing cannot be used, as we need additional parts
227+-- of the state saved and restored.
228+parseFromString :: OrgParser a -> String -> OrgParser a
229+parseFromString parser str' = do
230+ oldLastPreCharPos <- orgStateLastPreCharPos <$> getState
231+ updateState $ \s -> s{ orgStateLastPreCharPos = Nothing }
232+ result <- P.parseFromString parser str'
233+ updateState $ \s -> s{ orgStateLastPreCharPos = oldLastPreCharPos }
234+ return result
284
235
285-- | Like @Text.Parsec.Char.newline@, but causes additional state changes.
236-- | Like @Text.Parsec.Char.newline@, but causes additional state changes.
286newline :: OrgParser Char
237newline :: OrgParser Char
@@ -691,8 +642,9 @@ optionLine :: OrgParser ()
691optionLine = try $ do
642optionLine = try $ do
692 key <- metaKey
643 key <- metaKey
693 case key of
644 case key of
694− "link" -> parseLinkFormat >>= uncurry addLinkFormat
645+ "link" -> parseLinkFormat >>= uncurry addLinkFormat
646+ "options" -> () <$ sepBy spaces exportSetting
695− _ -> mzero
647+ _ -> mzero
696
648
697parseLinkFormat :: OrgParser ((String, String -> String))
649parseLinkFormat :: OrgParser ((String, String -> String))
698parseLinkFormat = try $ do
650parseLinkFormat = try $ do
@@ -1561,7 +1513,9 @@ subOrSuperExpr = try $
1561 where enclosing (left, right) s = left : s ++ [right]
1513 where enclosing (left, right) s = left : s ++ [right]
1562
1514
1563simpleSubOrSuperString :: OrgParser String
1515simpleSubOrSuperString :: OrgParser String
1564−simpleSubOrSuperString = try $
1516+simpleSubOrSuperString = try $ do
1517+ state <- getState
1518+ guard . exportSubSuperscripts . orgStateExportSettings $ state
1565 choice [ string "*"
1519 choice [ string "*"
1566 , mappend <$> option [] ((:[]) <$> oneOf "+-")
1520 , mappend <$> option [] ((:[]) <$> oneOf "+-")
1567 <*> many1 alphaNum
1521 <*> many1 alphaNum
src/Text/Pandoc/Readers/Org/ParserState.hsnew file+207
@@ -0,0 +1,207 @@
1+{-# LANGUAGE FlexibleInstances #-}
2+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
3+{-# LANGUAGE MultiParamTypeClasses #-}
4+{-
5+Copyright (C) 2014-2016 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de>
6+
7+This program is free software; you can redistribute it and/or modify
8+it under the terms of the GNU General Public License as published by
9+the Free Software Foundation; either version 2 of the License, or
10+(at your option) any later version.
11+
12+This program is distributed in the hope that it will be useful,
13+but WITHOUT ANY WARRANTY; without even the implied warranty of
14+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+GNU General Public License for more details.
16+
17+You should have received a copy of the GNU General Public License
18+along with this program; if not, write to the Free Software
19+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20+-}
21+
22+{- |
23+ Module : Text.Pandoc.Readers.Org.Options
24+ Copyright : Copyright (C) 2014-2016 Albert Krewinkel
25+ License : GNU GPL, version 2 or above
26+
27+ Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de>
28+
29+Define the Org-mode parser state.
30+-}
31+module Text.Pandoc.Readers.Org.ParserState
32+ ( OrgParserState(..)
33+ , OrgParserLocal(..)
34+ , OrgNoteRecord
35+ , F(..)
36+ , askF
37+ , asksF
38+ , trimInlinesF
39+ , runF
40+ , returnF
41+ , ExportSettingSetter
42+ , exportSubSuperscripts
43+ , setExportSubSuperscripts
44+ , modifyExportSettings
45+ ) where
46+
47+import Control.Monad (liftM, liftM2)
48+import Control.Monad.Reader (Reader, runReader, ask, asks, local)
49+
50+import Data.Default (Default(..))
51+import qualified Data.Map as M
52+import qualified Data.Set as Set
53+
54+import Text.Pandoc.Builder ( Inlines, Blocks, HasMeta(..),
55+ trimInlines )
56+import Text.Pandoc.Definition ( Meta(..), nullMeta )
57+import Text.Pandoc.Options ( ReaderOptions(..) )
58+import Text.Pandoc.Parsing ( HasHeaderMap(..)
59+ , HasIdentifierList(..)
60+ , HasLastStrPosition(..)
61+ , HasQuoteContext(..)
62+ , HasReaderOptions(..)
63+ , ParserContext(..)
64+ , QuoteContext(..)
65+ , SourcePos )
66+
67+-- | An inline note / footnote containing the note key and its (inline) value.
68+type OrgNoteRecord = (String, F Blocks)
69+-- | Table of footnotes
70+type OrgNoteTable = [OrgNoteRecord]
71+-- | Map of org block attributes (e.g. LABEL, CAPTION, NAME, etc)
72+type OrgBlockAttributes = M.Map String String
73+-- | Map of functions for link transformations. The map key is refers to the
74+-- link-type, the corresponding function transforms the given link string.
75+type OrgLinkFormatters = M.Map String (String -> String)
76+
77+-- | Export settings <http://orgmode.org/manual/Export-settings.html>
78+-- These settings can be changed via OPTIONS statements.
79+data ExportSettings = ExportSettings
80+ { exportSubSuperscripts :: Bool -- ^ TeX-like syntax for sub- and superscripts
81+ }
82+
83+-- | Org-mode parser state
84+data OrgParserState = OrgParserState
85+ { orgStateOptions :: ReaderOptions
86+ , orgStateAnchorIds :: [String]
87+ , orgStateBlockAttributes :: OrgBlockAttributes
88+ , orgStateEmphasisCharStack :: [Char]
89+ , orgStateEmphasisNewlines :: Maybe Int
90+ , orgStateExportSettings :: ExportSettings
91+ , orgStateLastForbiddenCharPos :: Maybe SourcePos
92+ , orgStateLastPreCharPos :: Maybe SourcePos
93+ , orgStateLastStrPos :: Maybe SourcePos
94+ , orgStateLinkFormatters :: OrgLinkFormatters
95+ , orgStateMeta :: Meta
96+ , orgStateMeta' :: F Meta
97+ , orgStateNotes' :: OrgNoteTable
98+ , orgStateParserContext :: ParserContext
99+ , orgStateIdentifiers :: Set.Set String
100+ , orgStateHeaderMap :: M.Map Inlines String
101+ }
102+
103+data OrgParserLocal = OrgParserLocal { orgLocalQuoteContext :: QuoteContext }
104+
105+instance Default OrgParserLocal where
106+ def = OrgParserLocal NoQuote
107+
108+instance HasReaderOptions OrgParserState where
109+ extractReaderOptions = orgStateOptions
110+
111+instance HasMeta OrgParserState where
112+ setMeta field val st =
113+ st{ orgStateMeta = setMeta field val $ orgStateMeta st }
114+ deleteMeta field st =
115+ st{ orgStateMeta = deleteMeta field $ orgStateMeta st }
116+
117+instance HasLastStrPosition OrgParserState where
118+ getLastStrPos = orgStateLastStrPos
119+ setLastStrPos pos st = st{ orgStateLastStrPos = Just pos }
120+
121+instance HasQuoteContext st (Reader OrgParserLocal) where
122+ getQuoteContext = asks orgLocalQuoteContext
123+ withQuoteContext q = local (\s -> s{orgLocalQuoteContext = q})
124+
125+instance HasIdentifierList OrgParserState where
126+ extractIdentifierList = orgStateIdentifiers
127+ updateIdentifierList f s = s{ orgStateIdentifiers = f (orgStateIdentifiers s) }
128+
129+instance HasHeaderMap OrgParserState where
130+ extractHeaderMap = orgStateHeaderMap
131+ updateHeaderMap f s = s{ orgStateHeaderMap = f (orgStateHeaderMap s) }
132+
133+instance Default ExportSettings where
134+ def = defaultExportSettings
135+
136+instance Default OrgParserState where
137+ def = defaultOrgParserState
138+
139+defaultOrgParserState :: OrgParserState
140+defaultOrgParserState = OrgParserState
141+ { orgStateOptions = def
142+ , orgStateAnchorIds = []
143+ , orgStateBlockAttributes = M.empty
144+ , orgStateEmphasisCharStack = []
145+ , orgStateEmphasisNewlines = Nothing
146+ , orgStateExportSettings = def
147+ , orgStateLastForbiddenCharPos = Nothing
148+ , orgStateLastPreCharPos = Nothing
149+ , orgStateLastStrPos = Nothing
150+ , orgStateLinkFormatters = M.empty
151+ , orgStateMeta = nullMeta
152+ , orgStateMeta' = return nullMeta
153+ , orgStateNotes' = []
154+ , orgStateParserContext = NullState
155+ , orgStateIdentifiers = Set.empty
156+ , orgStateHeaderMap = M.empty
157+ }
158+
159+defaultExportSettings :: ExportSettings
160+defaultExportSettings = ExportSettings
161+ { exportSubSuperscripts = True
162+ }
163+
164+
165+--
166+-- Setter for exporting options
167+--
168+type ExportSettingSetter a = a -> ExportSettings -> ExportSettings
169+
170+setExportSubSuperscripts :: ExportSettingSetter Bool
171+setExportSubSuperscripts val es = es { exportSubSuperscripts = val }
172+
173+-- | Modify a parser state
174+modifyExportSettings :: ExportSettingSetter a -> a -> OrgParserState -> OrgParserState
175+modifyExportSettings setter val state =
176+ state { orgStateExportSettings = setter val . orgStateExportSettings $ state }
177+
178+--
179+-- Parser state reader
180+--
181+
182+-- | Reader monad wrapping the parser state. This is used to delay evaluation
183+-- until all relevant information has been parsed and made available in the
184+-- parser state. See also the newtype of the same name in
185+-- Text.Pandoc.Parsing.
186+newtype F a = F { unF :: Reader OrgParserState a
187+ } deriving (Functor, Applicative, Monad)
188+
189+instance Monoid a => Monoid (F a) where
190+ mempty = return mempty
191+ mappend = liftM2 mappend
192+ mconcat = fmap mconcat . sequence
193+
194+runF :: F a -> OrgParserState -> a
195+runF = runReader . unF
196+
197+askF :: F OrgParserState
198+askF = F ask
199+
200+asksF :: (OrgParserState -> a) -> F a
201+asksF f = F $ asks f
202+
203+trimInlinesF :: F Inlines -> F Inlines
204+trimInlinesF = liftM trimInlines
205+
206+returnF :: Monad m => a -> m (F a)
207+returnF = return . return
tests/Tests/Readers/Org.hs+6
@@ -469,6 +469,12 @@ tests =
469 , "[[expl:foo][bar]]"
469 , "[[expl:foo][bar]]"
470 ] =?>
470 ] =?>
471 (para (link "http://example.com/foo" "" "bar"))
471 (para (link "http://example.com/foo" "" "bar"))
472+
473+ , "Export option: Disable simple sub/superscript syntax" =:
474+ unlines [ "#+OPTIONS: ^:nil"
475+ , "a^b"
476+ ] =?>
477+ para "a^b"
472 ]
478 ]
473
479
474 , testGroup "Basic Blocks" $
480 , testGroup "Basic Blocks" $