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.
This commit is contained in:
Simon Michael 2017-07-07 11:33:47 +01:00
parent daa9550929
commit 9eb1520b6f
5 changed files with 82 additions and 39 deletions

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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]

View File

@ -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.