From 9eb1520b6f1b32d52d62d23f72b90fdab446cec5 Mon Sep 17 00:00:00 2001 From: Simon Michael Date: Fri, 7 Jul 2017 11:33:47 +0100 Subject: [PATCH] help: add --info/--man/--pager/--cat flags, & choose best one by default (#579) You can select a docs format/viewer with one of the `--info`, `--man`, `--pager`, `--cat` flags. Otherwise, it will use info if available, otherwise man if available, otherwise $PAGER if defined, otherwise less if available, otherwise it prints on stdout (and always prints on stdout when piped). Preferring info over man might not suit everyone. --- hledger/Hledger/Cli/CliOptions.hs | 32 ++++++++++++--------- hledger/Hledger/Cli/DocFiles.hs | 10 +++++++ hledger/Hledger/Cli/Help.hs | 48 +++++++++++++++++++++++++------ hledger/Hledger/Cli/Main.hs | 7 ++--- hledger/doc/commands.m4.md | 24 ++++++++-------- 5 files changed, 82 insertions(+), 39 deletions(-) diff --git a/hledger/Hledger/Cli/CliOptions.hs b/hledger/Hledger/Cli/CliOptions.hs index fe2a1c34c..d5da171a0 100644 --- a/hledger/Hledger/Cli/CliOptions.hs +++ b/hledger/Hledger/Cli/CliOptions.hs @@ -25,6 +25,8 @@ module Hledger.Cli.CliOptions ( argsFlag, showModeUsage, withAliases, + likelyExecutablesInPath, + hledgerExecutablesInPath, -- * CLI options CliOpts(..), @@ -633,23 +635,27 @@ dropRedundantSourceVersion fs = fs compiledExts = ["",".com",".exe"] --- | Get the sorted unique filenames of all hledger-* executables in --- the current user's PATH. Currently 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. Limitations: --- we do not currently check that the file is really a file (not eg a --- directory) or whether it has execute permission. -hledgerExecutablesInPath :: IO [String] -hledgerExecutablesInPath = do +-- | Get all sorted unique filenames in the current user's PATH. +-- We do not currently filter out non-file objects or files without execute permission. +likelyExecutablesInPath :: IO [String] +likelyExecutablesInPath = do pathdirs <- splitOneOf "[:;]" `fmap` getEnvSafe "PATH" pathfiles <- concat `fmap` mapM getDirectoryContentsSafe pathdirs - return $ nub $ sort $ filter isHledgerExeName pathfiles - -- XXX should exclude directories and files without execute permission. + return $ nub $ sort pathfiles + -- exclude directories and files without execute permission. -- These will do a stat for each hledger-*, probably ok. -- But they need paths, not just filenames - -- hledgerexes <- filterM doesFileExist hledgernamed - -- hledgerexes' <- filterM isExecutable hledgerexes - -- return hledgerexes + -- exes' <- filterM doesFileExist exe' + -- exes'' <- filterM isExecutable exes' + -- return exes'' + +-- | 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 -- isExecutable f = getPermissions f >>= (return . executable) diff --git a/hledger/Hledger/Cli/DocFiles.hs b/hledger/Hledger/Cli/DocFiles.hs index 98a22c766..dd115bc42 100644 --- a/hledger/Hledger/Cli/DocFiles.hs +++ b/hledger/Hledger/Cli/DocFiles.hs @@ -16,6 +16,7 @@ module Hledger.Cli.DocFiles ( ,printHelpForTopic ,runManForTopic ,runInfoForTopic + ,runPagerForTopic ) where @@ -94,6 +95,15 @@ printHelpForTopic :: Topic -> IO () printHelpForTopic t = putStrLn $ lookupDocTxt t +runPagerForTopic :: FilePath -> Topic -> IO () +runPagerForTopic exe t = do + (Just inp, _, _, ph) <- createProcess (proc exe []){ + std_in=CreatePipe + } + hPutStrLn inp (lookupDocTxt t) + _ <- waitForProcess ph + return () + runManForTopic :: Topic -> IO () runManForTopic t = withSystemTempFile ("hledger-"++t++".nroff") $ \f h -> do diff --git a/hledger/Hledger/Cli/Help.hs b/hledger/Hledger/Cli/Help.hs index 737f2875b..fd242635c 100644 --- a/hledger/Hledger/Cli/Help.hs +++ b/hledger/Hledger/Cli/Help.hs @@ -3,6 +3,8 @@ The help command. |-} +--TODO rename manuals +--TODO substring matching module Hledger.Cli.Help ( @@ -14,29 +16,57 @@ module Hledger.Cli.Help ( import Prelude () import Prelude.Compat import Data.List +import Data.Maybe import System.Console.CmdArgs.Explicit +import System.Environment +import System.IO import Hledger.Data.RawOptions import Hledger.Cli.CliOptions import Hledger.Cli.DocFiles +--import Hledger.Utils.Debug helpmode = (defCommandMode $ ["help"] ++ aliases) { - modeHelp = "show any of the hledger manuals" `withAliases` aliases + modeHelp = "show any of the hledger manuals, as plain text. With no argument, list the manuals." `withAliases` aliases ,modeGroupFlags = Group { - groupUnnamed = [] + groupUnnamed = [ + flagNone ["info"] (setboolopt "info") "show the manual with info" + ,flagNone ["man"] (setboolopt "man") "show the manual with man" + ,flagNone ["pager"] (setboolopt "pager") "show the manual with $PAGER or less" + ,flagNone ["cat"] (setboolopt "cat") "show the manual on stdout" + ,flagNone ["help","h"] (setboolopt "help") "show this help" + ] ,groupHidden = [] ,groupNamed = [] } - } + ,modeArgs = ([], Just $ argsFlag "[MANUAL]") +} where aliases = [] --- | Print detailed help on various topics. +-- | List or display one of the hledger manuals in various formats. +-- You can select a docs viewer with one of the `--info`, `--man`, `--pager`, `--cat` flags. +-- Otherwise it will use the first available of: info, man, $PAGER, less, stdout +-- (and always stdout if output is non-interactive). help' :: CliOpts -> IO () help' opts = do - let args = listofstringopt "args" $ rawopts_ opts + exes <- likelyExecutablesInPath + pagerprog <- fromMaybe "less" <$> lookupEnv "PAGER" + interactive <- hIsTerminalDevice stdout + let + args = take 1 $ listofstringopt "args" $ rawopts_ opts + [info, man, pager, cat] = + [runInfoForTopic, runManForTopic, runPagerForTopic pagerprog, printHelpForTopic] + viewer + | boolopt "info" $ rawopts_ opts = info + | boolopt "man" $ rawopts_ opts = man + | boolopt "pager" $ rawopts_ opts = pager + | boolopt "cat" $ rawopts_ opts = cat + | not interactive = cat + | "info" `elem` exes = info + | "man" `elem` exes = man + | pagerprog `elem` exes = pager + | otherwise = cat case args of - [] -> putStrLn $ - "Choose a topic, eg: hledger help cli\n" ++ - intercalate ", " docTopics - topic:_ -> printHelpForTopic topic + [t] -> viewer t + _ -> putStrLn $ "Please choose a manual:\nhledger help " ++ intercalate "|" docTopics diff --git a/hledger/Hledger/Cli/Main.hs b/hledger/Hledger/Cli/Main.hs index 63db9a0cb..77f576361 100644 --- a/hledger/Hledger/Cli/Main.hs +++ b/hledger/Hledger/Cli/Main.hs @@ -128,10 +128,7 @@ PROGNAME CMD [--] [OPTS] [ARGS] run a command (use -- with addon commands) PROGNAME-CMD [OPTS] [ARGS] or run addon commands directly PROGNAME -h show general usage PROGNAME CMD -h show command usage -PROGNAME help list available manuals -PROGNAME help MANUAL show a manual as plain text -PROGNAME man MANUAL show a manual as man page -PROGNAME info MANUAL show a manual as info manual +PROGNAME help [MANUAL] show any of the hledger manuals in various formats |] } @@ -263,7 +260,7 @@ OTHERCMDS Help: hledger -h show general usage hledger CMD -h show command usage - help|man|info show any of the hledger manuals in plain text/man/info format + help show any of the hledger manuals in various formats |] knownCommands :: [String] diff --git a/hledger/doc/commands.m4.md b/hledger/doc/commands.m4.md index 3af778b1e..0cc5b4578 100644 --- a/hledger/doc/commands.m4.md +++ b/hledger/doc/commands.m4.md @@ -304,18 +304,18 @@ you can alter the report mode with `--change`/`--cumulative`/`--historical`. ## help Show any of the hledger manuals. -The `help` command displays any of the main [hledger man pages](/docs.html). -(Unlike `hledger --help`, which displays only the hledger man page.) -Run it with no arguments to list available topics (their names are shortened for easier typing), -and run `hledger help TOPIC` to select one. -The output is similar to a man page, but fixed width. -It may be long, so you may wish to pipe it into a pager. -See also [info](#info) and [man](#man). +The `help` command displays any of the main [hledger manuals](/docs.html), in one of several ways. +Run it with no argument to list the manuals (their names are shortened for easier typing), +and run `hledger help MANUAL` to select one. + +hledger help will choose one of these docs viewers, in order of preference: +info, man, $PAGER, less, stdout (and it always prints on stdout when piped). +Or you can force a particular viewer with the `--info`, `--man`, `--pager`, `--cat` flags. _shell_({{ $ hledger help -Choose a topic, eg: hledger help cli -cli, ui, web, api, journal, csv, timeclock, timedot +Please choose a manual: +hledger help cli|ui|web|api|journal|csv|timeclock|timedot }}) _shell_({{ @@ -329,9 +329,7 @@ NAME hledger - a command-line accounting tool SYNOPSIS - hledger [-f FILE] COMMAND [OPTIONS] [CMDARGS] - hledger [-f FILE] ADDONCMD -- [OPTIONS] [CMDARGS] -: + hledger [-f FILE] COMMAND [OPTIONS] [ARGS] }}) ## incomestatement @@ -404,6 +402,7 @@ Normally incomestatement shows revenues/expenses per period, though as with [multicolumn balance reports](#multicolumn-balance-reports) you can alter the report mode with `--change`/`--cumulative`/`--historical`. +... ## info Show any of the hledger manuals using info. @@ -423,6 +422,7 @@ This will fit the text to your terminal width, and probably invoke a pager autom It requires the "man" program to be available in your PATH. As with [help](#help), run it with no arguments to list available topics (manuals). +``` ## print Show transactions from the journal.