git.iko.soy/typeable/pandoc
on master help sign in
Org reader: Add partial support for `#+EXCLUDE_TAGS` option. (#4950)

Closes #4284. Headers with the corresponding tags should not appear in the output. If one or more of the specified tags contains a non-tag character like `+`, Org-mode will not treat that as a valid tag, but will nonetheless continue scanning for valid tags. That behavior is not replicated in this patch; entering `cat+dog` as one of the entries in `#+EXCLUDE_TAGS` and running the file through Pandoc will cause the parser to fail and result in the only excluded tag being the default, `noexport`.

commita26b3a2d6af8614e13299bbf477e28c5932ef680 authorBrian Leung <bkleung89@gmail.com> dateFri 5 Oct 2018, 14:28 (8 years ago) parente4ca51c
5 files changed+61−7
src/Text/Pandoc/Readers/Org/DocumentTree.hs+7−7 src/Text/Pandoc/Readers/Org/Meta.hs+11 src/Text/Pandoc/Readers/Org/ParserState.hs+6 src/Text/Pandoc/Readers/Org/Parsing.hs+8 test/command/4284.md+29
src/Text/Pandoc/Readers/Org/DocumentTree.hs+7−7
@@ -43,6 +43,7 @@ import Text.Pandoc.Readers.Org.BlockStarts
4343import Text.Pandoc.Readers.Org.ParserState
4444import Text.Pandoc.Readers.Org.Parsing
4545
46+import qualified Data.Set as Set
4647import qualified Text.Pandoc.Builder as B
4748
4849--
@@ -73,9 +74,6 @@ documentTree blocks inline = do
7374 , headlineChildren = headlines'
7475 }
7576
76−newtype Tag = Tag { fromTag :: String }
77− deriving (Show, Eq)
78−
7977-- | Create a tag containing the given string.
8078toTag :: String -> Tag
8179toTag = Tag
@@ -153,7 +151,7 @@ headline blocks inline lvl = try $ do
153151
154152 headerTags :: Monad m => OrgParser m [Tag]
155153 headerTags = try $
156− let tag = many1 (alphaNum <|> oneOf "@%#_") <* char ':'
154+ let tag = orgTagWord <* char ':'
157155 in map toTag <$> (skipSpaces *> char ':' *> many1 tag <* skipSpaces)
158156
159157-- | Convert an Org mode headline (i.e. a document tree) into pandoc's Blocks
@@ -163,15 +161,17 @@ headlineToBlocks hdln = do
163161 let tags = headlineTags hdln
164162 let text = headlineText hdln
165163 let level = headlineLevel hdln
164+ shouldNotExport <- hasDoNotExportTag tags
166165 case () of
167− _ | any isNoExportTag tags -> return mempty
166+ _ | shouldNotExport -> return mempty
168167 _ | any isArchiveTag tags -> archivedHeadlineToBlocks hdln
169168 _ | isCommentTitle text -> return mempty
170169 _ | maxLevel <= level -> headlineToHeaderWithList hdln
171170 _ | otherwise -> headlineToHeaderWithContents hdln
172171
173−isNoExportTag :: Tag -> Bool
174−isNoExportTag = (== toTag "noexport")
172+hasDoNotExportTag :: Monad m => [Tag] -> OrgParser m Bool
173+hasDoNotExportTag tags = containsExcludedTag . orgStateExcludedTags <$> getState
174+ where containsExcludedTag s = any (`Set.member` s) tags
175175
176176isArchiveTag :: Tag -> Bool
177177isArchiveTag = (== toTag "ARCHIVE")
src/Text/Pandoc/Readers/Org/Meta.hs+11
@@ -52,6 +52,7 @@ import Data.Char (toLower)
5252import Data.List (intersperse)
5353import Data.Maybe (fromMaybe)
5454import qualified Data.Map as M
55+import qualified Data.Set as Set
5556import Network.HTTP (urlEncode)
5657
5758-- | Returns the current meta, respecting export options.
@@ -158,6 +159,7 @@ optionLine = try $ do
158159 "seq_todo" -> todoSequence >>= updateState . registerTodoSequence
159160 "typ_todo" -> todoSequence >>= updateState . registerTodoSequence
160161 "macro" -> macroDefinition >>= updateState . registerMacro
162+ "exclude_tags" -> excludedTagSet >>= updateState . setExcludedTags
161163 "pandoc-emphasis-pre" -> emphChars >>= updateState . setEmphasisPreChar
162164 "pandoc-emphasis-post" -> emphChars >>= updateState . setEmphasisPostChar
163165 _ -> mzero
@@ -190,6 +192,15 @@ parseFormat = try $ replacePlain <|> replaceUrl <|> justAppend
190192 rest = manyTill anyChar (eof <|> () <$ oneOf "\n\r")
191193 tillSpecifier c = manyTill (noneOf "\n\r") (try $ string ('%':c:""))
192194
195+excludedTagSet :: Monad m => OrgParser m (Set.Set Tag)
196+excludedTagSet = do
197+ skipSpaces
198+ Set.fromList . map Tag <$>
199+ many (orgTagWord <* skipSpaces) <* newline
200+
201+setExcludedTags :: Set.Set Tag -> OrgParserState -> OrgParserState
202+setExcludedTags tagSet st = st { orgStateExcludedTags = tagSet }
203+
193204setEmphasisPreChar :: Maybe [Char] -> OrgParserState -> OrgParserState
194205setEmphasisPreChar csMb st =
195206 let preChars = fromMaybe (orgStateEmphasisPostChars defaultOrgParserState) csMb
src/Text/Pandoc/Readers/Org/ParserState.hs+6
@@ -33,6 +33,7 @@ module Text.Pandoc.Readers.Org.ParserState
3333 , defaultOrgParserState
3434 , OrgParserLocal (..)
3535 , OrgNoteRecord
36+ , Tag(..)
3637 , HasReaderOptions (..)
3738 , HasQuoteContext (..)
3839 , HasMacros (..)
@@ -88,6 +89,9 @@ type OrgNoteTable = [OrgNoteRecord]
8889type OrgLinkFormatters = M.Map String (String -> String)
8990-- | Macro expander function
9091type MacroExpander = [String] -> String
92+-- | Tag
93+newtype Tag = Tag { fromTag :: String }
94+ deriving (Show, Eq, Ord)
9195
9296-- | The states in which a todo item can be
9397data TodoState = Todo | Done
@@ -113,6 +117,7 @@ data OrgParserState = OrgParserState
113117 -- specified here.
114118 , orgStateEmphasisPostChars :: [Char] -- ^ Chars allowed at after emphasis
115119 , orgStateEmphasisNewlines :: Maybe Int
120+ , orgStateExcludedTags :: Set.Set Tag
116121 , orgStateExportSettings :: ExportSettings
117122 , orgStateHeaderMap :: M.Map Inlines String
118123 , orgStateIdentifiers :: Set.Set String
@@ -183,6 +188,7 @@ defaultOrgParserState = OrgParserState
183188 , orgStateEmphasisCharStack = []
184189 , orgStateEmphasisNewlines = Nothing
185190 , orgStateExportSettings = def
191+ , orgStateExcludedTags = Set.singleton $ Tag "noexport"
186192 , orgStateHeaderMap = M.empty
187193 , orgStateIdentifiers = Set.empty
188194 , orgStateIncludeFiles = []
src/Text/Pandoc/Readers/Org/Parsing.hs+8
@@ -46,6 +46,8 @@ module Text.Pandoc.Readers.Org.Parsing
4646 , orgArgKey
4747 , orgArgWord
4848 , orgArgWordChar
49+ , orgTagWord
50+ , orgTagWordChar
4951 -- * Re-exports from Text.Pandoc.Parser
5052 , ParserContext (..)
5153 , many1Till
@@ -220,3 +222,9 @@ orgArgWord = many1 orgArgWordChar
220222-- | Chars treated as part of a word in plists.
221223orgArgWordChar :: Monad m => OrgParser m Char
222224orgArgWordChar = alphaNum <|> oneOf "-_"
225+
226+orgTagWord :: Monad m => OrgParser m String
227+orgTagWord = many1 orgTagWordChar
228+
229+orgTagWordChar :: Monad m => OrgParser m Char
230+orgTagWordChar = alphaNum <|> oneOf "@%#_"
test/command/4284.mdnew file+29
@@ -0,0 +1,29 @@
1+```
2+% pandoc -f org -t native
3+#+EXCLUDE_TAGS:apple cat bye dog %
4+
5+* This should not appear :apple:
6+* NOEXPORT should appear if not specified in EXCLUDE_TAGS :noexport:
7+* This should not appear :cat:hi:laptop:
8+** Children of headers with excluded tags should not appear :xylophone:
9+* This should not appear :%:
10+^D
11+[Header 1 ("noexport-should-appear-if-not-specified-in-excludetags",[],[]) [Str "NOEXPORT",Space,Str "should",Space,Str "appear",Space,Str "if",Space,Str "not",Space,Str "specified",Space,Str "in",Space,Str "EXCLUDE",Subscript [Str "TAGS"],Space,Span ("",["tag"],[("tag-name","noexport")]) [SmallCaps [Str "noexport"]]]]
12+```
13+
14+```
15+% pandoc -f org -t native
16+#+EXCLUDE_TAGS:elephant
17+* This should not appear :elephant:
18+* This should appear :fawn:
19+^D
20+[Header 1 ("this-should-appear",[],[]) [Str "This",Space,Str "should",Space,Str "appear",Space,Span ("",["tag"],[("tag-name","fawn")]) [SmallCaps [Str "fawn"]]]]
21+```
22+
23+```
24+% pandoc -f org -t native
25+#+EXCLUDE_TAGS:
26+* NOEXPORT should appear if not specified in EXCLUDE_TAGS :noexport:
27+^D
28+[Header 1 ("noexport-should-appear-if-not-specified-in-excludetags",[],[]) [Str "NOEXPORT",Space,Str "should",Space,Str "appear",Space,Str "if",Space,Str "not",Space,Str "specified",Space,Str "in",Space,Str "EXCLUDE",Subscript [Str "TAGS"],Space,Span ("",["tag"],[("tag-name","noexport")]) [SmallCaps [Str "noexport"]]]]
29+```