feat: commands, an explicit command to show the commands list; refactor

This commit is contained in:
Simon Michael 2025-03-05 10:26:28 -10:00
parent 25edf03495
commit 34ba84ff9c
8 changed files with 195 additions and 138 deletions

View File

@ -108,7 +108,6 @@ import System.Console.CmdArgs.Explicit
import System.Console.CmdArgs.Explicit as CmdArgsWithoutName hiding (Name) import System.Console.CmdArgs.Explicit as CmdArgsWithoutName hiding (Name)
import System.Environment import System.Environment
import System.Exit import System.Exit
import System.FilePath
import System.Process import System.Process
import Text.Megaparsec (optional, takeWhile1P, eof) import Text.Megaparsec (optional, takeWhile1P, eof)
import Text.Megaparsec.Char (char) import Text.Megaparsec.Char (char)
@ -225,7 +224,7 @@ main = withGhcDebug' $ do
usecolor <- useColorOnStdout usecolor <- useColorOnStdout
when usecolor setupPager when usecolor setupPager
-- Search PATH for addon commands. Exclude any that match builtin command names. -- Search PATH for addon commands. Exclude any that match builtin command names.
addons <- hledgerAddons <&> filter (not . (`elem` builtinCommandNames) . dropExtension) addons <- addonCommandNames
--------------------------------------------------------------- ---------------------------------------------------------------
dbgIO "\n1. Preliminary command line parsing" () dbgIO "\n1. Preliminary command line parsing" ()
@ -366,6 +365,15 @@ main = withGhcDebug' $ do
infoFlag = boolopt "info" rawopts infoFlag = boolopt "info" rawopts
manFlag = boolopt "man" rawopts manFlag = boolopt "man" rawopts
versionFlag = boolopt "version" rawopts versionFlag = boolopt "version" rawopts
-- ignoredopts cmd = error' $ cmd ++ " tried to read options but is not supposed to"
ignoredjournal cmd = error' $ cmd ++ " tried to read the journal but is not supposed to"
-- validate opts/args more and convert to CliOpts
opts <- rawOptsToCliOpts rawopts >>= \opts0 -> return opts0{progstarttime_=starttime}
dbgIO2 "processed opts" opts
dbgIO "period from opts" (period_ . _rsReportOpts $ reportspec_ opts)
dbgIO "interval from opts" (interval_ . _rsReportOpts $ reportspec_ opts)
dbgIO "query from opts & args" (_rsQuery $ reportspec_ opts)
-- Ensure that anything calling getArgs later will see all args, including config file args. -- Ensure that anything calling getArgs later will see all args, including config file args.
-- Some things (--color, --debug, some checks in journalFinalise) are detected by unsafePerformIO, -- Some things (--color, --debug, some checks in journalFinalise) are detected by unsafePerformIO,
@ -386,32 +394,26 @@ main = withGhcDebug' $ do
| badcmdprovided -> error' $ "command "++clicmdarg++" is not recognized, run with no command to see a list" | badcmdprovided -> error' $ "command "++clicmdarg++" is not recognized, run with no command to see a list"
-- 6.4. no command found, nothing else to do - show the commands list -- 6.4. no command found, nothing else to do - show the commands list
| nocmdprovided -> dbgIO1 "no command, showing commands list" () >> printCommandsList prognameandversion addons | nocmdprovided -> do
dbgIO1 "no command, showing commands list" ()
commands opts (ignoredjournal "commands")
-- 6.5. builtin command found -- 6.5. builtin command found
| Just (cmdmode, cmdaction) <- mbuiltincmdaction -> do | Just (cmdmode, cmdaction) <- mbuiltincmdaction -> do
let mmodecmdname = headMay $ modeNames cmdmode let mmodecmdname = headMay $ modeNames cmdmode
dbgIO1 "running builtin command mode" $ fromMaybe "" mmodecmdname dbgIO1 "running builtin command mode" $ fromMaybe "" mmodecmdname
-- validate opts/args more and convert to CliOpts
opts <- rawOptsToCliOpts rawopts >>= \opts0 -> return opts0{progstarttime_=starttime}
dbgIO2 "processed opts" opts
dbgIO "period from opts" (period_ . _rsReportOpts $ reportspec_ opts)
dbgIO "interval from opts" (interval_ . _rsReportOpts $ reportspec_ opts)
dbgIO "query from opts & args" (_rsQuery $ reportspec_ opts)
let tldrpagename = maybe "hledger" (("hledger-"<>)) mmodecmdname
-- run the builtin command according to its type -- run the builtin command according to its type
if if
-- 6.5.1. help/doc flag - show command help/docs -- 6.5.1. help/doc flag - show command help/docs
| helpFlag -> runPager $ showModeUsage cmdmode ++ "\n" | helpFlag -> runPager $ showModeUsage cmdmode ++ "\n"
| tldrFlag -> runTldrForPage tldrpagename | tldrFlag -> runTldrForPage $ maybe "hledger" (("hledger-"<>)) mmodecmdname
| infoFlag -> runInfoForTopic "hledger" mmodecmdname | infoFlag -> runInfoForTopic "hledger" mmodecmdname
| manFlag -> runManForTopic "hledger" mmodecmdname | manFlag -> runManForTopic "hledger" mmodecmdname
-- 6.5.2. builtin command which should not require or read the journal - run it -- 6.5.2. builtin command which should not require or read the journal - run it
| cmdname `elem` ["demo","help","test"] -> | cmdname `elem` ["demo","help","test"] ->
cmdaction opts $ error' $ cmdname++" tried to read the journal but is not supposed to" cmdaction opts (ignoredjournal cmdname)
-- 6.5.3. builtin command which should create the journal if missing - do that and run it -- 6.5.3. builtin command which should create the journal if missing - do that and run it
| cmdname `elem` ["add","import"] -> do | cmdname `elem` ["add","import"] -> do

