2010-11-25 10:10:38 +03:00
|
|
|
{-|
|
2015-08-13 21:22:40 +03:00
|
|
|
hledger-ui - a hledger add-on providing a curses-style interface.
|
2015-08-18 03:44:18 +03:00
|
|
|
Copyright (c) 2007-2015 Simon Michael <simon@joyful.com>
|
2010-11-25 10:10:38 +03:00
|
|
|
Released under GPL version 3 or later.
|
2008-12-08 20:27:16 +03:00
|
|
|
-}
|
2015-08-18 03:44:18 +03:00
|
|
|
{-# LANGUAGE OverloadedStrings #-}
|
|
|
|
{-# LANGUAGE TemplateHaskell #-}
|
|
|
|
{-# LANGUAGE MultiParamTypeClasses #-}
|
2008-12-08 20:27:16 +03:00
|
|
|
|
2015-08-18 03:44:18 +03:00
|
|
|
module Hledger.UI.Main where
|
2010-11-25 10:10:38 +03:00
|
|
|
|
2015-08-18 03:44:18 +03:00
|
|
|
-- import Control.Applicative
|
2016-06-09 22:41:26 +03:00
|
|
|
-- import Lens.Micro.Platform ((^.))
|
2011-06-06 22:59:24 +04:00
|
|
|
import Control.Monad
|
2015-08-23 03:46:57 +03:00
|
|
|
-- import Control.Monad.IO.Class (liftIO)
|
2015-08-18 03:44:18 +03:00
|
|
|
-- import Data.Default
|
|
|
|
-- import Data.Monoid --
|
2015-09-04 06:40:43 +03:00
|
|
|
import Data.List
|
2015-08-23 03:46:57 +03:00
|
|
|
import Data.Maybe
|
lib: textification begins! account names
The first of several conversions from String to (strict) Text, hopefully
reducing space and time usage.
This one shows a small improvement, with GHC 7.10.3 and text-1.2.2.1:
hledger -f data/100x100x10.journal stats
string: <<ghc: 39471064 bytes, 77 GCs, 198421/275048 avg/max bytes residency (3 samples), 2M in use, 0.000 INIT (0.001 elapsed), 0.015 MUT (0.020 elapsed), 0.010 GC (0.014 elapsed) :ghc>>
text: <<ghc: 39268024 bytes, 77 GCs, 197018/270840 avg/max bytes residency (3 samples), 2M in use, 0.000 INIT (0.002 elapsed), 0.016 MUT (0.022 elapsed), 0.009 GC (0.011 elapsed) :ghc>>
hledger -f data/1000x100x10.journal stats
string: <<ghc: 318555920 bytes, 617 GCs, 2178997/7134472 avg/max bytes residency (7 samples), 16M in use, 0.000 INIT (0.001 elapsed), 0.129 MUT (0.136 elapsed), 0.067 GC (0.077 elapsed) :ghc>>
text: <<ghc: 314248496 bytes, 612 GCs, 2074045/6617960 avg/max bytes residency (7 samples), 16M in use, 0.000 INIT (0.003 elapsed), 0.137 MUT (0.145 elapsed), 0.067 GC (0.079 elapsed) :ghc>>
hledger -f data/10000x100x10.journal stats
string: <<ghc: 3114763608 bytes, 6026 GCs, 18858950/75552024 avg/max bytes residency (11 samples), 201M in use, 0.000 INIT (0.000 elapsed), 1.331 MUT (1.372 elapsed), 0.699 GC (0.812 elapsed) :ghc>>
text: <<ghc: 3071468920 bytes, 5968 GCs, 14120344/62951360 avg/max bytes residency (9 samples), 124M in use, 0.000 INIT (0.003 elapsed), 1.272 MUT (1.349 elapsed), 0.513 GC (0.578 elapsed) :ghc>>
hledger -f data/100000x100x10.journal stats
string: <<ghc: 31186579432 bytes, 60278 GCs, 135332581/740228992 avg/max bytes residency (13 samples), 1697M in use, 0.000 INIT (0.008 elapsed), 14.677 MUT (15.508 elapsed), 7.081 GC (8.074 elapsed) :ghc>>
text: <<ghc: 30753427672 bytes, 59763 GCs, 117595958/666457240 avg/max bytes residency (14 samples), 1588M in use, 0.000 INIT (0.008 elapsed), 13.713 MUT (13.966 elapsed), 6.220 GC (7.108 elapsed) :ghc>>
2016-05-24 04:16:21 +03:00
|
|
|
-- import Data.Text (Text)
|
|
|
|
import qualified Data.Text as T
|
2015-08-18 03:44:18 +03:00
|
|
|
-- import Data.Time.Calendar
|
2015-08-28 18:48:40 +03:00
|
|
|
import Safe
|
2011-08-22 18:55:39 +04:00
|
|
|
import System.Exit
|
2010-11-25 10:10:38 +03:00
|
|
|
|
2015-08-18 03:44:18 +03:00
|
|
|
import qualified Graphics.Vty as V
|
2015-08-20 14:54:23 +03:00
|
|
|
import Brick
|
2015-08-18 03:44:18 +03:00
|
|
|
|
2011-07-18 03:05:56 +04:00
|
|
|
import Hledger
|
2015-08-12 05:08:33 +03:00
|
|
|
import Hledger.Cli hiding (progname,prognameandversion,green)
|
2015-08-28 22:33:33 +03:00
|
|
|
import Hledger.UI.UIOptions
|
2015-08-18 03:44:18 +03:00
|
|
|
import Hledger.UI.UITypes
|
2015-08-23 03:46:57 +03:00
|
|
|
-- import Hledger.UI.UIUtils
|
|
|
|
import Hledger.UI.Theme
|
2016-06-08 21:03:49 +03:00
|
|
|
import Hledger.UI.AccountsScreen
|
|
|
|
import Hledger.UI.RegisterScreen
|
2010-11-25 10:10:38 +03:00
|
|
|
|
2015-08-18 03:44:18 +03:00
|
|
|
----------------------------------------------------------------------
|
|
|
|
|
2010-11-25 10:10:38 +03:00
|
|
|
main :: IO ()
|
|
|
|
main = do
|
2015-08-13 21:22:40 +03:00
|
|
|
opts <- getHledgerUIOpts
|
2015-08-12 05:08:33 +03:00
|
|
|
-- when (debug_ $ cliopts_ opts) $ printf "%s\n" prognameandversion >> printf "opts: %s\n" (show opts)
|
2015-08-18 03:44:18 +03:00
|
|
|
run opts
|
2010-11-25 10:10:38 +03:00
|
|
|
where
|
2011-08-16 02:50:09 +04:00
|
|
|
run opts
|
2016-04-20 00:40:58 +03:00
|
|
|
| "h" `inRawOpts` (rawopts_ $ cliopts_ opts) = putStr (showModeUsage uimode) >> exitSuccess
|
|
|
|
| "help" `inRawOpts` (rawopts_ $ cliopts_ opts) = printHelpForTopic (topicForMode uimode) >> exitSuccess
|
2016-06-04 04:27:35 +03:00
|
|
|
| "man" `inRawOpts` (rawopts_ $ cliopts_ opts) = runManForTopic (topicForMode uimode) >> exitSuccess
|
2016-04-20 00:40:58 +03:00
|
|
|
| "info" `inRawOpts` (rawopts_ $ cliopts_ opts) = runInfoForTopic (topicForMode uimode) >> exitSuccess
|
|
|
|
| "version" `inRawOpts` (rawopts_ $ cliopts_ opts) = putStrLn prognameandversion >> exitSuccess
|
2015-08-18 03:44:18 +03:00
|
|
|
| "binary-filename" `inRawOpts` (rawopts_ $ cliopts_ opts) = putStrLn (binaryfilename progname)
|
2015-09-04 02:02:44 +03:00
|
|
|
| otherwise = withJournalDoUICommand opts runBrickUi
|
|
|
|
|
|
|
|
-- XXX withJournalDo specialised for UIOpts
|
|
|
|
withJournalDoUICommand :: UIOpts -> (UIOpts -> Journal -> IO ()) -> IO ()
|
|
|
|
withJournalDoUICommand uopts@UIOpts{cliopts_=copts} cmd = do
|
|
|
|
rulespath <- rulesFilePathFromOpts copts
|
|
|
|
journalpath <- journalFilePathFromOpts copts
|
|
|
|
ej <- readJournalFiles Nothing rulespath (not $ ignore_assertions_ copts) journalpath
|
|
|
|
either error' (cmd uopts . journalApplyAliases (aliasesFromOpts copts)) ej
|
2008-12-08 20:27:16 +03:00
|
|
|
|
2015-08-18 03:44:18 +03:00
|
|
|
runBrickUi :: UIOpts -> Journal -> IO ()
|
2015-09-04 06:40:43 +03:00
|
|
|
runBrickUi uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}} j = do
|
2011-06-14 03:28:39 +04:00
|
|
|
d <- getCurrentDay
|
2008-12-08 20:27:16 +03:00
|
|
|
|
2015-08-18 03:44:18 +03:00
|
|
|
let
|
2015-09-04 06:40:43 +03:00
|
|
|
|
|
|
|
-- depth: is a bit different from other queries. In hledger cli,
|
|
|
|
-- - reportopts{depth_} indicates --depth options
|
|
|
|
-- - reportopts{query_} is the query arguments as a string
|
|
|
|
-- - the report query is based on both of these.
|
|
|
|
-- For hledger-ui, for now, move depth: arguments out of reportopts{query_}
|
|
|
|
-- and into reportopts{depth_}, so that depth and other kinds of filter query
|
|
|
|
-- can be displayed independently.
|
|
|
|
uopts' = uopts{
|
|
|
|
cliopts_=copts{
|
|
|
|
reportopts_= ropts{
|
|
|
|
-- ensure depth_ also reflects depth: args
|
|
|
|
depth_=depthfromoptsandargs,
|
|
|
|
-- remove depth: args from query_
|
|
|
|
query_=unwords $ -- as in ReportOptions, with same limitations
|
2016-06-03 19:01:54 +03:00
|
|
|
[v | (k,v) <- rawopts_ copts, k=="args", not $ "depth" `isPrefixOf` v],
|
|
|
|
-- show items with zero amount by default, unlike the CLI
|
|
|
|
empty_=True
|
2015-09-04 06:40:43 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
where
|
|
|
|
q = queryFromOpts d ropts
|
|
|
|
depthfromoptsandargs = case queryDepth q of 99999 -> Nothing
|
|
|
|
d -> Just d
|
|
|
|
|
2015-08-28 18:48:40 +03:00
|
|
|
-- XXX move this stuff into Options, UIOpts
|
2015-08-23 03:46:57 +03:00
|
|
|
theme = maybe defaultTheme (fromMaybe defaultTheme . getTheme) $
|
2015-09-04 06:40:43 +03:00
|
|
|
maybestringopt "theme" $ rawopts_ copts
|
|
|
|
mregister = maybestringopt "register" $ rawopts_ copts
|
|
|
|
|
|
|
|
(scr, prevscrs) = case mregister of
|
2016-06-08 21:03:49 +03:00
|
|
|
Nothing -> (accountsScreen, [])
|
2015-09-04 06:40:43 +03:00
|
|
|
-- with --register, start on the register screen, and also put
|
|
|
|
-- the accounts screen on the prev screens stack so you can exit
|
|
|
|
-- to that as usual.
|
2016-06-09 09:45:26 +03:00
|
|
|
Just apat -> (rsSetAccount acct registerScreen, [ascr'])
|
2015-08-28 18:48:40 +03:00
|
|
|
where
|
|
|
|
acct = headDef
|
|
|
|
(error' $ "--register "++apat++" did not match any account")
|
lib: textification begins! account names
The first of several conversions from String to (strict) Text, hopefully
reducing space and time usage.
This one shows a small improvement, with GHC 7.10.3 and text-1.2.2.1:
hledger -f data/100x100x10.journal stats
string: <<ghc: 39471064 bytes, 77 GCs, 198421/275048 avg/max bytes residency (3 samples), 2M in use, 0.000 INIT (0.001 elapsed), 0.015 MUT (0.020 elapsed), 0.010 GC (0.014 elapsed) :ghc>>
text: <<ghc: 39268024 bytes, 77 GCs, 197018/270840 avg/max bytes residency (3 samples), 2M in use, 0.000 INIT (0.002 elapsed), 0.016 MUT (0.022 elapsed), 0.009 GC (0.011 elapsed) :ghc>>
hledger -f data/1000x100x10.journal stats
string: <<ghc: 318555920 bytes, 617 GCs, 2178997/7134472 avg/max bytes residency (7 samples), 16M in use, 0.000 INIT (0.001 elapsed), 0.129 MUT (0.136 elapsed), 0.067 GC (0.077 elapsed) :ghc>>
text: <<ghc: 314248496 bytes, 612 GCs, 2074045/6617960 avg/max bytes residency (7 samples), 16M in use, 0.000 INIT (0.003 elapsed), 0.137 MUT (0.145 elapsed), 0.067 GC (0.079 elapsed) :ghc>>
hledger -f data/10000x100x10.journal stats
string: <<ghc: 3114763608 bytes, 6026 GCs, 18858950/75552024 avg/max bytes residency (11 samples), 201M in use, 0.000 INIT (0.000 elapsed), 1.331 MUT (1.372 elapsed), 0.699 GC (0.812 elapsed) :ghc>>
text: <<ghc: 3071468920 bytes, 5968 GCs, 14120344/62951360 avg/max bytes residency (9 samples), 124M in use, 0.000 INIT (0.003 elapsed), 1.272 MUT (1.349 elapsed), 0.513 GC (0.578 elapsed) :ghc>>
hledger -f data/100000x100x10.journal stats
string: <<ghc: 31186579432 bytes, 60278 GCs, 135332581/740228992 avg/max bytes residency (13 samples), 1697M in use, 0.000 INIT (0.008 elapsed), 14.677 MUT (15.508 elapsed), 7.081 GC (8.074 elapsed) :ghc>>
text: <<ghc: 30753427672 bytes, 59763 GCs, 117595958/666457240 avg/max bytes residency (14 samples), 1588M in use, 0.000 INIT (0.008 elapsed), 13.713 MUT (13.966 elapsed), 6.220 GC (7.108 elapsed) :ghc>>
2016-05-24 04:16:21 +03:00
|
|
|
$ filter (regexMatches apat . T.unpack) $ journalAccountNames j
|
2015-09-04 06:40:43 +03:00
|
|
|
-- Initialising the accounts screen is awkward, requiring
|
2016-06-11 03:30:45 +03:00
|
|
|
-- another temporary UIState value..
|
2015-09-04 06:40:43 +03:00
|
|
|
ascr' = aScreen $
|
2016-06-09 09:45:26 +03:00
|
|
|
asInit d True $
|
2016-06-11 03:30:45 +03:00
|
|
|
UIState{
|
2015-09-04 06:40:43 +03:00
|
|
|
aopts=uopts'
|
|
|
|
,ajournal=j
|
2016-06-08 21:03:49 +03:00
|
|
|
,aScreen=asSetSelectedAccount acct accountsScreen
|
2015-09-04 06:40:43 +03:00
|
|
|
,aPrevScreens=[]
|
2016-06-10 18:40:00 +03:00
|
|
|
,aMode=Normal
|
2015-09-04 06:40:43 +03:00
|
|
|
}
|
2015-10-28 21:13:33 +03:00
|
|
|
|
2016-06-11 03:30:45 +03:00
|
|
|
ui = (sInit scr) d True
|
|
|
|
UIState{
|
2015-09-04 06:40:43 +03:00
|
|
|
aopts=uopts'
|
2015-08-18 03:44:18 +03:00
|
|
|
,ajournal=j
|
|
|
|
,aScreen=scr
|
2015-09-04 06:40:43 +03:00
|
|
|
,aPrevScreens=prevscrs
|
2016-06-10 18:40:00 +03:00
|
|
|
,aMode=Normal
|
2015-08-18 03:44:18 +03:00
|
|
|
}
|
2016-06-07 17:04:32 +03:00
|
|
|
|
2016-06-11 03:30:45 +03:00
|
|
|
brickapp :: App (UIState) V.Event
|
2016-06-07 17:04:32 +03:00
|
|
|
brickapp = App {
|
2015-08-20 14:54:23 +03:00
|
|
|
appLiftVtyEvent = id
|
|
|
|
, appStartEvent = return
|
2015-08-23 03:46:57 +03:00
|
|
|
, appAttrMap = const theme
|
2015-08-20 14:54:23 +03:00
|
|
|
, appChooseCursor = showFirstCursor
|
2016-06-11 03:30:45 +03:00
|
|
|
, appHandleEvent = \ui ev -> sHandle (aScreen ui) ui ev
|
|
|
|
, appDraw = \ui -> sDraw (aScreen ui) ui
|
2015-08-18 03:44:18 +03:00
|
|
|
}
|
|
|
|
|
2016-06-11 03:30:45 +03:00
|
|
|
void $ defaultMain brickapp ui
|
2015-08-23 03:46:57 +03:00
|
|
|
|