1{-# LANGUAGE OverloadedStrings #-}
2{- |
3   Module      : Text.Pandoc.Readers.Org.ExportSettings
4   Copyright   : © 2016-2021 Albert Krewinkel
5   License     : GNU GPL, version 2 or above
6
7   Maintainer  : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de>
8
9Parsers for Org-mode export options.
10-}
11module Text.Pandoc.Readers.Org.ExportSettings
12  ( exportSettings
13  ) where
14
15import Text.Pandoc.Class.PandocMonad (PandocMonad, report)
16import Text.Pandoc.Logging (LogMessage (UnknownOrgExportOption))
17import Text.Pandoc.Readers.Org.ParserState
18import Text.Pandoc.Readers.Org.Parsing
19
20import Control.Monad (mzero, void)
21import Data.Char (toLower)
22import Data.Maybe (listToMaybe)
23import Data.Text (Text, unpack)
24
25-- | Read and handle space separated org-mode export settings.
26exportSettings :: PandocMonad m => OrgParser m ()
27exportSettings = void $ sepBy skipSpaces exportSetting
28
29-- | Setter function for export settings.
30type ExportSettingSetter a = a -> ExportSettings -> ExportSettings
31
32-- | Read and process a single org-mode export option.
33exportSetting :: PandocMonad m => OrgParser m ()
34exportSetting = choice
35  [ booleanSetting "^" (\val es -> es { exportSubSuperscripts = val })
36  , booleanSetting "'" (\val es -> es { exportSmartQuotes = val })
37  , booleanSetting "*" (\val es -> es { exportEmphasizedText = val })
38  , booleanSetting "-" (\val es -> es { exportSpecialStrings = val })
39  , ignoredSetting ":"
40  , ignoredSetting "<"
41  , booleanSetting "\\n" (\val es -> es { exportPreserveBreaks = val })
42  , archivedTreeSetting "arch" (\val es -> es { exportArchivedTrees = val })
43  , booleanSetting "author" (\val es -> es { exportWithAuthor = val })
44  , ignoredSetting "c"
45  -- org-mode allows the special value `comment` for creator, which we'll
46  -- interpret as true as it doesn't make sense in the context of Pandoc.
47  , booleanSetting "creator" (\val es -> es { exportWithCreator = val })
48  , complementableListSetting "d" (\val es -> es { exportDrawers = val })
49  , ignoredSetting "date"
50  , booleanSetting "e" (\val es -> es { exportWithEntities = val })
51  , booleanSetting "email" (\val es -> es { exportWithEmail = val })
52  , booleanSetting "f" (\val es -> es { exportWithFootnotes = val })
53  , integerSetting "H" (\val es -> es { exportHeadlineLevels = val })
54  , ignoredSetting "inline"
55  , ignoredSetting "num"
56  , booleanSetting "p" (\val es -> es { exportWithPlanning = val })
57  , ignoredSetting "pri"
58  , ignoredSetting "prop"
59  , ignoredSetting "stat"
60  , booleanSetting "tags" (\val es -> es { exportWithTags = val })
61  , ignoredSetting "tasks"
62  , texSetting     "tex" (\val es -> es { exportWithLatex = val })
63  , ignoredSetting "timestamp"
64  , ignoredSetting "title"
65  , ignoredSetting "toc"
66  , booleanSetting "todo" (\val es -> es { exportWithTodoKeywords = val })
67  , booleanSetting "|" (\val es -> es { exportWithTables = val })
68  , ignoreAndWarn
69  ] <?> "export setting"
70
71-- | Generic handler for export settings. Takes a parser which converts
72-- the plain option text into a data structure.
73genericExportSetting :: Monad m
74                     => OrgParser m a
75                     -> Text
76                     -> ExportSettingSetter a
77                     -> OrgParser m ()
78genericExportSetting optionParser settingIdentifier setter = try $ do
79  _     <- textStr settingIdentifier *> char ':'
80  value <- optionParser
81  updateState $ modifyExportSettings value
82 where
83   modifyExportSettings val st =
84     st { orgStateExportSettings = setter val . orgStateExportSettings $ st }
85
86-- | A boolean option, either nil (False) or non-nil (True).
87booleanSetting :: Monad m => Text ->  ExportSettingSetter Bool -> OrgParser m ()
88booleanSetting = genericExportSetting elispBoolean
89
90-- | An integer-valued option.
91integerSetting :: Monad m => Text -> ExportSettingSetter Int -> OrgParser m ()
92integerSetting = genericExportSetting parseInt
93 where
94   parseInt = try $
95     many1 digit >>= maybe mzero (return . fst) . listToMaybe . reads
96
97-- | Either the string "headline" or an elisp boolean and treated as an
98-- @ArchivedTreesOption@.
99archivedTreeSetting :: Monad m
100                    => Text
101                    -> ExportSettingSetter ArchivedTreesOption
102                    -> OrgParser m ()
103archivedTreeSetting =
104  genericExportSetting $ archivedTreesHeadlineSetting <|> archivedTreesBoolean
105 where
106   archivedTreesHeadlineSetting =
107     ArchivedTreesHeadlineOnly <$ optionString "headline"
108
109   archivedTreesBoolean = try $ do
110     exportBool <- elispBoolean
111     return $
112       if exportBool
113       then ArchivedTreesExport
114       else ArchivedTreesNoExport
115
116-- | A list or a complement list (i.e. a list starting with `not`).
117complementableListSetting :: Monad m
118                          => Text
119                          -> ExportSettingSetter (Either [Text] [Text])
120                          -> OrgParser m ()
121complementableListSetting = genericExportSetting $ choice
122  [ Left  <$> complementTextList
123  , Right <$> stringList
124  , (\b -> if b then Left [] else Right []) <$> elispBoolean
125  ]
126 where
127   -- Read a plain list of strings.
128   stringList :: Monad m => OrgParser m [Text]
129   stringList = try $
130     char '('
131       *> sepBy elispText spaces
132       <* char ')'
133
134   -- Read an emacs lisp list specifying a complement set.
135   complementTextList :: Monad m => OrgParser m [Text]
136   complementTextList = try $
137     string "(not "
138       *> sepBy elispText spaces
139       <* char ')'
140
141   elispText :: Monad m => OrgParser m Text
142   elispText = try $
143     char '"'
144       *> manyTillChar alphaNum (char '"')
145
146-- | Parses either @t@, @nil@, or @verbatim@ into a 'TeXExport' value.
147texSetting :: Monad m
148           => Text
149           -> ExportSettingSetter TeXExport
150           -> OrgParser m ()
151texSetting = genericExportSetting $ texVerbatim <|> texBoolean
152 where
153   texVerbatim = TeXVerbatim <$ optionString "verbatim"
154
155   texBoolean = try $ do
156     exportBool <- elispBoolean
157     return $
158       if exportBool
159       then TeXExport
160       else TeXIgnore
161
162-- | Read but ignore the export setting.
163ignoredSetting :: Monad m => Text -> OrgParser m ()
164ignoredSetting s = try (() <$ textStr s <* char ':' <* many1 nonspaceChar)
165
166-- | Read any setting string, but ignore it and emit a warning.
167ignoreAndWarn :: PandocMonad m => OrgParser m ()
168ignoreAndWarn = try $ do
169  opt <- many1Char nonspaceChar
170  report (UnknownOrgExportOption opt)
171  return ()
172
173-- | Read an elisp boolean.  Only NIL is treated as false, non-NIL values are
174-- interpreted as true.
175elispBoolean :: Monad m => OrgParser m Bool
176elispBoolean = try $ do
177  value <- many1 nonspaceChar
178  return $ case map toLower value of
179             "nil" -> False
180             "{}"  -> False
181             "()"  -> False
182             _     -> True
183
184-- | Try to parse a literal string as the option value. Returns the
185-- string on success.
186optionString :: Monad m => Text -> OrgParser m Text
187optionString s = try $ do
188  _ <- string (unpack s)
189  lookAhead (newline <|> spaceChar)
190  return s
191