packages feed

orgmode-parse 0.2.3 → 0.3.0

raw patch · 12 files changed

+236/−161 lines, 12 files

Files

CHANGELOG.md view
@@ -1,3 +1,14 @@+0.3.0+- Added support for parsing the following types of markup (thank you to+  @zhujinxuan for the significant contribution!):+  - `*bold*`+  - `/italic/`+  - `_underlined_`+  - `=verbatim=`+  - `~code~`+  - `+strikethrough`+  - LaTex markup+ 0.2.2 - Support building with GHC 8.4.3 (thank you @zhujinxuan!) 
README.md view
@@ -6,7 +6,6 @@ combinators for org-mode structured text.  - [What's Finished](#whats-finished)-- [What's Planned](#whats-planned) - [Building](#building)  You can find the package on [Hackage](https://hackage.haskell.org/package/orgmode-parse).@@ -27,30 +26,22 @@ - [X] Scheduled and deadline timestamps (timestamp, range, duration, periodicity)   - [X] Active and inactive timestamps - [X] Clock timestamps-- [ ] Markup-  - [ ] Emphasis-    - [ ] Bold-    - [ ] Italic-    - [ ] Strikethrough-    - [ ] Underline+- [-] Markup+  - [-] Emphasis+    - [X] Bold+    - [X] Italic+    - [X] Strikethrough+    - [X] Underline     - [ ] Superscript     - [ ] Subscript-    - [ ] Code / monospaced+    - [X] Code / monospaced   - [ ] Tables-  - [ ] Lists-    - [ ] Unordered lists-    - [ ] Numbered lists+  - [-] Lists+    - [X] Unordered lists+    - [X] Numbered lists     - [ ] Checkbox modified lists   - [ ] Blocks (src / quote / example blocks) -Parsing org-mode markup is currently being worked on.--## What's Planned (outside of what's not finished)-1. Modernizing this library and adding significantly more documentation to it-2. Writing a sister library, `orgmode-pretty`, providing a pretty printer-   implementation for an org-mode AST-3. Pandoc integration- ## Building There are a few ways to build this library if you're developing a patch: @@ -61,7 +52,7 @@ development:  ```shell-$ nix-shell --attr orgmode-parse.env release.nix+$ nix-shell $ cabal build ``` 
orgmode-parse.cabal view
@@ -1,5 +1,5 @@ Name:                   orgmode-parse-Version:                0.2.3+Version:                0.3.0 Author:                 Parnell Springmeyer <parnell@digitalmentat.com> Maintainer:             Parnell Springmeyer <parnell@digitalmentat.com> License:                BSD3@@ -16,8 +16,7 @@   document markup.   .   The provided Attoparsec combinators parse the human-readable and-  textual representation into a simple AST for storage or output to-  another format (HTML? Markdown?).+  textual representation into a simple AST.  Cabal-Version:          >= 1.10 Build-Type:             Simple@@ -64,7 +63,7 @@   Type:                 exitcode-stdio-1.0   Default-Language:     Haskell2010   Hs-Source-Dirs:       test, src-  Ghc-Options:          -Wall+  Ghc-Options:          -Wall -fwarn-tabs -funbox-strict-fields -fno-warn-orphans -fno-warn-unused-do-bind  -fbreak-on-error   Main-Is:              Test.hs   Other-Modules:                 Document,
src/Data/OrgMode/Parse/Attoparsec/Drawer/Generic.hs view
@@ -20,8 +20,7 @@ where  import           Control.Applicative                ((*>), (<*))-import           Data.Attoparsec.Text-import           Data.Attoparsec.Types              as Attoparsec+import           Data.Attoparsec.Text               ((<?>), char, takeWhile1, skipSpace, Parser, manyTill, asciiCI) import           Data.Text                          (Text) import qualified Data.Text                          as Text @@ -33,7 +32,7 @@ -- > :MYTEXT: -- > whatever I want, can go in here except for headlines and drawers -- > :END:-parseDrawer :: Attoparsec.Parser Text Drawer+parseDrawer :: Parser Drawer parseDrawer =   Drawer                <$>     parseDrawerName     <*>@@ -44,23 +43,23 @@ -- > :DRAWERNAME: -- > my text in a drawer -- > :END:-parseDrawerName :: Attoparsec.Parser Text Text+parseDrawerName :: Parser Text parseDrawerName =-  skipSpace *> skip (== ':') *>+  skipSpace *> char ':' *>   takeWhile1 (/= ':')        <*-  skip (== ':') <* skipSpace+  char ':' <* skipSpace  -- | Parse drawer delimiters, e.g the beginning and end of a property -- drawer: -- -- > :PROPERTIES: -- > :END:-parseDrawerDelim :: Text -> Attoparsec.Parser Text Text+parseDrawerDelim :: Text -> Parser Text parseDrawerDelim v =-  skipSpace *> skip (== ':') *>+  skipSpace *> char  ':' *>   asciiCI v                  <*-  skip (== ':') <* Util.skipOnlySpace+  char ':' <* Util.skipOnlySpace  -- | Parse the @:END:@ of a drawer.-drawerEnd :: Attoparsec.Parser Text Text-drawerEnd = parseDrawerDelim "END"+drawerEnd :: Parser Text+drawerEnd = parseDrawerDelim "END"   <?> "Expected Drawer End :END:"
src/Data/OrgMode/Parse/Attoparsec/Section.hs view
@@ -13,15 +13,15 @@  module Data.OrgMode.Parse.Attoparsec.Section where -import           Data.Attoparsec.Text                 (many', option, skipSpace)-import           Data.Attoparsec.Types                as Attoparsec+import           Control.Applicative                  ()+import           Data.Attoparsec.Text                 (skipSpace, many', option)+import qualified Data.Attoparsec.Text                 as Attoparsec.Text import           Data.Monoid                          ()+ import           Data.OrgMode.Parse.Attoparsec.Drawer import           Data.OrgMode.Parse.Attoparsec.Time-import qualified Data.OrgMode.Parse.Attoparsec.Util   as Util-import           Data.Text                            (Text)-import qualified Data.Text                            as Text-+import           Data.OrgMode.Parse.Attoparsec.Util          (skipEmptyLines)+import           Data.OrgMode.Parse.Attoparsec.Block         (parseBlocks) import           Data.OrgMode.Types  -- | Parse a heading section@@ -29,13 +29,14 @@ -- Headline sections contain optionally a property drawer, -- a list of clock entries, code blocks (not yet implemented), -- plain lists (not yet implemented), and unstructured text.-parseSection :: Attoparsec.Parser Text Section-parseSection =-  Section+parseSection :: Attoparsec.Text.Parser Section+parseSection = skipEmptyLines *> parseSection' <* skipEmptyLines+  where+  parseSection' = Section    <$> option Nothing (Just <$> (skipSpace *> parseTimestamp <* skipSpace))    <*> (Plns <$> parsePlannings)    <*> many' parseClock    <*> option mempty parseProperties    <*> option mempty parseLogbook-   <*> many' parseDrawer-   <*> (Text.unlines <$> many' Util.nonHeadline)+   <*> parseBlocks+
src/Data/OrgMode/Parse/Attoparsec/Time.hs view
@@ -22,26 +22,26 @@ ) where -import           Control.Applicative-import qualified Data.Attoparsec.ByteString         as Attoparsec.ByteString-import           Data.Attoparsec.Combinator         as Attoparsec+import           Control.Applicative        +import qualified Data.Attoparsec.ByteString as Attoparsec.ByteString+import           Data.Attoparsec.Combinator as Attoparsec import           Data.Attoparsec.Text-import           Data.Attoparsec.Types              as Attoparsec (Parser)-import qualified Data.ByteString.Char8              as BS-import           Data.Functor                       (($>))-import           Data.HashMap.Strict                (HashMap, fromList)-import           Data.Maybe                         (listToMaybe)-import           Data.Monoid                        ()-import qualified Data.OrgMode.Parse.Attoparsec.Util as Util+import           Data.Attoparsec.Types      as Attoparsec (Parser)+import qualified Data.ByteString.Char8      as BS+import           Data.Functor               (($>))+import           Data.HashMap.Strict        (HashMap, fromList)+import           Data.Maybe                 (listToMaybe)+import           Data.Monoid                ()+import           Data.Text                  (Text)+import qualified Data.Text                  as Text+import           Data.Thyme.Format          (buildTime, timeParser)+import           Data.Thyme.LocalTime       (Hours, Minutes)+import           System.Locale              (defaultTimeLocale)+import           Data.Semigroup             ((<>)) import           Data.OrgMode.Types-import           Data.Semigroup                     ((<>))-import           Data.Text                          (Text)-import qualified Data.Text                          as Text-import           Data.Thyme.Format                  (buildTime, timeParser)-import           Data.Thyme.LocalTime               (Hours, Minutes)-import           Prelude                            hiding (repeat)-import           System.Locale                      (defaultTimeLocale) +import           Prelude                    hiding (repeat)+ -- | Parse a planning line. -- -- Plannings inhabit a heading section and are formatted as a keyword@@ -50,7 +50,7 @@ -- -- > DEADLINE: <2015-05-10 17:00> CLOSED: <2015-04-1612:00> parsePlannings :: Attoparsec.Parser Text (HashMap PlanningKeyword Timestamp)-parsePlannings = fromList <$> many' (skipSpace *> planning <* Util.skipOnlySpace)+parsePlannings = fromList <$> many' (skipSpace *> planning <* skipSpace)   where     planning =  (,) <$> pType <* char ':' <*> (skipSpace *> parseTimestamp)     pType    = choice [string "SCHEDULED" $>  SCHEDULED@@ -67,8 +67,9 @@ parseClock :: Attoparsec.Parser Text Clock parseClock = Clock <$> ((,) <$> (skipSpace *> string "CLOCK: " *> ts) <*> dur)   where-    ts  = optional parseTimestamp-    dur = optional (string " => " *> skipSpace *> parseHM)+    ts  = option Nothing (Just <$> parseTimestamp)+    dur = option Nothing (Just <$> (string " => "+                                    *> skipSpace *> parseHM))  -- | Parse a timestamp. --@@ -110,7 +111,7 @@    where     optionalBracketedDateTime =-      optional (string "--" *> parseBracketedDateTime)+      option Nothing (Just <$> (string "--" *> parseBracketedDateTime))   -- | Parse a single time part.@@ -137,7 +138,7 @@   closingBracket <- char '>' <|> char ']'   finally brkDateTime openingBracket closingBracket   where-    optionalParse p  = optional p <* skipSpace+    optionalParse p  = option Nothing (Just <$> p) <* skipSpace     maybeListParse p = listToMaybe <$> many' p  <* skipSpace     activeBracket ((=='<') -> active) =       if active then Active else Inactive
src/Data/OrgMode/Parse/Attoparsec/Util.hs view
@@ -8,18 +8,22 @@ Attoparsec utilities. -} + module Data.OrgMode.Parse.Attoparsec.Util-( skipOnlySpace-, nonHeadline+( skipOnlySpace,+  nonHeadline,+  module Data.OrgMode.Parse.Attoparsec.Util.ParseLinesTill )+ where -import           Control.Applicative   ((<|>))+import           Data.Semigroup        ((<>)) import qualified Data.Attoparsec.Text  as Attoparsec.Text-import           Data.Attoparsec.Types (Parser)-import           Data.Text             (Text)+import           Data.Attoparsec.Text  (Parser, takeTill, isEndOfLine, endOfLine, notChar, isHorizontalSpace)+import           Data.Text             (Text, cons) import qualified Data.Text             as Text import           Data.Functor          (($>))+import           Data.OrgMode.Parse.Attoparsec.Util.ParseLinesTill  -- | Skip whitespace characters, only! --@@ -27,17 +31,12 @@ -- @Data.Char@ which also includes control characters such as a return -- and newline which we need to *not* consume in some cases during -- parsing.-skipOnlySpace :: Parser Text ()-skipOnlySpace = Attoparsec.Text.skipWhile spacePred-  where-    spacePred s = s == ' ' || s == '\t'+skipOnlySpace :: Parser ()+skipOnlySpace = Attoparsec.Text.skipWhile isHorizontalSpace  -- | Parse a non-heading line of a section.-nonHeadline :: Parser Text Text-nonHeadline = nonHeadline0 <|> nonHeadline1+nonHeadline :: Parser Text+nonHeadline = nonHeadline0 <> nonHeadline1   where-    nonHeadline0 = Attoparsec.Text.endOfLine $> Text.pack ""-    nonHeadline1 = Text.pack <$> do-      h <- Attoparsec.Text.notChar '*'-      t <- Attoparsec.Text.manyTill Attoparsec.Text.anyChar Attoparsec.Text.endOfLine-      pure (h:t)+    nonHeadline0 = endOfLine $> Text.empty+    nonHeadline1 = cons <$> notChar '*' <*> (takeTill isEndOfLine <* endOfLine)
src/Data/OrgMode/Types.hs view
@@ -23,7 +23,7 @@ , DelayType         (..) , Depth             (..) , Document          (..)-, Drawer            (..)+, Drawer , Duration , Headline          (..) , Logbook           (..)@@ -41,18 +41,23 @@ , TimeUnit          (..) , Timestamp         (..) , YearMonthDay      (..)+, Block             (..)+, MarkupText        (..)+, Item              (..)+, sectionDrawer ) where -import           Control.Monad        (mzero)-import           Data.Aeson           ((.:), (.=))-import qualified Data.Aeson           as Aeson-import           Data.Hashable        (Hashable (..))-import           Data.HashMap.Strict  (HashMap, fromList, keys, toList)-import           Data.Text            (Text, pack)-import           Data.Thyme.Calendar  (YearMonthDay (..))-import           Data.Thyme.LocalTime (Hour, Hours, Minute, Minutes)+import           Control.Monad                     (mzero)+import           Data.Aeson                        ((.:), (.=))+import qualified Data.Aeson                        as Aeson+import           Data.Hashable                     (Hashable (..))+import           Data.HashMap.Strict               (HashMap, fromList, keys, toList)+import           Data.Text                         (Text, pack)+import           Data.Thyme.Calendar               (YearMonthDay (..))+import           Data.Thyme.LocalTime              (Hour, Hours, Minute, Minutes) import           GHC.Generics-import           Data.Semigroup       (Semigroup)+import           Data.Semigroup                    (Semigroup)+ -- | Org-mode document. data Document = Document   { documentText      :: Text       -- ^ Text occurring before any Org headlines@@ -82,6 +87,7 @@ instance Aeson.ToJSON Depth instance Aeson.FromJSON Depth + -- | Section of text directly following a headline. data Section = Section   { sectionTimestamp  :: Maybe Timestamp -- ^ A headline's section timestamp@@ -89,14 +95,46 @@   , sectionClocks     :: [Clock]         -- ^ A list of clocks   , sectionProperties :: Properties      -- ^ A map of properties from the :PROPERTY: drawer   , sectionLogbook    :: Logbook         -- ^ A list of clocks from the :LOGBOOK: drawer-  , sectionDrawers    :: [Drawer]        -- ^ A list of parsed user-defined drawers-  , sectionParagraph  :: Text            -- ^ Arbitrary text+  , sectionBlocks     :: [Block]  -- ^ Content of Section   } deriving (Show, Eq, Generic) +sectionDrawer :: Section -> [Block]+sectionDrawer s = filter isDrawer (sectionBlocks s)+  where+  isDrawer (Drawer _ _) = True+  isDrawer _ = False+ newtype Properties = Properties { unProperties :: HashMap Text Text }   deriving (Show, Eq, Generic, Semigroup, Monoid) +data MarkupText+  = Plain         Text+  | LaTeX         Text+  | Verbatim      Text+  | Code          Text+  | Bold          [MarkupText]+  | Italic        [MarkupText]+  | UnderLine     [MarkupText]+  | Strikethrough [MarkupText]+  deriving (Show, Eq, Generic) +newtype Item = Item [Block] deriving (Show, Eq, Generic, Semigroup, Monoid)++data Block = OrderedList [Item] | UnorderedList [Item] | Paragraph [MarkupText] | Drawer {+    name     :: Text+  , contents :: Text+} deriving (Show, Eq, Generic)+type Drawer = Block++instance Aeson.ToJSON MarkupText+instance Aeson.FromJSON MarkupText++instance Aeson.ToJSON Item+instance Aeson.FromJSON Item++instance Aeson.ToJSON Block+instance Aeson.FromJSON Block+ instance Aeson.ToJSON Properties instance Aeson.FromJSON Properties @@ -107,14 +145,6 @@ instance Aeson.ToJSON Logbook instance Aeson.FromJSON Logbook -data Drawer = Drawer-  { name     :: Text-  , contents :: Text-  } deriving (Show, Eq, Generic)--instance Aeson.ToJSON Drawer-instance Aeson.FromJSON Drawer- -- | Sum type indicating the active state of a timestamp. data ActiveState   = Active@@ -310,3 +340,4 @@  instance Hashable PlanningKeyword where   hashWithSalt salt k = hashWithSalt salt (fromEnum k)+
test/Document.hs view
@@ -3,18 +3,20 @@ module Document where  import           Data.Attoparsec.Text-import           Data.Text+import           Data.Text                        hiding  (map) import qualified Data.Text                              as Text import qualified Data.Text.IO                           as TextIO import           Test.Tasty import           Test.Tasty.HUnit-import           Data.HashMap.Strict+import           Data.HashMap.Strict              hiding   (map)  import           Data.OrgMode.Parse.Attoparsec.Document import           Data.OrgMode.Parse.Attoparsec.Time import           Data.OrgMode.Types import           Util+import           Util.Builder + parserSmallDocumentTests :: TestTree parserSmallDocumentTests = testGroup "Attoparsec Small Document"   [ testCase "Parse Empty Document" $@@ -29,12 +31,6 @@   , testCase "Parse Headline with Planning" $       testDocS samplePText samplePParse -  , testCase "Parse Headline with properties" $-      testDocS sampleP2Text sampleP2Parse--  , testCase "Parse Headline with scheduled" $-      testDocS sampleP3Text sampleP3Parse-   , testCase "Parse Headline no \n" $       testDocS "* T" (Document "" [emptyHeadline {title="T"}]) @@ -49,18 +45,19 @@     testDocS s r = expectParse (parseDocument kw) s (Right r)      testDocFile  = do-      doc <- TextIO.readFile "test/test-document.org"+      doc <- TextIO.readFile "test/docs/test-document.org"        let testDoc = parseOnly (parseDocument kw) doc        assertBool "Expected to parse document" (parseSucceeded testDoc)      testSubtreeListItemDocFile  = do-      doc <- TextIO.readFile "test/subtree-list-items.org"+      doc <- TextIO.readFile "test/docs/subtree-list-items.org" -      let subtreeListItemsDoc = parseOnly (parseDocument []) doc+      -- let subtreeListItemsDoc = parseOnly (parseDocument []) doc -      assertBool "Expected to parse document" (subtreeListItemsDoc == goldenSubtreeListItemDoc)+      -- assertBool "Expected to parse document" (subtreeListItemsDoc == goldenSubtreeListItemDoc)+      expectParse (parseDocument []) doc  goldenSubtreeListItemDoc      kw           = ["TODO", "CANCELED", "DONE"]     pText        = "Paragraph text\n.No headline here.\n##--------\n"@@ -76,8 +73,8 @@ sampleAParse :: Document sampleAParse = Document                sampleParagraph-               [emptyHeadline {title="Test1", tags=["Hi there"]}-               ,emptyHeadline {section=emptySection{sectionParagraph=" *\n"}}+               -- Headline shall have space after *+               [emptyHeadline {title="Test1", tags=["Hi there"], section = emptySection {sectionBlocks= [toP (Bold [])]}}                ,emptyHeadline {title="Test2", tags=["Two","Tags"]}                ] @@ -97,36 +94,6 @@      Right con = parseOnly parsePlannings "SCHEDULED: <2015-06-12 Fri>" -sampleP2Text :: Text-sampleP2Text =-    Text.concat ["* Test3_1\n"-                ,"  :PROPERTIES:\n"-                ,"  :CATEGORY: testCategory\n"-                ,"  :END:"-                ]--sampleP2Parse :: Document-sampleP2Parse =-    Document "" [ emptyHeadline {-                      title = "Test3_1"-                    , section = emptySection {-                          sectionProperties = Properties (fromList [("CATEGORY", "testCategory")])}}]--sampleP3Text :: Text-sampleP3Text =-    Text.concat ["* Test4_1\n"-                ,"  SCHEDULED: <2004-02-29 Sun 10:20>"-                ]--sampleP3Parse :: Document-sampleP3Parse =-    Document "" [ emptyHeadline {-                      title = "Test4_1"-                    , section = emptySection {-                          sectionPlannings = Plns con}}]-  where-    Right con = parseOnly parsePlannings "SCHEDULED: <2004-02-29 Sun 10:20>"- emptyHeadline :: Headline emptyHeadline =   Headline@@ -148,7 +115,81 @@ spaces = flip Text.replicate " "  emptySection :: Section-emptySection = Section Nothing (Plns mempty) mempty mempty mempty mempty mempty+emptySection = Section Nothing (Plns mempty) mempty mempty mempty mempty +plainParagraphs :: Text -> [Block]+plainParagraphs str = [Paragraph [Plain str]]+ goldenSubtreeListItemDoc :: Either String Document-goldenSubtreeListItemDoc = Right (Document {documentText = "", documentHeadlines = [Headline {depth = Depth 1, stateKeyword = Nothing, priority = Nothing, title = "Header1", timestamp = Nothing, stats = Nothing, tags = [], section = Section {sectionTimestamp = Nothing, sectionPlannings = Plns (fromList []), sectionClocks = [], sectionProperties = Properties {unProperties = fromList []}, sectionLogbook = Logbook {unLogbook = []}, sectionDrawers = [], sectionParagraph = ""}, subHeadlines = [Headline {depth = Depth 2, stateKeyword = Nothing, priority = Nothing, title = "Header2", timestamp = Nothing, stats = Nothing, tags = [], section = Section {sectionTimestamp = Nothing, sectionPlannings = Plns (fromList []), sectionClocks = [], sectionProperties = Properties {unProperties = fromList []}, sectionLogbook = Logbook {unLogbook = []}, sectionDrawers = [], sectionParagraph = ""}, subHeadlines = [Headline {depth = Depth 3, stateKeyword = Nothing, priority = Nothing, title = "Header3", timestamp = Nothing, stats = Nothing, tags = [], section = Section {sectionTimestamp = Nothing, sectionPlannings = Plns (fromList []), sectionClocks = [], sectionProperties = Properties {unProperties = fromList [("ONE","two")]}, sectionLogbook = Logbook {unLogbook = []}, sectionDrawers = [], sectionParagraph = "\n    * Item1\n    * Item2\n"}, subHeadlines = []}]},Headline {depth = Depth 2, stateKeyword = Nothing, priority = Nothing, title = "Header4", timestamp = Nothing, stats = Nothing, tags = [], section = Section {sectionTimestamp = Nothing, sectionPlannings = Plns (fromList []), sectionClocks = [], sectionProperties = Properties {unProperties = fromList []}, sectionLogbook = Logbook {unLogbook = []}, sectionDrawers = [], sectionParagraph = ""}, subHeadlines = []}]}]})+goldenSubtreeListItemDoc = Right (Document +  {documentText = "", +  documentHeadlines = [Headline +    {depth = Depth 1,+    stateKeyword = Nothing,+    priority = Nothing,+    title = "Header1", +    timestamp = Nothing,+    stats = Nothing,+    tags = [], +    section = Section +      {sectionTimestamp = Nothing,+      sectionPlannings = Plns (fromList []), +      sectionClocks = [], +      sectionProperties = Properties {unProperties = fromList []}, +      sectionLogbook = Logbook {unLogbook = []}, +      sectionBlocks = [] +      },+    subHeadlines = [Headline {+      depth = Depth 2,+      stateKeyword = Nothing,+      priority = Nothing,+      title = "Header2", +      timestamp = Nothing,+      stats = Nothing,+      tags = [], +      section = Section {+        sectionTimestamp = Nothing,+        sectionPlannings = Plns (fromList []), +        sectionClocks = [], +        sectionProperties = Properties { unProperties = fromList []},+        sectionLogbook = Logbook {unLogbook = []},+        sectionBlocks = []+      },+      subHeadlines = [Headline {+        depth = Depth 3,+        stateKeyword = Nothing,+        priority = Nothing,+        title = "Header3",+        timestamp = Nothing,+        stats = Nothing,+        tags = [],+        section = Section {+          sectionTimestamp = Nothing,+          sectionPlannings = Plns (fromList []),+          sectionClocks = [],+          sectionProperties = Properties {unProperties = fromList [("ONE","two")]},+          sectionLogbook = Logbook {unLogbook = []},+          sectionBlocks = [UnorderedList $ map toI [pack "Item1",  pack "Item2"]]+        },+        subHeadlines = []+      }]+    },+    Headline {+      depth = Depth 2,+      stateKeyword = Nothing,+      priority = Nothing,+      title = "Header4",+      timestamp = Nothing,+      stats = Nothing,+      tags = [],+      section = Section {+        sectionTimestamp = Nothing,+        sectionPlannings = Plns (fromList []),+        sectionClocks = [],+        sectionProperties = Properties {unProperties = fromList []},+        sectionLogbook = Logbook {unLogbook = []},+        sectionBlocks = []+      },+      subHeadlines = []}+  ]}+]})
test/Test.hs view
@@ -1,6 +1,3 @@-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE RankNTypes        #-}- module Main where  import           Document@@ -8,6 +5,9 @@ import           Headline import           Test.Tasty import           Timestamps+import           Block.Paragraph+import           Block.List+import           Block.Blocks  main :: IO () main = defaultMain tests@@ -18,6 +18,9 @@           [ parserHeadlineTests           , parserDrawerTests           , parserTimestampTests-          , parserSmallDocumentTests+          , parserParagraphs+          , parserLists+          , parserBlocks           , parserWeekdayTests+          , parserSmallDocumentTests           ]
test/Timestamps.hs view
@@ -25,7 +25,7 @@     , testCase "Parse Sample Schedule"   $ testPlanningS sExampleStrA sExampleResA     ]   where-    testPlanning     = testParser parsePlannings +    testPlanning     = testParser parsePlannings     testPlanningS t r = expectParse parsePlannings t (Right r)      (sExampleStrA, sExampleResA) =
test/Util.hs view
@@ -1,15 +1,14 @@ module Util where -import           Data.Attoparsec.Text-import           Data.Attoparsec.Types as TP+import           Data.Attoparsec.Text    (Parser, parseOnly) import           Data.Either-import           Data.Text             as T+import           Data.Text               (pack, Text) import           Test.HUnit -testParser :: TP.Parser Text a -> String -> Assertion-testParser f v = fromEither (parseOnly f $ T.pack v)+testParser :: Parser a -> String -> Assertion+testParser f v = fromEither (parseOnly f $ pack v) -expectParse :: (Eq a, Show a) => TP.Parser Text a -- ^ Parser under test+expectParse :: (Eq a, Show a) => Parser a -- ^ Parser under test                               -> Text             -- ^ Message under test                               -> Either String a  -- ^ Expected parse result                               -> Assertion