ui: add --present/--future, hide future txns by default, toggle with F

You may have transactions dated later than today, perhaps piped from
print --forecast or recorded in the journal, which you don't want to
see except when forecasting.

By default, we now hide future transactions, showing "today's balance".
This can be toggled with the F key, which is easier than setting a
date query. --present and --future flags have been added to set the
initial mode.

(Experimental. Interactions with date queries have not been explored.)
This commit is contained in:
Simon Michael 2018-10-15 15:11:22 -07:00
parent e52430bd62
commit 1db9b018f1
6 changed files with 70 additions and 13 deletions

View File

@ -23,7 +23,7 @@ import Data.Maybe
import Data.Monoid import Data.Monoid
#endif #endif
import qualified Data.Text as T import qualified Data.Text as T
import Data.Time.Calendar (Day) import Data.Time.Calendar (Day, addDays)
import qualified Data.Vector as V import qualified Data.Vector as V
import Graphics.Vty (Event(..),Key(..),Modifier(..)) import Graphics.Vty (Event(..),Key(..),Modifier(..))
import Lens.Micro.Platform import Lens.Micro.Platform
@ -82,7 +82,10 @@ asInit d reset ui@UIState{
uopts' = uopts{cliopts_=copts{reportopts_=ropts'}} uopts' = uopts{cliopts_=copts{reportopts_=ropts'}}
ropts' = ropts{accountlistmode_=if tree_ ropts then ALTree else ALFlat} ropts' = ropts{accountlistmode_=if tree_ ropts then ALTree else ALFlat}
q = queryFromOpts d ropts pfq | presentorfuture_ uopts == PFFuture = Any
| otherwise = Date $ DateSpan Nothing (Just $ addDays 1 d)
q = And [queryFromOpts d ropts, pfq]
-- run the report -- run the report
(items,_total) = report ropts' q j (items,_total) = report ropts' q j
@ -116,7 +119,7 @@ asInit d reset ui@UIState{
asInit _ _ _ = error "init function called with wrong screen type, should not happen" asInit _ _ _ = error "init function called with wrong screen type, should not happen"
asDraw :: UIState -> [Widget Name] asDraw :: UIState -> [Widget Name]
asDraw UIState{aopts=UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}} asDraw UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}}
,ajournal=j ,ajournal=j
,aScreen=s@AccountsScreen{} ,aScreen=s@AccountsScreen{}
,aMode=mode ,aMode=mode
@ -223,6 +226,10 @@ asDraw UIState{aopts=UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}}
,if tree_ ropts ,if tree_ ropts
then str "flat/" <+> selectedstr "tree" then str "flat/" <+> selectedstr "tree"
else selectedstr "flat" <+> str "/tree") else selectedstr "flat" <+> str "/tree")
,("F"
,if presentorfuture_ uopts == PFFuture
then str "present/" <+> selectedstr "future"
else selectedstr "present" <+> str "/future")
,("-+", str "depth") ,("-+", str "depth")
--,("/", "filter") --,("/", "filter")
--,("DEL", "unfilter") --,("DEL", "unfilter")
@ -338,6 +345,7 @@ asHandle ui0@UIState{
VtyEvent (EvKey (KChar 'U') []) -> asCenterAndContinue $ regenerateScreens j d $ toggleUnmarked ui VtyEvent (EvKey (KChar 'U') []) -> asCenterAndContinue $ regenerateScreens j d $ toggleUnmarked ui
VtyEvent (EvKey (KChar 'P') []) -> asCenterAndContinue $ regenerateScreens j d $ togglePending ui VtyEvent (EvKey (KChar 'P') []) -> asCenterAndContinue $ regenerateScreens j d $ togglePending ui
VtyEvent (EvKey (KChar 'C') []) -> asCenterAndContinue $ regenerateScreens j d $ toggleCleared ui VtyEvent (EvKey (KChar 'C') []) -> asCenterAndContinue $ regenerateScreens j d $ toggleCleared ui
VtyEvent (EvKey (KChar 'F') []) -> asCenterAndContinue $ regenerateScreens j d $ toggleFuture ui
VtyEvent (EvKey (KDown) [MShift]) -> continue $ regenerateScreens j d $ shrinkReportPeriod d ui VtyEvent (EvKey (KDown) [MShift]) -> continue $ regenerateScreens j d $ shrinkReportPeriod d ui
VtyEvent (EvKey (KUp) [MShift]) -> continue $ regenerateScreens j d $ growReportPeriod d ui VtyEvent (EvKey (KUp) [MShift]) -> continue $ regenerateScreens j d $ growReportPeriod d ui

View File

@ -59,7 +59,7 @@ rsSetAccount a forceinclusive scr@RegisterScreen{} =
rsSetAccount _ _ scr = scr rsSetAccount _ _ scr = scr
rsInit :: Day -> Bool -> UIState -> UIState rsInit :: Day -> Bool -> UIState -> UIState
rsInit d reset ui@UIState{aopts=UIOpts{cliopts_=CliOpts{reportopts_=ropts}}, ajournal=j, aScreen=s@RegisterScreen{..}} = rsInit d reset ui@UIState{aopts=uopts@UIOpts{cliopts_=CliOpts{reportopts_=ropts}}, ajournal=j, aScreen=s@RegisterScreen{..}} =
ui{aScreen=s{rsList=newitems'}} ui{aScreen=s{rsList=newitems'}}
where where
-- gather arguments and queries -- gather arguments and queries
@ -69,7 +69,9 @@ rsInit d reset ui@UIState{aopts=UIOpts{cliopts_=CliOpts{reportopts_=ropts}}, ajo
ropts' = ropts{ ropts' = ropts{
depth_=Nothing depth_=Nothing
} }
q = queryFromOpts d ropts' pfq | presentorfuture_ uopts == PFFuture = Any
| otherwise = Date $ DateSpan Nothing (Just $ addDays 1 d)
q = And [queryFromOpts d ropts', pfq]
-- reportq = filterQuery (not . queryIsDepth) q -- reportq = filterQuery (not . queryIsDepth) q
(_label,items) = accountTransactionsReport ropts' j q thisacctq (_label,items) = accountTransactionsReport ropts' j q thisacctq
@ -131,7 +133,7 @@ rsInit d reset ui@UIState{aopts=UIOpts{cliopts_=CliOpts{reportopts_=ropts}}, ajo
rsInit _ _ _ = error "init function called with wrong screen type, should not happen" rsInit _ _ _ = error "init function called with wrong screen type, should not happen"
rsDraw :: UIState -> [Widget Name] rsDraw :: UIState -> [Widget Name]
rsDraw UIState{aopts=UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}} rsDraw UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}}
,aScreen=RegisterScreen{..} ,aScreen=RegisterScreen{..}
,aMode=mode ,aMode=mode
} = } =
@ -235,8 +237,12 @@ rsDraw UIState{aopts=UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}}
else str "historical/" <+> selectedstr "period") else str "historical/" <+> selectedstr "period")
,("T" ,("T"
,if inclusive ,if inclusive
then str "this/" <+> selectedstr "+subs" then str "flat/" <+> selectedstr "tree"
else selectedstr "this" <+> str "/+subs") else selectedstr "flat" <+> str "/tree")
,("F"
,if presentorfuture_ uopts == PFFuture
then str "present/" <+> selectedstr "future"
else selectedstr "present" <+> str "/future")
-- ,("a", "add") -- ,("a", "add")
-- ,("g", "reload") -- ,("g", "reload")
-- ,("q", "quit") -- ,("q", "quit")
@ -329,6 +335,7 @@ rsHandle ui@UIState{
VtyEvent (EvKey (KChar 'U') []) -> rsCenterAndContinue $ regenerateScreens j d $ toggleUnmarked ui VtyEvent (EvKey (KChar 'U') []) -> rsCenterAndContinue $ regenerateScreens j d $ toggleUnmarked ui
VtyEvent (EvKey (KChar 'P') []) -> rsCenterAndContinue $ regenerateScreens j d $ togglePending ui VtyEvent (EvKey (KChar 'P') []) -> rsCenterAndContinue $ regenerateScreens j d $ togglePending ui
VtyEvent (EvKey (KChar 'C') []) -> rsCenterAndContinue $ regenerateScreens j d $ toggleCleared ui VtyEvent (EvKey (KChar 'C') []) -> rsCenterAndContinue $ regenerateScreens j d $ toggleCleared ui
VtyEvent (EvKey (KChar 'F') []) -> rsCenterAndContinue $ regenerateScreens j d $ toggleFuture ui
VtyEvent (EvKey (KChar '/') []) -> continue $ regenerateScreens j d $ showMinibuffer ui VtyEvent (EvKey (KChar '/') []) -> continue $ regenerateScreens j d $ showMinibuffer ui
VtyEvent (EvKey (KDown) [MShift]) -> continue $ regenerateScreens j d $ shrinkReportPeriod d ui VtyEvent (EvKey (KDown) [MShift]) -> continue $ regenerateScreens j d $ shrinkReportPeriod d ui

View File

@ -1,11 +1,14 @@
{-# LANGUAGE CPP #-} {-# LANGUAGE CPP #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-| {-|
-} -}
module Hledger.UI.UIOptions module Hledger.UI.UIOptions
where where
import Data.Data (Data)
import Data.Default import Data.Default
import Data.Typeable (Typeable)
import Data.List (intercalate) import Data.List (intercalate)
import System.Environment import System.Environment
@ -35,6 +38,8 @@ uiflags = [
-- "show historical ending balance in each period (includes postings before report start date)\n " -- "show historical ending balance in each period (includes postings before report start date)\n "
,flagNone ["flat","F"] (\opts -> setboolopt "flat" opts) "show full account names, unindented (default)" ,flagNone ["flat","F"] (\opts -> setboolopt "flat" opts) "show full account names, unindented (default)"
,flagNone ["tree","T"] (\opts -> setboolopt "tree" opts) "show accounts as a tree" ,flagNone ["tree","T"] (\opts -> setboolopt "tree" opts) "show accounts as a tree"
,flagNone ["present"] (\opts -> setboolopt "present" opts) "exclude transactions dated later than today (default)"
,flagNone ["future"] (\opts -> setboolopt "future" opts) "include transactions dated later than today"
-- ,flagReq ["drop"] (\s opts -> Right $ setopt "drop" s opts) "N" "with --flat, omit this many leading account name components" -- ,flagReq ["drop"] (\s opts -> Right $ setopt "drop" s opts) "N" "with --flat, omit this many leading account name components"
-- ,flagReq ["format"] (\s opts -> Right $ setopt "format" s opts) "FORMATSTR" "use this custom line format" -- ,flagReq ["format"] (\s opts -> Right $ setopt "format" s opts) "FORMATSTR" "use this custom line format"
-- ,flagNone ["no-elide"] (\opts -> setboolopt "no-elide" opts) "don't compress empty parent accounts on one line" -- ,flagNone ["no-elide"] (\opts -> setboolopt "no-elide" opts) "don't compress empty parent accounts on one line"
@ -67,6 +72,7 @@ uimode = (mode "hledger-ui" [("command","ui")]
data UIOpts = UIOpts { data UIOpts = UIOpts {
watch_ :: Bool watch_ :: Bool
,change_ :: Bool ,change_ :: Bool
,presentorfuture_ :: PresentOrFutureOpt
,cliopts_ :: CliOpts ,cliopts_ :: CliOpts
} deriving (Show) } deriving (Show)
@ -74,6 +80,7 @@ defuiopts = UIOpts
def def
def def
def def
def
-- instance Default CliOpts where def = defcliopts -- instance Default CliOpts where def = defcliopts
@ -83,9 +90,22 @@ rawOptsToUIOpts rawopts = checkUIOpts <$> do
return defuiopts { return defuiopts {
watch_ = boolopt "watch" rawopts watch_ = boolopt "watch" rawopts
,change_ = boolopt "change" rawopts ,change_ = boolopt "change" rawopts
,presentorfuture_ = presentorfutureopt rawopts
,cliopts_ = cliopts ,cliopts_ = cliopts
} }
-- | Should transactions dated later than today be included ?
-- Like flat/tree mode, there are three states, and the meaning of default can vary by command.
data PresentOrFutureOpt = PFDefault | PFPresent | PFFuture deriving (Eq, Show, Data, Typeable)
instance Default PresentOrFutureOpt where def = PFDefault
presentorfutureopt :: RawOpts -> PresentOrFutureOpt
presentorfutureopt rawopts =
case reverse $ filter (`elem` ["present","future"]) $ map fst rawopts of
("present":_) -> PFPresent
("future":_) -> PFFuture
_ -> PFDefault
checkUIOpts :: UIOpts -> UIOpts checkUIOpts :: UIOpts -> UIOpts
checkUIOpts opts = checkUIOpts opts =
either usageError (const opts) $ do either usageError (const opts) $ do

View File

@ -124,6 +124,14 @@ toggleHistorical ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts
b | balancetype_ ropts == HistoricalBalance = PeriodChange b | balancetype_ ropts == HistoricalBalance = PeriodChange
| otherwise = HistoricalBalance | otherwise = HistoricalBalance
-- | Toggle between including and excluding transactions dated later than today.
toggleFuture :: UIState -> UIState
toggleFuture ui@UIState{aopts=uopts@UIOpts{presentorfuture_=pf}} =
ui{aopts=uopts{presentorfuture_=pf'}}
where
pf' | pf == PFFuture = PFPresent
| otherwise = PFFuture
-- | Toggle between showing all and showing only real (non-virtual) items. -- | Toggle between showing all and showing only real (non-virtual) items.
toggleReal :: UIState -> UIState toggleReal :: UIState -> UIState
toggleReal ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}}} = toggleReal ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}}} =

