@@ -70,6 +70,7 @@ documentTree blocks inline = do
70 , headlineTodoMarker = Nothing
70 , headlineTodoMarker = Nothing
71 , headlineText = B.fromList title'
71 , headlineText = B.fromList title'
72 , headlineTags = mempty
72 , headlineTags = mempty
73+ , headlinePlanning = emptyPlanning
73 , headlineProperties = mempty
74 , headlineProperties = mempty
74 , headlineContents = initialBlocks'
75 , headlineContents = initialBlocks'
75 , headlineChildren = headlines'
76 , headlineChildren = headlines'
@@ -117,6 +118,7 @@ data Headline = Headline
117 , headlineTodoMarker :: Maybe TodoMarker
118 , headlineTodoMarker :: Maybe TodoMarker
118 , headlineText :: Inlines
119 , headlineText :: Inlines
119 , headlineTags :: [Tag]
120 , headlineTags :: [Tag]
121+ , headlinePlanning :: PlanningInfo -- ^ subtree planning information
120 , headlineProperties :: Properties
122 , headlineProperties :: Properties
121 , headlineContents :: Blocks
123 , headlineContents :: Blocks
122 , headlineChildren :: [Headline]
124 , headlineChildren :: [Headline]
@@ -136,6 +138,7 @@ headline blocks inline lvl = try $ do
136 title <- trimInlinesF . mconcat <$> manyTill inline endOfTitle
138 title <- trimInlinesF . mconcat <$> manyTill inline endOfTitle
137 tags <- option [] headerTags
139 tags <- option [] headerTags
141+ planning <- option emptyPlanning planningInfo
139 properties <- option mempty propertiesDrawer
142 properties <- option mempty propertiesDrawer
140 contents <- blocks
143 contents <- blocks
141 children <- many (headline blocks inline (level + 1))
144 children <- many (headline blocks inline (level + 1))
@@ -148,6 +151,7 @@ headline blocks inline lvl = try $ do
148 , headlineTodoMarker = todoKw
151 , headlineTodoMarker = todoKw
149 , headlineText = title'
152 , headlineText = title'
150 , headlineTags = tags
153 , headlineTags = tags
154+ , headlinePlanning = planning
151 , headlineProperties = properties
155 , headlineProperties = properties
152 , headlineContents = contents'
156 , headlineContents = contents'
153 , headlineChildren = children'
157 , headlineChildren = children'
@@ -277,9 +281,39 @@ tagsToInlines tags =
277tagSpan :: Tag -> Inlines -> Inlines
281tagSpan :: Tag -> Inlines -> Inlines
278tagSpan t = B.spanWith ("", ["tag"], [("tag-name", fromTag t)])
282tagSpan t = B.spanWith ("", ["tag"], [("tag-name", fromTag t)])
284+-- | An Org timestamp, including repetition marks. TODO: improve
285+type Timestamp = String
287+timestamp :: Monad m => OrgParser m Timestamp
289+ openChar <- oneOf "<["
290+ let isActive = openChar == '<'
291+ let closeChar = if isActive then '>' else ']'
292+ content <- many1Till anyChar (char closeChar)
293+ return (openChar : content ++ [closeChar])
295+-- | Planning information for a subtree/headline.
296+data PlanningInfo = PlanningInfo
297+ { planningClosed :: Maybe Timestamp
298+ , planningDeadline :: Maybe Timestamp
299+ , planningScheduled :: Maybe Timestamp
302+emptyPlanning :: PlanningInfo
303+emptyPlanning = PlanningInfo Nothing Nothing Nothing
282−
305+-- | Read a single planning-related and timestamped line.
306+planningInfo :: Monad m => OrgParser m PlanningInfo
307+planningInfo = try $ do
308+ updaters <- many1 planningDatum <* skipSpaces <* newline
309+ return $ foldr ($) emptyPlanning updaters
311+ planningDatum = skipSpaces *> choice
312+ [ updateWith (\s p -> p { planningScheduled = Just s}) "SCHEDULED"
313+ , updateWith (\d p -> p { planningDeadline = Just d}) "DEADLINE"
314+ , updateWith (\c p -> p { planningClosed = Just c}) "CLOSED"
316+ updateWith fn cs = fn <$> (string cs *> char ':' *> skipSpaces *> timestamp)
284-- | Read a :PROPERTIES: drawer and return the key/value pairs contained
318-- | Read a :PROPERTIES: drawer and return the key/value pairs contained
285-- within.
319-- within.