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, argsFlag,
showModeUsage, showModeUsage,
withAliases, withAliases,
likelyExecutablesInPath,
hledgerExecutablesInPath,
-- * CLI options -- * CLI options
CliOpts(..), CliOpts(..),
@ -633,23 +635,27 @@ dropRedundantSourceVersion fs = fs
compiledExts = ["",".com",".exe"] compiledExts = ["",".com",".exe"]
-- | Get the sorted unique filenames of all hledger-* executables in -- | Get all sorted unique filenames in the current user's PATH.
-- the current user's PATH. Currently these are: files in any of the -- We do not currently filter out non-file objects or files without execute permission.
-- PATH directories, named hledger-*, with either no extension (and no likelyExecutablesInPath :: IO [String]
-- periods in the name) or one of the addonExtensions. Limitations: likelyExecutablesInPath = do
-- 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
pathdirs <- splitOneOf "[:;]" `fmap` getEnvSafe "PATH" pathdirs <- splitOneOf "[:;]" `fmap` getEnvSafe "PATH"
pathfiles <- concat `fmap` mapM getDirectoryContentsSafe pathdirs pathfiles <- concat `fmap` mapM getDirectoryContentsSafe pathdirs
return $ nub $ sort $ filter isHledgerExeName pathfiles return $ nub $ sort pathfiles
-- XXX should exclude directories and files without execute permission. -- exclude directories and files without execute permission.
-- These will do a stat for each hledger-*, probably ok. -- These will do a stat for each hledger-*, probably ok.
-- But they need paths, not just filenames -- But they need paths, not just filenames
-- hledgerexes <- filterM doesFileExist hledgernamed -- exes' <- filterM doesFileExist exe'
-- hledgerexes' <- filterM isExecutable hledgerexes -- exes'' <- filterM isExecutable exes'
-- return hledgerexes -- 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) -- isExecutable f = getPermissions f >>= (return . executable)

View File

@ -16,6 +16,7 @@ module Hledger.Cli.DocFiles (
,printHelpForTopic ,printHelpForTopic
,runManForTopic ,runManForTopic
,runInfoForTopic ,runInfoForTopic
,runPagerForTopic
) where ) where
@ -94,6 +95,15 @@ printHelpForTopic :: Topic -> IO ()
printHelpForTopic t = printHelpForTopic t =
putStrLn $ lookupDocTxt 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 :: Topic -> IO ()
runManForTopic t = runManForTopic t =
withSystemTempFile ("hledger-"++t++".nroff") $ \f h -> do withSystemTempFile ("hledger-"++t++".nroff") $ \f h -> do

View File

@ -3,6 +3,8 @@
The help command. The help command.
|-} |-}
--TODO rename manuals
--TODO substring matching
module Hledger.Cli.Help ( module Hledger.Cli.Help (
@ -14,29 +16,57 @@ module Hledger.Cli.Help (
import Prelude () import Prelude ()
import Prelude.Compat import Prelude.Compat
import Data.List import Data.List
import Data.Maybe
import System.Console.CmdArgs.Explicit import System.Console.CmdArgs.Explicit
import System.Environment
import System.IO
import Hledger.Data.RawOptions import Hledger.Data.RawOptions
import Hledger.Cli.CliOptions import Hledger.Cli.CliOptions
import Hledger.Cli.DocFiles import Hledger.Cli.DocFiles
--import Hledger.Utils.Debug
helpmode = (defCommandMode $ ["help"] ++ aliases) { 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 { ,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 = [] ,groupHidden = []
,groupNamed = [] ,groupNamed = []
} }
} ,modeArgs = ([], Just $ argsFlag "[MANUAL]")
}
where aliases = [] 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' :: CliOpts -> IO ()
help' opts = do 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 case args of
[] -> putStrLn $ [t] -> viewer t
"Choose a topic, eg: hledger help cli\n" ++ _ -> putStrLn $ "Please choose a manual:\nhledger help " ++ intercalate "|" docTopics
intercalate ", " docTopics
topic:_ -> printHelpForTopic topic

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-CMD [OPTS] [ARGS] or run addon commands directly
PROGNAME -h show general usage PROGNAME -h show general usage
PROGNAME CMD -h show command usage PROGNAME CMD -h show command usage
PROGNAME help list available manuals PROGNAME help [MANUAL] show any of the hledger manuals in various formats
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
|] |]
} }
@ -263,7 +260,7 @@ OTHERCMDS
Help: Help:
hledger -h show general usage hledger -h show general usage
hledger CMD -h show command 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] knownCommands :: [String]

View File

@ -304,18 +304,18 @@ you can alter the report mode with `--change`/`--cumulative`/`--historical`.
## help ## help
Show any of the hledger manuals. Show any of the hledger manuals.
The `help` command displays any of the main [hledger man pages](/docs.html). The `help` command displays any of the main [hledger manuals](/docs.html), in one of several ways.
(Unlike `hledger --help`, which displays only the hledger man page.) Run it with no argument to list the manuals (their names are shortened for easier typing),
Run it with no arguments to list available topics (their names are shortened for easier typing), and run `hledger help MANUAL` to select one.
and run `hledger help TOPIC` to select one.
The output is similar to a man page, but fixed width. hledger help will choose one of these docs viewers, in order of preference:
It may be long, so you may wish to pipe it into a pager. info, man, $PAGER, less, stdout (and it always prints on stdout when piped).
See also [info](#info) and [man](#man). Or you can force a particular viewer with the `--info`, `--man`, `--pager`, `--cat` flags.
_shell_({{ _shell_({{
$ hledger help $ hledger help
Choose a topic, eg: hledger help cli Please choose a manual:
cli, ui, web, api, journal, csv, timeclock, timedot hledger help cli|ui|web|api|journal|csv|timeclock|timedot
}}) }})
_shell_({{ _shell_({{
@ -329,9 +329,7 @@ NAME
hledger - a command-line accounting tool hledger - a command-line accounting tool
SYNOPSIS SYNOPSIS
hledger [-f FILE] COMMAND [OPTIONS] [CMDARGS] hledger [-f FILE] COMMAND [OPTIONS] [ARGS]
hledger [-f FILE] ADDONCMD -- [OPTIONS] [CMDARGS]
:
}}) }})
## incomestatement ## incomestatement
@ -404,6 +402,7 @@ Normally incomestatement shows revenues/expenses per period, though
as with [multicolumn balance reports](#multicolumn-balance-reports) as with [multicolumn balance reports](#multicolumn-balance-reports)
you can alter the report mode with `--change`/`--cumulative`/`--historical`. you can alter the report mode with `--change`/`--cumulative`/`--historical`.
...
## info ## info
Show any of the hledger manuals using 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. 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). As with [help](#help), run it with no arguments to list available topics (manuals).
```
## print ## print
Show transactions from the journal. Show transactions from the journal.