View File

@ -49,7 +49,6 @@ module Hledger.Cli.CliOptions (
showModeUsage, showModeUsage,
withAliases, withAliases,
likelyExecutablesInPath, likelyExecutablesInPath,
hledgerExecutablesInPath,
-- * CLI options -- * CLI options
CliOpts(..), CliOpts(..),
@ -78,7 +77,6 @@ module Hledger.Cli.CliOptions (
registerWidthsFromOpts, registerWidthsFromOpts,
-- * Other utils -- * Other utils
hledgerAddons,
topicForMode, topicForMode,
-- -- * Convenience re-exports -- -- * Convenience re-exports
@ -91,8 +89,7 @@ import qualified Control.Exception as C
import Control.Monad (when) import Control.Monad (when)
import Data.Char import Data.Char
import Data.Default import Data.Default
import Data.Either (isRight) import Data.List.Extra (intercalate, isInfixOf, nubSort)
import Data.List.Extra (groupSortOn, intercalate, isInfixOf, nubSort)
import qualified Data.List.NonEmpty as NE (NonEmpty, fromList, nonEmpty) import qualified Data.List.NonEmpty as NE (NonEmpty, fromList, nonEmpty)
import Data.List.Split (splitOn) import Data.List.Split (splitOn)
import Data.Maybe import Data.Maybe
@ -783,48 +780,6 @@ registerWidthsFromOpts CliOpts{width_=Just s} =
-- Other utils -- Other utils
-- | Get the sorted unique canonical names of hledger addon commands
-- found in the current user's PATH. These are used in command line
-- parsing and to display the commands list.
--
-- Canonical addon names are the filenames of hledger-* executables in
-- PATH, without the "hledger-" prefix, and without the file extension
-- except when it's needed for disambiguation (see below).
--
-- When there are exactly two versions of an executable (same base
-- name, different extensions) that look like a source and compiled
-- pair (one has .exe, .com, or no extension), the source version will
-- be excluded (even if it happens to be newer). When there are three
-- or more versions (or two versions that don't look like a
-- source/compiled pair), they are all included, with file extensions
-- intact.
--
hledgerAddons :: IO [String]
hledgerAddons = do
-- past bug generator
as1 <- hledgerExecutablesInPath -- ["hledger-check","hledger-check-dates","hledger-check-dates.hs","hledger-check.hs","hledger-check.py"]
let as2 = map stripPrognamePrefix as1 -- ["check","check-dates","check-dates.hs","check.hs","check.py"]
let as3 = groupSortOn takeBaseName as2 -- [["check","check.hs","check.py"],["check-dates","check-dates.hs"]]
let as4 = concatMap dropRedundantSourceVersion as3 -- ["check","check.hs","check.py","check-dates"]
return as4
stripPrognamePrefix = drop (length progname + 1)
dropRedundantSourceVersion [f,g]
| map toLower (takeExtension f) `elem` compiledExts = [f]
| map toLower (takeExtension g) `elem` compiledExts = [g]
dropRedundantSourceVersion fs = fs
compiledExts = ["",".com",".exe"]
-- | Get the sorted unique filenames of all hledger-* executables in
-- the current user's PATH. These are files in any of the PATH directories,
-- named hledger-*, with either no extension (and no periods in the name)
-- or one of the addonExtensions.
-- We do not currently filter out non-file objects or files without execute permission.
hledgerExecutablesInPath :: IO [String]
hledgerExecutablesInPath = filter isHledgerExeName <$> likelyExecutablesInPath
-- None of https://hackage.haskell.org/package/directory-1.3.8.1/docs/System-Directory.html#g:5 -- None of https://hackage.haskell.org/package/directory-1.3.8.1/docs/System-Directory.html#g:5
-- do quite what we need (find all the executables in PATH with a filename prefix). -- do quite what we need (find all the executables in PATH with a filename prefix).
-- | Get all sorted unique filenames in the current user's PATH. -- | Get all sorted unique filenames in the current user's PATH.
@ -845,39 +800,6 @@ likelyExecutablesInPath = do
-- return exes'' -- return exes''
-- where isExecutable f = getPermissions f >>= (return . executable) -- where isExecutable f = getPermissions f >>= (return . executable)
isHledgerExeName :: String -> Bool
isHledgerExeName = isRight . parsewith hledgerexenamep . T.pack
where
hledgerexenamep = do
_ <- string $ T.pack progname
_ <- char '-'
_ <- some $ noneOf ['.']
optional (string "." >> choice' (map (string . T.pack) addonExtensions))
eof
-- stripAddonExtension :: String -> String
-- stripAddonExtension = regexReplace re "" where re = "\\.(" ++ intercalate "|" addonExtensions ++ ")$"
addonExtensions :: [String]
addonExtensions =
["bat"
,"com"
,"exe"
,"hs"
,"js"
,"lhs"
,"lua"
,"php"
,"pl"
,"py"
,"rb"
,"rkt"
,"sh"
,"osh"
,"ysh"
-- ,""
]
getEnvSafe :: String -> IO String getEnvSafe :: String -> IO String
getEnvSafe v = getEnv v `C.catch` (\(_::C.IOException) -> return "") -- XXX should catch only isDoesNotExistError e getEnvSafe v = getEnv v `C.catch` (\(_::C.IOException) -> return "") -- XXX should catch only isDoesNotExistError e

View File

@ -15,11 +15,13 @@ the export list, the import list, builtinCommands, commandsList.
{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TemplateHaskell #-}
module Hledger.Cli.Commands ( module Hledger.Cli.Commands (
testcmd commands
,testcmd
,builtinCommands ,builtinCommands
,builtinCommandNames ,builtinCommandNames
,addonCommandNames
,knownAddonCommandNames
,findBuiltinCommand ,findBuiltinCommand
,knownAddonCommands
,knownCommands ,knownCommands
,printCommandsList ,printCommandsList
,tests_Hledger_Cli ,tests_Hledger_Cli
@ -52,17 +54,21 @@ module Hledger.Cli.Commands (
) )
where where
import Data.Char (isAlphaNum, isSpace) import Data.Char (isAlphaNum, isSpace, toLower)
import Data.Either (isRight)
import Data.List import Data.List
import Data.List.Extra (nubSort) import Data.List.Extra (groupSortOn, nubSort)
import Data.Text (Text) import Data.Text (Text)
import qualified Data.Text as T import qualified Data.Text as T
import Data.Time.Calendar import Data.Time.Calendar
import Safe (headErr) import Safe (headErr)
import String.ANSI import String.ANSI
import System.Environment (withArgs)
import System.Console.CmdArgs.Explicit as C import System.Console.CmdArgs.Explicit as C
import System.Environment (withArgs)
import System.FilePath (dropExtension, takeBaseName, takeExtension)
import Test.Tasty (defaultMain) import Test.Tasty (defaultMain)
import Text.Megaparsec
import Text.Megaparsec.Char
import Hledger import Hledger
import Hledger.Cli.CliOptions import Hledger.Cli.CliOptions
@ -96,6 +102,7 @@ import Hledger.Cli.Commands.Run
import Hledger.Cli.Commands.Stats import Hledger.Cli.Commands.Stats
import Hledger.Cli.Commands.Tags import Hledger.Cli.Commands.Tags
import Hledger.Cli.Utils (tests_Cli_Utils) import Hledger.Cli.Utils (tests_Cli_Utils)
import Data.Functor ((<&>))
-- | The cmdargs subcommand mode (for command-line parsing) -- | The cmdargs subcommand mode (for command-line parsing)
-- and IO action (for doing the command's work) for each builtin command. -- and IO action (for doing the command's work) for each builtin command.
@ -113,6 +120,7 @@ builtinCommands = [
,(checkmode , check) ,(checkmode , check)
,(closemode , close) ,(closemode , close)
,(codesmode , codes) ,(codesmode , codes)
,(commandsmode , commands)
,(commoditiesmode , commodities) ,(commoditiesmode , commodities)
,(demomode , demo) ,(demomode , demo)
,(descriptionsmode , descriptions) ,(descriptionsmode , descriptions)
@ -191,7 +199,7 @@ accent
-- --
commandsList :: String -> [String] -> [String] commandsList :: String -> [String] -> [String]
commandsList progversion othercmds = commandsList progversion othercmds =
map (bold'.accent) _banner_smslant ++ map (bold'.accent) _banner_smslant ++ -- XXX not showing bold, why ?
[ [
-- Keep the following synced with: -- Keep the following synced with:
-- commands.m4 -- commands.m4
@ -202,17 +210,18 @@ commandsList progversion othercmds =
"-------------------------------------------------------------------------------" "-------------------------------------------------------------------------------"
,progversion ,progversion
,"Usage: hledger COMMAND [OPTIONS] [-- ADDONOPTIONS]" ,"Usage: hledger COMMAND [OPTIONS] [-- ADDONOPTIONS]"
,"Commands (builtins + addons):" -- ,"Commands (builtins + addons):" -- XXX adapt for commands --builtin
,"Commands:"
,"" ,""
-----------------------------------------80------------------------------------- -----------------------------------------80-------------------------------------
,bold' "HELP (docs, demos..)" ,bold' "HELP (docs, demos..)"
," (no arguments) show this commands list" ," commands show the commands list (default)"
," -h [COMMAND] show command line help"
," --tldr [COMMAND] show command examples with tldr"
," --info [COMMAND] show the hledger manual with info"
," --man [COMMAND] show the hledger manual with man"
," help [-i|-m|-p] [TOPIC] show any topic in the hledger manual"
," demo [DEMO] show brief demos in the terminal" ," demo [DEMO] show brief demos in the terminal"
," help [-i|-m|-p] [TOPIC] show the hledger manual with info/man/pager"
," --tldr [COMMAND] show command examples [for command] with tldr"
," --help/-h [COMMAND] show command line help [for command]"
," --info [COMMAND] show the hledger manual [for command] with info"
," --man [COMMAND] show the hledger manual [for command] with man"
," more help: https://hledger.org" ," more help: https://hledger.org"
,"" ,""
-----------------------------------------80------------------------------------- -----------------------------------------80-------------------------------------
@ -301,18 +310,63 @@ commandsListExtractCommands addonsonly l =
, not $ "https://" `isInfixOf` line , not $ "https://" `isInfixOf` line
, let cmdname:_ = words line , let cmdname:_ = words line
] ]
-- KEEP SYNCED WITH commandsList. -- Keep synced with commandsList.
commandsmode =
hledgerCommandMode
$(embedFileRelative "Hledger/Cli/Commands/Commands.txt")
[flagNone ["builtin"] (setboolopt "builtin") "show only builtin commands, not addons"
]
[(helpflagstitle, helpflags)]
[]
-- flagReq ["debug"] (\s opts -> Right $ setopt "debug" s opts) "[N]" "show debug output (levels 1-9, default: 1)"
([], Nothing)
-- | Display the commands list.
commands :: CliOpts -> Journal -> IO ()
commands opts _ = do
addons <- if boolopt "builtin" (rawopts_ opts) then return [] else addonCommandNames
printCommandsList prognameandversion addons
{- | Print the commands list, with a pager if appropriate, customising the
commandsList template above with the given version string and the installed addons.
Uninstalled known addons will be removed from the list,
installed known addons will have the + prefix removed,
and installed unknown addons will be added under Misc.
-}
printCommandsList :: String -> [String] -> IO ()
printCommandsList progversion installedaddons =
seq (length $ dbg8 "uninstalledknownaddons" uninstalledknownaddons) $ -- for debug output
seq (length $ dbg8 "installedknownaddons" installedknownaddons) $
seq (length $ dbg8 "installedunknownaddons" installedunknownaddons) $
runPager $
unlines $
map unplus $
filter (not . isuninstalledaddon) $
commandsList progversion installedunknownaddons
where
knownaddons = knownAddonCommandNames
uninstalledknownaddons = knownaddons \\ installedaddons
installedknownaddons = knownaddons `intersect` installedaddons
installedunknownaddons = installedaddons \\ knownaddons
unplus ('+' : cs) = ' ' : cs
unplus s = s
isuninstalledaddon =
\case
('+' : l)
| cmd `notElem` installedaddons ->
dbg9With (const $ "hiding uninstalled addon: " <> cmd) $
True
where
cmd = takeWhile (not . isSpace) l
_ -> False
-- | Canonical names of all commands which have a slot in the commands list, in alphabetical order. -- | Canonical names of all commands which have a slot in the commands list, in alphabetical order.
-- These include the builtin commands and the known addon commands. -- These include the builtin commands and the known addon commands.
knownCommands :: [String] knownCommands :: [String]
knownCommands = nubSort . commandsListExtractCommands False $ commandsList progname [] knownCommands = nubSort . commandsListExtractCommands False $ commandsList progname []
-- | Canonical names of the known addon commands which have a slot in the commands list,
-- in alphabetical order.
knownAddonCommands :: [String]
knownAddonCommands = nubSort . commandsListExtractCommands True $ commandsList progname []
-- | All names and aliases of the builtin commands. -- | All names and aliases of the builtin commands.
builtinCommandNames :: [String] builtinCommandNames :: [String]
builtinCommandNames = concatMap (modeNames . fst) builtinCommands builtinCommandNames = concatMap (modeNames . fst) builtinCommands
@ -321,33 +375,91 @@ builtinCommandNames = concatMap (modeNames . fst) builtinCommands
findBuiltinCommand :: String -> Maybe (Mode RawOpts, CliOpts -> Journal -> IO ()) findBuiltinCommand :: String -> Maybe (Mode RawOpts, CliOpts -> Journal -> IO ())
findBuiltinCommand cmdname = find (elem cmdname . modeNames . fst) builtinCommands findBuiltinCommand cmdname = find (elem cmdname . modeNames . fst) builtinCommands
-- | Print the commands list, with a pager if appropriate, customising the {- | Canonical names of the known addon commands which have a slot in the commands list,
-- commandsList template above with the given version string and the installed addons. in alphabetical order.
-- Uninstalled known addons will be removed from the list, -}
-- installed known addons will have the + prefix removed, knownAddonCommandNames :: [String]
-- and installed unknown addons will be added under Misc. knownAddonCommandNames = nubSort . commandsListExtractCommands True $ commandsList progname []
printCommandsList :: String -> [String] -> IO ()
printCommandsList progversion installedaddons =
seq (length $ dbg8 "uninstalledknownaddons" uninstalledknownaddons) $ -- for debug output
seq (length $ dbg8 "installedknownaddons" installedknownaddons) $
seq (length $ dbg8 "installedunknownaddons" installedunknownaddons) $
runPager $ unlines $ map unplus $ filter (not.isuninstalledaddon) $
commandsList progversion installedunknownaddons
where
knownaddons = knownAddonCommands
uninstalledknownaddons = knownaddons \\ installedaddons
installedknownaddons = knownaddons `intersect` installedaddons
installedunknownaddons = installedaddons \\ knownaddons
unplus ('+':cs) = ' ':cs
unplus s = s
isuninstalledaddon =
\case
('+':l) | cmd `notElem` installedaddons ->
dbg9With (const $ "hiding uninstalled addon: "<>cmd) $
True where cmd = takeWhile (not . isSpace) l
_ -> False
-- The test command is defined here for easy access to other modules' tests. -- Search PATH for names of addon commands, that aren't shadowed by builtin commands.
addonCommandNames :: IO [String]
addonCommandNames = installedAddonCommandNames <&> filter (not . (`elem` builtinCommandNames) . dropExtension)
-- | Get the sorted unique canonical names of hledger addon commands
-- found in the current user's PATH. These are used in command line
-- parsing and to display the commands list.
--
-- Canonical addon names are the filenames of hledger-* executables in
-- PATH, without the "hledger-" prefix, and without the file extension
-- except when it's needed for disambiguation (see below).
--
-- When there are exactly two versions of an executable (same base
-- name, different extensions) that look like a source and compiled
-- pair (one has .exe, .com, or no extension), the source version will
-- be excluded (even if it happens to be newer). When there are three
-- or more versions (or two versions that don't look like a
-- source/compiled pair), they are all included, with file extensions
-- intact.
--
installedAddonCommandNames :: IO [String]
installedAddonCommandNames = do
-- past bug generator
as1 <- hledgerExecutablesInPath -- ["hledger-check","hledger-check-dates","hledger-check-dates.hs","hledger-check.hs","hledger-check.py"]
let as2 = map stripPrognamePrefix as1 -- ["check","check-dates","check-dates.hs","check.hs","check.py"]
let as3 = groupSortOn takeBaseName as2 -- [["check","check.hs","check.py"],["check-dates","check-dates.hs"]]
let as4 = concatMap dropRedundantSourceVersion as3 -- ["check","check.hs","check.py","check-dates"]
return as4
stripPrognamePrefix = drop (length progname + 1)
dropRedundantSourceVersion [f,g]
| map toLower (takeExtension f) `elem` compiledExts = [f]
| map toLower (takeExtension g) `elem` compiledExts = [g]
dropRedundantSourceVersion fs = fs
compiledExts = ["",".com",".exe"]
-- | Get the sorted unique filenames of all hledger-* executables in
-- the current user's PATH. These are files in any of the PATH directories,
-- named hledger-*, with either no extension (and no periods in the name)
-- or one of the addonExtensions.
-- We do not currently filter out non-file objects or files without execute permission.
hledgerExecutablesInPath :: IO [String]
hledgerExecutablesInPath = filter isHledgerExeName <$> likelyExecutablesInPath
isHledgerExeName :: String -> Bool
isHledgerExeName = isRight . parsewith hledgerexenamep . T.pack
where
hledgerexenamep = do
_ <- string $ T.pack progname
_ <- char '-'
_ <- some $ noneOf ['.']
optional (string "." >> choice' (map (string . T.pack) addonExtensions))
eof
-- stripAddonExtension :: String -> String
-- stripAddonExtension = regexReplace re "" where re = "\\.(" ++ intercalate "|" addonExtensions ++ ")$"
addonExtensions :: [String]
addonExtensions =
["bat"
,"com"
,"exe"
,"hs"
,"js"
,"lhs"
,"lua"
,"php"
,"pl"
,"py"
,"rb"
,"rkt"
,"sh"
,"osh"
,"ysh"
]
-- The test command is also defined here for easy access to other modules' tests.
testmode = hledgerCommandMode testmode = hledgerCommandMode
$(embedFileRelative "Hledger/Cli/Commands/Test.txt") $(embedFileRelative "Hledger/Cli/Commands/Test.txt")

View File

@ -0,0 +1,3 @@
## commands
Show the hledger commands list.

View File

@ -0,0 +1,3 @@
commands
Show the hledger commands list.

View File

@ -69,6 +69,7 @@ extra-source-files:
Hledger/Cli/Commands/Check.txt Hledger/Cli/Commands/Check.txt
Hledger/Cli/Commands/Close.txt Hledger/Cli/Commands/Close.txt
Hledger/Cli/Commands/Codes.txt Hledger/Cli/Commands/Codes.txt
Hledger/Cli/Commands/Commands.txt
Hledger/Cli/Commands/Commodities.txt Hledger/Cli/Commands/Commodities.txt
Hledger/Cli/Commands/Demo.txt Hledger/Cli/Commands/Demo.txt
Hledger/Cli/Commands/Descriptions.txt Hledger/Cli/Commands/Descriptions.txt

View File

@ -68,6 +68,7 @@ extra-source-files:
- Hledger/Cli/Commands/Check.txt - Hledger/Cli/Commands/Check.txt
- Hledger/Cli/Commands/Close.txt - Hledger/Cli/Commands/Close.txt
- Hledger/Cli/Commands/Codes.txt - Hledger/Cli/Commands/Codes.txt
- Hledger/Cli/Commands/Commands.txt
- Hledger/Cli/Commands/Commodities.txt - Hledger/Cli/Commands/Commodities.txt
- Hledger/Cli/Commands/Demo.txt - Hledger/Cli/Commands/Demo.txt
- Hledger/Cli/Commands/Descriptions.txt - Hledger/Cli/Commands/Descriptions.txt

View File

@ -0,0 +1,13 @@
# * commands command
# ** 0. Print the commands list by default.
$ hledger
> /Commands:/
# ** 0. Print the commands list with the commands command specified.
#$ hledger commands
#> /Commands /
# ** 0. Print the commands list showing only builtin commands.
$ hledger commands --builtin
> /Commands:$/