From 00e9e844ac591de396a0884a87f4a28392556be5 Mon Sep 17 00:00:00 2001 From: Simon Michael Date: Tue, 2 Jun 2020 17:13:07 -0700 Subject: [PATCH] journal: the include directive now accepts a file format prefix This works with glob patterns too, applying the prefix to each path. This can be useful when included files don't have the standard file extension, eg: include timedot:2020*.md --- hledger-lib/Hledger/Read/JournalReader.hs | 27 ++++++++++++++--------- hledger-lib/hledger_journal.m4.md | 18 +++++++++------ 2 files changed, 27 insertions(+), 18 deletions(-) diff --git a/hledger-lib/Hledger/Read/JournalReader.hs b/hledger-lib/Hledger/Read/JournalReader.hs index 9810ea9aa..0557b7893 100644 --- a/hledger-lib/Hledger/Read/JournalReader.hs +++ b/hledger-lib/Hledger/Read/JournalReader.hs @@ -240,19 +240,23 @@ directivep = (do ] ) "directive" +-- | Parse an include directive. include's argument is an optionally +-- file-format-prefixed file path or glob pattern. In the latter case, +-- the prefix is applied to each matched path. Examples: +-- foo.j, foo/bar.j, timedot:foo/2020*.md includedirectivep :: MonadIO m => ErroringJournalParser m () includedirectivep = do string "include" lift (skipSome spacenonewline) - filename <- T.unpack <$> takeWhileP Nothing (/= '\n') -- don't consume newline yet - + prefixedglob <- T.unpack <$> takeWhileP Nothing (/= '\n') -- don't consume newline yet parentoff <- getOffset parentpos <- getSourcePos - - filepaths <- getFilePaths parentoff parentpos filename - - forM_ filepaths $ parseChild parentpos - + let (mprefix,glob) = splitReaderPrefix prefixedglob + paths <- getFilePaths parentoff parentpos glob + let prefixedpaths = case mprefix of + Nothing -> paths + Just fmt -> map ((fmt++":")++) paths + forM_ prefixedpaths $ parseChild parentpos void newline where @@ -275,10 +279,11 @@ includedirectivep = do else customFailure $ parseErrorAt parseroff $ "No existing files match pattern: " ++ filename - parseChild :: MonadIO m => SourcePos -> FilePath -> ErroringJournalParser m () - parseChild parentpos filepath = do - parentj <- get + parseChild :: MonadIO m => SourcePos -> PrefixedFilePath -> ErroringJournalParser m () + parseChild parentpos prefixedpath = do + let (_mprefix,filepath) = splitReaderPrefix prefixedpath + parentj <- get let parentfilestack = jincludefilestack parentj when (filepath `elem` parentfilestack) $ Fail.fail ("Cyclic include: " ++ filepath) @@ -289,7 +294,7 @@ includedirectivep = do -- Choose a reader/format based on the file path, or fall back -- on journal. Duplicating readJournal a bit here. - let r = fromMaybe reader $ findReader Nothing (Just filepath) + let r = fromMaybe reader $ findReader Nothing (Just prefixedpath) parser = rParser r dbg1IO "trying reader" (rFormat r) updatedChildj <- journalAddFile (filepath, childInput) <$> diff --git a/hledger-lib/hledger_journal.m4.md b/hledger-lib/hledger_journal.m4.md index 668409456..714604309 100644 --- a/hledger-lib/hledger_journal.m4.md +++ b/hledger-lib/hledger_journal.m4.md @@ -878,16 +878,20 @@ See also [comments](#comments). You can pull in the content of additional files by writing an include directive, like this: ```journal -include path/to/file.journal +include FILEPATH ``` -If the path does not begin with a slash, it is relative to the current file. -The include file path may contain -[common glob patterns](https://hackage.haskell.org/package/Glob-0.9.2/docs/System-FilePath-Glob.html#v:compile) -(e.g. `*`). +Only journal files can include, and only journal, timeclock or timedot files can be included (not CSV files, currently). -The `include` directive can only be used in journal files. -It can include journal, timeclock or timedot files, but not CSV files. +If the file path does not begin with a slash, it is relative to the current file's folder. + +It may contain [glob patterns] to match multiple files, eg: `include *.journal`. + +Or a tilde, meaning home directory: `include ~/main.journal`. + +It may also be prefixed to force a specific file format, overriding the file extension (as described in [hledger.1 -> Input files](hledger.html#input-files)): `include timedot:~/notes/2020*.md`. + +[glob patterns]: https://hackage.haskell.org/package/Glob-0.9.2/docs/System-FilePath-Glob.html#v:compile ### Default year