cli: refactor: new Commands module
Builtin commands are now gathered more tightly in a single module, Hledger.Cli.Commands, reducing duplication and facilitating change. The tests command was difficult and has been dropped for now. The obsolete convert/info/man commands have been dropped. cli: refactor: a proper commands list, better Main/Commands separation The legacy "convert" command has been dropped. The activity command's module is now named consistently.
This commit is contained in:
		
							parent
							
								
									b1646b9f05
								
							
						
					
					
						commit
						dc191ec76e
					
				| @ -29,7 +29,8 @@ import System.Console.ANSI | |||||||
| import System.FilePath (takeFileName) | import System.FilePath (takeFileName) | ||||||
| 
 | 
 | ||||||
| import Hledger | import Hledger | ||||||
| import Hledger.Cli hiding (progname,prognameandversion,green) | import Hledger.Cli hiding (progname,prognameandversion) | ||||||
|  | import Hledger.Cli.Add (add) | ||||||
| import Hledger.UI.UIOptions | import Hledger.UI.UIOptions | ||||||
| import Hledger.UI.UITypes | import Hledger.UI.UITypes | ||||||
| import Hledger.UI.UIState | import Hledger.UI.UIState | ||||||
|  | |||||||
| @ -19,7 +19,7 @@ import Data.Time.Calendar (Day) | |||||||
| import Graphics.Vty (Event(..),Key(..)) | import Graphics.Vty (Event(..),Key(..)) | ||||||
| import Text.Megaparsec.Compat | import Text.Megaparsec.Compat | ||||||
| 
 | 
 | ||||||
| import Hledger.Cli hiding (progname,prognameandversion,green) | import Hledger.Cli hiding (progname,prognameandversion) | ||||||
| import Hledger.UI.UIOptions | import Hledger.UI.UIOptions | ||||||
| import Hledger.UI.UITypes | import Hledger.UI.UITypes | ||||||
| import Hledger.UI.UIState | import Hledger.UI.UIState | ||||||
|  | |||||||
| @ -40,7 +40,7 @@ import Control.Concurrent.Chan (newChan, writeChan) | |||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| import Hledger | import Hledger | ||||||
| import Hledger.Cli hiding (progname,prognameandversion,green) | import Hledger.Cli hiding (progname,prognameandversion) | ||||||
| import Hledger.UI.UIOptions | import Hledger.UI.UIOptions | ||||||
| import Hledger.UI.UITypes | import Hledger.UI.UITypes | ||||||
| import Hledger.UI.UIState (toggleHistorical) | import Hledger.UI.UIState (toggleHistorical) | ||||||
|  | |||||||
| @ -30,7 +30,8 @@ import System.Console.ANSI | |||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| import Hledger | import Hledger | ||||||
| import Hledger.Cli hiding (progname,prognameandversion,green) | import Hledger.Cli hiding (progname,prognameandversion) | ||||||
|  | import Hledger.Cli.Add (add) | ||||||
| import Hledger.UI.UIOptions | import Hledger.UI.UIOptions | ||||||
| -- import Hledger.UI.Theme | -- import Hledger.UI.Theme | ||||||
| import Hledger.UI.UITypes | import Hledger.UI.UITypes | ||||||
|  | |||||||
| @ -20,7 +20,7 @@ import Brick.Widgets.List (listMoveTo) | |||||||
| import Brick.Widgets.Border (borderAttr) | import Brick.Widgets.Border (borderAttr) | ||||||
| 
 | 
 | ||||||
| import Hledger | import Hledger | ||||||
| import Hledger.Cli hiding (progname,prognameandversion,green) | import Hledger.Cli hiding (progname,prognameandversion) | ||||||
| import Hledger.UI.UIOptions | import Hledger.UI.UIOptions | ||||||
| -- import Hledger.UI.Theme | -- import Hledger.UI.Theme | ||||||
| import Hledger.UI.UITypes | import Hledger.UI.UITypes | ||||||
|  | |||||||
| @ -20,9 +20,9 @@ import Data.Time.Calendar | |||||||
| import Text.Megaparsec.Compat (digitChar, eof, some, string, runParser, ParseError, MPErr) | import Text.Megaparsec.Compat (digitChar, eof, some, string, runParser, ParseError, MPErr) | ||||||
| 
 | 
 | ||||||
