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.Data.Types
|
||||||
import Hledger.Cli.CliOptions
|
import Hledger.Cli.CliOptions
|
||||||
import Hledger.Cli.DocFiles
|
import Hledger.Cli.DocFiles
|
||||||
|
import Safe (headMay)
|
||||||
--import Hledger.Utils.Debug
|
--import Hledger.Utils.Debug
|
||||||
|
|
||||||
helpmode = hledgerCommandMode
|
helpmode = hledgerCommandMode
|
||||||
$(embedFileRelative "Hledger/Cli/Commands/Help.txt")
|
$(embedFileRelative "Hledger/Cli/Commands/Help.txt")
|
||||||
[flagNone ["info","i"] (setboolopt "info") "show the manual with info"
|
[flagNone ["i"] (setboolopt "info") "show the manual with info"
|
||||||
,flagNone ["man","m"] (setboolopt "man") "show the manual with man"
|
,flagNone ["m"] (setboolopt "man") "show the manual with man"
|
||||||
,flagNone ["pager","p"] (setboolopt "pager") "show the manual with $PAGER or less"
|
,flagNone ["p"] (setboolopt "pager") "show the manual with $PAGER or less"
|
||||||
,flagNone ["help","h"] (setboolopt "help") "show this help"
|
,flagNone ["help","h"] (setboolopt "help") "show this help"
|
||||||
]
|
]
|
||||||
[]
|
[]
|
||||||
[]
|
[]
|
||||||
([], Nothing) -- Just $ argsFlag "[TOPIC]"
|
([], Just $ argsFlag "[TOPIC]")
|
||||||
|
|
||||||
-- | Display the hledger manual in various formats.
|
-- | Display the hledger manual in various formats.
|
||||||
-- You can select a docs viewer with one of the `--info`, `--man`, `--pager` flags.
|
-- 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"
|
pagerprog <- fromMaybe "less" <$> lookupEnv "PAGER"
|
||||||
interactive <- hIsTerminalDevice stdout
|
interactive <- hIsTerminalDevice stdout
|
||||||
let
|
let
|
||||||
-- args = take 1 $ listofstringopt "args" $ rawopts_ opts
|
args = take 1 $ listofstringopt "args" $ rawopts_ opts
|
||||||
-- topic = case args of
|
mtopic = headMay args
|
||||||
-- [pat] -> headMay [t | t <- docTopics, map toLower pat `isInfixOf` t]
|
|
||||||
-- _ -> Nothing
|
|
||||||
[info, man, pager, cat] =
|
[info, man, pager, cat] =
|
||||||
[runInfoForTopic, runManForTopic, runPagerForTopic pagerprog, printHelpForTopic]
|
[runInfoForTopic, runManForTopic, runPagerForTopic, printHelpForTopic]
|
||||||
viewer
|
viewer
|
||||||
| boolopt "info" $ rawopts_ opts = info
|
| boolopt "info" $ rawopts_ opts = info
|
||||||
| boolopt "man" $ rawopts_ opts = man
|
| boolopt "man" $ rawopts_ opts = man
|
||||||
@ -66,13 +65,7 @@ help' opts _ = do
|
|||||||
| "info" `elem` exes = info
|
| "info" `elem` exes = info
|
||||||
| "man" `elem` exes = man
|
| "man" `elem` exes = man
|
||||||
| pagerprog `elem` exes = pager
|
| pagerprog `elem` exes = pager
|
||||||
|
| "less" `elem` exes = pager
|
||||||
| otherwise = cat
|
| otherwise = cat
|
||||||
viewer "hledger"
|
|
||||||
-- case topic of
|
viewer "hledger" mtopic
|
||||||
-- 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
|
|
||||||
|
|||||||
@ -1,16 +1,17 @@
|
|||||||
help\
|
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
|
_FLAGS
|
||||||
|
|
||||||
This command shows the user manual built in to this hledger version,
|
This command shows the user manual built in to this hledger version.
|
||||||
using the best viewer it can find.
|
|
||||||
It can be useful if the correct version of the hledger manual,
|
It can be useful if the correct version of the hledger manual,
|
||||||
or the usual viewing tools, are not installed on your system.
|
or the usual viewing tools, are not installed on your system.
|
||||||
|
|
||||||
It will use the first of these viewers that it finds in $PATH:
|
By default it uses the best viewer it can find in $PATH, in this order:
|
||||||
`info`, `man`, $PAGER, `less`, or stdout.
|
`info`, `man`, $PAGER (unless a topic is specified), `less`, or stdout.
|
||||||
When run non-interactively, it always uses stdout.
|
When run non-interactively, it always uses stdout.
|
||||||
Or you can force a particular viewer with the
|
Or you can select a particular viewer with the
|
||||||
`--info/-i`, `--man/-m`, or `--pager/-p` flags.
|
`-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 (
|
module Hledger.Cli.DocFiles (
|
||||||
|
|
||||||
Topic
|
Topic
|
||||||
,docFiles
|
-- ,toolDocs
|
||||||
,docTopics
|
-- ,toolDocNames
|
||||||
,lookupDocNroff
|
-- ,toolDocMan
|
||||||
,lookupDocTxt
|
-- ,toolDocTxt
|
||||||
,lookupDocInfo
|
-- ,toolDocInfo
|
||||||
,printHelpForTopic
|
,printHelpForTopic
|
||||||
,runManForTopic
|
,runManForTopic
|
||||||
,runInfoForTopic
|
,runInfoForTopic
|
||||||
@ -30,14 +30,22 @@ import System.IO.Temp
|
|||||||
import System.Process
|
import System.Process
|
||||||
|
|
||||||
import Hledger.Utils (first3, second3, third3, embedFileRelative)
|
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
|
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,
|
-- Only files under the current package directory can be embedded,
|
||||||
-- so most of these are symlinked here from the other package directories.
|
-- so some of these are symlinked from the other package directories.
|
||||||
docFiles :: [(Topic, (ByteString, ByteString, ByteString))]
|
toolDocs :: [(Tool, (ByteString, ByteString, ByteString))]
|
||||||
docFiles = [
|
toolDocs = [
|
||||||
("hledger",
|
("hledger",
|
||||||
($(embedFileRelative "embeddedfiles/hledger.1")
|
($(embedFileRelative "embeddedfiles/hledger.1")
|
||||||
,$(embedFileRelative "embeddedfiles/hledger.txt")
|
,$(embedFileRelative "embeddedfiles/hledger.txt")
|
||||||
@ -55,46 +63,73 @@ docFiles = [
|
|||||||
))
|
))
|
||||||
]
|
]
|
||||||
|
|
||||||
docTopics :: [Topic]
|
-- toolNames :: [Tool]
|
||||||
docTopics = map fst docFiles
|
-- toolNames = map fst toolDocs
|
||||||
|
|
||||||
lookupDocTxt :: Topic -> ByteString
|
-- | Get the manual as plain text for this tool, or a not found message.
|
||||||
lookupDocTxt name =
|
toolDocTxt :: Tool -> ByteString
|
||||||
maybe (fromString $ "No text manual found for topic: "++name) second3 $ lookup name docFiles
|
toolDocTxt name =
|
||||||
|
maybe (fromString $ "No text manual found for tool: "++name) second3 $ lookup name toolDocs
|
||||||
|
|
||||||
lookupDocNroff :: Topic -> ByteString
|
-- | Get the manual as man source (nroff) for this tool, or a not found message.
|
||||||
lookupDocNroff name =
|
toolDocMan :: Tool -> ByteString
|
||||||
maybe (fromString $ "No man page found for topic: "++name) first3 $ lookup name docFiles
|
toolDocMan name =
|
||||||
|
maybe (fromString $ "No man page found for tool: "++name) first3 $ lookup name toolDocs
|
||||||
|
|
||||||
lookupDocInfo :: Topic -> ByteString
|
-- | Get the manual as info source (texinfo) for this tool, or a not found message.
|
||||||
lookupDocInfo name =
|
toolDocInfo :: Tool -> ByteString
|
||||||
maybe (fromString $ "No info manual found for topic: "++name) third3 $ lookup name docFiles
|
toolDocInfo name =
|
||||||
|
maybe (fromString $ "No info manual found for tool: "++name) third3 $ lookup name toolDocs
|
||||||
|
|
||||||
printHelpForTopic :: Topic -> IO ()
|
-- | Print plain text help for this tool.
|
||||||
printHelpForTopic t =
|
-- Takes an optional topic argument for convenience but it is currently ignored.
|
||||||
BC.putStr (lookupDocTxt t)
|
printHelpForTopic :: Tool -> Maybe Topic -> IO ()
|
||||||
|
printHelpForTopic tool _mtopic =
|
||||||
|
BC.putStr (toolDocTxt tool)
|
||||||
|
|
||||||
runPagerForTopic :: FilePath -> Topic -> IO ()
|
-- | Display plain text help for this tool, scrolled to the given topic
|
||||||
runPagerForTopic exe t = do
|
-- if provided, using the given pager executable.
|
||||||
(Just inp, _, _, ph) <- createProcess (proc exe []){
|
-- Note when a topic is provided we ignore the provided pager and
|
||||||
std_in=CreatePipe
|
-- use the "less" executable in $PATH.
|
||||||
}
|
runPagerForTopic :: Tool -> Maybe Topic -> IO ()
|
||||||
BC.hPutStrLn inp (lookupDocTxt t)
|
runPagerForTopic tool mtopic = do
|
||||||
_ <- waitForProcess ph
|
-- avoids a temp file but different from the others and not sure how to make it scroll
|
||||||
return ()
|
-- pager <- fromMaybe "less" <$> lookupEnv "PAGER"
|
||||||
|
-- (Just inp, _, _, ph) <- createProcess (proc pager []){
|
||||||
runManForTopic :: Topic -> IO ()
|
-- std_in=CreatePipe
|
||||||
runManForTopic t =
|
-- }
|
||||||
withSystemTempFile ("hledger-"++t++".nroff") $ \f h -> do
|
-- BC.hPutStrLn inp (toolDocTxt tool)
|
||||||
BC.hPutStrLn h $ lookupDocNroff t
|
-- _ <- waitForProcess ph
|
||||||
|
-- return ()
|
||||||
|
|
||||||
|
withSystemTempFile ("hledger-"++tool++".txt") $ \f h -> do
|
||||||
|
BC.hPutStrLn h $ toolDocTxt tool
|
||||||
hClose h
|
hClose h
|
||||||
-- the temp file path will presumably have a slash in it, so man should read it
|
let defpager = "less -is"
|
||||||
callCommand $ "man " ++ f
|
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 ()
|
-- | Display a man page for this tool, scrolled to the given topic if provided,
|
||||||
runInfoForTopic t =
|
-- using the "man" executable in $PATH. Note when a topic is provided we force
|
||||||
withSystemTempFile ("hledger-"++t++".info") $ \f h -> do
|
-- man to use the "less" executable in $PATH, ignoring $MANPAGER and $PAGER.
|
||||||
BC.hPutStrLn h $ lookupDocInfo t
|
runManForTopic :: Tool -> Maybe Topic -> IO ()
|
||||||
|
runManForTopic tool mtopic =
|
||||||
|
withSystemTempFile ("hledger-"++tool++".nroff") $ \f h -> do
|
||||||
|
BC.hPutStrLn h $ toolDocMan tool
|
||||||
hClose h
|
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"]
|
hasInfoFlag args = any (`elem` args) ["--info"]
|
||||||
f `orShowHelp` mode
|
f `orShowHelp` mode
|
||||||
| hasHelpFlag args = putStr $ showModeUsage mode
|
| hasHelpFlag args = putStr $ showModeUsage mode
|
||||||
| hasInfoFlag args = runInfoForTopic "hledger"
|
| hasInfoFlag args = runInfoForTopic "hledger" (headMay $ modeNames mode)
|
||||||
| hasManFlag args = runManForTopic "hledger"
|
| hasManFlag args = runManForTopic "hledger" (headMay $ modeNames mode)
|
||||||
| otherwise = f
|
| otherwise = f
|
||||||
-- where
|
-- where
|
||||||
-- lastdocflag
|
-- lastdocflag
|
||||||
@ -165,8 +165,8 @@ main = do
|
|||||||
runHledgerCommand
|
runHledgerCommand
|
||||||
-- high priority flags and situations. -h, then --help, then --info are highest priority.
|
-- high priority flags and situations. -h, then --help, then --info are highest priority.
|
||||||
| hasHelpFlag argsbeforecmd = dbgIO "" "-h/--help before command, showing general usage" >> printUsage
|
| hasHelpFlag argsbeforecmd = dbgIO "" "-h/--help before command, showing general usage" >> printUsage
|
||||||
| hasInfoFlag argsbeforecmd = dbgIO "" "--info before command, showing general info manual" >> runInfoForTopic "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"
|
| 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))
|
| not (hasHelpFlag argsaftercmd || hasInfoFlag argsaftercmd || hasManFlag argsaftercmd) && (hasVersion argsbeforecmd || (hasVersion argsaftercmd && isInternalCommand))
|
||||||
= putStrLn prognameandversion
|
= putStrLn prognameandversion
|
||||||
| not (hasHelpFlag argsaftercmd || hasInfoFlag argsaftercmd || hasManFlag argsaftercmd) && (hasDetailedVersion argsbeforecmd || (hasDetailedVersion argsaftercmd && isInternalCommand))
|
| not (hasHelpFlag argsaftercmd || hasInfoFlag argsaftercmd || hasManFlag argsaftercmd) && (hasDetailedVersion argsbeforecmd || (hasDetailedVersion argsaftercmd && isInternalCommand))
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user