View File

@ -92,6 +92,7 @@ helpDialog copts =
,"cycle cleared/not cleared/all" ,"cycle cleared/not cleared/all"
,"toggle cleared filter" ,"toggle cleared filter"
] !! (statusstyle-1)) ] !! (statusstyle-1))
,renderKey ("F", "toggle future/present")
,renderKey ("R", "toggle real/all") ,renderKey ("R", "toggle real/all")
,renderKey ("Z", "toggle nonzero/all") ,renderKey ("Z", "toggle nonzero/all")
,renderKey ("DEL/BS", "remove filters") ,renderKey ("DEL/BS", "remove filters")

View File

@ -44,6 +44,7 @@ hledger-ui is hledger's curses-style interface, providing an efficient full-wind
for viewing accounts and transactions, and some limited data entry capability. for viewing accounts and transactions, and some limited data entry capability.
It is easier than hledger's command-line interface, and It is easier than hledger's command-line interface, and
sometimes quicker and more convenient than the web interface. sometimes quicker and more convenient than the web interface.
Note hledger-ui hides transactions dated in the future, by default.
Like hledger, it reads _files_ Like hledger, it reads _files_
For more about this see hledger(1), hledger_journal(5) etc. For more about this see hledger(1), hledger_journal(5) etc.
@ -72,7 +73,13 @@ Any QUERYARGS are interpreted as a hledger search query which filters the data.
`-T --tree` `-T --tree`
: show accounts as a tree : show accounts as a tree
hledger input options: `--present`
: exclude transactions dated later than today (default) (experimental)
`--future`
: include transactions dated later than today (experimental)
hledger input options:
_inputoptions_ _inputoptions_
@ -122,6 +129,12 @@ press `ENTER` to set it, or `ESCAPE`to cancel.
There are also keys for quickly adjusting some common filters like account depth and transaction status (see below). There are also keys for quickly adjusting some common filters like account depth and transaction status (see below).
`BACKSPACE` or `DELETE` removes all filters, showing all transactions. `BACKSPACE` or `DELETE` removes all filters, showing all transactions.
As mentioned above, hledger-ui hides transactions in the future by default.
`F` toggles showing and hiding these future transactions.
This is similar to using a query like `date:-tomorrow`, but more convenient.
(experimental)
`ESCAPE` removes all filters and jumps back to the top screen. `ESCAPE` removes all filters and jumps back to the top screen.
Or, it cancels a minibuffer edit or help dialog in progress. Or, it cancels a minibuffer edit or help dialog in progress.
@ -211,10 +224,10 @@ Similar to the accounts screen, the historical total is affected by transactions
If the historical total is not disturbed by a filter query, it will be the If the historical total is not disturbed by a filter query, it will be the
running historical balance you would see on a bank register for the current account. running historical balance you would see on a bank register for the current account.
Transactions affecting this account's subaccounts will be shown if Transactions affecting this account's subaccounts will be included in the register
the accounts screen was in tree mode, if the accounts screen is in tree mode,
or if it was in flat mode but the selected account had depth-clipped subaccounts. or if it's in flat mode but this account has subaccounts which are not shown due to a depth limit.
In other words, the register always shows just the transactions contributing to the balance on the accounts screen. In other words, the register always shows the transactions contributing to the balance shown on the accounts screen.
Tree mode/flat mode can be toggled with `T` here also. Tree mode/flat mode can be toggled with `T` here also.
`U` toggles filtering by [unmarked status](/journal.html#status), showing or hiding unmarked transactions. `U` toggles filtering by [unmarked status](/journal.html#status), showing or hiding unmarked transactions.