hledger-ui 1.22.2 → 1.23
raw patch · 14 files changed
+477/−431 lines, 14 filesdep ~basedep ~hledgerdep ~hledger-lib
Dependency ranges changed: base, hledger, hledger-lib, megaparsec
Files
- CHANGES.md +18/−2
- Hledger/UI/AccountsScreen.hs +14/−23
- Hledger/UI/ErrorScreen.hs +9/−22
- Hledger/UI/Main.hs +21/−27
- Hledger/UI/RegisterScreen.hs +18/−32
- Hledger/UI/TransactionScreen.hs +11/−12
- Hledger/UI/UIOptions.hs +61/−33
- Hledger/UI/UIState.hs +73/−108
- Hledger/UI/UITypes.hs +21/−3
- Hledger/UI/UIUtils.hs +22/−4
- hledger-ui.1 +18/−2
- hledger-ui.cabal +6/−6
- hledger-ui.info +41/−27
- hledger-ui.txt +144/−130
CHANGES.md view
@@ -9,6 +9,23 @@ User-visible changes in hledger-ui. See also the hledger changelog. +# 1.23 2021-09-21++Improvements++- Require base >=4.11, prevent red squares on Hackage's build matrix.++API changes++- Lenses are now available for UIState etc., saving a lot of boilerplate. (Stephen Morgan)++- Renamed:+ ```+ version -> packageversion+ versiondescription -> versionStringFor+ UIOpts fields+ ```+ # 1.22.2 2021-08-07 - Use hledger 1.22.2.@@ -17,8 +34,7 @@ Improvements -- Document watch mode and its limitations.- (#1617, #911, #836)+- Document watch mode and its limitations. (#1617, #911, #836) - Allow megaparsec 9.1.
Hledger/UI/AccountsScreen.hs view
@@ -19,7 +19,7 @@ import Data.List import Data.Maybe import qualified Data.Text as T-import Data.Time.Calendar (Day, addDays)+import Data.Time.Calendar (Day) import qualified Data.Vector as V import Graphics.Vty (Event(..),Key(..),Modifier(..)) import Lens.Micro.Platform@@ -49,7 +49,7 @@ asInit :: Day -> Bool -> UIState -> UIState asInit d reset ui@UIState{- aopts=UIOpts{cliopts_=copts@CliOpts{reportspec_=rspec@ReportSpec{rsOpts=ropts}}},+ aopts=UIOpts{uoCliOpts=copts@CliOpts{reportspec_=rspec@ReportSpec{_rsReportOpts=ropts}}}, ajournal=j, aScreen=s@AccountsScreen{} } =@@ -69,7 +69,7 @@ (_, Nothing) -> 0 (_, Just (_,AccountsScreenItem{asItemAccountName=a})) -> headDef 0 $ catMaybes [- findIndex (a ==) as+ elemIndex a as ,findIndex (a `isAccountNamePrefixOf`) as ,Just $ max 0 (length (filter (< a) as) - 1) ]@@ -77,16 +77,7 @@ as = map asItemAccountName displayitems -- Further restrict the query based on the current period and future/forecast mode.- rspec' = rspec{rsQuery=simplifyQuery $ And [rsQuery rspec, periodq, excludeforecastq (forecast_ $ inputopts_ copts)]}- where- periodq = Date $ periodAsDateSpan $ period_ ropts- -- Except in forecast mode, exclude future/forecast transactions.- excludeforecastq (Just _) = Any- excludeforecastq Nothing = -- not:date:tomorrow- not:tag:generated-transaction- And [- Not (Date $ DateSpan (Just $ addDays 1 d) Nothing)- ,Not generatedTransactionTag- ]+ rspec' = reportSpecSetFutureAndForecast d (forecast_ $ inputopts_ copts) rspec -- run the report (items,_total) = balanceReport rspec' j@@ -112,7 +103,7 @@ asInit _ _ _ = error "init function called with wrong screen type, should not happen" -- PARTIAL: asDraw :: UIState -> [Widget Name]-asDraw UIState{aopts=_uopts@UIOpts{cliopts_=copts@CliOpts{reportspec_=rspec}}+asDraw UIState{aopts=_uopts@UIOpts{uoCliOpts=copts@CliOpts{reportspec_=rspec}} ,ajournal=j ,aScreen=s@AccountsScreen{} ,aMode=mode@@ -152,8 +143,8 @@ render $ defaultLayout toplabel bottomlabel $ renderList (asDrawItem colwidths) True (_asList s) where- ropts = rsOpts rspec- ishistorical = balancetype_ ropts == HistoricalBalance+ ropts = _rsReportOpts rspec+ ishistorical = balanceaccum_ ropts == Historical toplabel = withAttr ("border" <> "filename") files@@ -232,12 +223,12 @@ asHandle :: UIState -> BrickEvent Name AppEvent -> EventM Name (Next UIState) asHandle ui0@UIState{ aScreen=scr@AccountsScreen{..}- ,aopts=UIOpts{cliopts_=copts}+ ,aopts=UIOpts{uoCliOpts=copts} ,ajournal=j ,aMode=mode } ev = do- d <- liftIO getCurrentDay let+ d = copts^.rsDay nonblanks = V.takeWhile (not . T.null . asItemAccountName) $ _asList^.listElementsL lastnonblankidx = max 0 (length nonblanks - 1) @@ -259,8 +250,8 @@ VtyEvent ev -> do ed' <- handleEditorEvent ev ed continue $ ui{aMode=Minibuffer ed'} AppEvent _ -> continue ui- MouseDown _ _ _ _ -> continue ui- MouseUp _ _ _ -> continue ui+ MouseDown{} -> continue ui+ MouseUp{} -> continue ui Help -> case ev of@@ -274,7 +265,7 @@ VtyEvent (EvKey (KChar 'q') []) -> halt ui -- EvKey (KChar 'l') [MCtrl] -> do VtyEvent (EvKey KEsc []) -> continue $ resetScreens d ui- VtyEvent (EvKey (KChar c) []) | c `elem` ['?'] -> continue $ setMode Help ui+ VtyEvent (EvKey (KChar c) []) | c == '?' -> continue $ setMode Help ui -- XXX AppEvents currently handled only in Normal mode -- XXX be sure we don't leave unconsumed events piling up AppEvent (DateChange old _) | isStandardPeriod p && p `periodContainsDate` old ->@@ -365,8 +356,8 @@ } AppEvent _ -> continue ui- MouseDown _ _ _ _ -> continue ui- MouseUp _ _ _ -> continue ui+ MouseDown{} -> continue ui+ MouseUp{} -> continue ui where journalspan = journalDateSpan False j
Hledger/UI/ErrorScreen.hs view
@@ -19,6 +19,7 @@ import Data.Time.Calendar (Day) import Data.Void (Void) import Graphics.Vty (Event(..),Key(..),Modifier(..))+import Lens.Micro ((^.)) import Text.Megaparsec import Text.Megaparsec.Char @@ -28,7 +29,6 @@ import Hledger.UI.UIState import Hledger.UI.UIUtils import Hledger.UI.Editor-import Data.Foldable (asum) errorScreen :: Screen errorScreen = ErrorScreen{@@ -43,7 +43,7 @@ esInit _ _ _ = error "init function called with wrong screen type, should not happen" -- PARTIAL: esDraw :: UIState -> [Widget Name]-esDraw UIState{aopts=UIOpts{cliopts_=copts@CliOpts{}}+esDraw UIState{aopts=UIOpts{uoCliOpts=copts} ,aScreen=ErrorScreen{..} ,aMode=mode } =@@ -59,9 +59,10 @@ withAttr ("border" <> "bold") (str "Oops. Please fix this problem then press g to reload") -- <+> (if ignore_assertions_ copts then withAttr ("border" <> "query") (str " ignoring") else str " not ignoring") - bottomlabel = case mode of+ bottomlabel = quickhelp+ -- case mode of -- Minibuffer ed -> minibuffer ed- _ -> quickhelp+ -- _ -> quickhelp where quickhelp = borderKeysStr [ ("h", "help")@@ -75,7 +76,7 @@ esHandle :: UIState -> BrickEvent Name AppEvent -> EventM Name (Next UIState) esHandle ui@UIState{aScreen=ErrorScreen{..}- ,aopts=UIOpts{cliopts_=copts}+ ,aopts=UIOpts{uoCliOpts=copts} ,ajournal=j ,aMode=mode }@@ -89,7 +90,7 @@ _ -> helpHandle ui ev _ -> do- d <- liftIO getCurrentDay+ let d = copts^.rsDay case ev of VtyEvent (EvKey (KChar 'q') []) -> halt ui VtyEvent (EvKey KEsc []) -> continue $ uiCheckBalanceAssertions d $ resetScreens d ui@@ -177,26 +178,12 @@ UIState{aScreen=s@ErrorScreen{}} -> ui{aScreen=s{esError=err}} _ -> screenEnter d errorScreen{esError=err} ui --- | Ensure this CliOpts enables forecasted transactions.--- If a forecast period was specified in the old CliOpts,--- or in the provided UIState's startup options,--- it is preserved.-enableForecastPreservingPeriod :: UIState -> CliOpts -> CliOpts-enableForecastPreservingPeriod ui copts@CliOpts{inputopts_=iopts} =- copts{inputopts_=iopts{forecast_=mforecast}}- where- mforecast = asum [mprovidedforecastperiod, mstartupforecastperiod, mdefaultforecastperiod]- where- mprovidedforecastperiod = forecast_ $ inputopts_ copts- mstartupforecastperiod = forecast_ $ inputopts_ $ cliopts_ $ astartupopts ui- mdefaultforecastperiod = Just nulldatespan- -- Re-check any balance assertions in the current journal, and if any -- fail, enter (or update) the error screen. Or if balance assertions -- are disabled, do nothing. uiCheckBalanceAssertions :: Day -> UIState -> UIState-uiCheckBalanceAssertions d ui@UIState{aopts=UIOpts{cliopts_=copts}, ajournal=j}- | ignore_assertions_ . balancingopts_ $ inputopts_ copts = ui+uiCheckBalanceAssertions d ui@UIState{ajournal=j}+ | ui^.ignore_assertions = ui | otherwise = case journalCheckBalanceAssertions j of Nothing -> ui
Hledger/UI/Main.hs view
@@ -3,6 +3,7 @@ Copyright (c) 2007-2015 Simon Michael <simon@joyful.com> Released under GPL version 3 or later. -}+{-# LANGUAGE LambdaCase #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE OverloadedStrings #-} @@ -17,6 +18,7 @@ import Data.Maybe (fromMaybe) import qualified Data.Text as T import Graphics.Vty (mkVty)+import Lens.Micro ((^.)) import System.Directory (canonicalizePath) import System.FilePath (takeDirectory) import System.FSNotify (Event(Modified), isPollingManager, watchDir, withManager)@@ -28,7 +30,6 @@ import Hledger.Cli hiding (progname,prognameandversion) import Hledger.UI.UIOptions import Hledger.UI.UITypes-import Hledger.UI.UIState (toggleHistorical) import Hledger.UI.Theme import Hledger.UI.AccountsScreen import Hledger.UI.RegisterScreen@@ -44,7 +45,7 @@ main :: IO () main = do- opts@UIOpts{cliopts_=copts@CliOpts{inputopts_=iopts,rawopts_=rawopts}} <- getHledgerUIOpts+ opts@UIOpts{uoCliOpts=copts@CliOpts{inputopts_=iopts,rawopts_=rawopts}} <- getHledgerUIOpts -- when (debug_ $ cliopts_ opts) $ printf "%s\n" prognameandversion >> printf "opts: %s\n" (show opts) -- always generate forecasted periodic transactions; their visibility will be toggled by the UI.@@ -55,14 +56,13 @@ _ | "info" `inRawOpts` rawopts -> runInfoForTopic "hledger-ui" Nothing _ | "man" `inRawOpts` rawopts -> runManForTopic "hledger-ui" Nothing _ | "version" `inRawOpts` rawopts -> putStrLn prognameandversion- _ | "binary-filename" `inRawOpts` rawopts -> putStrLn (binaryfilename progname)+ -- _ | "binary-filename" `inRawOpts` rawopts -> putStrLn (binaryfilename progname) _ -> withJournalDo copts' (runBrickUi opts) runBrickUi :: UIOpts -> Journal -> IO ()-runBrickUi uopts@UIOpts{cliopts_=copts@CliOpts{inputopts_=_iopts,reportspec_=rspec@ReportSpec{rsOpts=ropts}}} j = do- d <- getCurrentDay-+runBrickUi uopts@UIOpts{uoCliOpts=copts@CliOpts{inputopts_=_iopts,reportspec_=rspec@ReportSpec{_rsReportOpts=ropts}}} j = do let+ today = copts^.rsDay -- hledger-ui's query handling is currently in flux, mixing old and new approaches. -- Related: #1340, #1383, #1387. Some notes and terminology:@@ -102,32 +102,26 @@ -- It's cleaner and less conflicting to keep the former out of the latter. uopts' = uopts{- cliopts_=copts{+ uoCliOpts=copts{ reportspec_=rspec{- rsQuery=filteredQuery $ rsQuery rspec, -- query with depth/date parts removed- rsOpts=ropts{- depth_ =queryDepth $ rsQuery rspec, -- query's depth part+ _rsQuery=filteredQuery $ _rsQuery rspec, -- query with depth/date parts removed+ _rsReportOpts=ropts{+ depth_ =queryDepth $ _rsQuery rspec, -- query's depth part period_=periodfromoptsandargs, -- query's date part no_elide_=True, -- avoid squashing boring account names, for a more regular tree (unlike hledger)- empty_=not $ empty_ ropts, -- show zero items by default, hide them with -E (unlike hledger)- balancetype_=HistoricalBalance -- show historical balances by default (unlike hledger)+ empty_=not $ empty_ ropts -- show zero items by default, hide them with -E (unlike hledger) } } } } where- datespanfromargs = queryDateSpan (date2_ ropts) $ rsQuery rspec+ datespanfromargs = queryDateSpan (date2_ ropts) $ _rsQuery rspec periodfromoptsandargs = dateSpanAsPeriod $ spansIntersect [periodAsDateSpan $ period_ ropts, datespanfromargs] filteredQuery q = simplifyQuery $ And [queryFromFlags ropts, filtered q] where filtered = filterQuery (\x -> not $ queryIsDepth x || queryIsDate x) - -- XXX move this stuff into Options, UIOpts- theme = maybe defaultTheme (fromMaybe defaultTheme . getTheme) $- maybestringopt "theme" $ rawopts_ copts- mregister = maybestringopt "register" $ rawopts_ copts-- (scr, prevscrs) = case mregister of+ (scr, prevscrs) = case uoRegister uopts' of Nothing -> (accountsScreen, []) -- with --register, start on the register screen, and also put -- the accounts screen on the prev screens stack so you can exit@@ -142,7 +136,7 @@ -- Initialising the accounts screen is awkward, requiring -- another temporary UIState value.. ascr' = aScreen $- asInit d True+ asInit today True UIState{ astartupopts=uopts' ,aopts=uopts'@@ -153,8 +147,7 @@ } ui =- (sInit scr) d True $- (if change_ uopts' then toggleHistorical else id) -- XXX+ (sInit scr) today True $ UIState{ astartupopts=uopts' ,aopts=uopts'@@ -167,7 +160,7 @@ brickapp :: App UIState AppEvent Name brickapp = App { appStartEvent = return- , appAttrMap = const theme+ , appAttrMap = const $ fromMaybe defaultTheme $ getTheme =<< uoTheme uopts' , appChooseCursor = showFirstCursor , appHandleEvent = \ui ev -> sHandle (aScreen ui) ui ev , appDraw = \ui -> sDraw (aScreen ui) ui@@ -175,7 +168,7 @@ -- print (length (show ui)) >> exitSuccess -- show any debug output to this point & quit - if not (watch_ uopts')+ if not (uoWatch uopts') then void $ Brick.defaultMain brickapp ui @@ -197,9 +190,10 @@ watchDate new withAsync+ -- run this small task asynchronously: (getCurrentDay >>= watchDate)- $ \_ ->-+ -- until this main task terminates:+ $ \_async -> -- start one or more background threads reporting changes in the directories of our files -- XXX many quick successive saves causes the problems listed in BUGS -- with Debounce increased to 1s it easily gets stuck on an error or blank screen@@ -218,7 +212,7 @@ mgr d -- predicate: ignore changes not involving our files- (\fev -> case fev of+ (\case Modified f _ False -> f `elem` files -- Added f _ -> f `elem` files -- Removed f _ -> f `elem` files
Hledger/UI/RegisterScreen.hs view
@@ -56,7 +56,7 @@ rsSetAccount _ _ scr = scr rsInit :: Day -> Bool -> UIState -> UIState-rsInit d reset ui@UIState{aopts=_uopts@UIOpts{cliopts_=CliOpts{inputopts_=iopts,reportspec_=rspec@ReportSpec{rsOpts=ropts}}}, ajournal=j, aScreen=s@RegisterScreen{..}} =+rsInit d reset ui@UIState{aopts=_uopts@UIOpts{uoCliOpts=copts@CliOpts{reportspec_=rspec@ReportSpec{_rsReportOpts=ropts}}}, ajournal=j, aScreen=s@RegisterScreen{..}} = ui{aScreen=s{rsList=newitems'}} where -- gather arguments and queries@@ -70,24 +70,12 @@ depth_=Nothing -- XXX aregister also has this, needed ? -- always show historical balance- -- , balancetype_= HistoricalBalance+ -- , balanceaccum_= Historical }- rspec' =+ rspec' = reportSpecSetFutureAndForecast d (forecast_ $ inputopts_ copts) . either (error "rsInit: adjusting the query for register, should not have failed") id $ -- PARTIAL:- updateReportSpec ropts' rspec-- -- Further restrict the query based on the current period and future/forecast mode.- q = simplifyQuery $ And [rsQuery rspec', periodq, excludeforecastq (forecast_ iopts)]- where- periodq = Date $ periodAsDateSpan $ period_ ropts- -- Except in forecast mode, exclude future/forecast transactions.- excludeforecastq (Just _) = Any- excludeforecastq Nothing = -- not:date:tomorrow- not:tag:generated-transaction- And [- Not (Date $ DateSpan (Just $ addDays 1 d) Nothing)- ,Not generatedTransactionTag- ]- items = accountTransactionsReport rspec' j q thisacctq+ updateReportSpec ropts' rspec{_rsDay=d}+ items = accountTransactionsReport rspec' j thisacctq items' = (if empty_ ropts then id else filter (not . mixedAmountLooksZero . fifth6)) $ -- without --empty, exclude no-change txns reverse -- most recent last items@@ -96,7 +84,7 @@ displayitems = map displayitem items' where displayitem (t, _, _issplit, otheracctsstr, change, bal) =- RegisterScreenItem{rsItemDate = showDate $ transactionRegisterDate q thisacctq t+ RegisterScreenItem{rsItemDate = showDate $ transactionRegisterDate (_rsQuery rspec') thisacctq t ,rsItemStatus = tstatus t ,rsItemDescription = tdescription t ,rsItemOtherAccounts = otheracctsstr@@ -141,12 +129,12 @@ nearestidbydatethenid = third3 <$> (headMay $ sort [(abs $ diffDays (tdate t) prevseld, abs (tindex t - prevselidx), tindex t) | t <- ts]) ts = map rsItemTransaction displayitems- endidx = length displayitems - 1+ endidx = max 0 $ length displayitems - 1 rsInit _ _ _ = error "init function called with wrong screen type, should not happen" -- PARTIAL: rsDraw :: UIState -> [Widget Name]-rsDraw UIState{aopts=_uopts@UIOpts{cliopts_=copts@CliOpts{reportspec_=rspec}}+rsDraw UIState{aopts=_uopts@UIOpts{uoCliOpts=copts@CliOpts{reportspec_=rspec}} ,aScreen=RegisterScreen{..} ,aMode=mode } =@@ -200,8 +188,8 @@ render $ defaultLayout toplabel bottomlabel $ renderList (rsDrawItem colwidths) True rsList where- ropts = rsOpts rspec- ishistorical = balancetype_ ropts == HistoricalBalance+ ropts = _rsReportOpts rspec+ ishistorical = balanceaccum_ ropts == Historical -- inclusive = tree_ ropts || rsForceInclusive toplabel =@@ -287,12 +275,12 @@ rsHandle :: UIState -> BrickEvent Name AppEvent -> EventM Name (Next UIState) rsHandle ui@UIState{ aScreen=s@RegisterScreen{..}- ,aopts=UIOpts{cliopts_=copts}+ ,aopts=UIOpts{uoCliOpts=copts} ,ajournal=j ,aMode=mode } ev = do- d <- liftIO getCurrentDay let+ d = copts^.rsDay journalspan = journalDateSpan False j nonblanks = V.takeWhile (not . T.null . rsItemDate) $ rsList^.listElementsL lastnonblankidx = max 0 (length nonblanks - 1)@@ -308,8 +296,8 @@ VtyEvent ev -> do ed' <- handleEditorEvent ev ed continue $ ui{aMode=Minibuffer ed'} AppEvent _ -> continue ui- MouseDown _ _ _ _ -> continue ui- MouseUp _ _ _ -> continue ui+ MouseDown{} -> continue ui+ MouseUp{} -> continue ui Help -> case ev of@@ -322,7 +310,7 @@ case ev of VtyEvent (EvKey (KChar 'q') []) -> halt ui VtyEvent (EvKey KEsc []) -> continue $ resetScreens d ui- VtyEvent (EvKey (KChar c) []) | c `elem` ['?'] -> continue $ setMode Help ui+ VtyEvent (EvKey (KChar c) []) | c == '?' -> continue $ setMode Help ui AppEvent (DateChange old _) | isStandardPeriod p && p `periodContainsDate` old -> continue $ regenerateScreens j d $ setReportPeriod (DayPeriod d) ui where@@ -338,9 +326,7 @@ (pos,f) = case listSelectedElement rsList of Nothing -> (endPosition, journalFilePath j) Just (_, RegisterScreenItem{- rsItemTransaction=Transaction{tsourcepos=GenericSourcePos f l c}}) -> (Just (l, Just c),f)- Just (_, RegisterScreenItem{- rsItemTransaction=Transaction{tsourcepos=JournalSourcePos f (l,_)}}) -> (Just (l, Nothing),f)+ rsItemTransaction=Transaction{tsourcepos=(SourcePos f l c,_)}}) -> (Just (unPos l, Just $ unPos c),f) -- display mode/query toggles VtyEvent (EvKey (KChar 'B') []) -> rsCenterAndContinue $ regenerateScreens j d $ toggleCost ui@@ -396,8 +382,8 @@ continue ui{aScreen=s{rsList=newitems}} AppEvent _ -> continue ui- MouseDown _ _ _ _ -> continue ui- MouseUp _ _ _ -> continue ui+ MouseDown{} -> continue ui+ MouseUp{} -> continue ui rsHandle _ _ = error "event handler called with wrong screen type, should not happen" -- PARTIAL:
Hledger/UI/TransactionScreen.hs view
@@ -41,7 +41,7 @@ } tsInit :: Day -> Bool -> UIState -> UIState-tsInit _d _reset ui@UIState{aopts=UIOpts{cliopts_=CliOpts{reportspec_=_rspec}}+tsInit _d _reset ui@UIState{aopts=UIOpts{} ,ajournal=_j ,aScreen=s@TransactionScreen{tsTransaction=(_,t),tsTransactions=nts} ,aPrevScreens=prevscreens@@ -60,7 +60,7 @@ tsInit _ _ _ = error "init function called with wrong screen type, should not happen" -- PARTIAL: tsDraw :: UIState -> [Widget Name]-tsDraw UIState{aopts=UIOpts{cliopts_=copts@CliOpts{reportspec_=rspec@ReportSpec{rsOpts=ropts}}}+tsDraw UIState{aopts=UIOpts{uoCliOpts=copts@CliOpts{reportspec_=rspec@ReportSpec{_rsReportOpts=ropts}}} ,ajournal=j ,aScreen=TransactionScreen{tsTransaction=(i,t') ,tsTransactions=nts@@ -77,7 +77,7 @@ t = transactionMapPostingAmounts mixedAmountSetFullPrecision t' maincontent = Widget Greedy Greedy $ do let- prices = journalPriceOracle (infer_value_ ropts) j+ prices = journalPriceOracle (infer_prices_ ropts) j styles = journalCommodityStyles j periodlast = fromMaybe (error' "TransactionScreen: expected a non-empty journal") $ -- PARTIAL: shouldn't happen@@ -85,7 +85,7 @@ render . defaultLayout toplabel bottomlabel . str . T.unpack . showTransactionOneLineAmounts- . maybe id (transactionApplyValuation prices styles periodlast (rsToday rspec)) (value_ ropts)+ . maybe id (transactionApplyValuation prices styles periodlast (_rsDay rspec)) (value_ ropts) $ case cost_ ropts of Cost -> transactionToCost styles t NoCost -> t@@ -113,9 +113,10 @@ [] -> str "" fs -> withAttr ("border" <> "query") (str $ " " ++ intercalate ", " fs) - bottomlabel = case mode of+ bottomlabel = quickhelp+ -- case mode of -- Minibuffer ed -> minibuffer ed- _ -> quickhelp+ -- _ -> quickhelp where quickhelp = borderKeysStr [ ("?", "help")@@ -132,7 +133,7 @@ tsHandle :: UIState -> BrickEvent Name AppEvent -> EventM Name (Next UIState) tsHandle ui@UIState{aScreen=TransactionScreen{tsTransaction=(i,t), tsTransactions=nts}- ,aopts=UIOpts{cliopts_=copts}+ ,aopts=UIOpts{uoCliOpts=copts} ,ajournal=j ,aMode=mode }@@ -146,26 +147,24 @@ _ -> helpHandle ui ev _ -> do- d <- liftIO getCurrentDay let+ d = copts^.rsDay (iprev,tprev) = maybe (i,t) ((i-1),) $ lookup (i-1) nts (inext,tnext) = maybe (i,t) ((i+1),) $ lookup (i+1) nts case ev of VtyEvent (EvKey (KChar 'q') []) -> halt ui VtyEvent (EvKey KEsc []) -> continue $ resetScreens d ui- VtyEvent (EvKey (KChar c) []) | c `elem` ['?'] -> continue $ setMode Help ui+ VtyEvent (EvKey (KChar c) []) | c == '?' -> continue $ setMode Help ui VtyEvent (EvKey (KChar 'E') []) -> suspendAndResume $ void (runEditor pos f) >> uiReloadJournalIfChanged copts d j ui where (pos,f) = case tsourcepos t of- GenericSourcePos f l c -> (Just (l, Just c),f)- JournalSourcePos f (l1,_) -> (Just (l1, Nothing),f)+ (SourcePos f l1 c1,_) -> (Just (unPos l1, Just $ unPos c1),f) AppEvent (DateChange old _) | isStandardPeriod p && p `periodContainsDate` old -> continue $ regenerateScreens j d $ setReportPeriod (DayPeriod d) ui where p = reportPeriod ui e | e `elem` [VtyEvent (EvKey (KChar 'g') []), AppEvent FileChange] -> do -- plog (if e == AppEvent FileChange then "file change" else "manual reload") "" `seq` return ()- d <- liftIO getCurrentDay ej <- liftIO $ journalReload copts case ej of Left err -> continue $ screenEnter d errorScreen{esError=err} ui
Hledger/UI/UIOptions.hs view
@@ -5,22 +5,32 @@ module Hledger.UI.UIOptions where-import Data.Default++import Data.Default (def) import Data.List (intercalate)-import System.Environment+import qualified Data.Map as M+import Data.Maybe (fromMaybe)+import Lens.Micro (set)+import System.Environment (getArgs) -import Hledger.Cli hiding (progname,version,prognameandversion)-import Hledger.UI.Theme (themeNames)+import Hledger.Cli hiding (packageversion, progname, prognameandversion)+import Hledger.UI.Theme (themes, themeNames) -progname, version, prognameandversion :: String-progname = "hledger-ui"+-- cf Hledger.Cli.Version++packageversion :: String #ifdef VERSION-version = VERSION+packageversion = VERSION #else-version = ""+packageversion = "" #endif-prognameandversion = versiondescription progname +progname :: String+progname = "hledger-ui"++prognameandversion :: String+prognameandversion = versionStringForProgname progname+ uiflags = [ -- flagNone ["debug-ui"] (setboolopt "rules-file") "run with no terminal output, showing console" flagNone ["watch"] (setboolopt "watch") "watch for data and date changes and reload automatically"@@ -55,35 +65,35 @@ } -- hledger-ui options, used in hledger-ui and above-data UIOpts = UIOpts {- watch_ :: Bool- ,change_ :: Bool- ,cliopts_ :: CliOpts- } deriving (Show)+data UIOpts = UIOpts+ { uoWatch :: Bool+ , uoTheme :: Maybe String+ , uoRegister :: Maybe String+ , uoCliOpts :: CliOpts+ } deriving (Show) defuiopts = UIOpts- { watch_ = False- , change_ = False- , cliopts_ = def+ { uoWatch = False+ , uoTheme = Nothing+ , uoRegister = Nothing+ , uoCliOpts = defcliopts } --- instance Default CliOpts where def = defcliopts-+-- | Process a RawOpts into a UIOpts.+-- This will return a usage error if provided an invalid theme. rawOptsToUIOpts :: RawOpts -> IO UIOpts-rawOptsToUIOpts rawopts = checkUIOpts <$> do- cliopts <- rawOptsToCliOpts rawopts- return defuiopts {- watch_ = boolopt "watch" rawopts- ,change_ = boolopt "change" rawopts- ,cliopts_ = cliopts- }--checkUIOpts :: UIOpts -> UIOpts-checkUIOpts opts =- either usageError (const opts) $ do- case maybestringopt "theme" $ rawopts_ $ cliopts_ opts of- Just t | not $ elem t themeNames -> Left $ "invalid theme name: "++t- _ -> Right ()+rawOptsToUIOpts rawopts = do+ cliopts <- set balanceaccum accum <$> rawOptsToCliOpts rawopts+ return defuiopts {+ uoWatch = boolopt "watch" rawopts+ ,uoTheme = checkTheme <$> maybestringopt "theme" rawopts+ ,uoRegister = maybestringopt "register" rawopts+ ,uoCliOpts = cliopts+ }+ where+ -- show historical balance by default (unlike hledger)+ accum = fromMaybe Historical $ balanceAccumulationOverride rawopts+ checkTheme t = if t `M.member` themes then t else usageError $ "invalid theme name: " ++ t -- XXX some refactoring seems due getHledgerUIOpts :: IO UIOpts@@ -93,3 +103,21 @@ let args' = replaceNumericFlags args let cmdargopts = either usageError id $ process uimode args' rawOptsToUIOpts cmdargopts++instance HasCliOpts UIOpts where+ cliOpts f uiopts = (\x -> uiopts{uoCliOpts=x}) <$> f (uoCliOpts uiopts)++instance HasInputOpts UIOpts where+ inputOpts = cliOpts.inputOpts++instance HasBalancingOpts UIOpts where+ balancingOpts = cliOpts.balancingOpts++instance HasReportSpec UIOpts where+ reportSpec = cliOpts.reportSpec++instance HasReportOptsNoUpdate UIOpts where+ reportOptsNoUpdate = cliOpts.reportOptsNoUpdate++instance HasReportOpts UIOpts where+ reportOpts = cliOpts.reportOpts
Hledger/UI/UIState.hs view
@@ -7,32 +7,30 @@ where import Brick.Widgets.Edit-import Control.Applicative ((<|>))+import Data.Foldable (asum)+import Data.Either (fromRight) import Data.List ((\\), foldl', sort) import Data.Semigroup (Max(..)) import qualified Data.Text as T import Data.Text.Zipper (gotoEOL) import Data.Time.Calendar (Day)+import Lens.Micro ((^.), over, set) import Hledger import Hledger.Cli.CliOptions import Hledger.UI.UITypes-import Hledger.UI.UIOptions -- | Toggle between showing only unmarked items or all items. toggleUnmarked :: UIState -> UIState-toggleUnmarked ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportspec_=rspec}}} =- ui{aopts=uopts{cliopts_=copts{reportspec_=reportSpecToggleStatus Unmarked copts rspec}}}+toggleUnmarked = over statuses (toggleStatus1 Unmarked) -- | Toggle between showing only pending items or all items. togglePending :: UIState -> UIState-togglePending ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportspec_=rspec}}} =- ui{aopts=uopts{cliopts_=copts{reportspec_=reportSpecToggleStatus Pending copts rspec}}}+togglePending = over statuses (toggleStatus1 Pending) -- | Toggle between showing only cleared items or all items. toggleCleared :: UIState -> UIState-toggleCleared ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportspec_=rspec}}} =- ui{aopts=uopts{cliopts_=copts{reportspec_=reportSpecToggleStatus Cleared copts rspec}}}+toggleCleared = over statuses (toggleStatus1 Cleared) -- TODO testing different status toggle styles @@ -52,17 +50,11 @@ showstatus Pending = "pending" showstatus Unmarked = "unmarked" -reportSpecToggleStatus :: Status -> CliOpts -> ReportSpec -> ReportSpec-reportSpecToggleStatus s _copts =- either (error "reportSpecToggleStatus: changing status should not have caused this error") id -- PARTIAL:- . updateReportSpecWith (reportOptsToggleStatus1 s)- -- various toggle behaviours: -- 1 UPC toggles only X/all-reportOptsToggleStatus1 s ropts@ReportOpts{statuses_=ss}- | ss == [s] = ropts{statuses_=[]}- | otherwise = ropts{statuses_=[s]}+toggleStatus1 :: Status -> [Status] -> [Status]+toggleStatus1 s ss = if ss == [s] then [] else [s] -- 2 UPC cycles X/not-X/all -- repeatedly pressing X cycles:@@ -72,25 +64,25 @@ -- pressing Y after first or second step starts new cycle: -- [u] P [p] -- [pc] P [p]--- reportOptsToggleStatus2 s ropts@ReportOpts{statuses_=ss}--- | ss == [s] = ropts{statuses_=complement [s]}--- | ss == complement [s] = ropts{statuses_=[]}--- | otherwise = ropts{statuses_=[s]} -- XXX assume only three values+-- toggleStatus2 s ss+-- | ss == [s] = complement [s]+-- | ss == complement [s] = []+-- | otherwise = [s] -- XXX assume only three values -- 3 UPC toggles each X--- reportOptsToggleStatus3 s ropts@ReportOpts{statuses_=ss}--- | s `elem` ss = ropts{statuses_=filter (/= s) ss}--- | otherwise = ropts{statuses_=simplifyStatuses (s:ss)}+-- toggleStatus3 s ss+-- | s `elem` ss = filter (/= s) ss+-- | otherwise = simplifyStatuses (s:ss) -- 4 upc sets X, UPC sets not-X---reportOptsToggleStatus4 s ropts@ReportOpts{statuses_=ss}--- | s `elem` ss = ropts{statuses_=filter (/= s) ss}--- | otherwise = ropts{statuses_=simplifyStatuses (s:ss)}+-- toggleStatus4 s ss+-- | s `elem` ss = filter (/= s) ss+-- | otherwise = simplifyStatuses (s:ss) -- 5 upc toggles X, UPC toggles not-X---reportOptsToggleStatus5 s ropts@ReportOpts{statuses_=ss}--- | s `elem` ss = ropts{statuses_=filter (/= s) ss}--- | otherwise = ropts{statuses_=simplifyStatuses (s:ss)}+-- toggleStatus5 s ss+-- | s `elem` ss = filter (/= s) ss+-- | otherwise = simplifyStatuses (s:ss) -- | Given a list of unique enum values, list the other possible values of that enum. complement :: (Bounded a, Enum a, Eq a) => [a] -> [a]@@ -100,56 +92,44 @@ -- | Toggle between showing all and showing only nonempty (more precisely, nonzero) items. toggleEmpty :: UIState -> UIState-toggleEmpty ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportspec_=rspec@ReportSpec{rsOpts=ropts}}}} =- ui{aopts=uopts{cliopts_=copts{reportspec_=rspec{rsOpts=toggleEmpty ropts}}}}- where- toggleEmpty ropts = ropts{empty_=not $ empty_ ropts}+toggleEmpty = over empty__ not -- | Toggle between showing the primary amounts or costs. toggleCost :: UIState -> UIState-toggleCost ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportspec_=rspec@ReportSpec{rsOpts=ropts}}}} =- ui{aopts=uopts{cliopts_=copts{reportspec_=rspec{rsOpts=ropts{cost_ = toggle $ cost_ ropts}}}}}+toggleCost = over cost toggleCostMode where- toggle Cost = NoCost- toggle NoCost = Cost+ toggleCostMode Cost = NoCost+ toggleCostMode NoCost = Cost -- | Toggle between showing primary amounts or default valuation. toggleValue :: UIState -> UIState-toggleValue ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportspec_=rspec@ReportSpec{rsOpts=ropts}}}} =- ui{aopts=uopts{cliopts_=copts{reportspec_=rspec{rsOpts=ropts{- value_ = valuationToggleValue $ value_ ropts}}}}}---- | Basic toggling of -V, for hledger-ui.-valuationToggleValue :: Maybe ValuationType -> Maybe ValuationType-valuationToggleValue (Just (AtEnd _)) = Nothing-valuationToggleValue _ = Just $ AtEnd Nothing+toggleValue = over value valuationToggleValue+ where+ -- | Basic toggling of -V, for hledger-ui.+ valuationToggleValue (Just (AtEnd _)) = Nothing+ valuationToggleValue _ = Just $ AtEnd Nothing -- | Set hierarchic account tree mode. setTree :: UIState -> UIState-setTree ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportspec_=rspec@ReportSpec{rsOpts=ropts}}}} =- ui{aopts=uopts{cliopts_=copts{reportspec_=rspec{rsOpts=ropts{accountlistmode_=ALTree}}}}}+setTree = set accountlistmode ALTree -- | Set flat account list mode. setList :: UIState -> UIState-setList ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportspec_=rspec@ReportSpec{rsOpts=ropts}}}} =- ui{aopts=uopts{cliopts_=copts{reportspec_=rspec{rsOpts=ropts{accountlistmode_=ALFlat}}}}}+setList = set accountlistmode ALFlat -- | Toggle between flat and tree mode. If current mode is unspecified/default, assume it's flat. toggleTree :: UIState -> UIState-toggleTree ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportspec_=rspec@ReportSpec{rsOpts=ropts}}}} =- ui{aopts=uopts{cliopts_=copts{reportspec_=rspec{rsOpts=toggleTreeMode ropts}}}}+toggleTree = over accountlistmode toggleTreeMode where- toggleTreeMode ropts- | accountlistmode_ ropts == ALTree = ropts{accountlistmode_=ALFlat}- | otherwise = ropts{accountlistmode_=ALTree}+ toggleTreeMode ALTree = ALFlat+ toggleTreeMode ALFlat = ALTree -- | Toggle between historical balances and period balances. toggleHistorical :: UIState -> UIState-toggleHistorical ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportspec_=rspec@ReportSpec{rsOpts=ropts}}}} =- ui{aopts=uopts{cliopts_=copts{reportspec_=rspec{rsOpts=ropts{balancetype_=b}}}}}+toggleHistorical = over balanceaccum toggleBalanceAccum where- b | balancetype_ ropts == HistoricalBalance = PeriodChange- | otherwise = HistoricalBalance+ toggleBalanceAccum Historical = PerPeriod+ toggleBalanceAccum _ = Historical -- | Toggle hledger-ui's "forecast/future mode". When this mode is enabled, -- hledger-shows regular transactions which have future dates, and@@ -157,32 +137,33 @@ -- (which are usually but not necessarily future-dated). -- In normal mode, both of these are hidden. toggleForecast :: Day -> UIState -> UIState-toggleForecast d ui@UIState{aopts=UIOpts{cliopts_=copts@CliOpts{inputopts_=iopts}}} =- uiSetForecast ui $- case forecast_ iopts of+toggleForecast _d ui = set forecast newForecast ui+ where+ newForecast = case ui^.forecast of Just _ -> Nothing- Nothing -> forecastPeriodFromRawOpts d (rawopts_ copts) <|> Just nulldatespan+ Nothing -> enableForecastPreservingPeriod ui (ui^.cliOpts) ^. forecast --- | Helper: set forecast mode (with the given forecast period) on or off in the UI state.-uiSetForecast :: UIState -> Maybe DateSpan -> UIState-uiSetForecast- ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{inputopts_=iopts}}}- mforecast =- -- we assume forecast mode has no effect on ReportSpec's derived fields- ui{aopts=uopts{cliopts_=copts{inputopts_=iopts{forecast_=mforecast}}}}+-- | Ensure this CliOpts enables forecasted transactions.+-- If a forecast period was specified in the old CliOpts,+-- or in the provided UIState's startup options,+-- it is preserved.+enableForecastPreservingPeriod :: UIState -> CliOpts -> CliOpts+enableForecastPreservingPeriod ui copts = set forecast mforecast copts+ where+ mforecast = asum [mprovidedforecastperiod, mstartupforecastperiod, mdefaultforecastperiod]+ where+ mprovidedforecastperiod = copts ^. forecast+ mstartupforecastperiod = astartupopts ui ^. forecast+ mdefaultforecastperiod = Just nulldatespan -- | Toggle between showing all and showing only real (non-virtual) items. toggleReal :: UIState -> UIState-toggleReal ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportspec_=rspec}}} =- ui{aopts=uopts{cliopts_=copts{reportspec_=update rspec}}}- where- update = either (error "toggleReal: updating Real should not result in an error") id -- PARTIAL:- . updateReportSpecWith (\ropts -> ropts{real_=not $ real_ ropts})+toggleReal = fromRight err . overEither real not -- PARTIAL:+ where err = error "toggleReal: updating Real should not result in an error" -- | Toggle the ignoring of balance assertions. toggleIgnoreBalanceAssertions :: UIState -> UIState-toggleIgnoreBalanceAssertions ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{inputopts_=iopts@InputOpts{balancingopts_=bopts}}}} =- ui{aopts=uopts{cliopts_=copts{inputopts_=iopts{balancingopts_=bopts{ignore_assertions_=not $ ignore_assertions_ bopts}}}}}+toggleIgnoreBalanceAssertions = over ignore_assertions not -- | Step through larger report periods, up to all. growReportPeriod :: Day -> UIState -> UIState@@ -207,47 +188,34 @@ moveReportPeriodToDate :: Day -> UIState -> UIState moveReportPeriodToDate d = updateReportPeriod (periodMoveTo d) +-- | Clear any report period limits.+resetReportPeriod :: UIState -> UIState+resetReportPeriod = setReportPeriod PeriodAll+ -- | Get the report period. reportPeriod :: UIState -> Period-reportPeriod = period_ . rsOpts . reportspec_ . cliopts_ . aopts+reportPeriod = (^.period) -- | Set the report period. setReportPeriod :: Period -> UIState -> UIState setReportPeriod p = updateReportPeriod (const p) --- | Clear any report period limits.-resetReportPeriod :: UIState -> UIState-resetReportPeriod = setReportPeriod PeriodAll- -- | Update report period by a applying a function. updateReportPeriod :: (Period -> Period) -> UIState -> UIState-updateReportPeriod updatePeriod ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportspec_=rspec}}} =- ui{aopts=uopts{cliopts_=copts{reportspec_=update rspec}}}- where- update = either (error "updateReportPeriod: updating period should not result in an error") id -- PARTIAL:- . updateReportSpecWith (\ropts -> ropts{period_=updatePeriod $ period_ ropts})+updateReportPeriod updatePeriod = fromRight err . overEither period updatePeriod -- PARTIAL:+ where err = error "updateReportPeriod: updating period should not result in an error" -- | Apply a new filter query. setFilter :: String -> UIState -> UIState-setFilter s ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportspec_=rspec}}} =- ui{aopts=uopts{cliopts_=copts{reportspec_=update rspec}}}+setFilter s = over reportSpec update where- update = either (const rspec) id . updateReportSpecWith (\ropts -> ropts{querystring_=querystring}) -- XXX silently ignores an error- querystring = words'' prefixes $ T.pack s+ update rspec = fromRight rspec $ setEither querystring (words'' prefixes $ T.pack s) rspec -- XXX silently ignores an error -- | Reset some filters & toggles. resetFilter :: UIState -> UIState-resetFilter ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportspec_=rspec@ReportSpec{rsOpts=ropts}}}} =- ui{aopts=uopts{cliopts_=copts{reportspec_=rspec{- rsQuery=Any- ,rsQueryOpts=[]- ,rsOpts=ropts{- empty_=True- ,statuses_=[]- ,real_=False- ,querystring_=[]- --,period_=PeriodAll- }}}}}+resetFilter = set querystringNoUpdate [] . set realNoUpdate False . set statusesNoUpdate []+ . set empty__ True -- set period PeriodAll+ . set rsQuery Any . set rsQueryOpts [] -- | Reset all options state to exactly what it was at startup -- (preserving any command-line options/arguments).@@ -282,15 +250,14 @@ setDepth mdepth = updateReportDepth (const mdepth) getDepth :: UIState -> Maybe Int-getDepth = depth_ . rsOpts . reportspec_ . cliopts_ . aopts+getDepth = (^.depth) -- | Update report depth by a applying a function. If asked to set a depth less -- than zero, it will leave it unchanged. updateReportDepth :: (Maybe Int -> Maybe Int) -> UIState -> UIState-updateReportDepth updateDepth ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportspec_=rspec}}} =- ui{aopts=uopts{cliopts_=copts{reportspec_=update rspec}}}+updateReportDepth updateDepth ui = over reportSpec update ui where- update = either (error "updateReportDepth: updating depth should not result in an error") id -- PARTIAL:+ update = fromRight (error "updateReportDepth: updating depth should not result in an error") -- PARTIAL: . updateReportSpecWith (\ropts -> ropts{depth_=updateDepth (depth_ ropts) >>= clipDepth ropts}) clipDepth ropts d | d < 0 = depth_ ropts | d >= maxDepth ui = Nothing@@ -301,8 +268,7 @@ showMinibuffer ui = setMode (Minibuffer e) ui where e = applyEdit gotoEOL $ editor MinibufferEditor (Just 1) oldq- oldq = T.unpack . T.unwords . map textQuoteIfNeeded- . querystring_ . rsOpts . reportspec_ . cliopts_ $ aopts ui+ oldq = T.unpack . T.unwords . map textQuoteIfNeeded $ ui^.querystring -- | Close the minibuffer, discarding any edit in progress. closeMinibuffer :: UIState -> UIState@@ -351,4 +317,3 @@ screenEnter d scr ui = (sInit scr) d True $ pushScreen scr ui-
Hledger/UI/UITypes.hs view
@@ -48,6 +48,7 @@ -- import the Show instance for functions. Warning, this also re-exports it import Hledger+import Hledger.Cli (HasCliOpts(..)) import Hledger.UI.UIOptions -- | hledger-ui's application state. This holds one or more stateful screens.@@ -160,7 +161,24 @@ -- mempty = list "" V.empty 1 -- XXX problem in 0.7, every list requires a unique Name -- mappend l1 l2 = l1 & listElementsL .~ (l1^.listElementsL <> l2^.listElementsL) -concat <$> mapM makeLenses [- ''Screen- ]+makeLenses ''Screen +uioptslens f ui = (\x -> ui{aopts=x}) <$> f (aopts ui)++instance HasCliOpts UIState where+ cliOpts = uioptslens.cliOpts++instance HasInputOpts UIState where+ inputOpts = uioptslens.inputOpts++instance HasBalancingOpts UIState where+ balancingOpts = uioptslens.balancingOpts++instance HasReportSpec UIState where+ reportSpec = uioptslens.reportSpec++instance HasReportOptsNoUpdate UIState where+ reportOptsNoUpdate = uioptslens.reportOptsNoUpdate++instance HasReportOpts UIState where+ reportOpts = uioptslens.reportOpts
Hledger/UI/UIUtils.hs view
@@ -24,6 +24,7 @@ ,scrollSelectionToMiddle ,suspend ,redraw+ ,reportSpecSetFutureAndForecast ) where @@ -34,15 +35,17 @@ import Brick.Widgets.Edit import Brick.Widgets.List (List, listSelectedL, listNameL, listItemHeightL) import Control.Monad.IO.Class+import Data.Bifunctor (second) import Data.List import qualified Data.Text as T+import Data.Time (Day, addDays) import Graphics.Vty (Event(..),Key(..),Modifier(..),Vty(..),Color,Attr,currentAttr,refresh -- ,Output(displayBounds,mkDisplayContext),DisplayContext(..) ) import Lens.Micro.Platform -import Hledger hiding (Color)+import Hledger import Hledger.Cli (CliOpts) import Hledger.Cli.DocFiles import Hledger.UI.UITypes@@ -190,7 +193,7 @@ borderPeriodStr preposition p = str (" "++preposition++" ") <+> withAttr ("border" <> "query") (str . T.unpack $ showPeriod p) borderKeysStr :: [(String,String)] -> Widget Name-borderKeysStr = borderKeysStr' . map (\(a,b) -> (a, str b))+borderKeysStr = borderKeysStr' . map (second str) borderKeysStr' :: [(String,Widget Name)] -> Widget Name borderKeysStr' keydescs =@@ -280,8 +283,7 @@ -- XXX May disrupt border style of inner widgets. -- XXX Should reduce the available size visible to inner widget, but doesn't seem to (cf rsDraw2). margin :: Int -> Int -> Maybe Color -> Widget Name -> Widget Name-margin h v mcolour = \w ->- Widget Greedy Greedy $ do+margin h v mcolour w = Widget Greedy Greedy $ do c <- getContext let w' = vLimit (c^.availHeightL - v*2) $ hLimit (c^.availWidthL - h*2) w attr = maybe currentAttr (\c -> c `on` c) mcolour@@ -342,3 +344,19 @@ | ev `elem` moveLeftEvents = EvKey KLeft [] | ev `elem` moveRightEvents = EvKey KRight [] | otherwise = ev++-- | Update the ReportSpec's query to exclude future transactions (later than the given day)+-- and forecast transactions (generated by --forecast), if the given forecast DateSpan is Nothing,+-- and include them otherwise.+reportSpecSetFutureAndForecast :: Day -> Maybe DateSpan -> ReportSpec -> ReportSpec+reportSpecSetFutureAndForecast d forecast rspec =+ rspec{_rsQuery=simplifyQuery $ And [_rsQuery rspec, periodq, excludeforecastq forecast]}+ where+ periodq = Date . periodAsDateSpan . period_ $ _rsReportOpts rspec+ -- Except in forecast mode, exclude future/forecast transactions.+ excludeforecastq (Just _) = Any+ excludeforecastq Nothing = -- not:date:tomorrow- not:tag:generated-transaction+ And [+ Not (Date $ DateSpan (Just $ addDays 1 d) Nothing)+ ,Not generatedTransactionTag+ ]
hledger-ui.1 view
@@ -1,5 +1,5 @@ -.TH "HLEDGER-UI" "1" "August 2021" "hledger-ui-1.22.2 " "hledger User Manuals"+.TH "HLEDGER-UI" "1" "September 2021" "hledger-ui-1.23 " "hledger User Manuals" @@ -7,7 +7,7 @@ .PP hledger-ui is a terminal interface (TUI) for the hledger accounting tool.-This manual is for hledger-ui 1.22.2.+This manual is for hledger-ui 1.23. .SH SYNOPSIS .PP \f[C]hledger-ui [OPTIONS] [QUERYARGS]\f[R]@@ -126,6 +126,10 @@ \f[B]\f[CB]--date2\f[B]\f[R] match the secondary date instead (see command help for other effects) .TP+\f[B]\f[CB]--today=DATE\f[B]\f[R]+override today\[aq]s date (affects relative smart dates, for+tests/examples)+.TP \f[B]\f[CB]-U --unmarked\f[B]\f[R] include only unmarked postings/txns (can combine with -P or -C) .TP@@ -169,6 +173,10 @@ next 6 months or till report end date. In hledger-ui, also make ordinary future transactions visible. .TP+\f[B]\f[CB]--commodity-style\f[B]\f[R]+Override the commodity style in the output for the specified commodity.+For example \[aq]EUR1.000,00\[aq].+.TP \f[B]\f[CB]--color=WHEN (or --colour=WHEN)\f[B]\f[R] Should color-supporting commands use ANSI color codes in text output. \[aq]auto\[aq] (default): whenever stdout seems to be a color-supporting@@ -177,6 +185,14 @@ into \[aq]less -R\[aq]. \[aq]never\[aq] or \[aq]no\[aq]: never. A NO_COLOR environment variable overrides this.+.TP+\f[B]\f[CB]--pretty[=WHEN]\f[B]\f[R]+Show prettier output, e.g.+using unicode box-drawing characters.+Accepts \[aq]yes\[aq] (the default) or \[aq]no\[aq] (\[aq]y\[aq],+\[aq]n\[aq], \[aq]always\[aq], \[aq]never\[aq] also work).+If you provide an argument you must use \[aq]=\[aq], e.g.+\[aq]--pretty=yes\[aq]. .PP When a reporting option appears more than once in the command line, the last one takes precedence.
hledger-ui.cabal view
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack name: hledger-ui-version: 1.22.2+version: 1.23 synopsis: Curses-style terminal interface for the hledger accounting system description: A simple curses-style terminal user interface for the hledger accounting system. It can be a more convenient way to browse your accounts than the CLI.@@ -27,7 +27,7 @@ license-file: LICENSE build-type: Simple tested-with:- GHC==8.6.5, GHC==8.8.4, GHC==8.10.4+ GHC==8.6.5, GHC==8.8.4, GHC==8.10.4, GHC==9.0.1 extra-source-files: CHANGES.md README.md@@ -63,11 +63,11 @@ hs-source-dirs: ./ ghc-options: -Wall -fno-warn-unused-do-bind -fno-warn-name-shadowing -fno-warn-missing-signatures -fno-warn-type-defaults -fno-warn-orphans- cpp-options: -DVERSION="1.22.2"+ cpp-options: -DVERSION="1.23" build-depends: ansi-terminal >=0.9 , async- , base >=4.10.1.0 && <4.16+ , base >=4.11 && <4.16 , base-compat-batteries >=0.10.1 && <0.12 , brick >=0.23 , cmdargs >=0.8@@ -77,8 +77,8 @@ , extra >=1.6.3 , filepath , fsnotify >=0.2.1.2 && <0.4- , hledger >=1.22.2 && <1.23- , hledger-lib >=1.22.2 && <1.23+ , hledger ==1.23.*+ , hledger-lib ==1.23.* , megaparsec >=7.0.0 && <9.2 , microlens >=0.4 , microlens-platform >=0.2.3.1
hledger-ui.info view
@@ -12,7 +12,7 @@ ************* hledger-ui is a terminal interface (TUI) for the hledger accounting-tool. This manual is for hledger-ui 1.22.2.+tool. This manual is for hledger-ui 1.23. 'hledger-ui [OPTIONS] [QUERYARGS]' 'hledger ui -- [OPTIONS] [QUERYARGS]'@@ -144,6 +144,10 @@ match the secondary date instead (see command help for other effects)+'--today=DATE'++ override today's date (affects relative smart dates, for+ tests/examples) '-U --unmarked' include only unmarked postings/txns (can combine with -P or -C)@@ -189,6 +193,10 @@ generate future transactions from periodic transaction rules, for the next 6 months or till report end date. In hledger-ui, also make ordinary future transactions visible.+'--commodity-style'++ Override the commodity style in the output for the specified+ commodity. For example 'EUR1.000,00'. '--color=WHEN (or --colour=WHEN)' Should color-supporting commands use ANSI color codes in text@@ -196,7 +204,13 @@ color-supporting terminal. 'always' or 'yes': always, useful eg when piping output into 'less -R'. 'never' or 'no': never. A NO_COLOR environment variable overrides this.+'--pretty[=WHEN]' + Show prettier output, e.g. using unicode box-drawing characters.+ Accepts 'yes' (the default) or 'no' ('y', 'n', 'always', 'never'+ also work). If you provide an argument you must use '=', e.g.+ '-pretty=yes'.+ When a reporting option appears more than once in the command line, the last one takes precedence. @@ -594,32 +608,32 @@ Tag Table: Node: Top221-Node: OPTIONS1646-Ref: #options1743-Node: KEYS6144-Ref: #keys6239-Node: SCREENS10310-Ref: #screens10408-Node: Accounts screen10498-Ref: #accounts-screen10626-Node: Register screen12841-Ref: #register-screen12996-Node: Transaction screen14993-Ref: #transaction-screen15151-Node: Error screen16021-Ref: #error-screen16143-Node: TIPS16387-Ref: #tips16486-Node: Watch mode16538-Ref: #watch-mode16655-Node: Watch mode limitations17401-Ref: #watch-mode-limitations17542-Node: ENVIRONMENT18678-Ref: #environment18789-Node: FILES19596-Ref: #files19695-Node: BUGS19908-Ref: #bugs19985+Node: OPTIONS1644+Ref: #options1741+Node: KEYS6620+Ref: #keys6715+Node: SCREENS10786+Ref: #screens10884+Node: Accounts screen10974+Ref: #accounts-screen11102+Node: Register screen13317+Ref: #register-screen13472+Node: Transaction screen15469+Ref: #transaction-screen15627+Node: Error screen16497+Ref: #error-screen16619+Node: TIPS16863+Ref: #tips16962+Node: Watch mode17014+Ref: #watch-mode17131+Node: Watch mode limitations17877+Ref: #watch-mode-limitations18018+Node: ENVIRONMENT19154+Ref: #environment19265+Node: FILES20072+Ref: #files20171+Node: BUGS20384+Ref: #bugs20461 End Tag Table
hledger-ui.txt view
@@ -5,7 +5,7 @@ NAME hledger-ui is a terminal interface (TUI) for the hledger accounting- tool. This manual is for hledger-ui 1.22.2.+ tool. This manual is for hledger-ui 1.23. SYNOPSIS hledger-ui [OPTIONS] [QUERYARGS]@@ -122,6 +122,10 @@ match the secondary date instead (see command help for other effects) + --today=DATE+ override today's date (affects relative smart dates, for+ tests/examples)+ -U --unmarked include only unmarked postings/txns (can combine with -P or -C) @@ -138,34 +142,38 @@ hide/aggregate accounts or postings more than NUM levels deep -E --empty- show items with zero amount, normally hidden (and vice-versa in+ show items with zero amount, normally hidden (and vice-versa in hledger-ui/hledger-web) -B --cost convert amounts to their cost/selling amount at transaction time -V --market- convert amounts to their market value in default valuation com-+ convert amounts to their market value in default valuation com- modities -X --exchange=COMM convert amounts to their market value in commodity COMM --value- convert amounts to cost or market value, more flexibly than+ convert amounts to cost or market value, more flexibly than -B/-V/-X --infer-market-prices- use transaction prices (recorded with @ or @@) as additional+ use transaction prices (recorded with @ or @@) as additional market prices, as if they were P directives --auto apply automated posting rules to modify transactions. --forecast- generate future transactions from periodic transaction rules,- for the next 6 months or till report end date. In hledger-ui,+ generate future transactions from periodic transaction rules,+ for the next 6 months or till report end date. In hledger-ui, also make ordinary future transactions visible. + --commodity-style+ Override the commodity style in the output for the specified+ commodity. For example 'EUR1.000,00'.+ --color=WHEN (or --colour=WHEN) Should color-supporting commands use ANSI color codes in text output. 'auto' (default): whenever stdout seems to be a color-@@ -173,6 +181,12 @@ piping output into 'less -R'. 'never' or 'no': never. A NO_COLOR environment variable overrides this. + --pretty[=WHEN]+ Show prettier output, e.g. using unicode box-drawing charac-+ ters. Accepts 'yes' (the default) or 'no' ('y', 'n', 'always',+ 'never' also work). If you provide an argument you must use+ '=', e.g. '--pretty=yes'.+ When a reporting option appears more than once in the command line, the last one takes precedence. @@ -194,86 +208,86 @@ show debug output (levels 1-9, default: 1) A @FILE argument will be expanded to the contents of FILE, which should- contain one command line option/argument per line. (To prevent this,+ contain one command line option/argument per line. (To prevent this, insert a -- argument before.) KEYS- ? shows a help dialog listing all keys. (Some of these also appear in+ ? shows a help dialog listing all keys. (Some of these also appear in the quick help at the bottom of each screen.) Press ? again (or ESCAPE, or LEFT, or q) to close it. The following keys work on most screens: The cursor keys navigate: right (or enter) goes deeper, left returns to- the previous screen, up/down/page up/page down/home/end move up and+ the previous screen, up/down/page up/page down/home/end move up and down through lists. Emacs-style (ctrl-p/ctrl-n/ctrl-f/ctrl-b) movement- keys are also supported (but not vi-style keys, since hledger-1.19,- sorry!). A tip: movement speed is limited by your keyboard repeat- rate, to move faster you may want to adjust it. (If you're on a mac,+ keys are also supported (but not vi-style keys, since hledger-1.19,+ sorry!). A tip: movement speed is limited by your keyboard repeat+ rate, to move faster you may want to adjust it. (If you're on a mac, the karabiner app is one way to do that.) - With shift pressed, the cursor keys adjust the report period, limiting- the transactions to be shown (by default, all are shown). shift-- down/up steps downward and upward through these standard report period- durations: year, quarter, month, week, day. Then, shift-left/right- moves to the previous/next period. T sets the report period to today.- With the --watch option, when viewing a "current" period (the current+ With shift pressed, the cursor keys adjust the report period, limiting+ the transactions to be shown (by default, all are shown). shift-+ down/up steps downward and upward through these standard report period+ durations: year, quarter, month, week, day. Then, shift-left/right+ moves to the previous/next period. T sets the report period to today.+ With the --watch option, when viewing a "current" period (the current day, week, month, quarter, or year), the period will move automatically to track the current date. To set a non-standard period, you can use / and a date: query. - / lets you set a general filter query limiting the data shown, using- the same query terms as in hledger and hledger-web. While editing the- query, you can use CTRL-a/e/d/k, BS, cursor keys; press ENTER to set+ / lets you set a general filter query limiting the data shown, using+ the same query terms as in hledger and hledger-web. While editing the+ query, you can use CTRL-a/e/d/k, BS, cursor keys; press ENTER to set it, or ESCAPEto cancel. There are also keys for quickly adjusting some- common filters like account depth and transaction status (see below).+ common filters like account depth and transaction status (see below). BACKSPACE or DELETE removes all filters, showing all transactions. - As mentioned above, by default hledger-ui hides future transactions -+ As mentioned above, by default hledger-ui hides future transactions - both ordinary transactions recorded in the journal, and periodic trans-- actions generated by rule. F toggles forecast mode, in which+ actions generated by rule. F toggles forecast mode, in which future/forecasted transactions are shown. - ESCAPE resets the UI state and jumps back to the top screen, restoring- the app's initial state at startup. Or, it cancels minibuffer data+ ESCAPE resets the UI state and jumps back to the top screen, restoring+ the app's initial state at startup. Or, it cancels minibuffer data entry or the help dialog. CTRL-l redraws the screen and centers the selection if possible (selec-- tions near the top won't be centered, since we don't scroll above the+ tions near the top won't be centered, since we don't scroll above the top). - g reloads from the data file(s) and updates the current screen and any- previous screens. (With large files, this could cause a noticeable+ g reloads from the data file(s) and updates the current screen and any+ previous screens. (With large files, this could cause a noticeable pause.) - I toggles balance assertion checking. Disabling balance assertions+ I toggles balance assertion checking. Disabling balance assertions temporarily can be useful for troubleshooting. - a runs command-line hledger's add command, and reloads the updated+ a runs command-line hledger's add command, and reloads the updated file. This allows some basic data entry. - A is like a, but runs the hledger-iadd tool, which provides a terminal- interface. This key will be available if hledger-iadd is installed in+ A is like a, but runs the hledger-iadd tool, which provides a terminal+ interface. This key will be available if hledger-iadd is installed in $path. - E runs $HLEDGER_UI_EDITOR, or $EDITOR, or a default (emacsclient -a ""- -nw) on the journal file. With some editors (emacs, vi), the cursor- will be positioned at the current transaction when invoked from the- register and transaction screens, and at the error location (if possi-+ E runs $HLEDGER_UI_EDITOR, or $EDITOR, or a default (emacsclient -a ""+ -nw) on the journal file. With some editors (emacs, vi), the cursor+ will be positioned at the current transaction when invoked from the+ register and transaction screens, and at the error location (if possi- ble) when invoked from the error screen. - B toggles cost mode, showing amounts in their transaction price's com-+ B toggles cost mode, showing amounts in their transaction price's com- modity (like toggling the -B/--cost flag). - V toggles value mode, showing amounts' current market value in their- default valuation commodity (like toggling the -V/--market flag).- Note, "current market value" means the value on the report end date if- specified, otherwise today. To see the value on another date, you can- temporarily set that as the report end date. Eg: to see a transaction- as it was valued on july 30, go to the accounts or register screen,+ V toggles value mode, showing amounts' current market value in their+ default valuation commodity (like toggling the -V/--market flag).+ Note, "current market value" means the value on the report end date if+ specified, otherwise today. To see the value on another date, you can+ temporarily set that as the report end date. Eg: to see a transaction+ as it was valued on july 30, go to the accounts or register screen, press /, and add date:-7/30 to the query. At most one of cost or value mode can be active at once. - There's not yet any visual reminder when cost or value mode is active;+ There's not yet any visual reminder when cost or value mode is active; for now pressing b b v should reliably reset to normal mode. q quits the application.@@ -282,44 +296,44 @@ SCREENS Accounts screen- This is normally the first screen displayed. It lists accounts and- their balances, like hledger's balance command. By default, it shows- all accounts and their latest ending balances (including the balances- of subaccounts). If you specify a query on the command line, it shows+ This is normally the first screen displayed. It lists accounts and+ their balances, like hledger's balance command. By default, it shows+ all accounts and their latest ending balances (including the balances+ of subaccounts). If you specify a query on the command line, it shows just the matched accounts and the balances from matched transactions. - Account names are shown as a flat list by default; press t to toggle- tree mode. In list mode, account balances are exclusive of subac-- counts, except where subaccounts are hidden by a depth limit (see- below). In tree mode, all account balances are inclusive of subac-+ Account names are shown as a flat list by default; press t to toggle+ tree mode. In list mode, account balances are exclusive of subac-+ counts, except where subaccounts are hidden by a depth limit (see+ below). In tree mode, all account balances are inclusive of subac- counts. - To see less detail, press a number key, 1 to 9, to set a depth limit.+ To see less detail, press a number key, 1 to 9, to set a depth limit. Or use - to decrease and +/= to increase the depth limit. 0 shows even- less detail, collapsing all accounts to a single total. To remove the- depth limit, set it higher than the maximum account depth, or press+ less detail, collapsing all accounts to a single total. To remove the+ depth limit, set it higher than the maximum account depth, or press ESCAPE. H toggles between showing historical balances or period balances. His-- torical balances (the default) are ending balances at the end of the- report period, taking into account all transactions before that date- (filtered by the filter query if any), including transactions before- the start of the report period. In other words, historical balances- are what you would see on a bank statement for that account (unless- disturbed by a filter query). Period balances ignore transactions+ torical balances (the default) are ending balances at the end of the+ report period, taking into account all transactions before that date+ (filtered by the filter query if any), including transactions before+ the start of the report period. In other words, historical balances+ are what you would see on a bank statement for that account (unless+ disturbed by a filter query). Period balances ignore transactions before the report start date, so they show the change in balance during the report period. They are more useful eg when viewing a time log. U toggles filtering by unmarked status, including or excluding unmarked postings in the balances. Similarly, P toggles pending postings, and C- toggles cleared postings. (By default, balances include all postings;- if you activate one or two status filters, only those postings are+ toggles cleared postings. (By default, balances include all postings;+ if you activate one or two status filters, only those postings are included; and if you activate all three, the filter is removed.) R toggles real mode, in which virtual postings are ignored. - Z toggles nonzero mode, in which only accounts with nonzero balances- are shown (hledger-ui shows zero items by default, unlike command-line+ Z toggles nonzero mode, in which only accounts with nonzero balances+ are shown (hledger-ui shows zero items by default, unlike command-line hledger). Press right or enter to view an account's transactions register.@@ -328,124 +342,124 @@ This screen shows the transactions affecting a particular account, like a check register. Each line represents one transaction and shows: - o the other account(s) involved, in abbreviated form. (If there are- both real and virtual postings, it shows only the accounts affected+ o the other account(s) involved, in abbreviated form. (If there are+ both real and virtual postings, it shows only the accounts affected by real postings.) - o the overall change to the current account's balance; positive for an+ o the overall change to the current account's balance; positive for an inflow to this account, negative for an outflow. o the running historical total or period total for the current account,- after the transaction. This can be toggled with H. Similar to the- accounts screen, the historical total is affected by transactions- (filtered by the filter query) before the report start date, while+ after the transaction. This can be toggled with H. Similar to the+ accounts screen, the historical total is affected by transactions+ (filtered by the filter query) before the report start date, while the period total is not. If the historical total is not disturbed by- a filter query, it will be the running historical balance you would+ a filter query, it will be the running historical balance you would see on a bank register for the current account. - Transactions affecting this account's subaccounts will be included in+ Transactions affecting this account's subaccounts will be included in the register if the accounts screen is in tree mode, or if it's in list- mode but this account has subaccounts which are not shown due to a- depth limit. In other words, the register always shows the transac-- tions contributing to the balance shown on the accounts screen. Tree+ mode but this account has subaccounts which are not shown due to a+ depth limit. In other words, the register always shows the transac-+ tions contributing to the balance shown on the accounts screen. Tree mode/list mode can be toggled with t here also. - U toggles filtering by unmarked status, showing or hiding unmarked+ U toggles filtering by unmarked status, showing or hiding unmarked transactions. Similarly, P toggles pending transactions, and C toggles- cleared transactions. (By default, transactions with all statuses are- shown; if you activate one or two status filters, only those transac-+ cleared transactions. (By default, transactions with all statuses are+ shown; if you activate one or two status filters, only those transac- tions are shown; and if you activate all three, the filter is removed.) R toggles real mode, in which virtual postings are ignored. - Z toggles nonzero mode, in which only transactions posting a nonzero- change are shown (hledger-ui shows zero items by default, unlike com-+ Z toggles nonzero mode, in which only transactions posting a nonzero+ change are shown (hledger-ui shows zero items by default, unlike com- mand-line hledger). Press right (or enter) to view the selected transaction in detail. Transaction screen- This screen shows a single transaction, as a general journal entry,- similar to hledger's print command and journal format (hledger_jour-+ This screen shows a single transaction, as a general journal entry,+ similar to hledger's print command and journal format (hledger_jour- nal(5)). - The transaction's date(s) and any cleared flag, transaction code,- description, comments, along with all of its account postings are- shown. Simple transactions have two postings, but there can be more+ The transaction's date(s) and any cleared flag, transaction code,+ description, comments, along with all of its account postings are+ shown. Simple transactions have two postings, but there can be more (or in certain cases, fewer). - up and down will step through all transactions listed in the previous- account register screen. In the title bar, the numbers in parentheses- show your position within that account register. They will vary+ up and down will step through all transactions listed in the previous+ account register screen. In the title bar, the numbers in parentheses+ show your position within that account register. They will vary depending on which account register you came from (remember most trans- actions appear in multiple account registers). The #N number preceding them is the transaction's position within the complete unfiltered jour- nal, which is a more stable id (at least until the next reload). Error screen- This screen will appear if there is a problem, such as a parse error,- when you press g to reload. Once you have fixed the problem, press g+ This screen will appear if there is a problem, such as a parse error,+ when you press g to reload. Once you have fixed the problem, press g again to reload and resume normal operation. (Or, you can press escape to cancel the reload attempt.) TIPS Watch mode- One of hledger-ui's best features is the auto-reloading --watch mode.- With this flag, it will update the display automatically whenever+ One of hledger-ui's best features is the auto-reloading --watch mode.+ With this flag, it will update the display automatically whenever changes are saved to the data files. - This is very useful when reconciling. A good workflow is to have your- bank's online register open in a browser window, for reference; the- journal file open in an editor window; and hledger-ui in watch mode in+ This is very useful when reconciling. A good workflow is to have your+ bank's online register open in a browser window, for reference; the+ journal file open in an editor window; and hledger-ui in watch mode in a terminal window, eg: $ hledger-ui --watch --register checking -C - As you mark things cleared in the editor, you can see the effect imme-- diately without having to context switch. This leaves more mental- bandwidth for your accounting. Of course you can still interact with- hledger-ui when needed, eg to toggle cleared mode, or to explore the+ As you mark things cleared in the editor, you can see the effect imme-+ diately without having to context switch. This leaves more mental+ bandwidth for your accounting. Of course you can still interact with+ hledger-ui when needed, eg to toggle cleared mode, or to explore the history. Watch mode limitations- There are situations in which it won't work, ie the display will not- update when you save a change (because the underlying inotify library+ There are situations in which it won't work, ie the display will not+ update when you save a change (because the underlying inotify library does not support it). Here are some that we know of: - o Certain editors: saving with gedit, and perhaps any Gnome applica-- tion, won't be detected (#1617). Jetbrains IDEs, such as IDEA, also+ o Certain editors: saving with gedit, and perhaps any Gnome applica-+ tion, won't be detected (#1617). Jetbrains IDEs, such as IDEA, also may not work (#911). - o Certain unusual filesystems might not be supported. (All the usual+ o Certain unusual filesystems might not be supported. (All the usual ones on unix, mac and windows are supported.) In such cases, the workaround is to switch to the hledger-ui window and- press g each time you want it to reload. (Actually, see #1617 for+ press g each time you want it to reload. (Actually, see #1617 for another workaround, and let us know if it works for you.) - If you leave hledger-ui --watch running for days, on certain platforms- (?), perhaps with many transactions in your journal (?), perhaps with- large numbers of other files present (?), you may see it gradually- using more and more memory and CPU over time, as seen in top or Activ-+ If you leave hledger-ui --watch running for days, on certain platforms+ (?), perhaps with many transactions in your journal (?), perhaps with+ large numbers of other files present (?), you may see it gradually+ using more and more memory and CPU over time, as seen in top or Activ- ity Monitor or Task Manager. - A workaround is to quit and restart it, or to suspend it (CTRL-z) and+ A workaround is to quit and restart it, or to suspend it (CTRL-z) and restart it (fg) if your shell supports that. ENVIRONMENT COLUMNS The screen width to use. Default: the full terminal width. LEDGER_FILE The journal file path when not specified with -f. Default:- ~/.hledger.journal (on windows, perhaps C:/Users/USER/.hledger.jour-+ ~/.hledger.journal (on windows, perhaps C:/Users/USER/.hledger.jour- nal). - A typical value is ~/DIR/YYYY.journal, where DIR is a version-con-- trolled finance directory and YYYY is the current year. Or ~/DIR/cur-+ A typical value is ~/DIR/YYYY.journal, where DIR is a version-con-+ trolled finance directory and YYYY is the current year. Or ~/DIR/cur- rent.journal, where current.journal is a symbolic link to YYYY.journal. On Mac computers, you can set this and other environment variables in a- more thorough way that also affects applications started from the GUI- (say, an Emacs dock icon). Eg on MacOS Catalina I have a+ more thorough way that also affects applications started from the GUI+ (say, an Emacs dock icon). Eg on MacOS Catalina I have a ~/.MacOSX/environment.plist file containing {@@ -455,13 +469,13 @@ To see the effect you may need to killall Dock, or reboot. FILES- Reads data from one or more files in hledger journal, timeclock, time-- dot, or CSV format specified with -f, or $LEDGER_FILE, or- $HOME/.hledger.journal (on windows, perhaps+ Reads data from one or more files in hledger journal, timeclock, time-+ dot, or CSV format specified with -f, or $LEDGER_FILE, or+ $HOME/.hledger.journal (on windows, perhaps C:/Users/USER/.hledger.journal). BUGS- The need to precede options with -- when invoked from hledger is awk-+ The need to precede options with -- when invoked from hledger is awk- ward. -f- doesn't work (hledger-ui can't read from stdin).@@ -469,24 +483,24 @@ -V affects only the accounts screen. When you press g, the current and all previous screens are regenerated,- which may cause a noticeable pause with large files. Also there is no+ which may cause a noticeable pause with large files. Also there is no visual indication that this is in progress. - --watch is not yet fully robust. It works well for normal usage, but- many file changes in a short time (eg saving the file thousands of- times with an editor macro) can cause problems at least on OSX. Symp-- toms include: unresponsive UI, periodic resetting of the cursor posi-+ --watch is not yet fully robust. It works well for normal usage, but+ many file changes in a short time (eg saving the file thousands of+ times with an editor macro) can cause problems at least on OSX. Symp-+ toms include: unresponsive UI, periodic resetting of the cursor posi- tion, momentary display of parse errors, high CPU usage eventually sub- siding, and possibly a small but persistent build-up of CPU usage until the program is restarted. - Also, if you are viewing files mounted from another machine, --watch+ Also, if you are viewing files mounted from another machine, --watch requires that both machine clocks are roughly in step. REPORTING BUGS- Report bugs at http://bugs.hledger.org (or on the #hledger IRC channel+ Report bugs at http://bugs.hledger.org (or on the #hledger IRC channel or hledger mail list) @@ -504,4 +518,4 @@ -hledger-ui-1.22.2 August 2021 HLEDGER-UI(1)+hledger-ui-1.23 September 2021 HLEDGER-UI(1)