diff --git a/neuron/neuron.cabal b/neuron/neuron.cabal index 21e3c741d..75fdf1edf 100644 --- a/neuron/neuron.cabal +++ b/neuron/neuron.cabal @@ -1,7 +1,7 @@ cabal-version: 2.4 name: neuron -- This version must be in sync with what's in Default.dhall -version: 1.9.10.0 +version: 1.9.11.0 license: AGPL-3.0-only copyright: 2020 Sridhar Ratnakumar maintainer: srid@srid.ca @@ -94,6 +94,8 @@ library Neuron.Zettelkasten.Graph.Type Neuron.Zettelkasten.Graph.Build Neuron.Zettelkasten.Resolver + Neuron.Zettelkasten.Format + Neuron.Zettelkasten.Format.Reader.Type Neuron.Cache.Type Neuron.Frontend.Route Neuron.Frontend.Route.Data @@ -137,6 +139,7 @@ common app-common file-embed, optparse-applicative, pandoc-types, + pandoc, -- Use skylighting >= 0.9 as they are written in pure Haskell skylighting-core >= 0.9, relude, @@ -202,6 +205,8 @@ library neuron-app-lib Neuron.Frontend.Static.HeadHtml Neuron.Frontend.Static.StructuredData Neuron.Zettelkasten.ID.Scheme + Neuron.Zettelkasten.Format.Reader + Neuron.Zettelkasten.Format.Reader.Org autogen-modules: Paths_neuron other-modules: diff --git a/neuron/src/app/Neuron/Reactor/Build.hs b/neuron/src/app/Neuron/Reactor/Build.hs index e485e46ca..72e300dc2 100644 --- a/neuron/src/app/Neuron/Reactor/Build.hs +++ b/neuron/src/app/Neuron/Reactor/Build.hs @@ -38,9 +38,12 @@ import qualified Neuron.Frontend.Route.Data as RD import qualified Neuron.Frontend.Static.HeadHtml as HeadHtml import qualified Neuron.Frontend.Static.Html as Html import qualified Neuron.Frontend.Widget as W +import Neuron.Markdown (parseMarkdown) import Neuron.Plugin (PluginRegistry) import qualified Neuron.Plugin as Plugin import Neuron.Version (neuronVersion) +import Neuron.Zettelkasten.Format +import qualified Neuron.Zettelkasten.Format.Reader.Org as Org import qualified Neuron.Zettelkasten.Graph.Build as G import Neuron.Zettelkasten.Graph.Type (ZettelGraph) import Neuron.Zettelkasten.ID (ZettelID (..)) @@ -120,7 +123,7 @@ reportAllErrors issues = do reportError :: ZettelID -> ZettelError -> m () reportError zid (zettelErrorText -> err) = do -- We don't know the full path to this zettel; so just print the ID. - log EE $ "Cannot accept Zettel ID: " <> unZettelID zid + log EE $ "Cannot accept Zettel with ID \"" <> unZettelID zid <> "\"" log E $ indentAllButFirstLine 4 err genRouteHtml :: @@ -216,8 +219,9 @@ loadZettelkastenFromFilesWithPlugins :: Map ZettelID ZettelIssue ) loadZettelkastenFromFilesWithPlugins plugins fileTree = do + let markdownParser = parseMarkdown (Plugin.markdownSpec plugins) !zidRefs <- - case DC.pruneDirTree =<< DC.filterDirTree ((== ".md") . takeExtension) fileTree of + case DC.pruneDirTree =<< DC.filterDirTree ((`elem` [".org", ".md"]) . takeExtension) fileTree of Nothing -> pure mempty Just mdFileTree -> do @@ -232,6 +236,9 @@ loadZettelkastenFromFilesWithPlugins plugins fileTree = do decodeUtf8With lenientDecode <$> readFileBS absPath Plugin.afterZettelRead plugins mdFileTree log D $ "Building graph (" <> show (length zidRefs) <> " notes) ..." + let getParser = \case + ZettelFormat_Markdown -> markdownParser + ZettelFormat_Org -> Org.parseOrg pure $ runWriter $ do filesWithContent <- @@ -239,8 +246,8 @@ loadZettelkastenFromFilesWithPlugins plugins fileTree = do R.ZIDRef_Ambiguous fps -> do tell $ one (zid, ZettelIssue_Error $ ZettelError_AmbiguousID fps) pure Nothing - R.ZIDRef_Available fp s pluginData -> - pure $ Just (fp, (s, pluginData)) - let !zs = Plugin.afterZettelParse plugins (Map.toList filesWithContent) + R.ZIDRef_Available fp fmt s pluginData -> + pure $ Just (fp, fmt, (s, pluginData)) + let !zs = Plugin.afterZettelParse plugins getParser (Map.toList filesWithContent) !g <- G.buildZettelkasten plugins zs pure (g, zs) diff --git a/neuron/src/app/Neuron/Zettelkasten/Format/Reader.hs b/neuron/src/app/Neuron/Zettelkasten/Format/Reader.hs new file mode 100644 index 000000000..1a804e499 --- /dev/null +++ b/neuron/src/app/Neuron/Zettelkasten/Format/Reader.hs @@ -0,0 +1,16 @@ +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeApplications #-} +{-# LANGUAGE ViewPatterns #-} +{-# LANGUAGE NoImplicitPrelude #-} + +module Neuron.Zettelkasten.Format.Reader + ( + ) +where + +-- getParser, + +-- import Relude + +-- getParser :: ZettelFormat -> ZettelParser +-- getParser = undefined diff --git a/neuron/src/app/Neuron/Zettelkasten/Format/Reader/Org.hs b/neuron/src/app/Neuron/Zettelkasten/Format/Reader/Org.hs new file mode 100644 index 000000000..3eedff405 --- /dev/null +++ b/neuron/src/app/Neuron/Zettelkasten/Format/Reader/Org.hs @@ -0,0 +1,60 @@ +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeApplications #-} +{-# LANGUAGE ViewPatterns #-} +{-# LANGUAGE NoImplicitPrelude #-} + +module Neuron.Zettelkasten.Format.Reader.Org + ( parseOrg, + ) +where + +import qualified Data.Map as Map +import Data.Text (toLower) +import qualified Data.YAML as Y +import qualified Data.YAML.Event as YE +import Neuron.Zettelkasten.Format.Reader.Type +import Relude +import Relude.Extra.Map (lookup) +import Text.Pandoc (def, runPure) +import Text.Pandoc.Definition (Pandoc) +import Text.Pandoc.Readers.Org (readOrg) +import Text.Pandoc.Util (getH1) + +parseOrg :: ZettelParser +parseOrg _ s = do + doc <- first show $ runPure $ readOrg def s + meta <- extractMetadata doc + pure (meta, doc) + +-- | Extract metadata from the properties that are attached to the first headline +extractMetadata :: Pandoc -> Either ZettelParseError (Maybe (Y.Node Y.Pos)) +extractMetadata doc + | Just ((_, _, Map.fromList -> properties), _) <- getH1 doc = do + let date = lookup "date" properties + tags = words <$> lookup "tags" properties + unlisted = parseUnlisted <$> lookup "unlisted" properties + pure $ + Just $ + Y.Mapping + noPos + yTagMap + $ Map.fromList $ + catMaybes + [ unlisted <&> \x -> + (Y.Scalar noPos $ Y.SStr "unlisted", Y.Scalar noPos $ Y.SBool x), + date <&> \x -> + (Y.Scalar noPos $ Y.SStr "date", Y.Scalar noPos $ Y.SStr x), + tags <&> \xs -> + ( Y.Scalar noPos $ Y.SStr "tags", + Y.Sequence noPos yTagSeq $ + xs <&> \x -> Y.Scalar noPos $ Y.SStr x + ) + ] + | otherwise = pure Nothing + where + noPos = Y.Pos 0 0 0 0 + yTagMap = YE.mkTag "tag:yaml.org,2002:map" + yTagSeq = YE.mkTag "tag:yaml.org,2002:seq" + parseUnlisted :: Text -> Bool + parseUnlisted a = toLower a == "true" diff --git a/neuron/src/lib/Neuron/Markdown.hs b/neuron/src/lib/Neuron/Markdown.hs index a6cd58c68..a5ae3013f 100644 --- a/neuron/src/lib/Neuron/Markdown.hs +++ b/neuron/src/lib/Neuron/Markdown.hs @@ -32,6 +32,7 @@ import qualified Commonmark.Pandoc as CP import Control.Monad.Combinators (manyTill) import Data.Tagged (Tagged (..)) import qualified Data.YAML as Y +import Neuron.Zettelkasten.Format.Reader.Type import Relude hiding (show, traceShowId) import qualified Text.Megaparsec as M import qualified Text.Megaparsec.Char as M @@ -41,10 +42,6 @@ import Text.Pandoc.Definition (Pandoc (..)) import qualified Text.Parsec as P import Text.Show (Show (show)) -type ZettelParser = FilePath -> Text -> Either ZettelParseError (Maybe (Y.Node Y.Pos), Pandoc) - -type ZettelParseError = Tagged "ZettelParserError" Text - -- | Parse Markdown document, along with the YAML metadata block in it. -- -- We are not using the Pandoc AST "metadata" field (as it is not clear whether diff --git a/neuron/src/lib/Neuron/Plugin.hs b/neuron/src/lib/Neuron/Plugin.hs index 8cfc33273..e4f8f07b2 100644 --- a/neuron/src/lib/Neuron/Plugin.hs +++ b/neuron/src/lib/Neuron/Plugin.hs @@ -22,7 +22,7 @@ import Data.Some import qualified Data.Text as T import Neuron.Frontend.Route (NeuronWebT) import Neuron.Frontend.Route.Data.Types -import Neuron.Markdown (NeuronSyntaxSpec, parseMarkdown) +import Neuron.Markdown (NeuronSyntaxSpec) import qualified Neuron.Plugin.Plugins.DirTree as DirTree import qualified Neuron.Plugin.Plugins.Links as Links import qualified Neuron.Plugin.Plugins.NeuronIgnore as NeuronIgnore @@ -30,6 +30,8 @@ import qualified Neuron.Plugin.Plugins.Tags as Tags import qualified Neuron.Plugin.Plugins.UpTree as UpTree import Neuron.Plugin.Type (Plugin (..)) import Neuron.Zettelkasten.Connection (ContextualConnection) +import Neuron.Zettelkasten.Format +import Neuron.Zettelkasten.Format.Reader.Type import Neuron.Zettelkasten.Graph.Type (ZettelGraph) import Neuron.Zettelkasten.ID (ZettelID) import Neuron.Zettelkasten.Resolver (ZIDRef) @@ -83,12 +85,12 @@ filterSources plugins t = do combiner = \ima f -> (fmap join . traverse f) =<< ima foldl' combiner (pure $ Just t) applyF -afterZettelParse :: PluginRegistry -> [(ZettelID, (FilePath, (Text, DMap PluginZettelData Identity)))] -> [ZettelC] -afterZettelParse plugins fs = do +afterZettelParse :: PluginRegistry -> (ZettelFormat -> ZettelParser) -> [(ZettelID, (FilePath, ZettelFormat, (Text, DMap PluginZettelData Identity)))] -> [ZettelC] +afterZettelParse plugins getParser fs = do let h = Map.elems plugins <&> \sp -> withSome sp $ \p (m, x) -> (m,) $ _plugin_afterZettelParse p m x - parseZettels (parseMarkdown $ markdownSpec plugins) fs <&> \x -> + parseZettels getParser fs <&> \x -> snd $ foldl' (\x1 f -> f x1) x h afterZettelRead :: MonadState (Map ZettelID ZIDRef) m => PluginRegistry -> DC.DirTree FilePath -> m () diff --git a/neuron/src/lib/Neuron/Plugin/Plugins/DirTree.hs b/neuron/src/lib/Neuron/Plugin/Plugins/DirTree.hs index d39602556..36f984b0f 100644 --- a/neuron/src/lib/Neuron/Plugin/Plugins/DirTree.hs +++ b/neuron/src/lib/Neuron/Plugin/Plugins/DirTree.hs @@ -37,6 +37,7 @@ import qualified Neuron.Plugin.Plugins.Links as Links import qualified Neuron.Plugin.Plugins.Tags as Tags import Neuron.Plugin.Type (Plugin (..)) import Neuron.Zettelkasten.Connection (Connection (Folgezettel, OrdinaryConnection), ContextualConnection) +import Neuron.Zettelkasten.Format import qualified Neuron.Zettelkasten.Graph as G import Neuron.Zettelkasten.Graph.Type (ZettelGraph) import Neuron.Zettelkasten.ID (ZettelID (ZettelID, unZettelID), indexZid) @@ -165,7 +166,7 @@ injectDirectoryZettels = \case DMap.singleton DirTree $ Identity $ DirZettel tags mparent (Just $ tagFromPath absPath) meta gets (Map.lookup dirZettelId) >>= \case - Just (ZIDRef_Available p s pluginDataPrev) -> do + Just (ZIDRef_Available p fmt s pluginDataPrev) -> do -- A zettel with this directory name was already registered. Deal with it. case runIdentity <$> DMap.lookup DirTree pluginDataPrev of Just _ -> do @@ -182,7 +183,7 @@ injectDirectoryZettels = \case [ guard (dirZettelId /= indexZid) >> parentDirTag absPath, parentDirTag p ] - newRef = ZIDRef_Available p s (DMap.union pluginData pluginDataPrev) + newRef = ZIDRef_Available p fmt s (DMap.union pluginData pluginDataPrev) modify $ Map.update (const $ Just newRef) dirZettelId Just ZIDRef_Ambiguous {} -> -- TODO: What to do here? @@ -190,7 +191,7 @@ injectDirectoryZettels = \case Nothing -> do -- Inject a new zettel corresponding to this directory, that is uniquely named. let pluginData = mkPluginData $ maybe Set.empty Set.singleton $ parentDirTag absPath - R.addZettel absPath dirZettelId pluginData $ do + R.addZettel absPath ZettelFormat_Markdown dirZettelId pluginData $ do -- Set an appropriate title (same as directory name) let heading = toText (takeFileName absPath) <> "/" pure $ "# " <> heading diff --git a/neuron/src/lib/Neuron/Plugin/Plugins/Links.hs b/neuron/src/lib/Neuron/Plugin/Plugins/Links.hs index 7922947a6..3de672d91 100644 --- a/neuron/src/lib/Neuron/Plugin/Plugins/Links.hs +++ b/neuron/src/lib/Neuron/Plugin/Plugins/Links.hs @@ -118,7 +118,7 @@ parseQueryLink attrs url = do let conn :: Connection = fromMaybe def $ readMaybe . toString =<< Map.lookup "title" (Map.fromList attrs) path <- asMarkdownPath url - zid <- getZettelID (toString path) + zid <- snd <$> getZettelID (toString path) pure (zid, conn) where -- Return .md file path, for the given link text. diff --git a/neuron/src/lib/Neuron/Zettelkasten/Format.hs b/neuron/src/lib/Neuron/Zettelkasten/Format.hs new file mode 100644 index 000000000..e5b033a3e --- /dev/null +++ b/neuron/src/lib/Neuron/Zettelkasten/Format.hs @@ -0,0 +1,30 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE DeriveFunctor #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE StandaloneDeriving #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE TupleSections #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE UndecidableInstances #-} +{-# LANGUAGE ViewPatterns #-} +{-# LANGUAGE NoImplicitPrelude #-} + +module Neuron.Zettelkasten.Format where + +import Data.Aeson hiding ((.:)) +import Relude + +-- TODO: rewrite json instance for brevity +data ZettelFormat + = ZettelFormat_Markdown + | ZettelFormat_Org + deriving (Eq, Show, Ord, Generic, ToJSON, FromJSON) diff --git a/neuron/src/lib/Neuron/Zettelkasten/Format/Reader/Type.hs b/neuron/src/lib/Neuron/Zettelkasten/Format/Reader/Type.hs new file mode 100644 index 000000000..d792a90b6 --- /dev/null +++ b/neuron/src/lib/Neuron/Zettelkasten/Format/Reader/Type.hs @@ -0,0 +1,26 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TypeApplications #-} +{-# LANGUAGE NoImplicitPrelude #-} + +module Neuron.Zettelkasten.Format.Reader.Type where + +import Data.Tagged (Tagged) +import qualified Data.YAML as Y +import Relude hiding (readEither, show) +import Text.Pandoc.Definition (Pandoc) + +type ZettelParser = + -- | Source filepath (used only in error messages) + FilePath -> + -- | Text to parse + Text -> + -- | Parse result: a tuple of YAML metadata and Pandoc AST. + Either + ZettelParseError + (Maybe (Y.Node Y.Pos), Pandoc) + +type ZettelParseError = Tagged "ZettelParserError" Text diff --git a/neuron/src/lib/Neuron/Zettelkasten/ID.hs b/neuron/src/lib/Neuron/Zettelkasten/ID.hs index 728bd44a6..fb3b74420 100644 --- a/neuron/src/lib/Neuron/Zettelkasten/ID.hs +++ b/neuron/src/lib/Neuron/Zettelkasten/ID.hs @@ -4,7 +4,9 @@ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TupleSections #-} {-# LANGUAGE TypeApplications #-} +{-# LANGUAGE ViewPatterns #-} {-# LANGUAGE NoImplicitPrelude #-} module Neuron.Zettelkasten.ID @@ -29,6 +31,7 @@ import Data.Aeson ToJSONKey (toJSONKey), ) import Data.Aeson.Types (toJSONKeyText) +import Neuron.Zettelkasten.Format import Relude hiding (traceShowId) import System.FilePath (splitExtension, takeFileName) import qualified Text.Megaparsec as M @@ -107,8 +110,11 @@ idParser' cs = do pure $ ZettelID $ toText s -- | Parse the ZettelID if the given filepath is a Markdown zettel. -getZettelID :: FilePath -> Maybe ZettelID +getZettelID :: FilePath -> Maybe (ZettelFormat, ZettelID) getZettelID fp = do - let (fileName, ext) = splitExtension $ takeFileName fp - guard $ ".md" == toText ext - rightToMaybe $ parseZettelID (toText fileName) + let (fileName, toText -> ext) = splitExtension $ takeFileName fp + fmt <- case ext of + ".md" -> pure ZettelFormat_Markdown + ".org" -> pure ZettelFormat_Org + _ -> Nothing + fmap (fmt,) $ rightToMaybe $ parseZettelID (toText fileName) diff --git a/neuron/src/lib/Neuron/Zettelkasten/Resolver.hs b/neuron/src/lib/Neuron/Zettelkasten/Resolver.hs index ee839c72c..6d8657475 100644 --- a/neuron/src/lib/Neuron/Zettelkasten/Resolver.hs +++ b/neuron/src/lib/Neuron/Zettelkasten/Resolver.hs @@ -12,6 +12,7 @@ module Neuron.Zettelkasten.Resolver where import Data.Dependent.Map (DMap) import qualified Data.Map.Strict as Map +import Neuron.Zettelkasten.Format import Neuron.Zettelkasten.ID (ZettelID, getZettelID) import Neuron.Zettelkasten.Zettel (PluginZettelData) import Relude hiding (traceShowId) @@ -20,7 +21,7 @@ import qualified System.Directory.Contents.Types as DC -- | What does a Zettel ID refer to? data ZIDRef = -- | The ZID maps to a file on disk with the given contents - ZIDRef_Available FilePath !Text (DMap PluginZettelData Identity) + ZIDRef_Available FilePath ZettelFormat !Text (DMap PluginZettelData Identity) | -- | The ZID maps to more than one file, hence ambiguous. ZIDRef_Ambiguous (NonEmpty FilePath) deriving (Eq, Show) @@ -32,8 +33,8 @@ resolveZidRefsFromDirTree :: StateT (Map ZettelID ZIDRef) m () resolveZidRefsFromDirTree readFileF = \case DC.DirTree_File relPath _ -> do - whenJust (getZettelID relPath) $ \zid -> do - addZettel relPath zid mempty $ + whenJust (getZettelID relPath) $ \(fmt, zid) -> do + addZettel relPath fmt zid mempty $ lift $ readFileF relPath DC.DirTree_Dir _absPath contents -> do forM_ (Map.toList contents) $ \(_, ct) -> @@ -42,10 +43,10 @@ resolveZidRefsFromDirTree readFileF = \case -- We ignore symlinks, and paths configured to be excluded. pure () -addZettel :: MonadState (Map ZettelID ZIDRef) m => FilePath -> ZettelID -> DMap PluginZettelData Identity -> m Text -> m () -addZettel zpath zid pluginData ms = do +addZettel :: MonadState (Map ZettelID ZIDRef) m => FilePath -> ZettelFormat -> ZettelID -> DMap PluginZettelData Identity -> m Text -> m () +addZettel zpath fmt zid pluginData ms = do gets (Map.lookup zid) >>= \case - Just (ZIDRef_Available oldPath _s _m) -> do + Just (ZIDRef_Available oldPath _fmt _s _m) -> do -- The zettel ID is already used by `oldPath`. Mark it as a dup. modify $ Map.insert zid (ZIDRef_Ambiguous $ zpath :| [oldPath]) Just (ZIDRef_Ambiguous (toList -> ambiguities)) -> do @@ -53,7 +54,7 @@ addZettel zpath zid pluginData ms = do markAmbiguous zid $ zpath :| ambiguities Nothing -> do s <- ms - modify $ Map.insert zid (ZIDRef_Available zpath s pluginData) + modify $ Map.insert zid (ZIDRef_Available zpath fmt s pluginData) markAmbiguous :: (MonadState (Map ZettelID ZIDRef) m) => ZettelID -> NonEmpty FilePath -> m () markAmbiguous zid fs = diff --git a/neuron/src/lib/Neuron/Zettelkasten/Zettel.hs b/neuron/src/lib/Neuron/Zettelkasten/Zettel.hs index ad454d2c3..0dbdb9660 100644 --- a/neuron/src/lib/Neuron/Zettelkasten/Zettel.hs +++ b/neuron/src/lib/Neuron/Zettelkasten/Zettel.hs @@ -39,6 +39,7 @@ import Data.YAML (FromYAML (parseYAML), (.:)) import qualified Data.YAML as Y import Neuron.Markdown (ZettelParseError) import Neuron.Zettelkasten.Connection (Connection) +import Neuron.Zettelkasten.Format import Neuron.Zettelkasten.ID (Slug, ZettelID) import Relude hiding (show) import Text.Pandoc.Builder (Block) @@ -141,6 +142,7 @@ data ZettelT c = Zettel zettelSlug :: Slug, -- | Relative path to this zettel in the zettelkasten directory zettelPath :: FilePath, + zettelFormat :: ZettelFormat, zettelTitle :: Text, -- | Whether the title was infered from the body. Used when conditionally -- rendering the title in HTML. diff --git a/neuron/src/lib/Neuron/Zettelkasten/Zettel/Parser.hs b/neuron/src/lib/Neuron/Zettelkasten/Zettel/Parser.hs index d70cb0e55..01f1fbfd7 100644 --- a/neuron/src/lib/Neuron/Zettelkasten/Zettel/Parser.hs +++ b/neuron/src/lib/Neuron/Zettelkasten/Zettel/Parser.hs @@ -14,6 +14,7 @@ import Data.Dependent.Map (DMap) import qualified Data.Text as T import qualified Data.YAML as Y import Neuron.Markdown +import Neuron.Zettelkasten.Format import Neuron.Zettelkasten.ID (Slug, ZettelID (unZettelID)) import Neuron.Zettelkasten.Zettel import qualified Neuron.Zettelkasten.Zettel.Meta as Meta @@ -21,15 +22,16 @@ import Relude import qualified Text.Pandoc.Util as P parseZettel :: - ZettelParser -> + (ZettelFormat -> ZettelParser) -> FilePath -> + ZettelFormat -> ZettelID -> Text -> DMap PluginZettelData Identity -> (Maybe (Y.Node Y.Pos), ZettelC) -parseZettel parser fn zid s pluginData = +parseZettel getParser fn fmt zid s pluginData = either unparseableZettel id $ do - (yamlNode, doc) <- parser fn s + (yamlNode, doc) <- getParser fmt fn s meta :: Maybe Meta.Meta <- parseYamlNode @Meta.Meta `traverse` yamlNode let -- Determine zettel title (title, titleInBody) = case Meta.title =<< meta of @@ -40,11 +42,11 @@ parseZettel parser fn zid s pluginData = date = Meta.date =<< meta slug = fromMaybe (mkDefaultSlug $ unZettelID zid) $ Meta.slug =<< meta unlisted = Just True == (Meta.unlisted =<< meta) - pure $ (yamlNode,) $ Right $ Zettel zid slug fn title titleInBody date unlisted doc pluginData + pure $ (yamlNode,) $ Right $ Zettel zid slug fn fmt title titleInBody date unlisted doc pluginData where unparseableZettel err = let slug = mkDefaultSlug $ unZettelID zid - in (Nothing,) $ Left $ Zettel zid slug fn "Unknown" False Nothing False (s, err) pluginData + in (Nothing,) $ Left $ Zettel zid slug fn fmt "Unknown" False Nothing False (s, err) pluginData mkDefaultSlug :: Text -> Slug mkDefaultSlug ss = foldl' (\s' x -> T.replace x "-" s') (T.toLower ss) (charsDisallowedInURL <> [" "]) @@ -54,9 +56,9 @@ parseZettel parser fn zid s pluginData = -- | Like `parseZettel` but operates on multiple files. parseZettels :: - ZettelParser -> - [(ZettelID, (FilePath, (Text, DMap PluginZettelData Identity)))] -> + (ZettelFormat -> ZettelParser) -> + [(ZettelID, (FilePath, ZettelFormat, (Text, DMap PluginZettelData Identity)))] -> [(Maybe (Y.Node Y.Pos), ZettelC)] -parseZettels p files = - flip fmap files $ \(zid, (path, (s, pluginData))) -> - parseZettel p path zid s pluginData +parseZettels getParser files = + flip fmap files $ \(zid, (path, fmt, (s, pluginData))) -> + parseZettel getParser path fmt zid s pluginData diff --git a/neuron/test/Neuron/Zettelkasten/IDSpec.hs b/neuron/test/Neuron/Zettelkasten/IDSpec.hs index 695bae81e..589bc8b6d 100644 --- a/neuron/test/Neuron/Zettelkasten/IDSpec.hs +++ b/neuron/test/Neuron/Zettelkasten/IDSpec.hs @@ -7,6 +7,7 @@ module Neuron.Zettelkasten.IDSpec where import qualified Data.Aeson as Aeson +import Neuron.Zettelkasten.Format (ZettelFormat (ZettelFormat_Markdown)) import qualified Neuron.Zettelkasten.ID as Z import Relude import Test.Hspec @@ -19,7 +20,7 @@ spec = do it "parses a custom zettel ID" $ do Z.parseZettelID "20abcde" `shouldBe` Right zid it "parses a custom zettel ID from zettel filename" $ do - Z.getZettelID "20abcde.md" `shouldBe` Just zid + Z.getZettelID "20abcde.md" `shouldBe` Just (ZettelFormat_Markdown, zid) Z.zettelIDSourceFileName zid `shouldBe` "20abcde.md" let deceptiveZid = Z.ZettelID "2136537e" it "parses a custom zettel ID that looks like date ID" $ do diff --git a/neuron/test/Neuron/Zettelkasten/ZettelSpec.hs b/neuron/test/Neuron/Zettelkasten/ZettelSpec.hs index 7cc07cf78..47058bdd5 100644 --- a/neuron/test/Neuron/Zettelkasten/ZettelSpec.hs +++ b/neuron/test/Neuron/Zettelkasten/ZettelSpec.hs @@ -15,6 +15,7 @@ import Data.Time.LocalTime ( LocalTime (LocalTime), TimeOfDay (TimeOfDay), ) +import Neuron.Zettelkasten.Format import Neuron.Zettelkasten.ID (ZettelID (ZettelID)) import Neuron.Zettelkasten.Zettel ( MetadataOnly, @@ -41,6 +42,7 @@ spec = do (ZettelID s) s ".md" + ZettelFormat_Markdown "Some title" False -- (Set.fromList [Tag "science", Tag "journal/class"])