| import Hledger.Utils | import Hledger.Utils | ||||||
| import Hledger.Data hiding (num) | import Hledger.Data | ||||||
| import Hledger.Read | import Hledger.Read | ||||||
| import Hledger.Cli hiding (num) | import Hledger.Cli.Add (appendToJournalFileOrStdout) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| -- Part of the data required from the add form. | -- Part of the data required from the add form. | ||||||
|  | |||||||
| @ -10,21 +10,8 @@ adds some more which are easier to define here. | |||||||
| {-# LANGUAGE OverloadedStrings #-} | {-# LANGUAGE OverloadedStrings #-} | ||||||
| 
 | 
 | ||||||
| module Hledger.Cli ( | module Hledger.Cli ( | ||||||
|                      module Hledger.Cli.Accounts, |  | ||||||
|                      module Hledger.Cli.Add, |  | ||||||
|                      module Hledger.Cli.Balance, |  | ||||||
|                      module Hledger.Cli.Balancesheet, |  | ||||||
|                      module Hledger.Cli.Balancesheetequity, |  | ||||||
|                      module Hledger.Cli.Cashflow, |  | ||||||
|                      module Hledger.Cli.Help, |  | ||||||
|                      module Hledger.Cli.Histogram, |  | ||||||
|                      module Hledger.Cli.Incomestatement, |  | ||||||
|                      module Hledger.Cli.Info, |  | ||||||
|                      module Hledger.Cli.Man, |  | ||||||
|                      module Hledger.Cli.Print, |  | ||||||
|                      module Hledger.Cli.Register, |  | ||||||
|                      module Hledger.Cli.Stats, |  | ||||||
|                      module Hledger.Cli.CliOptions, |                      module Hledger.Cli.CliOptions, | ||||||
|  |                      module Hledger.Cli.Commands, | ||||||
|                      module Hledger.Cli.DocFiles, |                      module Hledger.Cli.DocFiles, | ||||||
|                      module Hledger.Cli.Utils, |                      module Hledger.Cli.Utils, | ||||||
|                      module Hledger.Cli.Version, |                      module Hledger.Cli.Version, | ||||||
| @ -41,21 +28,8 @@ import System.Console.CmdArgs.Explicit hiding (Name) -- don't clash with hledger | |||||||
| import Test.HUnit | import Test.HUnit | ||||||
| 
 | 
 | ||||||
| import Hledger | import Hledger | ||||||
| import Hledger.Cli.Accounts |  | ||||||
| import Hledger.Cli.Add |  | ||||||
| import Hledger.Cli.Balance |  | ||||||
| import Hledger.Cli.Balancesheet |  | ||||||
| import Hledger.Cli.Balancesheetequity |  | ||||||
| import Hledger.Cli.Cashflow |  | ||||||
| import Hledger.Cli.Histogram |  | ||||||
| import Hledger.Cli.Help |  | ||||||
| import Hledger.Cli.Incomestatement |  | ||||||
| import Hledger.Cli.Info |  | ||||||
| import Hledger.Cli.Man |  | ||||||
| import Hledger.Cli.Print |  | ||||||
| import Hledger.Cli.Register |  | ||||||
| import Hledger.Cli.Stats |  | ||||||
| import Hledger.Cli.CliOptions | import Hledger.Cli.CliOptions | ||||||
|  | import Hledger.Cli.Commands | ||||||
| import Hledger.Cli.DocFiles | import Hledger.Cli.DocFiles | ||||||
| import Hledger.Cli.Utils | import Hledger.Cli.Utils | ||||||
| import Hledger.Cli.Version | import Hledger.Cli.Version | ||||||
| @ -65,16 +39,8 @@ tests_Hledger_Cli :: Test | |||||||
| tests_Hledger_Cli = TestList | tests_Hledger_Cli = TestList | ||||||
|  [ |  [ | ||||||
|     tests_Hledger |     tests_Hledger | ||||||
|    -- ,tests_Hledger_Cli_Add |  | ||||||
|    ,tests_Hledger_Cli_Balance |  | ||||||
|    ,tests_Hledger_Cli_Balancesheet |  | ||||||
|    ,tests_Hledger_Cli_Cashflow |  | ||||||
|    -- ,tests_Hledger_Cli_Histogram |  | ||||||
|    ,tests_Hledger_Cli_Incomestatement |  | ||||||
|    ,tests_Hledger_Cli_CliOptions |    ,tests_Hledger_Cli_CliOptions | ||||||
|    -- ,tests_Hledger_Cli_Print |    ,tests_Hledger_Cli_Commands | ||||||
|    ,tests_Hledger_Cli_Register |  | ||||||
|    -- ,tests_Hledger_Cli_Stats |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|    ,"apply account directive" ~:  |    ,"apply account directive" ~:  | ||||||
|  | |||||||
| @ -1,10 +1,10 @@ | |||||||
| {-| | {-| | ||||||
| 
 | 
 | ||||||
| Print a histogram report. (The "activity" command). | Print a bar chart of posting activity per day, or other report interval.  | ||||||
| 
 | 
 | ||||||
| -} | -} | ||||||
| 
 | 
 | ||||||
| module Hledger.Cli.Histogram | module Hledger.Cli.Activity | ||||||
| where | where | ||||||
| 
 | 
 | ||||||
| import Data.List | import Data.List | ||||||
| @ -33,10 +33,9 @@ activitymode = (defCommandMode $ ["activity"] ++ aliases) { | |||||||
| barchar :: Char | barchar :: Char | ||||||
| barchar = '*' | barchar = '*' | ||||||
| 
 | 
 | ||||||
| -- | Print a histogram of some statistic per report interval, such as | -- | Print a bar chart of number of postings per report interval. | ||||||
| -- number of postings per day. | activity :: CliOpts -> Journal -> IO () | ||||||
| histogram :: CliOpts -> Journal -> IO () | activity CliOpts{reportopts_=ropts} j = do | ||||||
| histogram CliOpts{reportopts_=ropts} j = do |  | ||||||
|   d <- getCurrentDay |   d <- getCurrentDay | ||||||
|   putStr $ showHistogram ropts (queryFromOpts d ropts) j |   putStr $ showHistogram ropts (queryFromOpts d ropts) j | ||||||
| 
 | 
 | ||||||
| @ -5,7 +5,12 @@ A history-aware add command to help with data entry. | |||||||
| {-# OPTIONS_GHC -fno-warn-missing-signatures -fno-warn-unused-do-bind #-} | {-# OPTIONS_GHC -fno-warn-missing-signatures -fno-warn-unused-do-bind #-} | ||||||
| {-# LANGUAGE ScopedTypeVariables, DeriveDataTypeable, RecordWildCards, TypeOperators, FlexibleContexts, OverloadedStrings #-} | {-# LANGUAGE ScopedTypeVariables, DeriveDataTypeable, RecordWildCards, TypeOperators, FlexibleContexts, OverloadedStrings #-} | ||||||
| 
 | 
 | ||||||
| module Hledger.Cli.Add | module Hledger.Cli.Add ( | ||||||
|  |    addmode | ||||||
|  |   ,add | ||||||
|  |   ,appendToJournalFileOrStdout | ||||||
|  |   ,transactionsSimilarTo | ||||||
|  | ) | ||||||
| where | where | ||||||
| 
 | 
 | ||||||
| import Prelude () | import Prelude () | ||||||
|  | |||||||
							
								
								
									
										163
									
								
								hledger/Hledger/Cli/Commands.hs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										163
									
								
								hledger/Hledger/Cli/Commands.hs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,163 @@ | |||||||
|  | {-| | ||||||
|  | hledger's built-in commands, and helpers for printing the commands list. | ||||||
|  | -} | ||||||
|  | 
 | ||||||
|  | {-# LANGUAGE QuasiQuotes #-} | ||||||
|  | 
 | ||||||
|  | module Hledger.Cli.Commands ( | ||||||
|  |    findCommand | ||||||
|  |   ,builtinCommands | ||||||
|  |   ,builtinCommandNames | ||||||
|  |   ,printCommandsList | ||||||
|  |   ,tests_Hledger_Cli_Commands | ||||||
|  | )  | ||||||
|  | where | ||||||
|  | 
 | ||||||
|  | import Data.String.Here | ||||||
|  | import Data.List | ||||||
|  | import Data.List.Split (splitOn) | ||||||
|  | import System.Console.CmdArgs.Explicit as C | ||||||
|  | import Test.HUnit | ||||||
|  | 
 | ||||||
|  | import Hledger.Cli.Accounts | ||||||
|  | import Hledger.Cli.Activity | ||||||
|  | import Hledger.Cli.Add | ||||||
|  | import Hledger.Cli.Balance | ||||||
|  | import Hledger.Cli.Balancesheet | ||||||
|  | import Hledger.Cli.Balancesheetequity | ||||||
|  | import Hledger.Cli.Cashflow | ||||||
|  | import Hledger.Cli.Help | ||||||
|  | import Hledger.Cli.Incomestatement | ||||||
|  | import Hledger.Cli.Print | ||||||
|  | import Hledger.Cli.Register | ||||||
|  | import Hledger.Cli.Stats | ||||||
|  | import Hledger.Cli.CliOptions | ||||||
|  | import Hledger.Data | ||||||
|  | import Hledger.Utils (regexReplace) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | -- | The cmdargs subcommand mode and IO action for each builtin command. | ||||||
|  | -- Command actions take parsed CLI options and a (lazy) finalised journal. | ||||||
|  | builtinCommands :: [(Mode RawOpts, CliOpts -> Journal -> IO ())] | ||||||
|  | builtinCommands = [ | ||||||
|  |    (accountsmode           , accounts)  | ||||||
|  |   ,(activitymode           , activity)  | ||||||
|  |   ,(addmode                , add)  | ||||||
|  |   ,(balancemode            , balance)  | ||||||
|  |   ,(balancesheetmode       , balancesheet)  | ||||||
|  |   ,(balancesheetequitymode , balancesheetequity)  | ||||||
|  |   ,(cashflowmode           , cashflow)  | ||||||
|  |   ,(helpmode               , help')  | ||||||
|  |   ,(incomestatementmode    , incomestatement)  | ||||||
|  |   ,(printmode              , print')  | ||||||
|  |   ,(registermode           , register)  | ||||||
|  |   ,(statsmode              , stats)  | ||||||
|  |   ] | ||||||
|  | 
 | ||||||
|  | -- | All names and aliases of builtin commands. | ||||||
|  | builtinCommandNames :: [String] | ||||||
|  | builtinCommandNames = concatMap (modeNames . fst) builtinCommands | ||||||
|  | 
 | ||||||
|  | -- | Look up a builtin command's mode and action by exact command name or alias.  | ||||||
|  | findCommand :: String -> Maybe (Mode RawOpts, CliOpts -> Journal -> IO ())  | ||||||
|  | findCommand cmdname = find (elem cmdname . modeNames . fst) builtinCommands  | ||||||
|  | 
 | ||||||
|  | -- | A template for the commands list, containing entries (indented lines) | ||||||
|  | -- for all currently known builtin and addon commands. | ||||||
|  | -- These will be filtered based on the commands found at runtime,   | ||||||
|  | -- except those beginning with "hledger", which are not filtered.  | ||||||
|  | -- OTHERCMDS is replaced with an entry for each unknown addon command found.  | ||||||
|  | -- COUNT is replaced with the number of commands found. | ||||||
|  | --   | ||||||
|  | -- The command descriptions here should be kept synced with  | ||||||
|  | -- each command's builtin help and with hledger manual's command list. | ||||||
|  | -- | ||||||
|  | commandsListTemplate :: String | ||||||
|  | commandsListTemplate = [here|Commands available (COUNT): | ||||||
|  | 
 | ||||||
|  | Standard reports: | ||||||
|  |  accounts             show chart of accounts | ||||||
|  |  balancesheet (bs)    show a balance sheet | ||||||
|  |  balancesheetequity (bse)    show a balance sheet with equity | ||||||
|  |  cashflow (cf)        show a cashflow statement | ||||||
|  |  incomestatement (is) show an income statement | ||||||
|  |  transactions (txns)  show transactions in some account | ||||||
|  | 
 | ||||||
|  | General reporting: | ||||||
|  |  activity             show a bar chart of posting counts per interval | ||||||
|  |  balance (bal)        show accounts and balances | ||||||
|  |  budget               add automated postings/txns/bucket accts (experimental) | ||||||
|  |  chart                generate simple balance pie charts (experimental) | ||||||
|  |  check                check more powerful balance assertions | ||||||
|  |  check-dates          check transactions are ordered by date | ||||||
|  |  check-dupes          check for accounts with the same leaf name | ||||||
|  |  irr                  calculate internal rate of return of an investment | ||||||
|  |  prices               show market price records | ||||||
|  |  print                show transaction journal entries | ||||||
|  |  print-unique         show only transactions with unique descriptions | ||||||
|  |  register (reg)       show postings and running total | ||||||
|  |  register-match       show best matching transaction for a description | ||||||
|  |  stats                show some journal statistics | ||||||
|  | 
 | ||||||
|  | Interfaces: | ||||||
|  |  add                  console ui for adding transactions | ||||||
|  |  api                  web api server | ||||||
|  |  iadd                 curses ui for adding transactions | ||||||
|  |  ui                   curses ui | ||||||
|  |  web                  web ui | ||||||
|  | 
 | ||||||
|  | Misc: | ||||||
|  |  autosync             download/deduplicate/convert OFX data | ||||||
|  |  equity               generate transactions to zero & restore account balances | ||||||
|  |  interest             generate interest transactions | ||||||
|  |  rewrite              add automated postings to certain transactions | ||||||
|  |  test                 run some self tests | ||||||
|  | OTHERCMDS | ||||||
|  | Help: | ||||||
|  |  help                 show any of the hledger manuals in various formats | ||||||
|  |  hledger CMD -h       show command usage | ||||||
|  |  hledger -h           show general usage | ||||||
|  | |] | ||||||
|  | 
 | ||||||
|  | -- | Print the commands list, modifying the template above based on | ||||||
|  | -- the currently available addons. Missing addons will be removed, and | ||||||
|  | -- extra addons will be added under Misc. | ||||||
|  | printCommandsList :: [String] -> IO () | ||||||
|  | printCommandsList addonsFound = putStr commandsList | ||||||
|  |   where | ||||||
|  |     commandsFound = builtinCommandNames ++ addonsFound | ||||||
|  |     unknownCommandsFound = addonsFound \\ knownCommands | ||||||
|  | 
 | ||||||
|  |     adjustline l | " hledger " `isPrefixOf` l = [l] | ||||||
|  |     adjustline (' ':l) | not $ w `elem` commandsFound = [] | ||||||
|  |       where w = takeWhile (not . (`elem` "| ")) l | ||||||
|  |     adjustline l = [l] | ||||||
|  | 
 | ||||||
|  |     commandsList1 = | ||||||
|  |       regexReplace "OTHERCMDS" (unlines [' ':w | w <- unknownCommandsFound]) $ | ||||||
|  |       unlines $ concatMap adjustline $ lines commandsListTemplate | ||||||
|  | 
 | ||||||
|  |     commandsList = | ||||||
|  |       regexReplace "COUNT" (show $ length $ commandsFromCommandsList commandsList1) | ||||||
|  |       commandsList1 | ||||||
|  | 
 | ||||||
|  | knownCommands :: [String] | ||||||
|  | knownCommands = sort $ commandsFromCommandsList commandsListTemplate | ||||||
|  | 
 | ||||||
|  | -- | Extract the command names from a commands list like the above: | ||||||
|  | -- the first word (or words separated by |) of lines beginning with a space. | ||||||
|  | commandsFromCommandsList :: String -> [String] | ||||||
|  | commandsFromCommandsList s = concatMap (splitOn "|") [w | ' ':l <- lines s, let w:_ = words l] | ||||||
|  | 
 | ||||||
|  | tests_Hledger_Cli_Commands :: Test | ||||||
|  | tests_Hledger_Cli_Commands = TestList [ | ||||||
|  |   -- ,tests_Hledger_Cli_Add | ||||||
|  |    tests_Hledger_Cli_Balance | ||||||
|  |   ,tests_Hledger_Cli_Balancesheet | ||||||
|  |   ,tests_Hledger_Cli_Cashflow | ||||||
|  |   -- ,tests_Hledger_Cli_Histogram | ||||||
|  |   ,tests_Hledger_Cli_Incomestatement | ||||||
|  |   -- ,tests_Hledger_Cli_Print | ||||||
|  |   ,tests_Hledger_Cli_Register | ||||||
|  |   -- ,tests_Hledger_Cli_Stats | ||||||
|  |   ] | ||||||
| @ -27,6 +27,7 @@ import System.Environment | |||||||
| import System.IO | import System.IO | ||||||
| 
 | 
 | ||||||
| import Hledger.Data.RawOptions | import Hledger.Data.RawOptions | ||||||
|  | import Hledger.Data.Types | ||||||
| import Hledger.Cli.CliOptions | import Hledger.Cli.CliOptions | ||||||
| import Hledger.Cli.DocFiles | import Hledger.Cli.DocFiles | ||||||
| --import Hledger.Utils.Debug | --import Hledger.Utils.Debug | ||||||
| @ -52,8 +53,8 @@ helpmode = (defCommandMode $ ["help"] ++ aliases) { | |||||||
| -- You can select a docs viewer with one of the `--info`, `--man`, `--pager`, `--cat` flags. | -- 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 | -- Otherwise it will use the first available of: info, man, $PAGER, less, stdout | ||||||
| -- (and always stdout if output is non-interactive).  | -- (and always stdout if output is non-interactive).  | ||||||
| help' :: CliOpts -> IO () | help' :: CliOpts -> Journal -> IO () | ||||||
| help' opts = do | help' opts _ = do | ||||||
|   exes <- likelyExecutablesInPath |   exes <- likelyExecutablesInPath | ||||||
|   pagerprog <- fromMaybe "less" <$> lookupEnv "PAGER" |   pagerprog <- fromMaybe "less" <$> lookupEnv "PAGER" | ||||||
|   interactive <- hIsTerminalDevice stdout |   interactive <- hIsTerminalDevice stdout | ||||||
|  | |||||||
| @ -1,41 +0,0 @@ | |||||||
| {-| |  | ||||||
| 
 |  | ||||||
| The info command. |  | ||||||
| 
 |  | ||||||
| |-} |  | ||||||
| 
 |  | ||||||
| module Hledger.Cli.Info ( |  | ||||||
| 
 |  | ||||||
|    infomode |  | ||||||
|   ,info' |  | ||||||
| 
 |  | ||||||
|   ) where |  | ||||||
| 
 |  | ||||||
| import Prelude () |  | ||||||
| import Prelude.Compat |  | ||||||
| import Data.List |  | ||||||
| import System.Console.CmdArgs.Explicit |  | ||||||
| 
 |  | ||||||
| import Hledger.Data.RawOptions |  | ||||||
| import Hledger.Cli.CliOptions |  | ||||||
| import Hledger.Cli.DocFiles |  | ||||||
| 
 |  | ||||||
| infomode = (defCommandMode $ ["info"] ++ aliases) { |  | ||||||
|   modeHelp = "show any of the hledger manuals with info" `withAliases` aliases |  | ||||||
|  ,modeGroupFlags = Group { |  | ||||||
|      groupUnnamed = [] |  | ||||||
|     ,groupHidden = [] |  | ||||||
|     ,groupNamed = [] |  | ||||||
|     } |  | ||||||
|  } |  | ||||||
|   where aliases = [] |  | ||||||
| 
 |  | ||||||
| -- | Try to use info to view the selected manual. |  | ||||||
| info' :: CliOpts -> IO () |  | ||||||
| info' opts = do |  | ||||||
|   let args = listofstringopt "args" $ rawopts_ opts |  | ||||||
|   case args of |  | ||||||
|     []    -> putStrLn $ |  | ||||||
|              "Choose a topic, eg: hledger info cli\n" ++ |  | ||||||
|              intercalate ", " docTopics |  | ||||||
|     topic:_ -> runInfoForTopic topic |  | ||||||
| @ -40,11 +40,9 @@ See "Hledger.Data.Ledger" for more examples. | |||||||
| 
 | 
 | ||||||
| module Hledger.Cli.Main where | module Hledger.Cli.Main where | ||||||
| 
 | 
 | ||||||
| -- import Control.Monad |  | ||||||
| import Data.Char (isDigit) | import Data.Char (isDigit) | ||||||
| import Data.String.Here |  | ||||||
| import Data.List | import Data.List | ||||||
| import Data.List.Split (splitOn) | import Data.String.Here | ||||||
| import Safe | import Safe | ||||||
| import System.Console.CmdArgs.Explicit as C | import System.Console.CmdArgs.Explicit as C | ||||||
| import System.Environment | import System.Environment | ||||||
| @ -54,31 +52,16 @@ import System.Process | |||||||
| import Text.Printf | import Text.Printf | ||||||
| 
 | 
 | ||||||
| import Hledger (ensureJournalFileExists) | import Hledger (ensureJournalFileExists) | ||||||
| import Hledger.Cli.Add |  | ||||||
| import Hledger.Cli.Accounts |  | ||||||
| import Hledger.Cli.Balance |  | ||||||
| import Hledger.Cli.Balancesheet |  | ||||||
| import Hledger.Cli.Balancesheetequity |  | ||||||
| import Hledger.Cli.Cashflow |  | ||||||
| import Hledger.Cli.Help |  | ||||||
| import Hledger.Cli.Histogram |  | ||||||
| import Hledger.Cli.Incomestatement |  | ||||||
| import Hledger.Cli.Info |  | ||||||
| import Hledger.Cli.Man |  | ||||||
| import Hledger.Cli.Print |  | ||||||
| import Hledger.Cli.Register |  | ||||||
| import Hledger.Cli.Stats |  | ||||||
| import Hledger.Cli.CliOptions | import Hledger.Cli.CliOptions | ||||||
| import Hledger.Cli.Tests | import Hledger.Cli.Commands | ||||||
| import Hledger.Cli.Utils | import Hledger.Cli.Utils | ||||||
| import Hledger.Cli.Version | import Hledger.Cli.Version | ||||||
| import Hledger.Data.Dates (getCurrentDay) | import Hledger.Data.Dates (getCurrentDay) | ||||||
| import Hledger.Data.RawOptions (RawOpts) |  | ||||||
| import Hledger.Reports.ReportOptions (period_, interval_, queryFromOpts) | import Hledger.Reports.ReportOptions (period_, interval_, queryFromOpts) | ||||||
| import Hledger.Utils | import Hledger.Utils | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| -- | The overall cmdargs mode describing command-line options for hledger. | -- | The overall cmdargs mode describing hledger's command-line options and subcommands. | ||||||
| mainmode addons = defMode { | mainmode addons = defMode { | ||||||
|   modeNames = [progname ++ " [CMD]"] |   modeNames = [progname ++ " [CMD]"] | ||||||
|  ,modeArgs = ([], Just $ argsFlag "[ARGS]") |  ,modeArgs = ([], Just $ argsFlag "[ARGS]") | ||||||
| @ -91,24 +74,7 @@ mainmode addons = defMode { | |||||||
|    ,groupNamed = [ |    ,groupNamed = [ | ||||||
|      ] |      ] | ||||||
|     -- subcommands handled but not shown in the help: |     -- subcommands handled but not shown in the help: | ||||||
|    ,groupHidden = [ |    ,groupHidden = map fst builtinCommands ++ map quickAddonCommandMode addons | ||||||
|       oldconvertmode |  | ||||||
|      ,accountsmode |  | ||||||
|      ,activitymode |  | ||||||
|      ,addmode |  | ||||||
|      ,balancemode |  | ||||||
|      ,balancesheetmode |  | ||||||
|      ,balancesheetequitymode |  | ||||||
|      ,cashflowmode |  | ||||||
|      ,helpmode |  | ||||||
|      ,incomestatementmode |  | ||||||
|      ,infomode |  | ||||||
|      ,manmode |  | ||||||
|      ,printmode |  | ||||||
|      ,registermode |  | ||||||
|      ,statsmode |  | ||||||
|      ,testmode |  | ||||||
|      ] ++ map quickAddonCommandMode addons |  | ||||||
|    } |    } | ||||||
|  ,modeGroupFlags = Group { |  ,modeGroupFlags = Group { | ||||||
|      -- flags in named groups: |      -- flags in named groups: | ||||||
| @ -134,171 +100,7 @@ PROGNAME help [MANUAL]           show any of the hledger manuals in various form | |||||||
| |] | |] | ||||||
|  } |  } | ||||||
| 
 | 
 | ||||||
| oldconvertmode = (defCommandMode ["convert"]) { | -- | Let's go! | ||||||
|   modeValue = [("command","convert")] |  | ||||||
|  ,modeHelp = "convert is no longer needed, just use -f FILE.csv" |  | ||||||
|  ,modeArgs = ([], Just $ argsFlag "[CSVFILE]") |  | ||||||
|  ,modeGroupFlags = Group { |  | ||||||
|      groupUnnamed = [] |  | ||||||
|     ,groupHidden = helpflags |  | ||||||
|     ,groupNamed = [] |  | ||||||
|     } |  | ||||||
|  } |  | ||||||
| 
 |  | ||||||
| builtinCommands :: [Mode RawOpts] |  | ||||||
| builtinCommands = |  | ||||||
|   let gs = modeGroupModes $ mainmode [] |  | ||||||
|   in concatMap snd (groupNamed gs) ++ groupUnnamed gs ++ groupHidden gs |  | ||||||
| 
 |  | ||||||
| builtinCommandNames :: [String] |  | ||||||
| builtinCommandNames = concatMap modeNames builtinCommands |  | ||||||
| 
 |  | ||||||
| -- | Parse hledger CLI options from these command line arguments and |  | ||||||
| -- add-on command names, or raise any error. |  | ||||||
| argsToCliOpts :: [String] -> [String] -> IO CliOpts |  | ||||||
| argsToCliOpts args addons = do |  | ||||||
|   let |  | ||||||
|     args'        = moveFlagsAfterCommand args |  | ||||||
|     cmdargsopts  = either usageError id $ process (mainmode addons) args' |  | ||||||
|     cmdargsopts' = decodeRawOpts cmdargsopts |  | ||||||
|   rawOptsToCliOpts cmdargsopts' |  | ||||||
| 
 |  | ||||||
| -- | A hacky workaround for cmdargs not accepting flags before the |  | ||||||
| -- subcommand name: try to detect and move such flags after the |  | ||||||
| -- command.  This allows the user to put them in either position. |  | ||||||
| -- The order of options is not preserved, but this should be ok. |  | ||||||
| -- |  | ||||||
| -- Since we're not parsing flags as precisely as cmdargs here, this is |  | ||||||
| -- imperfect. We make a decent effort to: |  | ||||||
| -- - move all no-argument help/input/report flags |  | ||||||
| -- - move all required-argument help/input/report flags along with their values, space-separated or not |  | ||||||
| -- - not confuse things further or cause misleading errors. |  | ||||||
| moveFlagsAfterCommand :: [String] -> [String] |  | ||||||
| moveFlagsAfterCommand args = moveArgs $ ensureDebugHasArg args |  | ||||||
|   where |  | ||||||
|     -- quickly! make sure --debug has a numeric argument, or this all goes to hell |  | ||||||
|     ensureDebugHasArg as = |  | ||||||
|       case break (=="--debug") as of |  | ||||||
|        (bs,"--debug":c:cs) | null c || not (all isDigit c) -> bs++"--debug=1":c:cs |  | ||||||
|        (bs,"--debug":[])                                   -> bs++"--debug=1":[] |  | ||||||
|        _                                                   -> as |  | ||||||
| 
 |  | ||||||
|     -- -h ..., --version ... |  | ||||||
|     moveArgs (f:a:as)   | isMovableNoArgFlag f                   = (moveArgs $ a:as) ++ [f] |  | ||||||
|     -- -f FILE ..., --alias ALIAS ... |  | ||||||
|     moveArgs (f:v:a:as) | isMovableReqArgFlag f, isValue v       = (moveArgs $ a:as) ++ [f,v] |  | ||||||
|     -- -fFILE ..., --alias=ALIAS ... |  | ||||||
|     moveArgs (fv:a:as)  | isMovableReqArgFlagAndValue fv         = (moveArgs $ a:as) ++ [fv] |  | ||||||
|     -- -f(missing arg) |  | ||||||
|     moveArgs (f:a:as)   | isMovableReqArgFlag f, not (isValue a) = (moveArgs $ a:as) ++ [f] |  | ||||||
|     -- anything else |  | ||||||
|     moveArgs as = as |  | ||||||
| 
 |  | ||||||
| isMovableNoArgFlag a  = "-" `isPrefixOf` a && dropWhile (=='-') a `elem` noargflagstomove |  | ||||||
| 
 |  | ||||||
| isMovableReqArgFlag a = "-" `isPrefixOf` a && dropWhile (=='-') a `elem` reqargflagstomove |  | ||||||
| 
 |  | ||||||
| isMovableReqArgFlagAndValue ('-':'-':a:as) = case break (== '=') (a:as) of (f:fs,_:_) -> (f:fs) `elem` reqargflagstomove |  | ||||||
|                                                                            _          -> False |  | ||||||
| isMovableReqArgFlagAndValue ('-':shortflag:_:_) = [shortflag] `elem` reqargflagstomove |  | ||||||
| isMovableReqArgFlagAndValue _ = False |  | ||||||
| 
 |  | ||||||
| isValue "-"     = True |  | ||||||
| isValue ('-':_) = False |  | ||||||
| isValue _       = True |  | ||||||
| 
 |  | ||||||
| flagstomove = inputflags ++ reportflags ++ helpflags |  | ||||||
| noargflagstomove  = concatMap flagNames $ filter ((==FlagNone).flagInfo) flagstomove |  | ||||||
| reqargflagstomove = -- filter (/= "debug") $ |  | ||||||
|                     concatMap flagNames $ filter ((==FlagReq ).flagInfo) flagstomove |  | ||||||
| 
 |  | ||||||
| -- | Template for the commands list.  |  | ||||||
| -- Includes an entry for all known or hypothetical builtin and addon commands;  |  | ||||||
| -- these will be filtered based on the commands found at runtime.   |  | ||||||
| -- Commands beginning with "hledger" are not filtered ("hledger -h" etc.)  |  | ||||||
| -- COUNT is replaced with the number of commands found.   |  | ||||||
| -- OTHERCMDS is replaced with an entry for each unknown addon command found.  |  | ||||||
| -- The command descriptions here should be synced with each command's builtin help  |  | ||||||
| -- and with hledger manual's command list. |  | ||||||
| commandsListTemplate :: String |  | ||||||
| commandsListTemplate = [here|Commands available (COUNT): |  | ||||||
| 
 |  | ||||||
| Standard reports: |  | ||||||
|  accounts             show chart of accounts |  | ||||||
|  balancesheet (bs)    show a balance sheet |  | ||||||
|  balancesheetequity (bse)    show a balance sheet with equity |  | ||||||
|  cashflow (cf)        show a cashflow statement |  | ||||||
|  incomestatement (is) show an income statement |  | ||||||
|  transactions (txns)  show transactions in some account |  | ||||||
| 
 |  | ||||||
| General reporting: |  | ||||||
|  activity             show a bar chart of posting counts per interval |  | ||||||
|  balance (bal)        show accounts and balances |  | ||||||
|  budget               add automated postings/txns/bucket accts (experimental) |  | ||||||
|  chart                generate simple balance pie charts (experimental) |  | ||||||
|  check                check more powerful balance assertions |  | ||||||
|  check-dates          check transactions are ordered by date |  | ||||||
|  check-dupes          check for accounts with the same leaf name |  | ||||||
|  irr                  calculate internal rate of return of an investment |  | ||||||
|  prices               show market price records |  | ||||||
|  print                show transaction journal entries |  | ||||||
|  print-unique         show only transactions with unique descriptions |  | ||||||
|  register (reg)       show postings and running total |  | ||||||
|  register-match       show best matching transaction for a description |  | ||||||
|  stats                show some journal statistics |  | ||||||
| 
 |  | ||||||
| Interfaces: |  | ||||||
|  add                  console ui for adding transactions |  | ||||||
|  api                  web api server |  | ||||||
|  iadd                 curses ui for adding transactions |  | ||||||
|  ui                   curses ui |  | ||||||
|  web                  web ui |  | ||||||
| 
 |  | ||||||
| Misc: |  | ||||||
|  autosync             download/deduplicate/convert OFX data |  | ||||||
|  equity               generate transactions to zero & restore account balances |  | ||||||
|  interest             generate interest transactions |  | ||||||
|  rewrite              add automated postings to certain transactions |  | ||||||
|  test                 run some self tests |  | ||||||
| OTHERCMDS |  | ||||||
| Help: |  | ||||||
|  help                 show any of the hledger manuals in various formats |  | ||||||
|  hledger CMD -h       show command usage |  | ||||||
|  hledger -h           show general usage |  | ||||||
| |] |  | ||||||
| 
 |  | ||||||
| knownCommands :: [String] |  | ||||||
| knownCommands = sort $ commandsFromCommandsList commandsListTemplate |  | ||||||
| 
 |  | ||||||
| -- | Extract the command names from a commands list like the above: |  | ||||||
| -- the first word (or words separated by |) of lines beginning with a space. |  | ||||||
| commandsFromCommandsList :: String -> [String] |  | ||||||
| commandsFromCommandsList s = concatMap (splitOn "|") [w | ' ':l <- lines s, let w:_ = words l] |  | ||||||
| 
 |  | ||||||
| -- | Print the commands list, modifying the template above based on |  | ||||||
| -- the currently available addons. Missing addons will be removed, and |  | ||||||
| -- extra addons will be added under Misc. |  | ||||||
| printCommandsList :: [String] -> IO () |  | ||||||
| printCommandsList addonsFound = putStr commandsList |  | ||||||
|   where |  | ||||||
|     commandsFound = builtinCommandNames ++ addonsFound |  | ||||||
|     unknownCommandsFound = addonsFound \\ knownCommands |  | ||||||
| 
 |  | ||||||
|     adjustline l | " hledger " `isPrefixOf` l = [l] |  | ||||||
|     adjustline (' ':l) | not $ w `elem` commandsFound = [] |  | ||||||
|       where w = takeWhile (not . (`elem` "| ")) l |  | ||||||
|     adjustline l = [l] |  | ||||||
| 
 |  | ||||||
|     commandsList1 = |  | ||||||
|       regexReplace "OTHERCMDS" (unlines [' ':w | w <- unknownCommandsFound]) $ |  | ||||||
|       unlines $ concatMap adjustline $ lines commandsListTemplate |  | ||||||
| 
 |  | ||||||
|     commandsList = |  | ||||||
|       regexReplace "COUNT" (show $ length $ commandsFromCommandsList commandsList1) |  | ||||||
|       commandsList1 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| -- | Let's go. |  | ||||||
| main :: IO () | main :: IO () | ||||||
| main = do | main = do | ||||||
| 
 | 
 | ||||||
| @ -373,24 +175,17 @@ main = do | |||||||
|       | isNullCommand            = dbgIO "" "no command, showing commands list" >> printCommandsList addons |       | isNullCommand            = dbgIO "" "no command, showing commands list" >> printCommandsList addons | ||||||
|       | isBadCommand             = badCommandError |       | isBadCommand             = badCommandError | ||||||
| 
 | 
 | ||||||
|       -- internal commands |       -- builtin commands | ||||||
|       | cmd == "activity"        = withJournalDo opts histogram       `orShowHelp` activitymode |       | Just (cmdmode, cmdaction) <- findCommand cmd = do  | ||||||
|       | cmd == "add"             = (journalFilePathFromOpts opts >>= (ensureJournalFileExists . head) >> withJournalDo opts add) `orShowHelp` addmode |         if cmd=="add" -- add command does extra work before reading journal | ||||||
|       | cmd == "accounts"        = withJournalDo opts accounts        `orShowHelp` accountsmode |         then (do | ||||||
|       | cmd == "balance"         = withJournalDo opts balance         `orShowHelp` balancemode |           journalFilePathFromOpts opts >>= (ensureJournalFileExists . head)  | ||||||
|       | cmd == "balancesheet"    = withJournalDo opts balancesheet    `orShowHelp` balancesheetmode |           withJournalDo opts cmdaction) | ||||||
|       | cmd == "balancesheetequity"    = withJournalDo opts balancesheetequity    `orShowHelp` balancesheetequitymode |           `orShowHelp` cmdmode | ||||||
|       | cmd == "cashflow"        = withJournalDo opts cashflow        `orShowHelp` cashflowmode |         else | ||||||
|       | cmd == "incomestatement" = withJournalDo opts incomestatement `orShowHelp` incomestatementmode |           withJournalDo opts cmdaction `orShowHelp` cmdmode | ||||||
|       | cmd == "print"           = withJournalDo opts print'          `orShowHelp` printmode |  | ||||||
|       | cmd == "register"        = withJournalDo opts register        `orShowHelp` registermode |  | ||||||
|       | cmd == "stats"           = withJournalDo opts stats           `orShowHelp` statsmode |  | ||||||
|       | cmd == "test"            = test' opts                         `orShowHelp` testmode |  | ||||||
|       | cmd == "help"            = help' opts                         `orShowHelp` helpmode |  | ||||||
|       | cmd == "man"             = man opts                           `orShowHelp` manmode |  | ||||||
|       | cmd == "info"            = info' opts                         `orShowHelp` infomode |  | ||||||
| 
 | 
 | ||||||
|       -- an external command |       -- addon commands | ||||||
|       | isExternalCommand = do |       | isExternalCommand = do | ||||||
|           let externalargs = argsbeforecmd ++ filter (not.(=="--")) argsaftercmd |           let externalargs = argsbeforecmd ++ filter (not.(=="--")) argsaftercmd | ||||||
|           let shellcmd = printf "%s-%s %s" progname cmd (unwords' externalargs) :: String |           let shellcmd = printf "%s-%s %s" progname cmd (unwords' externalargs) :: String | ||||||
| @ -400,20 +195,69 @@ main = do | |||||||
|           system shellcmd >>= exitWith |           system shellcmd >>= exitWith | ||||||
| 
 | 
 | ||||||
|       -- deprecated commands |       -- deprecated commands | ||||||
|       | cmd == "convert"         = error' (modeHelp oldconvertmode) >> exitFailure |       -- | cmd == "convert"         = error' (modeHelp oldconvertmode) >> exitFailure | ||||||
| 
 | 
 | ||||||
|       -- shouldn't reach here |       -- shouldn't reach here | ||||||
|       | otherwise                = usageError ("could not understand the arguments "++show args) >> exitFailure |       | otherwise                = usageError ("could not understand the arguments "++show args) >> exitFailure | ||||||
| 
 | 
 | ||||||
|   runHledgerCommand |   runHledgerCommand | ||||||
| 
 | 
 | ||||||
|  | -- | Parse hledger CLI options from these command line arguments and | ||||||
|  | -- add-on command names, or raise any error. | ||||||
|  | argsToCliOpts :: [String] -> [String] -> IO CliOpts | ||||||
|  | argsToCliOpts args addons = do | ||||||
|  |   let | ||||||
|  |     args'        = moveFlagsAfterCommand args | ||||||
|  |     cmdargsopts  = either usageError id $ process (mainmode addons) args' | ||||||
|  |     cmdargsopts' = decodeRawOpts cmdargsopts | ||||||
|  |   rawOptsToCliOpts cmdargsopts' | ||||||
| 
 | 
 | ||||||
| -- tests_runHledgerCommand = [ | -- | A hacky workaround for cmdargs not accepting flags before the | ||||||
| --   -- "runHledgerCommand" ~: do | -- subcommand name: try to detect and move such flags after the | ||||||
| --   --   let opts = defreportopts{query_="expenses"} | -- command.  This allows the user to put them in either position. | ||||||
| --   --   d <- getCurrentDay | -- The order of options is not preserved, but this should be ok. | ||||||
| --   --   runHledgerCommand addons opts@CliOpts{command_=cmd} args | -- | ||||||
|  | -- Since we're not parsing flags as precisely as cmdargs here, this is | ||||||
|  | -- imperfect. We make a decent effort to: | ||||||
|  | -- - move all no-argument help/input/report flags | ||||||
|  | -- - move all required-argument help/input/report flags along with their values, space-separated or not | ||||||
|  | -- - not confuse things further or cause misleading errors. | ||||||
|  | moveFlagsAfterCommand :: [String] -> [String] | ||||||
|  | moveFlagsAfterCommand args = moveArgs $ ensureDebugHasArg args | ||||||
|  |   where | ||||||
|  |     -- quickly! make sure --debug has a numeric argument, or this all goes to hell | ||||||
|  |     ensureDebugHasArg as = | ||||||
|  |       case break (=="--debug") as of | ||||||
|  |        (bs,"--debug":c:cs) | null c || not (all isDigit c) -> bs++"--debug=1":c:cs | ||||||
|  |        (bs,"--debug":[])                                   -> bs++"--debug=1":[] | ||||||
|  |        _                                                   -> as | ||||||
| 
 | 
 | ||||||
| --  ] |     -- -h ..., --version ... | ||||||
|  |     moveArgs (f:a:as)   | isMovableNoArgFlag f                   = (moveArgs $ a:as) ++ [f] | ||||||
|  |     -- -f FILE ..., --alias ALIAS ... | ||||||
|  |     moveArgs (f:v:a:as) | isMovableReqArgFlag f, isValue v       = (moveArgs $ a:as) ++ [f,v] | ||||||
|  |     -- -fFILE ..., --alias=ALIAS ... | ||||||
|  |     moveArgs (fv:a:as)  | isMovableReqArgFlagAndValue fv         = (moveArgs $ a:as) ++ [fv] | ||||||
|  |     -- -f(missing arg) | ||||||
|  |     moveArgs (f:a:as)   | isMovableReqArgFlag f, not (isValue a) = (moveArgs $ a:as) ++ [f] | ||||||
|  |     -- anything else | ||||||
|  |     moveArgs as = as | ||||||
| 
 | 
 | ||||||
|  | isMovableNoArgFlag a  = "-" `isPrefixOf` a && dropWhile (=='-') a `elem` noargflagstomove | ||||||
|  | 
 | ||||||
|  | isMovableReqArgFlag a = "-" `isPrefixOf` a && dropWhile (=='-') a `elem` reqargflagstomove | ||||||
|  | 
 | ||||||
|  | isMovableReqArgFlagAndValue ('-':'-':a:as) = case break (== '=') (a:as) of (f:fs,_:_) -> (f:fs) `elem` reqargflagstomove | ||||||
|  |                                                                            _          -> False | ||||||
|  | isMovableReqArgFlagAndValue ('-':shortflag:_:_) = [shortflag] `elem` reqargflagstomove | ||||||
|  | isMovableReqArgFlagAndValue _ = False | ||||||
|  | 
 | ||||||
|  | isValue "-"     = True | ||||||
|  | isValue ('-':_) = False | ||||||
|  | isValue _       = True | ||||||
|  | 
 | ||||||
|  | flagstomove = inputflags ++ reportflags ++ helpflags | ||||||
|  | noargflagstomove  = concatMap flagNames $ filter ((==FlagNone).flagInfo) flagstomove | ||||||
|  | reqargflagstomove = -- filter (/= "debug") $ | ||||||
|  |                     concatMap flagNames $ filter ((==FlagReq ).flagInfo) flagstomove | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,41 +0,0 @@ | |||||||
| {-| |  | ||||||
| 
 |  | ||||||
| The man command. |  | ||||||
| 
 |  | ||||||
| |-} |  | ||||||
| 
 |  | ||||||
| module Hledger.Cli.Man ( |  | ||||||
| 
 |  | ||||||
|    manmode |  | ||||||
|   ,man |  | ||||||
| 
 |  | ||||||
|   ) where |  | ||||||
| 
 |  | ||||||
| import Prelude () |  | ||||||
| import Prelude.Compat |  | ||||||
| import Data.List |  | ||||||
| import System.Console.CmdArgs.Explicit |  | ||||||
| 
 |  | ||||||
| import Hledger.Data.RawOptions |  | ||||||
| import Hledger.Cli.CliOptions |  | ||||||
| import Hledger.Cli.DocFiles |  | ||||||
| 
 |  | ||||||
| manmode = (defCommandMode $ ["man"] ++ aliases) { |  | ||||||
|   modeHelp = "show any of the hledger manuals with man" `withAliases` aliases |  | ||||||
|  ,modeGroupFlags = Group { |  | ||||||
|      groupUnnamed = [] |  | ||||||
|     ,groupHidden = [] |  | ||||||
|     ,groupNamed = [] |  | ||||||
|     } |  | ||||||
|  } |  | ||||||
|   where aliases = [] |  | ||||||
| 
 |  | ||||||
| -- | Try to use man to view the selected manual. |  | ||||||
| man :: CliOpts -> IO () |  | ||||||
| man opts = do |  | ||||||
|   let args = listofstringopt "args" $ rawopts_ opts |  | ||||||
|   case args of |  | ||||||
|     []    -> putStrLn $ |  | ||||||
|              "Choose a topic, eg: hledger man cli\n" ++ |  | ||||||
|              intercalate ", " docTopics |  | ||||||
|     topic:_ -> runManForTopic topic |  | ||||||
| @ -122,18 +122,17 @@ library | |||||||
|       Hledger.Cli.Tests |       Hledger.Cli.Tests | ||||||
|       Hledger.Cli.Utils |       Hledger.Cli.Utils | ||||||
|       Hledger.Cli.Version |       Hledger.Cli.Version | ||||||
|       Hledger.Cli.Add |  | ||||||
|       Hledger.Cli.Accounts |       Hledger.Cli.Accounts | ||||||
|  |       Hledger.Cli.Activity | ||||||
|  |       Hledger.Cli.Add | ||||||
|       Hledger.Cli.Balance |       Hledger.Cli.Balance | ||||||
|       Hledger.Cli.Balancesheet |       Hledger.Cli.Balancesheet | ||||||
|       Hledger.Cli.Balancesheetequity |       Hledger.Cli.Balancesheetequity | ||||||
|  |       Hledger.Cli.Commands | ||||||
|       Hledger.Cli.CompoundBalanceCommand |       Hledger.Cli.CompoundBalanceCommand | ||||||
|       Hledger.Cli.Cashflow |       Hledger.Cli.Cashflow | ||||||
|       Hledger.Cli.Help |       Hledger.Cli.Help | ||||||
|       Hledger.Cli.Histogram |  | ||||||
|       Hledger.Cli.Incomestatement |       Hledger.Cli.Incomestatement | ||||||
|       Hledger.Cli.Info |  | ||||||
|       Hledger.Cli.Man |  | ||||||
|       Hledger.Cli.Print |       Hledger.Cli.Print | ||||||
|       Hledger.Cli.Register |       Hledger.Cli.Register | ||||||
|       Hledger.Cli.Stats |       Hledger.Cli.Stats | ||||||
|  | |||||||
| @ -103,18 +103,17 @@ library: | |||||||
|   - Hledger.Cli.Tests |   - Hledger.Cli.Tests | ||||||
|   - Hledger.Cli.Utils |   - Hledger.Cli.Utils | ||||||
|   - Hledger.Cli.Version |   - Hledger.Cli.Version | ||||||
|   - Hledger.Cli.Add |  | ||||||
|   - Hledger.Cli.Accounts |   - Hledger.Cli.Accounts | ||||||
|  |   - Hledger.Cli.Activity | ||||||
|  |   - Hledger.Cli.Add | ||||||
|   - Hledger.Cli.Balance |   - Hledger.Cli.Balance | ||||||
|   - Hledger.Cli.Balancesheet |   - Hledger.Cli.Balancesheet | ||||||
|   - Hledger.Cli.Balancesheetequity |   - Hledger.Cli.Balancesheetequity | ||||||
|  |   - Hledger.Cli.Commands | ||||||
|   - Hledger.Cli.CompoundBalanceCommand |   - Hledger.Cli.CompoundBalanceCommand | ||||||
|   - Hledger.Cli.Cashflow |   - Hledger.Cli.Cashflow | ||||||
|   - Hledger.Cli.Help |   - Hledger.Cli.Help | ||||||
|   - Hledger.Cli.Histogram |  | ||||||
|   - Hledger.Cli.Incomestatement |   - Hledger.Cli.Incomestatement | ||||||
|   - Hledger.Cli.Info |  | ||||||
|   - Hledger.Cli.Man |  | ||||||
|   - Hledger.Cli.Print |   - Hledger.Cli.Print | ||||||
|   - Hledger.Cli.Register |   - Hledger.Cli.Register | ||||||
|   - Hledger.Cli.Stats |   - Hledger.Cli.Stats | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user