git.iko.soy/typeable/pandoc
on master help sign in
Merge pull request #2941 from tarleb/org-drawer-improvements

Org drawer improvements

commit654bdf72bfe608cea1b606028a4bf9570bb63b8f authorJohn MacFarlane <jgm@berkeley.edu> dateMon 23 May 2016, 10:39 (10 years ago) parente3ca9795667e09
5 files changed+152−50
src/Text/Pandoc/Readers/Org.hs+69−14 src/Text/Pandoc/Readers/Org/ParserState.hs+14−1 src/Text/Pandoc/Writers/Org.hs+13−2 tests/Tests/Readers/Org.hs+25−2 tests/writer.org+31−31
src/Text/Pandoc/Readers/Org.hs+69−14
@@ -138,7 +138,7 @@ exportSetting = choice
138138 , ignoredSetting "author"
139139 , ignoredSetting "c"
140140 , ignoredSetting "creator"
141− , ignoredSetting "d"
141+ , complementableListSetting "d" setExportDrawers
142142 , ignoredSetting "date"
143143 , ignoredSetting "e"
144144 , ignoredSetting "email"
@@ -164,15 +164,53 @@ booleanSetting :: String -> ExportSettingSetter Bool -> OrgParser ()
164164booleanSetting settingIdentifier setter = try $ do
165165 string settingIdentifier
166166 char ':'
167− value <- many nonspaceChar
168− let boolValue = case value of
169− "nil" -> False
170− "{}" -> False
171− _ -> True
172− updateState $ modifyExportSettings setter boolValue
167+ value <- elispBoolean
168+ updateState $ modifyExportSettings setter value
169+
170+-- | Read an elisp boolean. Only NIL is treated as false, non-NIL values are
171+-- interpreted as true.
172+elispBoolean :: OrgParser Bool
173+elispBoolean = try $ do
174+ value <- many1 nonspaceChar
175+ return $ case map toLower value of
176+ "nil" -> False
177+ "{}" -> False
178+ "()" -> False
179+ _ -> True
180+
181+-- | A list or a complement list (i.e. a list starting with `not`).
182+complementableListSetting :: String
183+ -> ExportSettingSetter (Either [String] [String])
184+ -> OrgParser ()
185+complementableListSetting settingIdentifier setter = try $ do
186+ _ <- string settingIdentifier <* char ':'
187+ value <- choice [ Left <$> complementStringList
188+ , Right <$> stringList
189+ , (\b -> if b then Left [] else Right []) <$> elispBoolean
190+ ]
191+ updateState $ modifyExportSettings setter value
192+ where
193+ -- Read a plain list of strings.
194+ stringList :: OrgParser [String]
195+ stringList = try $
196+ char '('
197+ *> sepBy elispString spaces
198+ <* char ')'
199+
200+ -- Read an emacs lisp list specifying a complement set.
201+ complementStringList :: OrgParser [String]
202+ complementStringList = try $
203+ string "(not "
204+ *> sepBy elispString spaces
205+ <* char ')'
206+
207+ elispString :: OrgParser String
208+ elispString = try $
209+ char '"'
210+ *> manyTill alphaNum (char '"')
173211
174212ignoredSetting :: String -> OrgParser ()
175−ignoredSetting s = try (() <$ string s <* char ':' <* many nonspaceChar)
213+ignoredSetting s = try (() <$ string s <* char ':' <* many1 nonspaceChar)
176214
177215--
178216-- Parser
@@ -588,11 +626,27 @@ exampleLine = try $ skipSpaces *> string ": " *> anyLine
588626--
589627
590628-- | A generic drawer which has no special meaning for org-mode.
629+-- Whether or not this drawer is included in the output depends on the drawers
630+-- export setting.
591631genericDrawer :: OrgParser (F Blocks)
592632genericDrawer = try $ do
593− drawerStart
594− manyTill drawerLine (try drawerEnd)
595− return mempty
633+ name <- map toUpper <$> drawerStart
634+ content <- manyTill drawerLine (try drawerEnd)
635+ state <- getState
636+ -- Include drawer if it is explicitly included in or not explicitly excluded
637+ -- from the list of drawers that should be exported. PROPERTIES drawers are
638+ -- never exported.
639+ case (exportDrawers . orgStateExportSettings $ state) of
640+ _ | name == "PROPERTIES" -> return mempty
641+ Left names | name `elem` names -> return mempty
642+ Right names | name `notElem` names -> return mempty
643+ _ -> drawerDiv name <$> parseLines content
644+ where
645+ parseLines :: [String] -> OrgParser (F Blocks)
646+ parseLines = parseFromString parseBlocks . (++ "\n") . unlines
647+
648+ drawerDiv :: String -> F Blocks -> F Blocks
649+ drawerDiv drawerName = fmap $ B.divWith (mempty, [drawerName, "drawer"], mempty)
596650
597651drawerStart :: OrgParser String
598652drawerStart = try $
@@ -626,9 +680,10 @@ propertiesDrawer = try $ do
626680keyValuesToAttr :: [(String, String)] -> Attr
627681keyValuesToAttr kvs =
628682 let
629− id' = fromMaybe mempty . lookup "id" $ kvs
630− cls = fromMaybe mempty . lookup "class" $ kvs
631− kvs' = filter (flip notElem ["id", "class"] . fst) kvs
683+ lowerKvs = map (\(k, v) -> (map toLower k, v)) kvs
684+ id' = fromMaybe mempty . lookup "custom_id" $ lowerKvs
685+ cls = fromMaybe mempty . lookup "class" $ lowerKvs
686+ kvs' = filter (flip notElem ["custom_id", "class"] . fst) lowerKvs
632687 in
633688 (id', words cls, kvs')
634689
src/Text/Pandoc/Readers/Org/ParserState.hs+14−1
@@ -39,8 +39,9 @@ module Text.Pandoc.Readers.Org.ParserState
3939 , runF
4040 , returnF
4141 , ExportSettingSetter
42− , exportSubSuperscripts
42+ , ExportSettings (..)
4343 , setExportSubSuperscripts
44+ , setExportDrawers
4445 , modifyExportSettings
4546 ) where
4647
@@ -76,6 +77,10 @@ type OrgLinkFormatters = M.Map String (String -> String)
7677-- These settings can be changed via OPTIONS statements.
7778data ExportSettings = ExportSettings
7879 { exportSubSuperscripts :: Bool -- ^ TeX-like syntax for sub- and superscripts
80+ , exportDrawers :: Either [String] [String]
81+ -- ^ Specify drawer names which should be exported. @Left@ names are
82+ -- explicitly excluded from the resulting output while @Right@ means that
83+ -- only the listed drawer names should be included.
7984 }
8085
8186-- | Org-mode parser state
@@ -155,6 +160,7 @@ defaultOrgParserState = OrgParserState
155160defaultExportSettings :: ExportSettings
156161defaultExportSettings = ExportSettings
157162 { exportSubSuperscripts = True
163+ , exportDrawers = Left ["LOGBOOK"]
158164 }
159165
160166
@@ -163,9 +169,16 @@ defaultExportSettings = ExportSettings
163169--
164170type ExportSettingSetter a = a -> ExportSettings -> ExportSettings
165171
172+-- | Set export options for sub/superscript parsing. The short syntax will
173+-- not be parsed if this is set set to @False@.
166174setExportSubSuperscripts :: ExportSettingSetter Bool
167175setExportSubSuperscripts val es = es { exportSubSuperscripts = val }
168176
177+-- | Set export options for drawers. See the @exportDrawers@ in ADT
178+-- @ExportSettings@ for details.
179+setExportDrawers :: ExportSettingSetter (Either [String] [String])
180+setExportDrawers val es = es { exportDrawers = val }
181+
169182-- | Modify a parser state
170183modifyExportSettings :: ExportSettingSetter a -> a -> OrgParserState -> OrgParserState
171184modifyExportSettings setter val state =
src/Text/Pandoc/Writers/Org.hs+13−2
@@ -110,6 +110,17 @@ isRawFormat f =
110110blockToOrg :: Block -- ^ Block element
111111 -> State WriterState Doc
112112blockToOrg Null = return empty
113+blockToOrg (Div (_,classes@(cls:_),kvs) bs) | "drawer" `elem` classes = do
114+ contents <- blockListToOrg bs
115+ let drawerNameTag = ":" <> text cls <> ":"
116+ let keys = vcat $ map (\(k,v) ->
117+ ":" <> text k <> ":"
118+ <> space <> text v) kvs
119+ let drawerEndTag = text ":END:"
120+ return $ drawerNameTag $$ cr $$ keys $$
121+ blankline $$ contents $$
122+ blankline $$ drawerEndTag $$
123+ blankline
113124blockToOrg (Div attrs bs) = do
114125 contents <- blockListToOrg bs
115126 let startTag = tagWithAttrs "div" attrs
@@ -239,8 +250,8 @@ propertiesDrawer (ident, classes, kv) =
239250 let
240251 drawerStart = text ":PROPERTIES:"
241252 drawerEnd = text ":END:"
242− kv' = if (classes == mempty) then kv else ("class", unwords classes):kv
243− kv'' = if (ident == mempty) then kv' else ("id", ident):kv'
253+ kv' = if (classes == mempty) then kv else ("CLASS", unwords classes):kv
254+ kv'' = if (ident == mempty) then kv' else ("CUSTOM_ID", ident):kv'
244255 properties = vcat $ map kvToOrgProperty kv''
245256 in
246257 drawerStart <> cr <> properties <> cr <> drawerEnd
tests/Tests/Readers/Org.hs+25−2
@@ -420,9 +420,10 @@ tests =
420420
421421 , "Drawers can be arbitrary" =:
422422 unlines [ ":FOO:"
423+ , "/bar/"
423424 , ":END:"
424425 ] =?>
425− (mempty::Blocks)
426+ divWith (mempty, ["FOO", "drawer"], mempty) (para $ emph "bar")
426427
427428 , "Anchor reference" =:
428429 unlines [ "<<link-here>> Target."
@@ -475,6 +476,28 @@ tests =
475476 , "a^b"
476477 ] =?>
477478 para "a^b"
479+
480+ , "Export option: directly select drawers to be exported" =:
481+ unlines [ "#+OPTIONS: d:(\"IMPORTANT\")"
482+ , ":IMPORTANT:"
483+ , "23"
484+ , ":END:"
485+ , ":BORING:"
486+ , "very boring"
487+ , ":END:"
488+ ] =?>
489+ divWith (mempty, ["IMPORTANT", "drawer"], mempty) (para "23")
490+
491+ , "Export option: exclude drawers from being exported" =:
492+ unlines [ "#+OPTIONS: d:(not \"BORING\")"
493+ , ":IMPORTANT:"
494+ , "5"
495+ , ":END:"
496+ , ":BORING:"
497+ , "very boring"
498+ , ":END:"
499+ ] =?>
500+ divWith (mempty, ["IMPORTANT", "drawer"], mempty) (para "5")
478501 ]
479502
480503 , testGroup "Basic Blocks" $
@@ -600,7 +623,7 @@ tests =
600623 , "Preferences are treated as header attributes" =:
601624 unlines [ "* foo"
602625 , " :PROPERTIES:"
603− , " :id: fubar"
626+ , " :custom_id: fubar"
604627 , " :bar: baz"
605628 , " :END:"
606629 ] =?>
tests/writer.org+31−31
@@ -10,49 +10,49 @@ markdown test suite.
1010
1111* Headers
1212 :PROPERTIES:
13− :id: headers
13+ :CUSTOM_ID: headers
1414 :END:
1515
1616** Level 2 with an [[/url][embedded link]]
1717 :PROPERTIES:
18− :id: level-2-with-an-embedded-link
18+ :CUSTOM_ID: level-2-with-an-embedded-link
1919 :END:
2020
2121*** Level 3 with /emphasis/
2222 :PROPERTIES:
23− :id: level-3-with-emphasis
23+ :CUSTOM_ID: level-3-with-emphasis
2424 :END:
2525
2626**** Level 4
2727 :PROPERTIES:
28− :id: level-4
28+ :CUSTOM_ID: level-4
2929 :END:
3030
3131***** Level 5
3232 :PROPERTIES:
33− :id: level-5
33+ :CUSTOM_ID: level-5
3434 :END:
3535
3636* Level 1
3737 :PROPERTIES:
38− :id: level-1
38+ :CUSTOM_ID: level-1
3939 :END:
4040
4141** Level 2 with /emphasis/
4242 :PROPERTIES:
43− :id: level-2-with-emphasis
43+ :CUSTOM_ID: level-2-with-emphasis
4444 :END:
4545
4646*** Level 3
4747 :PROPERTIES:
48− :id: level-3
48+ :CUSTOM_ID: level-3
4949 :END:
5050
5151with no blank line
5252
5353** Level 2
5454 :PROPERTIES:
55− :id: level-2
55+ :CUSTOM_ID: level-2
5656 :END:
5757
5858with no blank line
@@ -61,7 +61,7 @@ with no blank line
6161
6262* Paragraphs
6363 :PROPERTIES:
64− :id: paragraphs
64+ :CUSTOM_ID: paragraphs
6565 :END:
6666
6767Here's a regular paragraph.
@@ -79,7 +79,7 @@ here.
7979
8080* Block Quotes
8181 :PROPERTIES:
82− :id: block-quotes
82+ :CUSTOM_ID: block-quotes
8383 :END:
8484
8585E-mail style:
@@ -121,7 +121,7 @@ And a following paragraph.
121121
122122* Code Blocks
123123 :PROPERTIES:
124− :id: code-blocks
124+ :CUSTOM_ID: code-blocks
125125 :END:
126126
127127Code:
@@ -148,12 +148,12 @@ And:
148148
149149* Lists
150150 :PROPERTIES:
151− :id: lists
151+ :CUSTOM_ID: lists
152152 :END:
153153
154154** Unordered
155155 :PROPERTIES:
156− :id: unordered
156+ :CUSTOM_ID: unordered
157157 :END:
158158
159159Asterisks tight:
@@ -200,7 +200,7 @@ Minuses loose:
200200
201201** Ordered
202202 :PROPERTIES:
203− :id: ordered
203+ :CUSTOM_ID: ordered
204204 :END:
205205
206206Tight:
@@ -243,7 +243,7 @@ Multiple paragraphs:
243243
244244** Nested
245245 :PROPERTIES:
246− :id: nested
246+ :CUSTOM_ID: nested
247247 :END:
248248
249249- Tab
@@ -277,7 +277,7 @@ Same thing but with paragraphs:
277277
278278** Tabs and spaces
279279 :PROPERTIES:
280− :id: tabs-and-spaces
280+ :CUSTOM_ID: tabs-and-spaces
281281 :END:
282282
283283- this is a list item indented with tabs
@@ -290,7 +290,7 @@ Same thing but with paragraphs:
290290
291291** Fancy list markers
292292 :PROPERTIES:
293− :id: fancy-list-markers
293+ :CUSTOM_ID: fancy-list-markers
294294 :END:
295295
2962962) begins with 2
@@ -331,7 +331,7 @@ B. Williams
331331
332332* Definition Lists
333333 :PROPERTIES:
334− :id: definition-lists
334+ :CUSTOM_ID: definition-lists
335335 :END:
336336
337337Tight using spaces:
@@ -400,7 +400,7 @@ Blank line after term, indented marker, alternate markers:
400400
401401* HTML Blocks
402402 :PROPERTIES:
403− :id: html-blocks
403+ :CUSTOM_ID: html-blocks
404404 :END:
405405
406406Simple block on one line:
@@ -630,7 +630,7 @@ Hr's:
630630
631631* Inline Markup
632632 :PROPERTIES:
633− :id: inline-markup
633+ :CUSTOM_ID: inline-markup
634634 :END:
635635
636636This is /emphasized/, and so /is this/.
@@ -662,7 +662,7 @@ spaces: a\^b c\^d, a~b c~d.
662662
663663* Smart quotes, ellipses, dashes
664664 :PROPERTIES:
665− :id: smart-quotes-ellipses-dashes
665+ :CUSTOM_ID: smart-quotes-ellipses-dashes
666666 :END:
667667
668668"Hello," said the spider. "'Shelob' is my name."
@@ -686,7 +686,7 @@ Ellipses...and...and....
686686
687687* LaTeX
688688 :PROPERTIES:
689− :id: latex
689+ :CUSTOM_ID: latex
690690 :END:
691691
692692- \cite[22-23]{smith.1899}
@@ -719,7 +719,7 @@ Cat & 1 \\ \hline
719719
720720* Special Characters
721721 :PROPERTIES:
722− :id: special-characters
722+ :CUSTOM_ID: special-characters
723723 :END:
724724
725725Here is some unicode:
@@ -776,12 +776,12 @@ Minus: -
776776
777777* Links
778778 :PROPERTIES:
779− :id: links
779+ :CUSTOM_ID: links
780780 :END:
781781
782782** Explicit
783783 :PROPERTIES:
784− :id: explicit
784+ :CUSTOM_ID: explicit
785785 :END:
786786
787787Just a [[/url/][URL]].
@@ -804,7 +804,7 @@ Just a [[/url/][URL]].
804804
805805** Reference
806806 :PROPERTIES:
807− :id: reference
807+ :CUSTOM_ID: reference
808808 :END:
809809
810810Foo [[/url/][bar]].
@@ -835,7 +835,7 @@ Foo [[/url/][biz]].
835835
836836** With ampersands
837837 :PROPERTIES:
838− :id: with-ampersands
838+ :CUSTOM_ID: with-ampersands
839839 :END:
840840
841841Here's a [[http://example.com/?foo=1&bar=2][link with an ampersand in the
@@ -849,7 +849,7 @@ Here's an [[/script?foo=1&bar=2][inline link in pointy braces]].
849849
850850** Autolinks
851851 :PROPERTIES:
852− :id: autolinks
852+ :CUSTOM_ID: autolinks
853853 :END:
854854
855855With an ampersand: [[http://example.com/?foo=1&bar=2]]
@@ -874,7 +874,7 @@ Auto-links should not occur here: =<http://example.com/>=
874874
875875* Images
876876 :PROPERTIES:
877− :id: images
877+ :CUSTOM_ID: images
878878 :END:
879879
880880From "Voyage dans la Lune" by Georges Melies (1902):
@@ -888,7 +888,7 @@ Here is a movie [[movie.jpg]] icon.
888888
889889* Footnotes
890890 :PROPERTIES:
891− :id: footnotes
891+ :CUSTOM_ID: footnotes
892892 :END:
893893
894894Here is a footnote reference, [1] and another. [2] This should /not/ be a