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
This commit is contained in:
Simon Michael 2020-06-02 17:13:07 -07:00
parent 9bc8c5d668
commit 00e9e844ac
2 changed files with 27 additions and 18 deletions

View File

@ -240,19 +240,23 @@ directivep = (do
] ]
) <?> "directive" ) <?> "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 :: MonadIO m => ErroringJournalParser m ()
includedirectivep = do includedirectivep = do
string "include" string "include"
lift (skipSome spacenonewline) 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 parentoff <- getOffset
parentpos <- getSourcePos parentpos <- getSourcePos
let (mprefix,glob) = splitReaderPrefix prefixedglob
filepaths <- getFilePaths parentoff parentpos filename paths <- getFilePaths parentoff parentpos glob
let prefixedpaths = case mprefix of
forM_ filepaths $ parseChild parentpos Nothing -> paths
Just fmt -> map ((fmt++":")++) paths
forM_ prefixedpaths $ parseChild parentpos
void newline void newline
where where
@ -275,10 +279,11 @@ includedirectivep = do
else customFailure $ parseErrorAt parseroff $ else customFailure $ parseErrorAt parseroff $
"No existing files match pattern: " ++ filename "No existing files match pattern: " ++ filename
parseChild :: MonadIO m => SourcePos -> FilePath -> ErroringJournalParser m () parseChild :: MonadIO m => SourcePos -> PrefixedFilePath -> ErroringJournalParser m ()
parseChild parentpos filepath = do parseChild parentpos prefixedpath = do
parentj <- get let (_mprefix,filepath) = splitReaderPrefix prefixedpath
parentj <- get
let parentfilestack = jincludefilestack parentj let parentfilestack = jincludefilestack parentj
when (filepath `elem` parentfilestack) $ when (filepath `elem` parentfilestack) $
Fail.fail ("Cyclic include: " ++ filepath) Fail.fail ("Cyclic include: " ++ filepath)
@ -289,7 +294,7 @@ includedirectivep = do
-- Choose a reader/format based on the file path, or fall back -- Choose a reader/format based on the file path, or fall back
-- on journal. Duplicating readJournal a bit here. -- 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 parser = rParser r
dbg1IO "trying reader" (rFormat r) dbg1IO "trying reader" (rFormat r)
updatedChildj <- journalAddFile (filepath, childInput) <$> updatedChildj <- journalAddFile (filepath, childInput) <$>

View File

@ -878,16 +878,20 @@ See also [comments](#comments).
You can pull in the content of additional files by writing an include directive, like this: You can pull in the content of additional files by writing an include directive, like this:
```journal ```journal
include path/to/file.journal include FILEPATH
``` ```
If the path does not begin with a slash, it is relative to the current file. Only journal files can include, and only journal, timeclock or timedot files can be included (not CSV files, currently).
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. `*`).
The `include` directive can only be used in journal files. If the file path does not begin with a slash, it is relative to the current file's folder.
It can include journal, timeclock or timedot files, but not CSV files.
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 ### Default year