cli/help: --info/--man/help show CMD/topic-specific docs
hledger CMD --info will now open CMD's info node, hledger CMD --man will scroll the man page to CMD's section, and hledger help -i/-m/-p TOPIC will do similar. This is not perfectly robust but hopefully will be good enough in practice. Also the help command's long --info/--man/--pager flags have been dropped in favour of -i/-m/-p.
This commit is contained in:
parent
54af71a0b1
commit
ae9595c321
@ -29,18 +29,19 @@ import Hledger.Data.RawOptions
|
||||
import Hledger.Data.Types
|
||||
import Hledger.Cli.CliOptions
|
||||
import Hledger.Cli.DocFiles
|
||||
import Safe (headMay)
|
||||
--import Hledger.Utils.Debug
|
||||
|
||||
helpmode = hledgerCommandMode
|
||||
$(embedFileRelative "Hledger/Cli/Commands/Help.txt")
|
||||
[flagNone ["info","i"] (setboolopt "info") "show the manual with info"
|
||||
,flagNone ["man","m"] (setboolopt "man") "show the manual with man"
|
||||
,flagNone ["pager","p"] (setboolopt "pager") "show the manual with $PAGER or less"
|
||||
[flagNone ["i"] (setboolopt "info") "show the manual with info"
|
||||
,flagNone ["m"] (setboolopt "man") "show the manual with man"
|
||||
,flagNone ["p"] (setboolopt "pager") "show the manual with $PAGER or less"
|
||||
,flagNone ["help","h"] (setboolopt "help") "show this help"
|
||||
]
|
||||
[]
|
||||
[]
|
||||
([], Nothing) -- Just $ argsFlag "[TOPIC]"
|
||||
([], Just $ argsFlag "[TOPIC]")
|
||||
|
||||
-- | Display the hledger manual in various formats.
|
||||
-- You can select a docs viewer with one of the `--info`, `--man`, `--pager` flags.
|
||||
@ -52,12 +53,10 @@ help' opts _ = do
|
||||
pagerprog <- fromMaybe "less" <$> lookupEnv "PAGER"
|
||||
interactive <- hIsTerminalDevice stdout
|
||||
let
|
||||
-- args = take 1 $ listofstringopt "args" $ rawopts_ opts
|
||||
-- topic = case args of
|
||||
-- [pat] -> headMay [t | t <- docTopics, map toLower pat `isInfixOf` t]
|
||||
-- _ -> Nothing
|
||||
args = take 1 $ listofstringopt "args" $ rawopts_ opts
|
||||
mtopic = headMay args
|
||||
[info, man, pager, cat] =
|
||||
[runInfoForTopic, runManForTopic, runPagerForTopic pagerprog, printHelpForTopic]
|
||||
[runInfoForTopic, runManForTopic, runPagerForTopic, printHelpForTopic]
|
||||
viewer
|
||||
| boolopt "info" $ rawopts_ opts = info
|
||||
| boolopt "man" $ rawopts_ opts = man
|
||||
@ -66,13 +65,7 @@ help' opts _ = do
|
||||
| "info" `elem` exes = info
|
||||
| "man" `elem` exes = man
|
||||
| pagerprog `elem` exes = pager
|
||||
| "less" `elem` exes = pager
|
||||
| otherwise = cat
|
||||
viewer "hledger"
|
||||
-- case topic of
|
||||
-- Nothing -> putStrLn $ unlines [
|
||||
-- "Please choose a manual by typing \"hledger help MANUAL\" (any substring is ok)."
|
||||
-- ,"A viewer (info, man, $PAGER, or stdout) will be auto-selected,"
|
||||
-- ,"or type \"hledger help -h\" to see options. Manuals available:"
|
||||
-- ]
|
||||
-- ++ "\n " ++ unwords docTopics
|
||||
-- Just t -> viewer t
|
||||
|
||||
viewer "hledger" mtopic
|
||||
|
||||
@ -1,16 +1,17 @@
|
||||
help\
|
||||
Show the hledger user manual in one of several formats.
|
||||
Show the hledger user manual in one of several formats,
|
||||
optionally positioned at a given TOPIC (if possible).
|
||||
TOPIC is any heading, or heading prefix, in the manual.
|
||||
Some examples: commands, print, 'auto postings', periodic.
|
||||
|
||||
_FLAGS
|
||||
|
||||
This command shows the user manual built in to this hledger version,
|
||||
using the best viewer it can find.
|
||||
This command shows the user manual built in to this hledger version.
|
||||
It can be useful if the correct version of the hledger manual,
|
||||
or the usual viewing tools, are not installed on your system.
|
||||
|
||||
It will use the first of these viewers that it finds in $PATH:
|
||||
`info`, `man`, $PAGER, `less`, or stdout.
|
||||
By default it uses the best viewer it can find in $PATH, in this order:
|
||||
`info`, `man`, $PAGER (unless a topic is specified), `less`, or stdout.
|
||||
When run non-interactively, it always uses stdout.
|
||||
Or you can force a particular viewer with the
|
||||
`--info/-i`, `--man/-m`, or `--pager/-p` flags.
|
||||
|
||||
Or you can select a particular viewer with the
|
||||
`-i` (info), `-m` (man), or `-p` (pager) flags.
|
||||
|
||||
@ -8,11 +8,11 @@ Embedded documentation files in various formats, and helpers for viewing them.
|
||||
module Hledger.Cli.DocFiles (
|
||||
|
||||
Topic
|
||||
,docFiles
|
||||
,docTopics
|
||||
,lookupDocNroff
|
||||
,lookupDocTxt
|
||||
,lookupDocInfo
|
||||
-- ,toolDocs
|
||||
-- ,toolDocNames
|
||||
-- ,toolDocMan
|
||||
-- ,toolDocTxt
|
||||
-- ,toolDocInfo
|
||||
,printHelpForTopic
|
||||
,runManForTopic
|
||||
,runInfoForTopic
|
||||
@ -30,14 +30,22 @@ import System.IO.Temp
|
||||
import System.Process
|
||||
|
||||
import Hledger.Utils (first3, second3, third3, embedFileRelative)
|
||||
import Text.Printf (printf)
|
||||
import Data.Maybe (fromMaybe)
|
||||
import System.Environment (lookupEnv)
|
||||
import Hledger.Utils.Debug
|
||||
|
||||
-- The name of any hledger executable.
|
||||
type Tool = String
|
||||
|
||||
-- Any heading in the hledger user manual (and perhaps later the hledger-ui/hledger-web manuals).
|
||||
type Topic = String
|
||||
|
||||
-- | These are all the main hledger manuals, in man, txt, and info formats.
|
||||
-- | The main hledger manuals as source for man, info and as plain text.
|
||||
-- Only files under the current package directory can be embedded,
|
||||
-- so most of these are symlinked here from the other package directories.
|
||||
docFiles :: [(Topic, (ByteString, ByteString, ByteString))]
|
||||
docFiles = [
|
||||
-- so some of these are symlinked from the other package directories.
|
||||
toolDocs :: [(Tool, (ByteString, ByteString, ByteString))]
|
||||
toolDocs = [
|
||||
("hledger",
|
||||
($(embedFileRelative "embeddedfiles/hledger.1")
|
||||
,$(embedFileRelative "embeddedfiles/hledger.txt")
|
||||
@ -55,46 +63,73 @@ docFiles = [
|
||||
))
|
||||
]
|
||||
|
||||
docTopics :: [Topic]
|
||||
docTopics = map fst docFiles
|
||||
-- toolNames :: [Tool]
|
||||
-- toolNames = map fst toolDocs
|
||||
|
||||
lookupDocTxt :: Topic -> ByteString
|
||||
lookupDocTxt name =
|
||||
maybe (fromString $ "No text manual found for topic: "++name) second3 $ lookup name docFiles
|
||||
-- | Get the manual as plain text for this tool, or a not found message.
|
||||
toolDocTxt :: Tool -> ByteString
|
||||
toolDocTxt name =
|
||||
maybe (fromString $ "No text manual found for tool: "++name) second3 $ lookup name toolDocs
|
||||
|
||||
lookupDocNroff :: Topic -> ByteString
|
||||
lookupDocNroff name =
|
||||
maybe (fromString $ "No man page found for topic: "++name) first3 $ lookup name docFiles
|
||||
-- | Get the manual as man source (nroff) for this tool, or a not found message.
|
||||
toolDocMan :: Tool -> ByteString
|
||||
toolDocMan name =
|
||||
maybe (fromString $ "No man page found for tool: "++name) first3 $ lookup name toolDocs
|
||||
|
||||
lookupDocInfo :: Topic -> ByteString
|
||||
lookupDocInfo name =
|
||||
maybe (fromString $ "No info manual found for topic: "++name) third3 $ lookup name docFiles
|
||||
-- | Get the manual as info source (texinfo) for this tool, or a not found message.
|
||||
toolDocInfo :: Tool -> ByteString
|
||||
toolDocInfo name =
|
||||
maybe (fromString $ "No info manual found for tool: "++name) third3 $ lookup name toolDocs
|
||||
|
||||
printHelpForTopic :: Topic -> IO ()
|
||||
printHelpForTopic t =
|
||||
BC.putStr (lookupDocTxt t)
|
||||
-- | Print plain text help for this tool.
|
||||
-- Takes an optional topic argument for convenience but it is currently ignored.
|
||||
printHelpForTopic :: Tool -> Maybe Topic -> IO ()
|
||||
printHelpForTopic tool _mtopic =
|
||||
BC.putStr (toolDocTxt tool)
|
||||
|
||||
runPagerForTopic :: FilePath -> Topic -> IO ()
|
||||
runPagerForTopic exe t = do
|
||||
(Just inp, _, _, ph) <- createProcess (proc exe []){
|
||||
std_in=CreatePipe
|
||||
}
|
||||
BC.hPutStrLn inp (lookupDocTxt t)
|
||||
_ <- waitForProcess ph
|
||||
return ()
|
||||
|
||||
runManForTopic :: Topic -> IO ()
|
||||
runManForTopic t =
|
||||
withSystemTempFile ("hledger-"++t++".nroff") $ \f h -> do
|
||||
BC.hPutStrLn h $ lookupDocNroff t
|
||||
-- | Display plain text help for this tool, scrolled to the given topic
|
||||
-- if provided, using the given pager executable.
|
||||
-- Note when a topic is provided we ignore the provided pager and
|
||||
-- use the "less" executable in $PATH.
|
||||
runPagerForTopic :: Tool -> Maybe Topic -> IO ()
|
||||
runPagerForTopic tool mtopic = do
|
||||
-- avoids a temp file but different from the others and not sure how to make it scroll
|
||||
-- pager <- fromMaybe "less" <$> lookupEnv "PAGER"
|
||||
-- (Just inp, _, _, ph) <- createProcess (proc pager []){
|
||||
-- std_in=CreatePipe
|
||||
-- }
|
||||
-- BC.hPutStrLn inp (toolDocTxt tool)
|
||||
-- _ <- waitForProcess ph
|
||||
-- return ()
|
||||
|
||||
withSystemTempFile ("hledger-"++tool++".txt") $ \f h -> do
|
||||
BC.hPutStrLn h $ toolDocTxt tool
|
||||
hClose h
|
||||
-- the temp file path will presumably have a slash in it, so man should read it
|
||||
callCommand $ "man " ++ f
|
||||
let defpager = "less -is"
|
||||
envpager <- fromMaybe defpager <$> lookupEnv "PAGER"
|
||||
-- force the use of less if a topic is provided, since we know how to scroll it
|
||||
let pager = if mtopic==Nothing then envpager else defpager
|
||||
callCommand $ dbg1 "pager command" $
|
||||
pager ++ maybe "" (printf " +'/^( )?%s'") mtopic ++ " " ++ f
|
||||
|
||||
runInfoForTopic :: Topic -> IO ()
|
||||
runInfoForTopic t =
|
||||
withSystemTempFile ("hledger-"++t++".info") $ \f h -> do
|
||||
BC.hPutStrLn h $ lookupDocInfo t
|
||||
-- | Display a man page for this tool, scrolled to the given topic if provided,
|
||||
-- using the "man" executable in $PATH. Note when a topic is provided we force
|
||||
-- man to use the "less" executable in $PATH, ignoring $MANPAGER and $PAGER.
|
||||
runManForTopic :: Tool -> Maybe Topic -> IO ()
|
||||
runManForTopic tool mtopic =
|
||||
withSystemTempFile ("hledger-"++tool++".nroff") $ \f h -> do
|
||||
BC.hPutStrLn h $ toolDocMan tool
|
||||
hClose h
|
||||
callCommand $ "info " ++ f
|
||||
-- the temp file path will presumably have a slash in it, so man should read it
|
||||
callCommand $ dbg1 "man command" $
|
||||
"man " ++ f ++ maybe "" (printf " -P \"less -is +'/^( )?%s'\"") mtopic
|
||||
|
||||
-- | Display an info manual for this topic, opened at the given topic if provided,
|
||||
-- using the "info" executable in $PATH.
|
||||
runInfoForTopic :: Tool -> Maybe Topic -> IO ()
|
||||
runInfoForTopic tool mtopic =
|
||||
withSystemTempFile ("hledger-"++tool++".info") $ \f h -> do
|
||||
BC.hPutStrLn h $ toolDocInfo tool
|
||||
hClose h
|
||||
callCommand $ dbg1 "info command" $
|
||||
"info " ++ f ++ maybe "" (printf " -n '%s'") mtopic
|
||||
|
||||
@ -146,8 +146,8 @@ main = do
|
||||
hasInfoFlag args = any (`elem` args) ["--info"]
|
||||
f `orShowHelp` mode
|
||||
| hasHelpFlag args = putStr $ showModeUsage mode
|
||||
| hasInfoFlag args = runInfoForTopic "hledger"
|
||||
| hasManFlag args = runManForTopic "hledger"
|
||||
| hasInfoFlag args = runInfoForTopic "hledger" (headMay $ modeNames mode)
|
||||
| hasManFlag args = runManForTopic "hledger" (headMay $ modeNames mode)
|
||||
| otherwise = f
|
||||
-- where
|
||||
-- lastdocflag
|
||||
@ -165,8 +165,8 @@ main = do
|
||||
runHledgerCommand
|
||||
-- high priority flags and situations. -h, then --help, then --info are highest priority.
|
||||
| hasHelpFlag argsbeforecmd = dbgIO "" "-h/--help before command, showing general usage" >> printUsage
|
||||
| hasInfoFlag argsbeforecmd = dbgIO "" "--info before command, showing general info manual" >> runInfoForTopic "hledger"
|
||||
| hasManFlag argsbeforecmd = dbgIO "" "--man before command, showing general man page" >> runManForTopic "hledger"
|
||||
| hasInfoFlag argsbeforecmd = dbgIO "" "--info before command, showing general info manual" >> runInfoForTopic "hledger" Nothing
|
||||
| hasManFlag argsbeforecmd = dbgIO "" "--man before command, showing general man page" >> runManForTopic "hledger" Nothing
|
||||
| not (hasHelpFlag argsaftercmd || hasInfoFlag argsaftercmd || hasManFlag argsaftercmd) && (hasVersion argsbeforecmd || (hasVersion argsaftercmd && isInternalCommand))
|
||||
= putStrLn prognameandversion
|
||||
| not (hasHelpFlag argsaftercmd || hasInfoFlag argsaftercmd || hasManFlag argsaftercmd) && (hasDetailedVersion argsbeforecmd || (hasDetailedVersion argsaftercmd && isInternalCommand))
|
||||
|
||||
Loading…
Reference in New Issue
Block a user