run: split run and repl, fix journal passed to nested "run"s
This commit is contained in:
parent
a7116a8b0f
commit
61faca16e4
@ -449,6 +449,7 @@ src/hledger/
|
||||
README.md
|
||||
Register.md
|
||||
Rewrite.md
|
||||
Repl.md
|
||||
Roi.md
|
||||
Run.md
|
||||
Stats.md
|
||||
|
||||
@ -429,8 +429,9 @@ main = withGhcDebug' $ do
|
||||
ensureJournalFileExists . NE.head =<< journalFilePathFromOpts opts
|
||||
withJournalDo opts (cmdaction opts)
|
||||
|
||||
-- 6.5.4. run needs findBuiltinCommands passed to it to avoid circular dependency in the code
|
||||
| cmdname == "run" -> do withJournalDo opts $ Hledger.Cli.Commands.Run.run findBuiltinCommand opts
|
||||
-- 6.5.4. "run" and "repl" need findBuiltinCommands passed to it to avoid circular dependency in the code
|
||||
| cmdname == "run" -> Hledger.Cli.Commands.Run.run Nothing findBuiltinCommand opts
|
||||
| cmdname == "repl" -> Hledger.Cli.Commands.Run.repl findBuiltinCommand opts
|
||||
|
||||
-- 6.5.5. all other builtin commands - read the journal and if successful run the command with it
|
||||
| otherwise -> withJournalDo opts $ cmdaction opts
|
||||
|
||||
@ -136,7 +136,8 @@ builtinCommands = [
|
||||
,(registermode , register)
|
||||
,(rewritemode , rewrite)
|
||||
,(roimode , roi)
|
||||
,(runmode , run')
|
||||
,(runmode , runOrReplStub)
|
||||
,(replmode , runOrReplStub)
|
||||
,(statsmode , stats)
|
||||
,(tagsmode , tags)
|
||||
,(testmode , testcmd)
|
||||
|
||||
45
hledger/Hledger/Cli/Commands/Repl.md
Normal file
45
hledger/Hledger/Cli/Commands/Repl.md
Normal file
@ -0,0 +1,45 @@
|
||||
## repl
|
||||
|
||||
Runs hledger commands interactively.
|
||||
|
||||
This command is EXPERIMENTAL and could change in the future.
|
||||
|
||||
```flags
|
||||
Flags:
|
||||
no command-specific flags
|
||||
```
|
||||
|
||||
This command starts a read-eval-print loop (REPL) where you can enter commands interactively. To exit REPL, use "exit" or "quit", or send EOF.
|
||||
|
||||
It could also accept commands from standard input, if you pipe commands into it.
|
||||
|
||||
The commands will run more quickly than if run individually, because the input files would be parsed only once.
|
||||
|
||||
Syntax of the commands is intentionally simple:
|
||||
- each line is a single hledger command
|
||||
- lines that can't be interpreted as hledger commands are printed out as-is
|
||||
- empty lines are skipped
|
||||
- everything after `#` is considered to be a comment and will be ignored, and will not be printed out
|
||||
- `echo <text>` will print out text, even if it could be recognized as a hledger command
|
||||
|
||||
You can use single quotes or double quotes to quote aguments that need quoting.
|
||||
|
||||
### Caveats:
|
||||
|
||||
- `Repl`, like any other command, will load the input file(s) (specified by `LEDGER_JOURNAL` or by `-f` arguments). The contents of those files would be used by all the commands that `repl` runs. If you want a particular command to use a different input file, you can use `-f` flag for that particular command. This will override (not add) the input for that particular command. All the input files would be cached, and would be read only once.
|
||||
|
||||
### Examples:
|
||||
|
||||
To start the REPL:
|
||||
```cli
|
||||
hledger repl
|
||||
```
|
||||
or
|
||||
```cli
|
||||
hledger repl -f some.journal
|
||||
```
|
||||
|
||||
To pipe commands into REPL:
|
||||
```cli
|
||||
(echo "files"; echo "stats") | hledger repl -f some.journal
|
||||
```
|
||||
51
hledger/Hledger/Cli/Commands/Repl.txt
Normal file
51
hledger/Hledger/Cli/Commands/Repl.txt
Normal file
@ -0,0 +1,51 @@
|
||||
repl
|
||||
|
||||
Runs hledger commands interactively.
|
||||
|
||||
This command is EXPERIMENTAL and could change in the future.
|
||||
|
||||
Flags:
|
||||
no command-specific flags
|
||||
|
||||
This command starts a read-eval-print loop (REPL) where you can enter
|
||||
commands interactively. To exit REPL, use "exit" or "quit", or send EOF.
|
||||
|
||||
It could also accept commands from standard input, if you pipe commands
|
||||
into it.
|
||||
|
||||
The commands will run more quickly than if run individually, because the
|
||||
input files would be parsed only once.
|
||||
|
||||
Syntax of the commands is intentionally simple: - each line is a single
|
||||
hledger command - lines that can't be interpreted as hledger commands
|
||||
are printed out as-is - empty lines are skipped - everything after # is
|
||||
considered to be a comment and will be ignored, and will not be printed
|
||||
out - echo <text> will print out text, even if it could be recognized as
|
||||
a hledger command
|
||||
|
||||
You can use single quotes or double quotes to quote aguments that need
|
||||
quoting.
|
||||
|
||||
Caveats:
|
||||
|
||||
- Repl, like any other command, will load the input file(s) (specified
|
||||
by LEDGER_JOURNAL or by -f arguments). The contents of those files
|
||||
would be used by all the commands that repl runs. If you want a
|
||||
particular command to use a different input file, you can use -f
|
||||
flag for that particular command. This will override (not add) the
|
||||
input for that particular command. All the input files would be
|
||||
cached, and would be read only once.
|
||||
|
||||
Examples:
|
||||
|
||||
To start the REPL:
|
||||
|
||||
hledger repl
|
||||
|
||||
or
|
||||
|
||||
hledger repl -f some.journal
|
||||
|
||||
To pipe commands into REPL:
|
||||
|
||||
(echo "files"; echo "stats") | hledger repl -f some.journal
|
||||
@ -12,7 +12,9 @@ The @run@ command allows you to run multiple commands via REPL or from the suppl
|
||||
module Hledger.Cli.Commands.Run (
|
||||
runmode
|
||||
,run
|
||||
,run'
|
||||
,replmode
|
||||
,repl
|
||||
,runOrReplStub
|
||||
) where
|
||||
|
||||
import qualified Data.Map.Strict as Map
|
||||
@ -46,23 +48,30 @@ runmode = hledgerCommandMode
|
||||
hiddenflags
|
||||
([], Just $ argsFlag "[COMMANDS_FILE1 COMMANDS_FILE2 ...] OR [command1 args... -- command2 args... -- command3 args...]")
|
||||
|
||||
-- | The fake run command introduced to break circular dependency.
|
||||
replmode = hledgerCommandMode
|
||||
$(embedFileRelative "Hledger/Cli/Commands/Repl.txt")
|
||||
(
|
||||
[]
|
||||
)
|
||||
cligeneralflagsgroups1
|
||||
hiddenflags
|
||||
([], Nothing)
|
||||
|
||||
-- | The fake run/repl command introduced to break circular dependency.
|
||||
-- This module needs access to `findBuiltinCommand`, which is defined in Hledger.Cli.Commands
|
||||
-- However, Hledger.Cli.Commands imports this module, which creates circular dependency.
|
||||
-- We expose this do-nothing function so that it could be included in the list of all commands inside
|
||||
-- Hledger.Cli.Commands and ensure that "run" is recognized as a valid command by the Hledger.Cli top-level
|
||||
-- command line parser. That parser, however, would not call run'. It has a special case for "run", and
|
||||
-- will call "run" (see below), passing it `findBuiltinCommand`, thus breaking circular dependency.
|
||||
run' :: CliOpts -> Journal -> IO ()
|
||||
run' _opts _j = return ()
|
||||
runOrReplStub :: CliOpts -> Journal -> IO ()
|
||||
runOrReplStub _opts _j = return ()
|
||||
|
||||
-- | The actual run command.
|
||||
run :: (String -> Maybe (Mode RawOpts, CliOpts -> Journal -> IO ())) -> CliOpts -> Journal -> IO ()
|
||||
run findBuiltinCommand CliOpts{rawopts_=rawopts} j = do
|
||||
let args = dbg1 "args" $ listofstringopt "args" rawopts
|
||||
if args == []
|
||||
then runREPL j findBuiltinCommand
|
||||
else do
|
||||
run :: Maybe Journal -> (String -> Maybe (Mode RawOpts, CliOpts -> Journal -> IO ())) -> CliOpts -> IO ()
|
||||
run defaultJournalOverride findBuiltinCommand cliopts@CliOpts{rawopts_=rawopts} = do
|
||||
withJournalCached defaultJournalOverride cliopts $ \j -> do
|
||||
let args = dbg1 "args" $ listofstringopt "args" rawopts
|
||||
-- Check if arguments could be interpreted as files.
|
||||
-- If not, assume that they are commands specified directly on the command line
|
||||
allAreFiles <- and <$> mapM (doesFileExist . snd . splitReaderPrefix) args
|
||||
@ -70,6 +79,12 @@ run findBuiltinCommand CliOpts{rawopts_=rawopts} j = do
|
||||
True -> runFromFiles j findBuiltinCommand args
|
||||
False -> runFromArgs j findBuiltinCommand args
|
||||
|
||||
-- | The actual repl command.
|
||||
repl :: (String -> Maybe (Mode RawOpts, CliOpts -> Journal -> IO ())) -> CliOpts -> IO ()
|
||||
repl findBuiltinCommand cliopts = do
|
||||
withJournalCached Nothing cliopts $ \j -> do
|
||||
runREPL j findBuiltinCommand
|
||||
|
||||
-- | Run commands from files given to "run".
|
||||
runFromFiles :: Journal -> (String -> Maybe (Mode RawOpts, CliOpts -> Journal -> IO ())) -> [String] -> IO ()
|
||||
runFromFiles defaultJrnl findBuiltinCommand inputfiles = do
|
||||
@ -105,14 +120,15 @@ runCommand defaultJrnl findBuiltinCommand cmdline = do
|
||||
case findBuiltinCommand cmdname of
|
||||
Nothing -> putStrLn $ unwords (cmdname:args)
|
||||
Just (cmdmode,cmdaction) -> do
|
||||
-- Allow "run" to call "run"
|
||||
let cmdaction' = if cmdname == "run" then run findBuiltinCommand else cmdaction
|
||||
-- Even though expandArgsAt is done by the Cli.hs, it stops at the first '--', so we need
|
||||
-- to do it here as well to make sure that each command can use @ARGFILEs
|
||||
args' <- replaceNumericFlags <$> expandArgsAt args
|
||||
dbg1IO "runCommand final args" (cmdname,args')
|
||||
opts <- getHledgerCliOpts' cmdmode args'
|
||||
withJournalCached defaultJrnl opts (cmdaction' opts)
|
||||
withJournalCached (Just defaultJrnl) opts $ \j -> do
|
||||
if cmdname == "run" -- allow "run" to call "run"
|
||||
then run (Just j) findBuiltinCommand opts
|
||||
else cmdaction opts j
|
||||
[] -> return ()
|
||||
|
||||
-- | Run an interactive REPL.
|
||||
@ -139,14 +155,19 @@ journalCache = unsafePerformIO $ newMVar Map.empty
|
||||
{-# NOINLINE journalCache #-}
|
||||
|
||||
-- | Similar to `withJournal`, but uses caches all the journals it reads.
|
||||
withJournalCached :: Journal -> CliOpts -> (Journal -> IO ()) -> IO ()
|
||||
withJournalCached defaultJrnl cliopts cmd = do
|
||||
mbjournalpaths <- journalFilePathFromOptsNoDefault cliopts
|
||||
j <- case mbjournalpaths of
|
||||
Nothing -> return defaultJrnl -- use the journal given to the "run" itself
|
||||
Just journalpaths -> journalTransform cliopts . sconcat <$> mapM (readAndCacheJournalFile (inputopts_ cliopts)) journalpaths
|
||||
withJournalCached :: Maybe Journal -> CliOpts -> (Journal -> IO ()) -> IO ()
|
||||
withJournalCached defaultJournalOverride cliopts cmd = do
|
||||
j <- case defaultJournalOverride of
|
||||
Nothing -> journalFilePathFromOpts cliopts >>= readFiles
|
||||
Just defaultJrnl -> do
|
||||
mbjournalpaths <- journalFilePathFromOptsNoDefault cliopts
|
||||
case mbjournalpaths of
|
||||
Nothing -> return defaultJrnl -- use the journal given to the "run" itself
|
||||
Just journalpaths -> readFiles journalpaths
|
||||
cmd j
|
||||
where
|
||||
readFiles journalpaths =
|
||||
journalTransform cliopts . sconcat <$> mapM (readAndCacheJournalFile (inputopts_ cliopts)) journalpaths
|
||||
-- | Read a journal file, caching it if it has not been read before.
|
||||
readAndCacheJournalFile :: InputOpts -> PrefixedFilePath -> IO Journal
|
||||
readAndCacheJournalFile iopts fp | snd (splitReaderPrefix fp) == "-" = do
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
## run
|
||||
|
||||
Runs a sequence of hledger commands on the same input file(s), either interactively or as a script.
|
||||
Runs a sequence of hledger commands on the same input file(s), taking them from the command line or from file(s).
|
||||
|
||||
This command is EXPERIMENTAL and syntax could change in the future.
|
||||
|
||||
@ -11,20 +11,19 @@ no command-specific flags
|
||||
|
||||
The commands will run more quickly than if run individually, because the input files would be parsed only once.
|
||||
|
||||
"run" has three ways of invocation:
|
||||
- when invoked without arguments, it start a read-eval-print loop (REPL) where you can enter commands interactively. To exit REPL, use "exit" or "quit", or send EOF.
|
||||
"run" has two ways of invocation:
|
||||
|
||||
- when file names are given to "run", it will read commands from these files, in order.
|
||||
- when all positional arguments of "run" are valid file names, "run" will read commands from these files, in order: `run -f some.journal file1.txt file2.txt file3.txt`.
|
||||
|
||||
- lastly, commands could be specified directly on the command line. All commands (including the very first one) should be preceded by argument "--"
|
||||
- commands could be specified directly on the command line. All commands (including the very first one) should be preceded by argument "--": `run -f some.journal -- cmd1 -- cmd2 -- cmd3`.
|
||||
|
||||
Syntax of the commands (either in the file, or in REPL) is intentionally simple:
|
||||
- each line is a single hledger command
|
||||
Syntax of the command is intentionally simple:
|
||||
- each line read from a file is a single hledger command
|
||||
- lines that can't be interpreted as hledger commands are printed out as-is
|
||||
- empty lines are skipped
|
||||
- everything after `#` is considered to be a comment and will be ignored, and will not be printed out
|
||||
- `echo <text>` will print out text, even if it could be recognized as a hledger command
|
||||
- `run` is a valid command to give use as well, so you can have `run` call `run` if you want to.
|
||||
- `run` is a valid command to use as well, so you can have `run` call `run` if you want to.
|
||||
|
||||
You can use single quotes or double quotes to quote aguments that need quoting.
|
||||
|
||||
@ -34,20 +33,10 @@ You can use `#!/usr/bin/env hledger run` in the first line of the file to make i
|
||||
|
||||
- If you meant to provide file name as an argument, but made a mistake and a gave file name that does not exist, "run" will attempt to interpret it as a command.
|
||||
|
||||
- `Run`, like any other command, will load the input file(s) (specified by `LEDGER_JOURNAL` or by `-f` arguments). The contents of those files would be used by all the commands that `run` runs. If you want a particular command to use a different input file, you can use `-f` flag for that particular command. This will override (not add) the input for that particular command. All the files read would be cached, and would be read only once.
|
||||
- `Run`, like any other command, will load the input file(s) (specified by `LEDGER_JOURNAL` or by `-f` arguments). The contents of those files would be used by all the commands that `run` runs. If you want a particular command to use a different input file, you can use `-f` flag for that particular command. This will override (not add) the input for that particular command. All the input files would be cached, and would be read only once.
|
||||
|
||||
### Examples:
|
||||
|
||||
To start the REPL:
|
||||
```cli
|
||||
hledger run
|
||||
```
|
||||
or
|
||||
```cli
|
||||
hledger run -f some.journal
|
||||
```
|
||||
|
||||
|
||||
To provide commands on the command line, separate them with `--`:
|
||||
```cli
|
||||
hledger run -f some.journal -- balance assets --depth 2 -- balance liabilities -f /some/other.journal --depth 3 --transpose -- stats
|
||||
@ -56,11 +45,11 @@ This would load `some.journal`, run `balance assets --depth 2` on it, then run `
|
||||
|
||||
To provide commands in the file, as a runnable scripts:
|
||||
```cli
|
||||
#!/usr/bin/env -S hledger run
|
||||
echo "List of accounts"
|
||||
#!/usr/bin/env -S hledger run -f some.journal
|
||||
echo "List of accounts in some.journal"
|
||||
accounts
|
||||
|
||||
echo "Assets"
|
||||
echo "Assets of some.journal"
|
||||
balance assets --depth 2
|
||||
|
||||
echo "Liabilities from /some/other.journal"
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
run
|
||||
|
||||
Runs a sequence of hledger commands on the same input file(s), either
|
||||
interactively or as a script.
|
||||
Runs a sequence of hledger commands on the same input file(s), taking
|
||||
them from the command line or from file(s).
|
||||
|
||||
This command is EXPERIMENTAL and syntax could change in the future.
|
||||
|
||||
@ -11,25 +11,23 @@ no command-specific flags
|
||||
The commands will run more quickly than if run individually, because the
|
||||
input files would be parsed only once.
|
||||
|
||||
"run" has three ways of invocation: - when invoked without arguments, it
|
||||
start a read-eval-print loop (REPL) where you can enter commands
|
||||
interactively. To exit REPL, use "exit" or "quit", or send EOF.
|
||||
"run" has two ways of invocation:
|
||||
|
||||
- when file names are given to "run", it will read commands from these
|
||||
files, in order.
|
||||
- when all positional arguments of "run" are valid file names, "run"
|
||||
will read commands from these files, in order:
|
||||
run -f some.journal file1.txt file2.txt file3.txt.
|
||||
|
||||
- lastly, commands could be specified directly on the command line.
|
||||
All commands (including the very first one) should be preceded by
|
||||
argument "--"
|
||||
- commands could be specified directly on the command line. All
|
||||
commands (including the very first one) should be preceded by
|
||||
argument "--": run -f some.journal -- cmd1 -- cmd2 -- cmd3.
|
||||
|
||||
Syntax of the commands (either in the file, or in REPL) is intentionally
|
||||
simple: - each line is a single hledger command - lines that can't be
|
||||
interpreted as hledger commands are printed out as-is - empty lines are
|
||||
skipped - everything after # is considered to be a comment and will be
|
||||
ignored, and will not be printed out - echo <text> will print out text,
|
||||
even if it could be recognized as a hledger command - run is a valid
|
||||
command to give use as well, so you can have run call run if you want
|
||||
to.
|
||||
Syntax of the command is intentionally simple: - each line read from a
|
||||
file is a single hledger command - lines that can't be interpreted as
|
||||
hledger commands are printed out as-is - empty lines are skipped -
|
||||
everything after # is considered to be a comment and will be ignored,
|
||||
and will not be printed out - echo <text> will print out text, even if
|
||||
it could be recognized as a hledger command - run is a valid command to
|
||||
use as well, so you can have run call run if you want to.
|
||||
|
||||
You can use single quotes or double quotes to quote aguments that need
|
||||
quoting.
|
||||
@ -49,19 +47,11 @@ Caveats:
|
||||
would be used by all the commands that run runs. If you want a
|
||||
particular command to use a different input file, you can use -f
|
||||
flag for that particular command. This will override (not add) the
|
||||
input for that particular command. All the files read would be
|
||||
input for that particular command. All the input files would be
|
||||
cached, and would be read only once.
|
||||
|
||||
Examples:
|
||||
|
||||
To start the REPL:
|
||||
|
||||
hledger run
|
||||
|
||||
or
|
||||
|
||||
hledger run -f some.journal
|
||||
|
||||
To provide commands on the command line, separate them with --:
|
||||
|
||||
hledger run -f some.journal -- balance assets --depth 2 -- balance liabilities -f /some/other.journal --depth 3 --transpose -- stats
|
||||
@ -72,11 +62,11 @@ and finally will run stats on some.journal
|
||||
|
||||
To provide commands in the file, as a runnable scripts:
|
||||
|
||||
#!/usr/bin/env -S hledger run
|
||||
echo "List of accounts"
|
||||
#!/usr/bin/env -S hledger run -f some.journal
|
||||
echo "List of accounts in some.journal"
|
||||
accounts
|
||||
|
||||
echo "Assets"
|
||||
echo "Assets of some.journal"
|
||||
balance assets --depth 2
|
||||
|
||||
echo "Liabilities from /some/other.journal"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user