packages feed

hledger-ui 1.26.1 → 1.27

raw patch · 14 files changed

+638/−564 lines, 14 filesdep +breakpointdep ~basedep ~brickdep ~hledger

Dependencies added: breakpoint

Dependency ranges changed: base, brick, hledger, hledger-lib

Files

CHANGES.md view
@@ -9,6 +9,16 @@ User-visible changes in hledger-ui. See also the hledger changelog. +# 1.27 2022-09-01++Improvements++- At --debug=2 and up, log debug output to ./debug.log.++- Use/require brick 1.0+. (#1889)++- Use hledger 1.27+ # 1.26.1 2022-07-11  - support doclayout 0.4, brick 0.72+
Hledger/UI/AccountsScreen.hs view
@@ -29,7 +29,7 @@ import Text.DocLayout (realLength)  import Hledger-import Hledger.Cli hiding (progname,prognameandversion)+import Hledger.Cli hiding (mode, progname, prognameandversion) import Hledger.UI.UIOptions import Hledger.UI.UITypes import Hledger.UI.UIState@@ -54,7 +54,7 @@   aopts=UIOpts{uoCliOpts=copts@CliOpts{reportspec_=rspec@ReportSpec{_rsReportOpts=ropts}}},   ajournal=j,   aScreen=s@AccountsScreen{}-  } =+  } = dlogUiTrace "asInit 1" $   ui{aScreen=s & asList .~ newitems'}    where     newitems = list AccountsList (V.fromList $ displayitems ++ blankitems) 1@@ -97,7 +97,7 @@     displayitems = map displayitem items     -- blanks added for scrolling control, cf RegisterScreen.     -- XXX Ugly. Changing to 0 helps when debugging.-    blankitems = replicate 100+    blankitems = replicate uiNumBlankItems       AccountsScreenItem{asItemIndentLevel        = 0                         ,asItemAccountName        = ""                         ,asItemDisplayAccountName = ""@@ -105,14 +105,14 @@                         }  -asInit _ _ _ = error "init function called with wrong screen type, should not happen"  -- PARTIAL:+asInit _ _ _ = dlogUiTrace "asInit 2" $ errorWrongScreenType "init function"  -- PARTIAL:  asDraw :: UIState -> [Widget Name] asDraw UIState{aopts=_uopts@UIOpts{uoCliOpts=copts@CliOpts{reportspec_=rspec}}               ,ajournal=j               ,aScreen=s@AccountsScreen{}               ,aMode=mode-              } =+              } = dlogUiTrace "asDraw 1" $     case mode of       Help       -> [helpDialog copts, maincontent]       -- Minibuffer e -> [minibuffer e, maincontent]@@ -152,7 +152,7 @@         ishistorical = balanceaccum_ ropts == Historical          toplabel =-              withAttr ("border" <> "filename") files+              withAttr (attrName "border" <> attrName "filename") files           <+> toggles           <+> str (" account " ++ if ishistorical then "balances" else "changes")           <+> borderPeriodStr (if ishistorical then "at end of" else "in") (period_ ropts)@@ -160,7 +160,7 @@           <+> borderDepthStr mdepth           <+> str (" ("++curidx++"/"++totidx++")")           <+> (if ignore_assertions_ . balancingopts_ $ inputopts_ copts-               then withAttr ("border" <> "query") (str " ignoring balance assertions")+               then withAttr (attrName "border" <> attrName "query") (str " ignoring balance assertions")                else str "")           where             files = case journalFilePaths j of@@ -168,7 +168,7 @@                            f:_ -> str $ takeFileName f                            -- [f,_:[]] -> (withAttr ("border" <> "bold") $ str $ takeFileName f) <+> str " (& 1 included file)"                            -- f:fs  -> (withAttr ("border" <> "bold") $ str $ takeFileName f) <+> str (" (& " ++ show (length fs) ++ " included files)")-            toggles = withAttr ("border" <> "query") $ str $ unwords $ concat [+            toggles = withAttr (attrName "border" <> attrName "query") $ str $ unwords $ concat [                [""]               ,if empty_ ropts then [] else ["nonzero"]               ,uiShowStatus copts $ statuses_ ropts@@ -203,7 +203,7 @@               ,("q", str "quit")               ] -asDraw _ = error "draw function called with wrong screen type, should not happen"  -- PARTIAL:+asDraw _ =  dlogUiTrace "asDraw 2" $ errorWrongScreenType "draw function"  -- PARTIAL:  asDrawItem :: (Int,Int) -> Bool -> AccountsScreenItem -> Widget Name asDrawItem (acctwidth, balwidth) selected AccountsScreenItem{..} =@@ -220,187 +220,183 @@         balspace = T.replicate (2 + balwidth - wbWidth balBuilder) " "         splitAmounts = foldr1 (<+>) . intersperse (str ", ") . map renderamt . T.splitOn ", " . wbToText         renderamt :: T.Text -> Widget Name-        renderamt a | T.any (=='-') a = withAttr (sel $ "list" <> "balance" <> "negative") $ txt a-                    | otherwise       = withAttr (sel $ "list" <> "balance" <> "positive") $ txt a-        sel | selected  = (<> "selected")+        renderamt a | T.any (=='-') a = withAttr (sel $ attrName "list" <> attrName "balance" <> attrName "negative") $ txt a+                    | otherwise       = withAttr (sel $ attrName "list" <> attrName "balance" <> attrName "positive") $ txt a+        sel | selected  = (<> attrName "selected")             | otherwise = id -asHandle :: UIState -> BrickEvent Name AppEvent -> EventM Name (Next UIState)-asHandle ui0@UIState{-   aScreen=scr@AccountsScreen{..}-  ,aopts=UIOpts{uoCliOpts=copts}-  ,ajournal=j-  ,aMode=mode-  } ev = do-  let-    d = copts^.rsDay-    nonblanks = V.takeWhile (not . T.null . asItemAccountName) $ _asList^.listElementsL-    lastnonblankidx = max 0 (length nonblanks - 1)--  -- save the currently selected account, in case we leave this screen and lose the selection-  let-    selacct = case listSelectedElement _asList of-                Just (_, AccountsScreenItem{..}) -> asItemAccountName-                Nothing -> scr ^. asSelectedAccount-    ui = ui0{aScreen=scr & asSelectedAccount .~ selacct}+asHandle :: BrickEvent Name AppEvent -> EventM Name UIState ()+asHandle ev = do+  ui0 <- get'+  dlogUiTraceM "asHandle 1"+  case ui0 of+    ui1@UIState{+      aScreen=scr@AccountsScreen{..}+      ,aopts=UIOpts{uoCliOpts=copts}+      ,ajournal=j+      ,aMode=mode+      } -> do -  case mode of-    Minibuffer _ ed ->-      case ev of-        VtyEvent (EvKey KEsc   []) -> continue $ closeMinibuffer ui-        VtyEvent (EvKey KEnter []) -> continue $ regenerateScreens j d $-            case setFilter s $ closeMinibuffer ui of-              Left bad -> showMinibuffer "Cannot compile regular expression" (Just bad) ui-              Right ui' -> ui'-          where s = chomp $ unlines $ map strip $ getEditContents ed-        VtyEvent (EvKey (KChar 'l') [MCtrl]) -> redraw ui-        VtyEvent (EvKey (KChar 'z') [MCtrl]) -> suspend ui-        VtyEvent ev        -> do-          ed' <- handleEditorEvent -#if MIN_VERSION_brick(0,72,0)-            (VtyEvent ev)-#else-            ev-#endif-            ed-          continue $ ui{aMode=Minibuffer "filter" ed'}-        AppEvent _        -> continue ui-        MouseDown{}       -> continue ui-        MouseUp{}         -> continue ui+      let+        -- save the currently selected account, in case we leave this screen and lose the selection+        selacct = case listSelectedElement _asList of+                    Just (_, AccountsScreenItem{..}) -> asItemAccountName+                    Nothing -> scr ^. asSelectedAccount+        ui = ui1{aScreen=scr & asSelectedAccount .~ selacct}+        nonblanks = V.takeWhile (not . T.null . asItemAccountName) $ _asList^.listElementsL+        lastnonblankidx = max 0 (length nonblanks - 1)+        journalspan = journalDateSpan False j+        d = copts^.rsDay -    Help ->-      case ev of-        -- VtyEvent (EvKey (KChar 'q') []) -> halt ui-        VtyEvent (EvKey (KChar 'l') [MCtrl]) -> redraw ui-        VtyEvent (EvKey (KChar 'z') [MCtrl]) -> suspend ui-        _                    -> helpHandle ui ev+      case mode of+        Minibuffer _ ed ->+          case ev of+            VtyEvent (EvKey KEsc   []) -> put' $ closeMinibuffer ui+            VtyEvent (EvKey KEnter []) -> put' $ regenerateScreens j d $+                case setFilter s $ closeMinibuffer ui of+                  Left bad -> showMinibuffer "Cannot compile regular expression" (Just bad) ui+                  Right ui' -> ui'+              where s = chomp $ unlines $ map strip $ getEditContents ed+            VtyEvent (EvKey (KChar 'l') [MCtrl]) -> redraw+            VtyEvent (EvKey (KChar 'z') [MCtrl]) -> suspend ui+            VtyEvent e -> do+              ed' <- nestEventM' ed $ handleEditorEvent (VtyEvent e)+              put' ui{aMode=Minibuffer "filter" ed'}+            AppEvent _  -> return ()+            MouseDown{} -> return ()+            MouseUp{}   -> return () -    Normal ->-      case ev of-        VtyEvent (EvKey (KChar 'q') []) -> halt ui-        -- EvKey (KChar 'l') [MCtrl] -> do-        VtyEvent (EvKey KEsc        []) -> continue $ resetScreens d 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 ->-          continue $ regenerateScreens j d $ setReportPeriod (DayPeriod d) ui-          where-            p = reportPeriod ui-        e | e `elem` [VtyEvent (EvKey (KChar 'g') []), AppEvent FileChange] ->-          liftIO (uiReloadJournal copts d ui) >>= continue-        VtyEvent (EvKey (KChar 'I') []) -> continue $ uiCheckBalanceAssertions d (toggleIgnoreBalanceAssertions ui)-        VtyEvent (EvKey (KChar 'a') []) -> suspendAndResume $ clearScreen >> setCursorPosition 0 0 >> add copts j >> uiReloadJournalIfChanged copts d j ui-        VtyEvent (EvKey (KChar 'A') []) -> suspendAndResume $ void (runIadd (journalFilePath j)) >> uiReloadJournalIfChanged copts d j ui-        VtyEvent (EvKey (KChar 'E') []) -> suspendAndResume $ void (runEditor endPosition (journalFilePath j)) >> uiReloadJournalIfChanged copts d j ui-        VtyEvent (EvKey (KChar 'B') []) -> continue $ regenerateScreens j d $ toggleConversionOp ui-        VtyEvent (EvKey (KChar 'V') []) -> continue $ regenerateScreens j d $ toggleValue ui-        VtyEvent (EvKey (KChar '0') []) -> continue $ regenerateScreens j d $ setDepth (Just 0) ui-        VtyEvent (EvKey (KChar '1') []) -> continue $ regenerateScreens j d $ setDepth (Just 1) ui-        VtyEvent (EvKey (KChar '2') []) -> continue $ regenerateScreens j d $ setDepth (Just 2) ui-        VtyEvent (EvKey (KChar '3') []) -> continue $ regenerateScreens j d $ setDepth (Just 3) ui-        VtyEvent (EvKey (KChar '4') []) -> continue $ regenerateScreens j d $ setDepth (Just 4) ui-        VtyEvent (EvKey (KChar '5') []) -> continue $ regenerateScreens j d $ setDepth (Just 5) ui-        VtyEvent (EvKey (KChar '6') []) -> continue $ regenerateScreens j d $ setDepth (Just 6) ui-        VtyEvent (EvKey (KChar '7') []) -> continue $ regenerateScreens j d $ setDepth (Just 7) ui-        VtyEvent (EvKey (KChar '8') []) -> continue $ regenerateScreens j d $ setDepth (Just 8) ui-        VtyEvent (EvKey (KChar '9') []) -> continue $ regenerateScreens j d $ setDepth (Just 9) ui-        VtyEvent (EvKey (KChar '-') []) -> continue $ regenerateScreens j d $ decDepth ui-        VtyEvent (EvKey (KChar '_') []) -> continue $ regenerateScreens j d $ decDepth ui-        VtyEvent (EvKey (KChar c)   []) | c `elem` ['+','='] -> continue $ regenerateScreens j d $ incDepth ui-        VtyEvent (EvKey (KChar 'T') []) -> continue $ regenerateScreens j d $ setReportPeriod (DayPeriod d) ui+        Help ->+          case ev of+            -- VtyEvent (EvKey (KChar 'q') []) -> halt+            VtyEvent (EvKey (KChar 'l') [MCtrl]) -> redraw+            VtyEvent (EvKey (KChar 'z') [MCtrl]) -> suspend ui+            _ -> helpHandle ev -        -- display mode/query toggles-        VtyEvent (EvKey (KChar 'H') []) -> asCenterAndContinue $ regenerateScreens j d $ toggleHistorical ui-        VtyEvent (EvKey (KChar 't') []) -> asCenterAndContinue $ regenerateScreens j d $ toggleTree ui-        VtyEvent (EvKey (KChar c) []) | c `elem` ['z','Z'] -> asCenterAndContinue $ regenerateScreens j d $ toggleEmpty ui-        VtyEvent (EvKey (KChar 'R') []) -> asCenterAndContinue $ regenerateScreens j d $ toggleReal ui-        VtyEvent (EvKey (KChar 'U') []) -> asCenterAndContinue $ regenerateScreens j d $ toggleUnmarked ui-        VtyEvent (EvKey (KChar 'P') []) -> asCenterAndContinue $ regenerateScreens j d $ togglePending ui-        VtyEvent (EvKey (KChar 'C') []) -> asCenterAndContinue $ regenerateScreens j d $ toggleCleared ui-        VtyEvent (EvKey (KChar 'F') []) -> continue $ regenerateScreens j d $ toggleForecast d ui+        Normal ->+          case ev of+            VtyEvent (EvKey (KChar 'q') []) -> halt+            -- EvKey (KChar 'l') [MCtrl] -> do+            VtyEvent (EvKey KEsc        []) -> put' $ resetScreens d ui+            VtyEvent (EvKey (KChar c)   []) | c == '?' -> put' $ 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 ->+              put' $ regenerateScreens j d $ setReportPeriod (DayPeriod d) ui+              where+                p = reportPeriod ui+            e | e `elem` [VtyEvent (EvKey (KChar 'g') []), AppEvent FileChange] ->+              liftIO (uiReloadJournal copts d ui) >>= put'+            VtyEvent (EvKey (KChar 'I') []) -> put' $ uiCheckBalanceAssertions d (toggleIgnoreBalanceAssertions ui)+            VtyEvent (EvKey (KChar 'a') []) -> suspendAndResume $ clearScreen >> setCursorPosition 0 0 >> add copts j >> uiReloadJournalIfChanged copts d j ui+            VtyEvent (EvKey (KChar 'A') []) -> suspendAndResume $ void (runIadd (journalFilePath j)) >> uiReloadJournalIfChanged copts d j ui+            VtyEvent (EvKey (KChar 'E') []) -> suspendAndResume $ void (runEditor endPosition (journalFilePath j)) >> uiReloadJournalIfChanged copts d j ui+            VtyEvent (EvKey (KChar 'B') []) -> put' $ regenerateScreens j d $ toggleConversionOp ui+            VtyEvent (EvKey (KChar 'V') []) -> put' $ regenerateScreens j d $ toggleValue ui+            VtyEvent (EvKey (KChar '0') []) -> put' $ regenerateScreens j d $ setDepth (Just 0) ui+            VtyEvent (EvKey (KChar '1') []) -> put' $ regenerateScreens j d $ setDepth (Just 1) ui+            VtyEvent (EvKey (KChar '2') []) -> put' $ regenerateScreens j d $ setDepth (Just 2) ui+            VtyEvent (EvKey (KChar '3') []) -> put' $ regenerateScreens j d $ setDepth (Just 3) ui+            VtyEvent (EvKey (KChar '4') []) -> put' $ regenerateScreens j d $ setDepth (Just 4) ui+            VtyEvent (EvKey (KChar '5') []) -> put' $ regenerateScreens j d $ setDepth (Just 5) ui+            VtyEvent (EvKey (KChar '6') []) -> put' $ regenerateScreens j d $ setDepth (Just 6) ui+            VtyEvent (EvKey (KChar '7') []) -> put' $ regenerateScreens j d $ setDepth (Just 7) ui+            VtyEvent (EvKey (KChar '8') []) -> put' $ regenerateScreens j d $ setDepth (Just 8) ui+            VtyEvent (EvKey (KChar '9') []) -> put' $ regenerateScreens j d $ setDepth (Just 9) ui+            VtyEvent (EvKey (KChar '-') []) -> put' $ regenerateScreens j d $ decDepth ui+            VtyEvent (EvKey (KChar '_') []) -> put' $ regenerateScreens j d $ decDepth ui+            VtyEvent (EvKey (KChar c)   []) | c `elem` ['+','='] -> put' $ regenerateScreens j d $ incDepth ui+            VtyEvent (EvKey (KChar 'T') []) -> put' $ regenerateScreens j d $ setReportPeriod (DayPeriod 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 (KRight)    [MShift]) -> continue $ regenerateScreens j d $ nextReportPeriod journalspan ui-        VtyEvent (EvKey (KLeft)     [MShift]) -> continue $ regenerateScreens j d $ previousReportPeriod journalspan ui-        VtyEvent (EvKey (KChar '/') []) -> continue $ regenerateScreens j d $ showMinibuffer "filter" Nothing ui-        VtyEvent (EvKey k           []) | k `elem` [KBS, KDel] -> (continue $ regenerateScreens j d $ resetFilter ui)-        VtyEvent e | e `elem` moveLeftEvents -> continue $ popScreen ui-        VtyEvent (EvKey (KChar 'l') [MCtrl]) -> scrollSelectionToMiddle _asList >> redraw ui-        VtyEvent (EvKey (KChar 'z') [MCtrl]) -> suspend ui+            -- display mode/query toggles+            VtyEvent (EvKey (KChar 'H') []) -> modify' (regenerateScreens j d . toggleHistorical) >> asCenterAndContinue+            VtyEvent (EvKey (KChar 't') []) -> modify' (regenerateScreens j d . toggleTree) >> asCenterAndContinue+            VtyEvent (EvKey (KChar c) []) | c `elem` ['z','Z'] -> modify' (regenerateScreens j d . toggleEmpty) >> asCenterAndContinue+            VtyEvent (EvKey (KChar 'R') []) -> modify' (regenerateScreens j d . toggleReal) >> asCenterAndContinue+            VtyEvent (EvKey (KChar 'U') []) -> modify' (regenerateScreens j d . toggleUnmarked) >> asCenterAndContinue+            VtyEvent (EvKey (KChar 'P') []) -> modify' (regenerateScreens j d . togglePending) >> asCenterAndContinue+            VtyEvent (EvKey (KChar 'C') []) -> modify' (regenerateScreens j d . toggleCleared) >> asCenterAndContinue+            VtyEvent (EvKey (KChar 'F') []) -> modify' (regenerateScreens j d . toggleForecast d) -        -- enter register screen for selected account (if there is one),-        -- centering its selected transaction if possible-        VtyEvent e | e `elem` moveRightEvents-                   , not $ isBlankElement $ listSelectedElement _asList -> asEnterRegister d selacct ui+            VtyEvent (EvKey (KDown)     [MShift]) -> put' $ regenerateScreens j d $ shrinkReportPeriod d ui+            VtyEvent (EvKey (KUp)       [MShift]) -> put' $ regenerateScreens j d $ growReportPeriod d ui+            VtyEvent (EvKey (KRight)    [MShift]) -> put' $ regenerateScreens j d $ nextReportPeriod journalspan ui+            VtyEvent (EvKey (KLeft)     [MShift]) -> put' $ regenerateScreens j d $ previousReportPeriod journalspan ui+            VtyEvent (EvKey (KChar '/') []) -> put' $ regenerateScreens j d $ showMinibuffer "filter" Nothing ui+            VtyEvent (EvKey k           []) | k `elem` [KBS, KDel] -> (put' $ regenerateScreens j d $ resetFilter ui)+            VtyEvent e | e `elem` moveLeftEvents -> put' $ popScreen ui+            VtyEvent (EvKey (KChar 'l') [MCtrl]) -> scrollSelectionToMiddle _asList >> redraw+            VtyEvent (EvKey (KChar 'z') [MCtrl]) -> suspend ui -        -- MouseDown is sometimes duplicated, https://github.com/jtdaugherty/brick/issues/347-        -- just use it to move the selection-        MouseDown _n BLeft _mods Location{loc=(_x,y)} | not $ (=="") clickedacct -> do-          continue ui{aScreen=scr{_asList=listMoveTo y _asList}}-          where clickedacct = maybe "" asItemAccountName $ listElements _asList !? y-        -- and on MouseUp, enter the subscreen-        MouseUp _n (Just BLeft) Location{loc=(_x,y)} | not $ (=="") clickedacct -> do-          asEnterRegister d clickedacct ui-          where clickedacct = maybe "" asItemAccountName $ listElements _asList !? y+            -- enter register screen for selected account (if there is one),+            -- centering its selected transaction if possible+            VtyEvent e | e `elem` moveRightEvents+                      , not $ isBlankElement $ listSelectedElement _asList -> asEnterRegister d selacct ui -        -- when selection is at the last item, DOWN scrolls instead of moving, until maximally scrolled-        VtyEvent e | e `elem` moveDownEvents, isBlankElement mnextelement -> do-          vScrollBy (viewportScroll $ _asList^.listNameL) 1 >> continue ui-          where mnextelement = listSelectedElement $ listMoveDown _asList+            -- MouseDown is sometimes duplicated, https://github.com/jtdaugherty/brick/issues/347+            -- just use it to move the selection+            MouseDown _n BLeft _mods Location{loc=(_x,y)} | not $ (=="") clickedacct -> do+              put' ui{aScreen=scr}  -- XXX does this do anything ?+              where clickedacct = maybe "" asItemAccountName $ listElements _asList !? y+            -- and on MouseUp, enter the subscreen+            MouseUp _n (Just BLeft) Location{loc=(_x,y)} | not $ (=="") clickedacct -> do+              asEnterRegister d clickedacct ui+              where clickedacct = maybe "" asItemAccountName $ listElements _asList !? y -        -- mouse scroll wheel scrolls the viewport up or down to its maximum extent,-        -- pushing the selection when necessary.-        MouseDown name btn _mods _loc | btn `elem` [BScrollUp, BScrollDown] -> do-          let scrollamt = if btn==BScrollUp then -1 else 1-          list' <- listScrollPushingSelection name _asList (asListSize _asList) scrollamt-          continue ui{aScreen=scr{_asList=list'}}+            -- when selection is at the last item, DOWN scrolls instead of moving, until maximally scrolled+            VtyEvent e | e `elem` moveDownEvents, isBlankElement mnextelement -> do+              vScrollBy (viewportScroll $ _asList^.listNameL) 1+              where mnextelement = listSelectedElement $ listMoveDown _asList -        -- if page down or end leads to a blank padding item, stop at last non-blank-        VtyEvent e@(EvKey k           []) | k `elem` [KPageDown, KEnd] -> do-          list <- handleListEvent e _asList-          if isBlankElement $ listSelectedElement list-          then do-            let list' = listMoveTo lastnonblankidx list-            scrollSelectionToMiddle list'-            continue ui{aScreen=scr{_asList=list'}}-          else-            continue ui{aScreen=scr{_asList=list}}+            -- mouse scroll wheel scrolls the viewport up or down to its maximum extent,+            -- pushing the selection when necessary.+            MouseDown name btn _mods _loc | btn `elem` [BScrollUp, BScrollDown] -> do+              let scrollamt = if btn==BScrollUp then -1 else 1+              list' <- nestEventM' _asList $ listScrollPushingSelection name (asListSize _asList) scrollamt+              put' ui{aScreen=scr{_asList=list'}} -        -- fall through to the list's event handler (handles up/down)-        VtyEvent ev -> do-          newitems <- handleListEvent (normaliseMovementKeys ev) _asList-          continue $ ui{aScreen=scr & asList .~ newitems-                                    & asSelectedAccount .~ selacct-                                    }+            -- if page down or end leads to a blank padding item, stop at last non-blank+            VtyEvent e@(EvKey k           []) | k `elem` [KPageDown, KEnd] -> do+              l <- nestEventM' _asList $ handleListEvent e+              if isBlankElement $ listSelectedElement l+              then do+                let l' = listMoveTo lastnonblankidx l+                scrollSelectionToMiddle l'+                put' ui{aScreen=scr{_asList=l'}}+              else+                put' ui{aScreen=scr{_asList=l}} -        MouseDown{} -> continue ui-        MouseUp{}   -> continue ui-        AppEvent _  -> continue ui+            -- fall through to the list's event handler (handles up/down)+            VtyEvent e -> do+              list' <- nestEventM' _asList $ handleListEvent (normaliseMovementKeys e)+              put' ui{aScreen=scr & asList .~ list' & asSelectedAccount .~ selacct } -  where-    journalspan = journalDateSpan False j+            MouseDown{} -> return ()+            MouseUp{}   -> return ()+            AppEvent _  -> return () -asHandle _ _ = error "event handler called with wrong screen type, should not happen"  -- PARTIAL:+    _ -> dlogUiTraceM "asHandle 2" >> errorWrongScreenType "event handler" +asEnterRegister :: Day -> AccountName -> UIState -> EventM Name UIState () asEnterRegister d selacct ui = do-  rsCenterAndContinue $-  -- flip rsHandle (VtyEvent (EvKey (KChar 'l') [MCtrl])) $-    screenEnter d regscr ui+  dlogUiTraceM "asEnterRegister"+  let+    regscr = rsSetAccount selacct isdepthclipped registerScreen       where-        regscr = rsSetAccount selacct isdepthclipped registerScreen         isdepthclipped = case getDepth ui of-                            Just d  -> accountNameLevel selacct >= d-                            Nothing -> False+                          Just de -> accountNameLevel selacct >= de+                          Nothing -> False+  rsCenterSelection (screenEnter d regscr ui) >>= put'  asSetSelectedAccount a s@AccountsScreen{} = s & asSelectedAccount .~ a asSetSelectedAccount _ s = s  isBlankElement mel = ((asItemAccountName . snd) <$> mel) == Just "" -asCenterAndContinue ui = do-  scrollSelectionToMiddle $ _asList $ aScreen ui-  continue ui+asCenterAndContinue :: EventM Name UIState ()+asCenterAndContinue = do+  ui <- get'+  scrollSelectionToMiddle (_asList $ aScreen ui)  asListSize = V.length . V.takeWhile ((/="").asItemAccountName) . listElements+
Hledger/UI/ErrorScreen.hs view
@@ -24,7 +24,7 @@ import Text.Megaparsec import Text.Megaparsec.Char -import Hledger.Cli hiding (progname,prognameandversion)+import Hledger.Cli hiding (mode, progname,prognameandversion) import Hledger.UI.UIOptions import Hledger.UI.UITypes import Hledger.UI.UIState@@ -54,10 +54,10 @@     _          -> [maincontent]   where     maincontent = Widget Greedy Greedy $ do-      render $ defaultLayout toplabel bottomlabel $ withAttr "error" $ str $ esError+      render $ defaultLayout toplabel bottomlabel $ withAttr (attrName "error") $ str $ esError       where         toplabel =-              withAttr ("border" <> "bold") (str "Oops. Please fix this problem then press g to reload")+              withAttr (attrName "border" <> attrName "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 = quickhelp@@ -75,44 +75,46 @@  esDraw _ = error "draw function called with wrong screen type, should not happen"  -- PARTIAL: -esHandle :: UIState -> BrickEvent Name AppEvent -> EventM Name (Next UIState)-esHandle ui@UIState{aScreen=ErrorScreen{..}-                   ,aopts=UIOpts{uoCliOpts=copts}-                   ,ajournal=j-                   ,aMode=mode-                   }-         ev =-  case mode of-    Help ->-      case ev of-        VtyEvent (EvKey (KChar 'q') []) -> halt ui-        VtyEvent (EvKey (KChar 'l') [MCtrl]) -> redraw ui-        VtyEvent (EvKey (KChar 'z') [MCtrl]) -> suspend ui-        _                    -> helpHandle ui ev+esHandle :: BrickEvent Name AppEvent -> EventM Name UIState ()+esHandle ev = do+  ui0 <- get'+  case ui0 of+    ui@UIState{aScreen=ErrorScreen{..}+              ,aopts=UIOpts{uoCliOpts=copts}+              ,ajournal=j+              ,aMode=mode+              } ->+      case mode of+        Help ->+          case ev of+            VtyEvent (EvKey (KChar 'q') []) -> halt+            VtyEvent (EvKey (KChar 'l') [MCtrl]) -> redraw+            VtyEvent (EvKey (KChar 'z') [MCtrl]) -> suspend ui+            _                    -> helpHandle ev -    _ -> do-      let d = copts^.rsDay-      case ev of-        VtyEvent (EvKey (KChar 'q') []) -> halt ui-        VtyEvent (EvKey KEsc        []) -> continue $ uiCheckBalanceAssertions d $ resetScreens d ui-        VtyEvent (EvKey (KChar c)   []) | c `elem` ['h','?'] -> continue $ setMode Help ui-        VtyEvent (EvKey (KChar 'E') []) -> suspendAndResume $ void (runEditor pos f) >> uiReloadJournalIfChanged copts d j (popScreen ui)-          where-            (pos,f) = case parsewithString hledgerparseerrorpositionp esError of-                        Right (f,l,c) -> (Just (l, Just c),f)-                        Left  _       -> (endPosition, journalFilePath j)-        e | e `elem` [VtyEvent (EvKey (KChar 'g') []), AppEvent FileChange] ->-          liftIO (uiReloadJournal copts d (popScreen ui)) >>= continue . uiCheckBalanceAssertions d---           (ej, _) <- liftIO $ journalReloadIfChanged copts d j---           case ej of---             Left err -> continue ui{aScreen=s{esError=err}} -- show latest parse error---             Right j' -> continue $ regenerateScreens j' d $ popScreen ui  -- return to previous screen, and reload it-        VtyEvent (EvKey (KChar 'I') []) -> continue $ uiCheckBalanceAssertions d (popScreen $ toggleIgnoreBalanceAssertions ui)-        VtyEvent (EvKey (KChar 'l') [MCtrl]) -> redraw ui-        VtyEvent (EvKey (KChar 'z') [MCtrl]) -> suspend ui-        _ -> continue ui+        _ -> do+          let d = copts^.rsDay+          case ev of+            VtyEvent (EvKey (KChar 'q') []) -> halt+            VtyEvent (EvKey KEsc        []) -> put' $ uiCheckBalanceAssertions d $ resetScreens d ui+            VtyEvent (EvKey (KChar c)   []) | c `elem` ['h','?'] -> put' $ setMode Help ui+            VtyEvent (EvKey (KChar 'E') []) -> suspendAndResume $ void (runEditor pos f) >> uiReloadJournalIfChanged copts d j (popScreen ui)+              where+                (pos,f) = case parsewithString hledgerparseerrorpositionp esError of+                            Right (f',l,c) -> (Just (l, Just c),f')+                            Left  _       -> (endPosition, journalFilePath j)+            e | e `elem` [VtyEvent (EvKey (KChar 'g') []), AppEvent FileChange] ->+              liftIO (uiReloadJournal copts d (popScreen ui)) >>= put' . uiCheckBalanceAssertions d+              -- (ej, _) <- liftIO $ journalReloadIfChanged copts d j+              -- case ej of+              --   Left err -> continue ui{aScreen=s{esError=err}} -- show latest parse error+              --   Right j' -> continue $ regenerateScreens j' d $ popScreen ui  -- return to previous screen, and reload it+            VtyEvent (EvKey (KChar 'I') []) -> put' $ uiCheckBalanceAssertions d (popScreen $ toggleIgnoreBalanceAssertions ui)+            VtyEvent (EvKey (KChar 'l') [MCtrl]) -> redraw+            VtyEvent (EvKey (KChar 'z') [MCtrl]) -> suspend ui+            _ -> return () -esHandle _ _ = error "event handler called with wrong screen type, should not happen"  -- PARTIAL:+    _ -> errorWrongScreenType "event handler"  -- | Parse the file name, line and column number from a hledger parse error message, if possible. -- Temporary, we should keep the original parse error location. XXX
Hledger/UI/Main.hs view
@@ -1,3 +1,5 @@+-- TODO: brick 1 support+-- https://hackage.haskell.org/package/brick-1.0/changelog {-| hledger-ui - a hledger add-on providing a curses-style interface. Copyright (c) 2007-2015 Simon Michael <simon@joyful.com>@@ -33,6 +35,7 @@ import Hledger.UI.Theme import Hledger.UI.AccountsScreen import Hledger.UI.RegisterScreen+import Hledger.UI.UIUtils (dlogUiTrace)  ---------------------------------------------------------------------- @@ -60,7 +63,8 @@     _                                         -> withJournalDo copts' (runBrickUi opts)  runBrickUi :: UIOpts -> Journal -> IO ()-runBrickUi uopts@UIOpts{uoCliOpts=copts@CliOpts{inputopts_=_iopts,reportspec_=rspec@ReportSpec{_rsReportOpts=ropts}}} j = do+runBrickUi uopts@UIOpts{uoCliOpts=copts@CliOpts{inputopts_=_iopts,reportspec_=rspec@ReportSpec{_rsReportOpts=ropts}}} j =+  dlogUiTrace "========= runBrickUi" $ do   let     today = copts^.rsDay @@ -159,11 +163,11 @@      brickapp :: App UIState AppEvent Name     brickapp = App {-        appStartEvent   = return+        appStartEvent   = return ()       , appAttrMap      = const $ fromMaybe defaultTheme $ getTheme =<< uoTheme uopts'       , appChooseCursor = showFirstCursor-      , appHandleEvent  = \ui ev -> sHandle (aScreen ui) ui ev-      , appDraw         = \ui    -> sDraw   (aScreen ui) ui+      , appHandleEvent  = \ev -> do ui' <- get; sHandle (aScreen ui') ev+      , appDraw         = \ui' -> sDraw (aScreen ui') ui'       }    -- print (length (show ui)) >> exitSuccess  -- show any debug output to this point & quit
Hledger/UI/RegisterScreen.hs view
@@ -4,14 +4,13 @@ {-# LANGUAGE FlexibleContexts  #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards   #-}-{-# LANGUAGE NamedFieldPuns #-} {-# OPTIONS_GHC -Wno-incomplete-record-updates #-}  module Hledger.UI.RegisterScreen  (registerScreen  ,rsHandle  ,rsSetAccount- ,rsCenterAndContinue+ ,rsCenterSelection  ) where @@ -32,7 +31,7 @@   import Hledger-import Hledger.Cli hiding (progname,prognameandversion)+import Hledger.Cli hiding (mode, progname,prognameandversion) import Hledger.UI.UIOptions -- import Hledger.UI.Theme import Hledger.UI.UITypes@@ -60,6 +59,7 @@  rsInit :: Day -> Bool -> UIState -> UIState rsInit d reset ui@UIState{aopts=_uopts@UIOpts{uoCliOpts=copts@CliOpts{reportspec_=rspec@ReportSpec{_rsReportOpts=ropts}}}, ajournal=j, aScreen=s@RegisterScreen{..}} =+  dlogUiTrace "rsInit 1" $   ui{aScreen=s{rsList=newitems'}}   where     -- gather arguments and queries@@ -102,7 +102,7 @@             where showamt = showMixedAmountB oneLine{displayMaxWidth=Just 32}     -- blank items are added to allow more control of scroll position; we won't allow movement over these.     -- XXX Ugly. Changing to 0 helps when debugging.-    blankitems = replicate 100  -- "100 ought to be enough for anyone"+    blankitems = replicate uiNumBlankItems           RegisterScreenItem{rsItemDate          = ""                             ,rsItemStatus        = Unmarked                             ,rsItemDescription   = ""@@ -137,13 +137,13 @@                 ts = map rsItemTransaction displayitems         endidx = max 0 $ length displayitems - 1 -rsInit _ _ _ = error "init function called with wrong screen type, should not happen"  -- PARTIAL:+rsInit _ _ _ = dlogUiTrace "rsInit 2" $ errorWrongScreenType "init function"  -- PARTIAL:  rsDraw :: UIState -> [Widget Name] rsDraw UIState{aopts=_uopts@UIOpts{uoCliOpts=copts@CliOpts{reportspec_=rspec}}               ,aScreen=RegisterScreen{..}               ,aMode=mode-              } =+              } = dlogUiTrace "rsDraw 1" $   case mode of     Help       -> [helpDialog copts, maincontent]     -- Minibuffer e -> [minibuffer e, maincontent]@@ -199,7 +199,7 @@         -- inclusive = tree_ ropts || rsForceInclusive          toplabel =-              withAttr ("border" <> "bold") (str $ T.unpack $ replaceHiddenAccountsNameWith "All" rsAccount)+              withAttr (attrName "border" <> attrName "bold") (str $ T.unpack $ replaceHiddenAccountsNameWith "All" rsAccount) --           <+> withAttr ("border" <> "query") (str $ if inclusive then "" else " exclusive")           <+> togglefilters           <+> str " transactions"@@ -212,7 +212,7 @@           <+> str "/"           <+> total           <+> str ")"-          <+> (if ignore_assertions_ . balancingopts_ $ inputopts_ copts then withAttr ("border" <> "query") (str " ignoring balance assertions") else str "")+          <+> (if ignore_assertions_ . balancingopts_ $ inputopts_ copts then withAttr (attrName "border" <> attrName "query") (str " ignoring balance assertions") else str "")           where             togglefilters =               case concat [@@ -221,7 +221,7 @@                   ,if empty_ ropts then [] else ["nonzero"]                   ] of                 [] -> str ""-                fs -> withAttr ("border" <> "query") (str $ " " ++ intercalate ", " fs)+                fs -> withAttr (attrName "border" <> attrName "query") (str $ " " ++ intercalate ", " fs)             cur = str $ case rsList ^. listSelectedL of                          Nothing -> "-"                          Just i -> show (i + 1)@@ -251,7 +251,7 @@ --               ,("q", "quit")               ] -rsDraw _ = error "draw function called with wrong screen type, should not happen"  -- PARTIAL:+rsDraw _ = dlogUiTrace "rsDraw 2" $ errorWrongScreenType "draw function"  -- PARTIAL:  rsDrawItem :: (Int,Int,Int,Int,Int) -> Bool -> RegisterScreenItem -> Widget Name rsDrawItem (datewidth,descwidth,acctswidth,changewidth,balwidth) selected RegisterScreenItem{..} =@@ -271,164 +271,163 @@   where     changeAmt  = wbToText rsItemChangeAmount     balanceAmt = wbToText rsItemBalanceAmount-    changeattr | T.any (=='-') changeAmt  = sel $ "list" <> "amount" <> "decrease"-               | otherwise                = sel $ "list" <> "amount" <> "increase"-    balattr    | T.any (=='-') balanceAmt = sel $ "list" <> "balance" <> "negative"-               | otherwise                = sel $ "list" <> "balance" <> "positive"-    sel | selected  = (<> "selected")+    changeattr | T.any (=='-') changeAmt  = sel $ attrName "list" <> attrName "amount" <> attrName "decrease"+               | otherwise                = sel $ attrName "list" <> attrName "amount" <> attrName "increase"+    balattr    | T.any (=='-') balanceAmt = sel $ attrName "list" <> attrName "balance" <> attrName "negative"+               | otherwise                = sel $ attrName "list" <> attrName "balance" <> attrName "positive"+    sel | selected  = (<> attrName "selected")         | otherwise = id -rsHandle :: UIState -> BrickEvent Name AppEvent -> EventM Name (Next UIState)-rsHandle ui@UIState{-   aScreen=s@RegisterScreen{..}-  ,aopts=UIOpts{uoCliOpts=copts}-  ,ajournal=j-  ,aMode=mode-  } ev = do-  let-    d = copts^.rsDay-    journalspan = journalDateSpan False j-    nonblanks = V.takeWhile (not . T.null . rsItemDate) $ rsList^.listElementsL-    lastnonblankidx = max 0 (length nonblanks - 1)+rsHandle :: BrickEvent Name AppEvent -> EventM Name UIState ()+rsHandle ev = do+  ui0 <- get'+  dlogUiTraceM "rsHandle 1"+  case ui0 of+    ui@UIState{+      aScreen=scr@RegisterScreen{..}+      ,aopts=UIOpts{uoCliOpts=copts}+      ,ajournal=j+      ,aMode=mode+      } -> do+      let+        d = copts^.rsDay+        journalspan = journalDateSpan False j+        nonblanks = V.takeWhile (not . T.null . rsItemDate) $ rsList^.listElementsL+        lastnonblankidx = max 0 (length nonblanks - 1) -  case mode of-    Minibuffer _ ed ->-      case ev of-        VtyEvent (EvKey KEsc   []) -> continue $ closeMinibuffer ui-        VtyEvent (EvKey KEnter []) -> continue $ regenerateScreens j d $-            case setFilter s $ closeMinibuffer ui of-              Left bad -> showMinibuffer "Cannot compile regular expression" (Just bad) ui-              Right ui' -> ui'-          where s = chomp . unlines . map strip $ getEditContents ed-        -- VtyEvent (EvKey (KChar '/') []) -> continue $ regenerateScreens j d $ showMinibuffer ui-        VtyEvent (EvKey (KChar 'l') [MCtrl]) -> redraw ui-        VtyEvent (EvKey (KChar 'z') [MCtrl]) -> suspend ui-        VtyEvent ev -> do-          ed' <- handleEditorEvent -#if MIN_VERSION_brick(0,72,0)-            (VtyEvent ev)-#else-            ev-#endif-            ed-          continue $ ui{aMode=Minibuffer "filter" ed'}-        AppEvent _        -> continue ui-        MouseDown{}       -> continue ui-        MouseUp{}         -> continue ui+      case mode of+        Minibuffer _ ed ->+          case ev of+            VtyEvent (EvKey KEsc   []) -> modify' closeMinibuffer+            VtyEvent (EvKey KEnter []) -> put' $ regenerateScreens j d $+                case setFilter s $ closeMinibuffer ui of+                  Left bad -> showMinibuffer "Cannot compile regular expression" (Just bad) ui+                  Right ui' -> ui'+              where s = chomp . unlines . map strip $ getEditContents ed+            -- VtyEvent (EvKey (KChar '/') []) -> put' $ regenerateScreens j d $ showMinibuffer ui+            VtyEvent (EvKey (KChar 'l') [MCtrl]) -> redraw+            VtyEvent (EvKey (KChar 'z') [MCtrl]) -> suspend ui+            VtyEvent e -> do+              ed' <- nestEventM' ed $ handleEditorEvent (VtyEvent e)+              put' ui{aMode=Minibuffer "filter" ed'}+            AppEvent _  -> return ()+            MouseDown{} -> return ()+            MouseUp{}   -> return () -    Help ->-      case ev of-        -- VtyEvent (EvKey (KChar 'q') []) -> halt ui-        VtyEvent (EvKey (KChar 'l') [MCtrl]) -> redraw ui-        VtyEvent (EvKey (KChar 'z') [MCtrl]) -> suspend ui-        _                    -> helpHandle ui ev+        Help ->+          case ev of+            -- VtyEvent (EvKey (KChar 'q') []) -> halt+            VtyEvent (EvKey (KChar 'l') [MCtrl]) -> redraw+            VtyEvent (EvKey (KChar 'z') [MCtrl]) -> suspend ui+            _ -> helpHandle ev -    Normal ->-      case ev of-        VtyEvent (EvKey (KChar 'q') []) -> halt ui-        VtyEvent (EvKey KEsc        []) -> continue $ resetScreens d 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-            p = reportPeriod ui-        e | e `elem` [VtyEvent (EvKey (KChar 'g') []), AppEvent FileChange] ->-          liftIO (uiReloadJournal copts d ui) >>= continue-        VtyEvent (EvKey (KChar 'I') []) -> continue $ uiCheckBalanceAssertions d (toggleIgnoreBalanceAssertions ui)-        VtyEvent (EvKey (KChar 'a') []) -> suspendAndResume $ clearScreen >> setCursorPosition 0 0 >> add copts j >> uiReloadJournalIfChanged copts d j ui-        VtyEvent (EvKey (KChar 'A') []) -> suspendAndResume $ void (runIadd (journalFilePath j)) >> uiReloadJournalIfChanged copts d j ui-        VtyEvent (EvKey (KChar 'T') []) -> continue $ regenerateScreens j d $ setReportPeriod (DayPeriod d) ui-        VtyEvent (EvKey (KChar 'E') []) -> suspendAndResume $ void (runEditor pos f) >> uiReloadJournalIfChanged copts d j ui-          where-            (pos,f) = case listSelectedElement rsList of-                        Nothing -> (endPosition, journalFilePath j)-                        Just (_, RegisterScreenItem{-                          rsItemTransaction=Transaction{tsourcepos=(SourcePos f l c,_)}}) -> (Just (unPos l, Just $ unPos c),f)+        Normal ->+          case ev of+            VtyEvent (EvKey (KChar 'q') []) -> halt+            VtyEvent (EvKey KEsc        []) -> put' $ resetScreens d ui+            VtyEvent (EvKey (KChar c)   []) | c == '?' -> put' $ setMode Help ui+            AppEvent (DateChange old _) | isStandardPeriod p && p `periodContainsDate` old ->+              put' $ regenerateScreens j d $ setReportPeriod (DayPeriod d) ui+              where+                p = reportPeriod ui+            e | e `elem` [VtyEvent (EvKey (KChar 'g') []), AppEvent FileChange] ->+              liftIO (uiReloadJournal copts d ui) >>= put'+            VtyEvent (EvKey (KChar 'I') []) -> put' $ uiCheckBalanceAssertions d (toggleIgnoreBalanceAssertions ui)+            VtyEvent (EvKey (KChar 'a') []) -> suspendAndResume $ clearScreen >> setCursorPosition 0 0 >> add copts j >> uiReloadJournalIfChanged copts d j ui+            VtyEvent (EvKey (KChar 'A') []) -> suspendAndResume $ void (runIadd (journalFilePath j)) >> uiReloadJournalIfChanged copts d j ui+            VtyEvent (EvKey (KChar 'T') []) -> put' $ regenerateScreens j d $ setReportPeriod (DayPeriod d) ui+            VtyEvent (EvKey (KChar 'E') []) -> suspendAndResume $ void (runEditor pos f) >> uiReloadJournalIfChanged copts d j ui+              where+                (pos,f) = case listSelectedElement rsList of+                            Nothing -> (endPosition, journalFilePath j)+                            Just (_, RegisterScreenItem{+                              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 $ toggleConversionOp ui-        VtyEvent (EvKey (KChar 'V') []) -> rsCenterAndContinue $ regenerateScreens j d $ toggleValue ui-        VtyEvent (EvKey (KChar 'H') []) -> rsCenterAndContinue $ regenerateScreens j d $ toggleHistorical ui-        VtyEvent (EvKey (KChar 't') []) -> rsCenterAndContinue $ regenerateScreens j d $ toggleTree ui-        VtyEvent (EvKey (KChar c) []) | c `elem` ['z','Z'] -> rsCenterAndContinue $ regenerateScreens j d $ toggleEmpty ui-        VtyEvent (EvKey (KChar 'R') []) -> rsCenterAndContinue $ regenerateScreens j d $ toggleReal ui-        VtyEvent (EvKey (KChar 'U') []) -> rsCenterAndContinue $ regenerateScreens j d $ toggleUnmarked ui-        VtyEvent (EvKey (KChar 'P') []) -> rsCenterAndContinue $ regenerateScreens j d $ togglePending ui-        VtyEvent (EvKey (KChar 'C') []) -> rsCenterAndContinue $ regenerateScreens j d $ toggleCleared ui-        VtyEvent (EvKey (KChar 'F') []) -> rsCenterAndContinue $ regenerateScreens j d $ toggleForecast d ui+            -- display mode/query toggles+            VtyEvent (EvKey (KChar 'B') []) -> rsCenterSelection (regenerateScreens j d $ toggleConversionOp ui) >>= put'+            VtyEvent (EvKey (KChar 'V') []) -> rsCenterSelection (regenerateScreens j d $ toggleValue ui) >>= put'+            VtyEvent (EvKey (KChar 'H') []) -> rsCenterSelection (regenerateScreens j d $ toggleHistorical ui) >>= put'+            VtyEvent (EvKey (KChar 't') []) -> rsCenterSelection (regenerateScreens j d $ toggleTree ui) >>= put'+            VtyEvent (EvKey (KChar c) []) | c `elem` ['z','Z'] -> rsCenterSelection (regenerateScreens j d $ toggleEmpty ui) >>= put'+            VtyEvent (EvKey (KChar 'R') []) -> rsCenterSelection (regenerateScreens j d $ toggleReal ui) >>= put'+            VtyEvent (EvKey (KChar 'U') []) -> rsCenterSelection (regenerateScreens j d $ toggleUnmarked ui) >>= put'+            VtyEvent (EvKey (KChar 'P') []) -> rsCenterSelection (regenerateScreens j d $ togglePending ui) >>= put'+            VtyEvent (EvKey (KChar 'C') []) -> rsCenterSelection (regenerateScreens j d $ toggleCleared ui) >>= put'+            VtyEvent (EvKey (KChar 'F') []) -> rsCenterSelection (regenerateScreens j d $ toggleForecast d ui) >>= put' -        VtyEvent (EvKey (KChar '/') []) -> continue $ regenerateScreens j d $ showMinibuffer "filter" Nothing 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 (KRight)    [MShift]) -> continue $ regenerateScreens j d $ nextReportPeriod journalspan ui-        VtyEvent (EvKey (KLeft)     [MShift]) -> continue $ regenerateScreens j d $ previousReportPeriod journalspan ui-        VtyEvent (EvKey k           []) | k `elem` [KBS, KDel] -> (continue $ regenerateScreens j d $ resetFilter ui)-        VtyEvent (EvKey (KChar 'l') [MCtrl]) -> scrollSelectionToMiddle rsList >> redraw ui-        VtyEvent (EvKey (KChar 'z') [MCtrl]) -> suspend ui+            VtyEvent (EvKey (KChar '/') []) -> put' $ regenerateScreens j d $ showMinibuffer "filter" Nothing ui+            VtyEvent (EvKey (KDown)     [MShift]) -> put' $ regenerateScreens j d $ shrinkReportPeriod d ui+            VtyEvent (EvKey (KUp)       [MShift]) -> put' $ regenerateScreens j d $ growReportPeriod d ui+            VtyEvent (EvKey (KRight)    [MShift]) -> put' $ regenerateScreens j d $ nextReportPeriod journalspan ui+            VtyEvent (EvKey (KLeft)     [MShift]) -> put' $ regenerateScreens j d $ previousReportPeriod journalspan ui+            VtyEvent (EvKey k           []) | k `elem` [KBS, KDel] -> (put' $ regenerateScreens j d $ resetFilter ui)+            VtyEvent (EvKey (KChar 'l') [MCtrl]) -> scrollSelectionToMiddle rsList >> redraw+            VtyEvent (EvKey (KChar 'z') [MCtrl]) -> suspend ui -        -- exit screen on LEFT-        VtyEvent e | e `elem` moveLeftEvents  -> continue $ popScreen ui-        -- or on a click in the app's left margin. This is a VtyEvent since not in a clickable widget.-        VtyEvent (EvMouseUp x _y (Just BLeft)) | x==0 -> continue $ popScreen ui-        -- or on clicking a blank list item.-        MouseUp _ (Just BLeft) Location{loc=(_,y)} | clickeddate == "" -> continue $ popScreen ui-          where clickeddate = maybe "" rsItemDate $ listElements rsList !? y+            -- exit screen on LEFT+            VtyEvent e | e `elem` moveLeftEvents  -> put' $ popScreen ui+            -- or on a click in the app's left margin. This is a VtyEvent since not in a clickable widget.+            VtyEvent (EvMouseUp x _y (Just BLeft)) | x==0 -> put' $ popScreen ui+            -- or on clicking a blank list item.+            MouseUp _ (Just BLeft) Location{loc=(_,y)} | clickeddate == "" -> put' $ popScreen ui+              where clickeddate = maybe "" rsItemDate $ listElements rsList !? y -        -- enter transaction screen on RIGHT-        VtyEvent e | e `elem` moveRightEvents ->-          case listSelectedElement rsList of-            Just _  -> continue $ screenEnter d transactionScreen{tsAccount=rsAccount} ui-            Nothing -> continue ui-        -- or on transaction click-        -- MouseDown is sometimes duplicated, https://github.com/jtdaugherty/brick/issues/347-        -- just use it to move the selection-        MouseDown _n BLeft _mods Location{loc=(_x,y)} | not $ (=="") clickeddate -> do-          continue $ ui{aScreen=s{rsList=listMoveTo y rsList}}-          where clickeddate = maybe "" rsItemDate $ listElements rsList !? y-        -- and on MouseUp, enter the subscreen-        MouseUp _n (Just BLeft) Location{loc=(_x,y)} | not $ (=="") clickeddate -> do-          continue $ screenEnter d transactionScreen{tsAccount=rsAccount} ui-          where clickeddate = maybe "" rsItemDate $ listElements rsList !? y+            -- enter transaction screen on RIGHT+            VtyEvent e | e `elem` moveRightEvents ->+              case listSelectedElement rsList of+                Just _  -> put' $ screenEnter d transactionScreen{tsAccount=rsAccount} ui+                Nothing -> put' ui+            -- or on transaction click+            -- MouseDown is sometimes duplicated, https://github.com/jtdaugherty/brick/issues/347+            -- just use it to move the selection+            MouseDown _n BLeft _mods Location{loc=(_x,y)} | not $ (=="") clickeddate -> do+              put' $ ui{aScreen=scr{rsList=listMoveTo y rsList}}+              where clickeddate = maybe "" rsItemDate $ listElements rsList !? y+            -- and on MouseUp, enter the subscreen+            MouseUp _n (Just BLeft) Location{loc=(_x,y)} | not $ (=="") clickeddate -> do+              put' $ screenEnter d transactionScreen{tsAccount=rsAccount} ui+              where clickeddate = maybe "" rsItemDate $ listElements rsList !? y -        -- when selection is at the last item, DOWN scrolls instead of moving, until maximally scrolled-        VtyEvent e | e `elem` moveDownEvents, isBlankElement mnextelement -> do-          vScrollBy (viewportScroll $ rsList ^. listNameL) 1 >> continue ui-          where mnextelement = listSelectedElement $ listMoveDown rsList+            -- when selection is at the last item, DOWN scrolls instead of moving, until maximally scrolled+            VtyEvent e | e `elem` moveDownEvents, isBlankElement mnextelement -> do+              vScrollBy (viewportScroll $ rsList ^. listNameL) 1+              where mnextelement = listSelectedElement $ listMoveDown rsList -        -- mouse scroll wheel scrolls the viewport up or down to its maximum extent,-        -- pushing the selection when necessary.-        MouseDown name btn _mods _loc | btn `elem` [BScrollUp, BScrollDown] -> do-          let scrollamt = if btn==BScrollUp then -1 else 1-          list' <- listScrollPushingSelection name rsList (rsListSize rsList) scrollamt-          continue ui{aScreen=s{rsList=list'}}+            -- mouse scroll wheel scrolls the viewport up or down to its maximum extent,+            -- pushing the selection when necessary.+            MouseDown name btn _mods _loc | btn `elem` [BScrollUp, BScrollDown] -> do+              let scrollamt = if btn==BScrollUp then -1 else 1+              list' <- nestEventM' rsList $ listScrollPushingSelection name (rsListSize rsList) scrollamt+              put' ui{aScreen=scr{rsList=list'}} -        -- if page down or end leads to a blank padding item, stop at last non-blank-        VtyEvent e@(EvKey k           []) | k `elem` [KPageDown, KEnd] -> do-          list <- handleListEvent e rsList-          if isBlankElement $ listSelectedElement list-          then do-            let list' = listMoveTo lastnonblankidx list-            scrollSelectionToMiddle list'-            continue ui{aScreen=s{rsList=list'}}-          else-            continue ui{aScreen=s{rsList=list}}+            -- if page down or end leads to a blank padding item, stop at last non-blank+            VtyEvent e@(EvKey k           []) | k `elem` [KPageDown, KEnd] -> do+              l <- nestEventM' rsList $ handleListEvent e+              if isBlankElement $ listSelectedElement l+              then do+                let l' = listMoveTo lastnonblankidx l+                scrollSelectionToMiddle l'+                put' ui{aScreen=scr{rsList=l'}}+              else+                put' ui{aScreen=scr{rsList=l}} -        -- fall through to the list's event handler (handles other [pg]up/down events)-        VtyEvent ev -> do-          let ev' = normaliseMovementKeys ev-          newitems <- handleListEvent ev' rsList-          continue ui{aScreen=s{rsList=newitems}}+            -- fall through to the list's event handler (handles other [pg]up/down events)+            VtyEvent e -> do+              let e' = normaliseMovementKeys e+              newitems <- nestEventM' rsList $ handleListEvent e'+              put' ui{aScreen=scr{rsList=newitems}} -        MouseDown{}       -> continue ui-        MouseUp{}         -> continue ui-        AppEvent _        -> continue ui+            MouseDown{}       -> return ()+            MouseUp{}         -> return ()+            AppEvent _        -> return () -rsHandle _ _ = error "event handler called with wrong screen type, should not happen"  -- PARTIAL:+    _ -> dlogUiTrace "rsHandle 2" $ errorWrongScreenType "event handler"  isBlankElement mel = ((rsItemDate . snd) <$> mel) == Just "" -rsCenterAndContinue ui = do+rsCenterSelection :: UIState -> EventM Name UIState UIState+rsCenterSelection ui = do   scrollSelectionToMiddle $ rsList $ aScreen ui-  continue ui+  return ui  -- ui is unchanged, but this makes the function more chainable  rsListSize = V.length . V.takeWhile ((/="").rsItemDate) . listElements
Hledger/UI/Theme.hs view
@@ -65,42 +65,42 @@ themesList :: [(String, AttrMap)] themesList = [    ("default", attrMap (black `on` white) [-     ("border"                                        , white `on` black & dim)-    ,("border" <> "bold"                              , currentAttr & bold)-    ,("border" <> "depth"                             , active)-    ,("border" <> "filename"                          , currentAttr)-    ,("border" <> "key"                               , active)-    ,("border" <> "minibuffer"                        , white `on` black & bold)-    ,("border" <> "query"                             , active)-    ,("border" <> "selected"                          , active)-    ,("error"                                         , fg red)-    ,("help"                                          , white `on` black & dim)-    ,("help" <> "heading"                             , fg yellow)-    ,("help" <> "key"                                 , active)-    -- ,("list"                                          , black `on` white)-    -- ,("list" <> "amount"                              , currentAttr)-    ,("list" <> "amount" <> "decrease"                , fg red)-    -- ,("list" <> "amount" <> "increase"                , fg green)-    ,("list" <> "amount" <> "decrease" <> "selected"  , red `on` selectbg & bold)-    -- ,("list" <> "amount" <> "increase" <> "selected"  , green `on` selectbg & bold)-    ,("list" <> "balance"                             , currentAttr & bold)-    ,("list" <> "balance" <> "negative"               , fg red)-    ,("list" <> "balance" <> "positive"               , fg black)-    ,("list" <> "balance" <> "negative" <> "selected" , red `on` selectbg & bold)-    ,("list" <> "balance" <> "positive" <> "selected" , select & bold)-    ,("list" <> "selected"                            , select)-    -- ,("list" <> "accounts"                         , white `on` brightGreen)-    -- ,("list" <> "selected"                         , black `on` brightYellow)+     (attrName "border"                                        , white `on` black & dim)+    ,(attrName "border" <> attrName "bold"                              , currentAttr & bold)+    ,(attrName "border" <> attrName "depth"                             , active)+    ,(attrName "border" <> attrName "filename"                          , currentAttr)+    ,(attrName "border" <> attrName "key"                               , active)+    ,(attrName "border" <> attrName "minibuffer"                        , white `on` black & bold)+    ,(attrName "border" <> attrName "query"                             , active)+    ,(attrName "border" <> attrName "selected"                          , active)+    ,(attrName "error"                                         , fg red)+    ,(attrName "help"                                          , white `on` black & dim)+    ,(attrName "help" <> attrName "heading"                             , fg yellow)+    ,(attrName "help" <> attrName "key"                                 , active)+    -- ,(attrName "list"                                          , black `on` white)+    -- ,(attrName "list" <> attrName "amount"                              , currentAttr)+    ,(attrName "list" <> attrName "amount" <> attrName "decrease"                , fg red)+    -- ,(attrName "list" <> attrName "amount" <> attrName "increase"                , fg green)+    ,(attrName "list" <> attrName "amount" <> attrName "decrease" <> attrName "selected"  , red `on` selectbg & bold)+    -- ,(attrName "list" <> attrName "amount" <> attrName "increase" <> attrName "selected"  , green `on` selectbg & bold)+    ,(attrName "list" <> attrName "balance"                             , currentAttr & bold)+    ,(attrName "list" <> attrName "balance" <> attrName "negative"               , fg red)+    ,(attrName "list" <> attrName "balance" <> attrName "positive"               , fg black)+    ,(attrName "list" <> attrName "balance" <> attrName "negative" <> attrName "selected" , red `on` selectbg & bold)+    ,(attrName "list" <> attrName "balance" <> attrName "positive" <> attrName "selected" , select & bold)+    ,(attrName "list" <> attrName "selected"                            , select)+    -- ,(attrName "list" <> attrName "accounts"                         , white `on` brightGreen)+    -- ,(attrName "list" <> attrName "selected"                         , black `on` brightYellow)   ])    ,("greenterm", attrMap (green `on` black) [-    ("list" <> "selected"                             , black `on` green)+    (attrName "list" <> attrName "selected"                             , black `on` green)   ])    ,("terminal", attrMap defAttr [-    ("border"                                         , white `on` black),-    ("list"                                           , defAttr),-    ("list" <> "selected"                             , defAttr & reverseVideo)+    (attrName "border"                                         , white `on` black),+    (attrName "list"                                           , defAttr),+    (attrName "list" <> attrName "selected"                             , defAttr & reverseVideo)   ])    ]
Hledger/UI/TransactionScreen.hs view
@@ -22,7 +22,7 @@ import Brick.Widgets.List (listElementsL, listMoveTo, listSelectedElement)  import Hledger-import Hledger.Cli hiding (progname,prognameandversion)+import Hledger.Cli hiding (mode, prices, progname,prognameandversion) import Hledger.UI.UIOptions -- import Hledger.UI.Theme import Hledger.UI.UITypes@@ -59,7 +59,7 @@             seltxn = maybe nulltransaction (rsItemTransaction . snd) $ listSelectedElement xs             nonblanks = V.toList . V.takeWhile (not . T.null . rsItemDate) $ xs ^. listElementsL         _                           -> (t, nts)-tsInit _ _ _ = error "init function called with wrong screen type, should not happen"  -- PARTIAL:+tsInit _ _ _ = errorWrongScreenType "init function"  -- PARTIAL:  -- Render a transaction suitably for the transaction screen. showTxn :: ReportOpts -> ReportSpec -> Journal -> Transaction -> T.Text@@ -107,12 +107,12 @@           -- <+> str (" ("++show i++" of "++show (length nts)++" in "++acct++")")           <+> (str $ "#" ++ show (tindex t))           <+> str " ("-          <+> withAttr ("border" <> "bold") (str $ show i)+          <+> withAttr (attrName "border" <> attrName "bold") (str $ show i)           <+> str (" of "++show (length nts))           <+> togglefilters           <+> borderQueryStr (unwords . map (quoteIfNeeded . T.unpack) $ querystring_ ropts)           <+> str (" in "++T.unpack (replaceHiddenAccountsNameWith "All" acct)++")")-          <+> (if ignore_assertions_ . balancingopts_ $ inputopts_ copts then withAttr ("border" <> "query") (str " ignoring balance assertions") else str "")+          <+> (if ignore_assertions_ . balancingopts_ $ inputopts_ copts then withAttr (attrName "border" <> attrName "query") (str " ignoring balance assertions") else str "")           where             togglefilters =               case concat [@@ -121,7 +121,7 @@                   ,if empty_ ropts then [] else ["nonzero"]                   ] of                 [] -> str ""-                fs -> withAttr ("border" <> "query") (str $ " " ++ intercalate ", " fs)+                fs -> withAttr (attrName "border" <> attrName "query") (str $ " " ++ intercalate ", " fs)          bottomlabel = quickhelp                         -- case mode of@@ -139,72 +139,74 @@               ,("q", "quit")               ] -tsDraw _ = error "draw function called with wrong screen type, should not happen"  -- PARTIAL:+tsDraw _ = errorWrongScreenType "draw function"  -- PARTIAL: -tsHandle :: UIState -> BrickEvent Name AppEvent -> EventM Name (Next UIState)-tsHandle ui@UIState{aScreen=TransactionScreen{tsTransaction=(i,t), tsTransactions=nts}-                   ,aopts=UIOpts{uoCliOpts=copts@CliOpts{reportspec_=rspec@ReportSpec{_rsReportOpts=ropts}}}-                   ,ajournal=j-                   ,aMode=mode-                   }-         ev =-  case mode of-    Help ->-      case ev of-        -- VtyEvent (EvKey (KChar 'q') []) -> halt ui-        VtyEvent (EvKey (KChar 'l') [MCtrl]) -> redraw ui-        VtyEvent (EvKey (KChar 'z') [MCtrl]) -> suspend ui-        _                    -> helpHandle ui ev+tsHandle :: BrickEvent Name AppEvent -> EventM Name UIState ()+tsHandle ev = do+  ui0 <- get'+  case ui0 of+    ui@UIState{aScreen=TransactionScreen{tsTransaction=(i,t), tsTransactions=nts}+                    ,aopts=UIOpts{uoCliOpts=copts@CliOpts{reportspec_=rspec@ReportSpec{_rsReportOpts=ropts}}}+                    ,ajournal=j+                    ,aMode=mode+                    } ->+      case mode of+        Help ->+          case ev of+            -- VtyEvent (EvKey (KChar 'q') []) -> halt+            VtyEvent (EvKey (KChar 'l') [MCtrl]) -> redraw+            VtyEvent (EvKey (KChar 'z') [MCtrl]) -> suspend ui+            _ -> helpHandle ev -    _ -> do-      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 == '?' -> 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-                        (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 ()-          ej <- liftIO . runExceptT $ journalReload copts-          case ej of-            Left err -> continue $ screenEnter d errorScreen{esError=err} ui-            Right j' -> continue $ regenerateScreens j' d ui-        VtyEvent (EvKey (KChar 'I') []) -> continue $ uiCheckBalanceAssertions d (toggleIgnoreBalanceAssertions ui)+        _ -> do+          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+            VtyEvent (EvKey KEsc        []) -> put' $ resetScreens d ui+            VtyEvent (EvKey (KChar c)   []) | c == '?' -> put' $ setMode Help ui+            VtyEvent (EvKey (KChar 'E') []) -> suspendAndResume $ void (runEditor pos f) >> uiReloadJournalIfChanged copts d j ui+              where+                (pos,f) = case tsourcepos t of+                            (SourcePos f' l1 c1,_) -> (Just (unPos l1, Just $ unPos c1),f')+            AppEvent (DateChange old _) | isStandardPeriod p && p `periodContainsDate` old ->+              put' $ 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 ()+              ej <- liftIO . runExceptT $ journalReload copts+              case ej of+                Left err -> put' $ screenEnter d errorScreen{esError=err} ui+                Right j' -> put' $ regenerateScreens j' d ui+            VtyEvent (EvKey (KChar 'I') []) -> put' $ uiCheckBalanceAssertions d (toggleIgnoreBalanceAssertions ui) -        -- for toggles that may change the current/prev/next transactions,-        -- we must regenerate the transaction list, like the g handler above ? with regenerateTransactions ? TODO WIP-        -- EvKey (KChar 'E') [] -> continue $ regenerateScreens j d $ stToggleEmpty ui-        -- EvKey (KChar 'C') [] -> continue $ regenerateScreens j d $ stToggleCleared ui-        -- EvKey (KChar 'R') [] -> continue $ regenerateScreens j d $ stToggleReal ui-        VtyEvent (EvKey (KChar 'B') []) -> continue . regenerateScreens j d $ toggleConversionOp ui-        VtyEvent (EvKey (KChar 'V') []) -> continue . regenerateScreens j d $ toggleValue ui+            -- for toggles that may change the current/prev/next transactions,+            -- we must regenerate the transaction list, like the g handler above ? with regenerateTransactions ? TODO WIP+            -- EvKey (KChar 'E') [] -> put' $ regenerateScreens j d $ stToggleEmpty ui+            -- EvKey (KChar 'C') [] -> put' $ regenerateScreens j d $ stToggleCleared ui+            -- EvKey (KChar 'R') [] -> put' $ regenerateScreens j d $ stToggleReal ui+            VtyEvent (EvKey (KChar 'B') []) -> put' . regenerateScreens j d $ toggleConversionOp ui+            VtyEvent (EvKey (KChar 'V') []) -> put' . regenerateScreens j d $ toggleValue ui -        VtyEvent e | e `elem` moveUpEvents   -> continue $ tsSelect iprev tprev ui-        VtyEvent e | e `elem` moveDownEvents -> continue $ tsSelect inext tnext ui+            VtyEvent e | e `elem` moveUpEvents   -> put' $ tsSelect iprev tprev ui+            VtyEvent e | e `elem` moveDownEvents -> put' $ tsSelect inext tnext ui -        -- exit screen on LEFT-        VtyEvent e | e `elem` moveLeftEvents -> continue . popScreen $ tsSelect i t ui  -- Probably not necessary to tsSelect here, but it's safe.-        -- or on a click in the app's left margin.-        VtyEvent (EvMouseUp x _y (Just BLeft)) | x==0 -> continue . popScreen $ tsSelect i t ui-        -- or on clicking the blank area below the transaction.-        MouseUp _ (Just BLeft) Location{loc=(_,y)} | y+1 > numentrylines -> continue . popScreen $ tsSelect i t ui-          where numentrylines = length (T.lines $ showTxn ropts rspec j t) - 1+            -- exit screen on LEFT+            VtyEvent e | e `elem` moveLeftEvents -> put' . popScreen $ tsSelect i t ui  -- Probably not necessary to tsSelect here, but it's safe.+            -- or on a click in the app's left margin.+            VtyEvent (EvMouseUp x _y (Just BLeft)) | x==0 -> put' . popScreen $ tsSelect i t ui+            -- or on clicking the blank area below the transaction.+            MouseUp _ (Just BLeft) Location{loc=(_,y)} | y+1 > numentrylines -> put' . popScreen $ tsSelect i t ui+              where numentrylines = length (T.lines $ showTxn ropts rspec j t) - 1 -        VtyEvent (EvKey (KChar 'l') [MCtrl]) -> redraw ui-        VtyEvent (EvKey (KChar 'z') [MCtrl]) -> suspend ui-        _ -> continue ui+            VtyEvent (EvKey (KChar 'l') [MCtrl]) -> redraw+            VtyEvent (EvKey (KChar 'z') [MCtrl]) -> suspend ui+            _ -> return () -tsHandle _ _ = error "event handler called with wrong screen type, should not happen"  -- PARTIAL:+    _ -> errorWrongScreenType "event handler"  -- | Select a new transaction and update the previous register screen tsSelect i t ui@UIState{aScreen=s@TransactionScreen{}} = case aPrevScreens ui of
Hledger/UI/UIState.hs view
@@ -210,7 +210,7 @@  -- | Apply a new filter query, or return the failing query. setFilter :: String -> UIState -> Either String UIState-setFilter s = first (const s) . setEither querystring (words'' prefixes $ T.pack s)+setFilter s = first (const s) . setEither querystring (words'' queryprefixes $ T.pack s)  -- | Reset some filters & toggles. resetFilter :: UIState -> UIState@@ -286,11 +286,11 @@   -- remove all the screens from the appstate and then add them back   -- one at a time, regenerating as we go.   let-    first:rest = reverse $ s:ss :: [Screen]-    ui0 = ui{ajournal=j, aScreen=first, aPrevScreens=[]} :: UIState+    frst:rest = reverse $ s:ss :: [Screen]+    ui0 = ui{ajournal=j, aScreen=frst, aPrevScreens=[]} :: UIState -    ui1 = (sInit first) d False ui0 :: UIState-    ui2 = foldl' (\ui s -> (sInit s) d False $ pushScreen s ui) ui1 rest :: UIState+    ui1 = (sInit frst) d False ui0 :: UIState+    ui2 = foldl' (\ui' s' -> (sInit s') d False $ pushScreen s' ui') ui1 rest :: UIState   in     ui2 
Hledger/UI/UITypes.hs view
@@ -33,11 +33,14 @@  {-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE DeriveAnyClass     #-}+{-# LANGUAGE FlexibleInstances  #-} {-# LANGUAGE OverloadedStrings  #-} {-# LANGUAGE TemplateHaskell    #-}  module Hledger.UI.UITypes where +-- import Control.Concurrent (threadDelay)+-- import GHC.IO (unsafePerformIO) import Data.Text (Text) import Data.Time.Calendar (Day) import Brick@@ -100,7 +103,7 @@     AccountsScreen {        sInit   :: Day -> Bool -> UIState -> UIState              -- ^ function to initialise or update this screen's state       ,sDraw   :: UIState -> [Widget Name]                             -- ^ brick renderer for this screen-      ,sHandle :: UIState -> BrickEvent Name AppEvent -> EventM Name (Next UIState)  -- ^ brick event handler for this screen+      ,sHandle :: BrickEvent Name AppEvent -> EventM Name UIState ()  -- ^ brick event handler for this screen       -- state fields.These ones have lenses:       ,_asList            :: List Name AccountsScreenItem  -- ^ list widget showing account names & balances       ,_asSelectedAccount :: AccountName              -- ^ a backup of the account name from the list widget's selected item (or "")@@ -108,7 +111,7 @@   | RegisterScreen {        sInit   :: Day -> Bool -> UIState -> UIState       ,sDraw   :: UIState -> [Widget Name]-      ,sHandle :: UIState -> BrickEvent Name AppEvent -> EventM Name (Next UIState)+      ,sHandle :: BrickEvent Name AppEvent -> EventM Name UIState ()       --       ,rsList    :: List Name RegisterScreenItem      -- ^ list widget showing transactions affecting this account       ,rsAccount :: AccountName                       -- ^ the account this register is for@@ -119,7 +122,7 @@   | TransactionScreen {        sInit   :: Day -> Bool -> UIState -> UIState       ,sDraw   :: UIState -> [Widget Name]-      ,sHandle :: UIState -> BrickEvent Name AppEvent -> EventM Name (Next UIState)+      ,sHandle :: BrickEvent Name AppEvent -> EventM Name UIState ()       --       ,tsTransaction  :: NumberedTransaction          -- ^ the transaction we are currently viewing, and its position in the list       ,tsTransactions :: [NumberedTransaction]        -- ^ list of transactions we can step through@@ -128,11 +131,18 @@   | ErrorScreen {        sInit   :: Day -> Bool -> UIState -> UIState       ,sDraw   :: UIState -> [Widget Name]-      ,sHandle :: UIState -> BrickEvent Name AppEvent -> EventM Name (Next UIState)+      ,sHandle :: BrickEvent Name AppEvent -> EventM Name UIState ()       --       ,esError :: String                              -- ^ error message to show     }   deriving (Show)+-- XXX check for ideas: https://github.com/jtdaugherty/brick/issues/379#issuecomment-1191993357++-- | Error message to use in case statements adapting to the different Screen shapes.+errorWrongScreenType :: String -> a+errorWrongScreenType lbl =+  -- unsafePerformIO $ threadDelay 2000000 >>  -- delay to allow console output to be seen+  error' (unwords [lbl, "called with wrong screen type, should not happen"])  -- | An item in the accounts screen's list of accounts and balances. data AccountsScreenItem = AccountsScreenItem {
Hledger/UI/UIUtils.hs view
@@ -23,10 +23,17 @@   ,renderToggle1   ,replaceHiddenAccountsNameWith   ,scrollSelectionToMiddle+  ,get'+  ,put'+  ,modify'   ,suspend   ,redraw   ,reportSpecSetFutureAndForecast   ,listScrollPushingSelection+  ,dlogUiTrace+  ,dlogUiTraceM+  ,uiDebugLevel+  ,uiNumBlankItems   ) where @@ -35,7 +42,7 @@ import Brick.Widgets.Border.Style import Brick.Widgets.Dialog import Brick.Widgets.Edit-import Brick.Widgets.List (List, listSelectedL, listNameL, listItemHeightL, listSelected, listMoveDown, listMoveUp, GenericList, Splittable)+import Brick.Widgets.List (List, listSelectedL, listNameL, listItemHeightL, listSelected, listMoveDown, listMoveUp, GenericList) import Control.Monad.IO.Class import Data.Bifunctor (second) import Data.List@@ -60,20 +67,38 @@ suspendSignal = return () #else import System.Posix.Signals+import Data.Vector (Vector) suspendSignal :: IO () suspendSignal = raiseSignal sigSTOP #endif +-- Debug logging for UI state changes.++get' = do+  x <- get+  dlogUiTraceM $ "getting state: " ++ (head $ lines $ pshow $ aScreen x)+  return x++put' x = do+  dlogUiTraceM $ "putting state: " ++ (head $ lines $ pshow $ aScreen x)+  put x++modify' f = do+  x <- get+  let x' = f x+  dlogUiTraceM $ "modifying state: " ++ (head $ lines $ pshow $ aScreen x')+  modify f+ -- | On posix platforms, suspend the program using the STOP signal, -- like control-z in bash, returning to the original shell prompt, -- and when resumed, continue where we left off. -- On windows, does nothing.-suspend :: s -> EventM a (Next s)+suspend :: Ord a => s -> EventM a s () suspend st = suspendAndResume $ suspendSignal >> return st --- | Tell vty to redraw the whole screen, and continue.-redraw :: s -> EventM a (Next s)-redraw st = getVtyHandle >>= liftIO . refresh >> continue st+-- | Tell vty to redraw the whole screen.+redraw :: EventM a s ()+redraw = getVtyHandle >>= liftIO . refresh  -- | Wrap a widget in the default hledger-ui screen layout. defaultLayout :: Widget Name -> Widget Name -> Widget Name -> Widget Name@@ -90,14 +115,14 @@   Widget Fixed Fixed $ do     c <- getContext     render $-      withDefAttr "help" $+      withDefAttr (attrName "help") $       renderDialog (dialog (Just "Help (LEFT/ESC/?/q to close help)") Nothing (c^.availWidthL)) $ -- (Just (0,[("ok",())]))       padTop (Pad 0) $ padLeft (Pad 1) $ padRight (Pad 1) $         vBox [            hBox [               padRight (Pad 1) $                 vBox [-                   withAttr ("help" <> "heading") $ str "Navigation"+                   withAttr (attrName "help" <> attrName "heading") $ str "Navigation"                   ,renderKey ("UP/DOWN/PUP/PDN/HOME/END/k/j/C-p/C-n", "")                   ,str "     move selection up/down"                   ,renderKey ("RIGHT/l/C-f", "show txns, or txn detail")@@ -105,23 +130,23 @@                   ,renderKey ("ESC ", "cancel, or reset app state")                    ,str " "-                  ,withAttr ("help" <> "heading") $ str "Accounts screen"+                  ,withAttr (attrName "help" <> attrName "heading") $ str "Accounts screen"                   ,renderKey ("1234567890-+ ", "set/adjust depth limit")                   ,renderKey ("t ", "toggle accounts tree/list mode")                   ,renderKey ("H ", "toggle historical balance/change")                   ,str " "-                  ,withAttr ("help" <> "heading") $ str "Register screen"+                  ,withAttr (attrName "help" <> attrName "heading") $ str "Register screen"                   ,renderKey ("t ", "toggle subaccount txns\n(and accounts tree/list mode)")                   ,renderKey ("H ", "toggle historical/period total")                   ,str " "-                  ,withAttr ("help" <> "heading") $ str "Help"+                  ,withAttr (attrName "help" <> attrName "heading") $ str "Help"                   ,renderKey ("?    ", "toggle this help")                   ,renderKey ("p/m/i", "while help is open:\nshow manual in pager/man/info")                   ,str " "                 ]              ,padLeft (Pad 1) $ padRight (Pad 0) $                 vBox [-                   withAttr ("help" <> "heading") $ str "Filtering"+                   withAttr (attrName "help" <> attrName "heading") $ str "Filtering"                   ,renderKey ("/   ", "set a filter query")                   ,renderKey ("F   ", "show future & periodic txns")                   ,renderKey ("R   ", "show real/all postings")@@ -132,7 +157,7 @@                   ,renderKey ("T             ", "set period to today")                   ,renderKey ("DEL ", "reset filters")                   ,str " "-                  ,withAttr ("help" <> "heading") $ str "Other"+                  ,withAttr (attrName "help" <> attrName "heading") $ str "Other"                   ,renderKey ("a   ", "add transaction (hledger add)")                   ,renderKey ("A   ", "add transaction (hledger-iadd)")                   ,renderKey ("B   ", "show amounts/costs")@@ -160,39 +185,40 @@ --             ]           ]   where-    renderKey (key,desc) = withAttr ("help" <> "key") (str key) <+> str " " <+> str desc+    renderKey (key,desc) = withAttr (attrName "help" <> attrName "key") (str key) <+> str " " <+> str desc  -- | Event handler used when help mode is active. -- May invoke $PAGER, less, man or info, which is likely to fail on MS Windows, TODO.-helpHandle :: UIState -> BrickEvent Name AppEvent -> EventM Name (Next UIState)-helpHandle ui ev = do+helpHandle :: BrickEvent Name AppEvent -> EventM Name UIState ()+helpHandle ev = do+  ui <- get+  let ui' = setMode Normal ui   case ev of-    VtyEvent e | e `elem` closeHelpEvents -> continue $ setMode Normal ui-    VtyEvent (EvKey (KChar 'p') []) -> suspendAndResume $ runPagerForTopic "hledger-ui" Nothing >> return ui'-    VtyEvent (EvKey (KChar 'm') []) -> suspendAndResume $ runManForTopic   "hledger-ui" Nothing >> return ui'-    VtyEvent (EvKey (KChar 'i') []) -> suspendAndResume $ runInfoForTopic  "hledger-ui" Nothing >> return ui'-    _ -> continue ui+    VtyEvent e | e `elem` closeHelpEvents -> put' ui'+    VtyEvent (EvKey (KChar 'p') []) -> suspendAndResume (runPagerForTopic "hledger-ui" Nothing >> return ui')+    VtyEvent (EvKey (KChar 'm') []) -> suspendAndResume (runManForTopic   "hledger-ui" Nothing >> return ui')+    VtyEvent (EvKey (KChar 'i') []) -> suspendAndResume (runInfoForTopic  "hledger-ui" Nothing >> return ui')+    _ -> return ()   where-    ui' = setMode Normal ui     closeHelpEvents = moveLeftEvents ++ [EvKey KEsc [], EvKey (KChar '?') [], EvKey (KChar 'q') []]  -- | Draw the minibuffer with the given label. minibuffer :: T.Text -> Editor String Name -> Widget Name minibuffer string ed =-  forceAttr ("border" <> "minibuffer") $+  forceAttr (attrName "border" <> attrName "minibuffer") $   hBox [txt $ string <> ": ", renderEditor (str . unlines) True ed]  borderQueryStr :: String -> Widget Name borderQueryStr ""  = str ""-borderQueryStr qry = str " matching " <+> withAttr ("border" <> "query") (str qry)+borderQueryStr qry = str " matching " <+> withAttr (attrName "border" <> attrName "query") (str qry)  borderDepthStr :: Maybe Int -> Widget Name borderDepthStr Nothing  = str ""-borderDepthStr (Just d) = str " to depth " <+> withAttr ("border" <> "query") (str $ show d)+borderDepthStr (Just d) = str " to depth " <+> withAttr (attrName "border" <> attrName "query") (str $ show d)  borderPeriodStr :: String -> Period -> Widget Name borderPeriodStr _           PeriodAll = str ""-borderPeriodStr preposition p         = str (" "++preposition++" ") <+> withAttr ("border" <> "query") (str . T.unpack $ showPeriod p)+borderPeriodStr preposition p         = str (" "++preposition++" ") <+> withAttr (attrName "border" <> attrName "query") (str . T.unpack $ showPeriod p)  borderKeysStr :: [(String,String)] -> Widget Name borderKeysStr = borderKeysStr' . map (second str)@@ -201,7 +227,7 @@ borderKeysStr' keydescs =   hBox $   intersperse sep $-  [withAttr ("border" <> "key") (str keys) <+> str ":" <+> desc | (keys, desc) <- keydescs]+  [withAttr (attrName "border" <> attrName "key") (str keys) <+> str ":" <+> desc | (keys, desc) <- keydescs]   where     -- sep = str " | "     sep = str " "@@ -209,7 +235,7 @@ -- | Show both states of a toggle ("aaa/bbb"), highlighting the active one. renderToggle :: Bool -> String -> String -> Widget Name renderToggle isright l r =-  let bold = withAttr ("border" <> "selected") in+  let bold = withAttr (attrName "border" <> attrName "selected") in   if isright   then str (l++"/") <+> bold (str r)   else bold (str l) <+> str ("/"++r)@@ -217,7 +243,7 @@ -- | Show a toggle's label, highlighted (bold) when the toggle is active. renderToggle1 :: Bool -> String -> Widget Name renderToggle1 isactive l =-  let bold = withAttr ("border" <> "selected") in+  let bold = withAttr (attrName "border" <> attrName "selected") in   if isactive   then bold (str l)   else str l@@ -262,11 +288,11 @@           ""           -- "  debug: "++show (_w,h')     render $-      hBorderWithLabel (withAttr "border" $ toplabel <+> str debugmsg)+      hBorderWithLabel (withAttr (attrName "border") $ toplabel <+> str debugmsg)       <=>       body'       <=>-      hBorderWithLabel (withAttr "border" bottomlabel)+      hBorderWithLabel (withAttr (attrName "border") bottomlabel)  ---- XXX should be equivalent to the above, but isn't (page down goes offscreen) --_topBottomBorderWithLabel2 :: Widget Name -> Widget Name -> Widget Name@@ -286,8 +312,8 @@ -- 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-    c <- getContext-    let w' = vLimit (c^.availHeightL - v*2) $ hLimit (c^.availWidthL - h*2) w+    ctx <- getContext+    let w' = vLimit (ctx^.availHeightL - v*2) $ hLimit (ctx^.availWidthL - h*2) w         attr = maybe currentAttr (\c -> c `on` c) mcolour     render $       withBorderAttr attr $@@ -303,7 +329,7 @@    -- applyN n border  withBorderAttr :: Attr -> Widget Name -> Widget Name-withBorderAttr attr = updateAttrMap (applyAttrMappings [("border", attr)])+withBorderAttr attr = updateAttrMap (applyAttrMappings [(attrName "border", attr)])  ---- | Like brick's continue, but first run some action to modify brick's state. ---- This action does not affect the app state, but might eg adjust a widget's scroll position.@@ -319,7 +345,7 @@  -- | Scroll a list's viewport so that the selected item is centered in the -- middle of the display area.-scrollSelectionToMiddle :: List Name e -> EventM Name ()+scrollSelectionToMiddle :: List Name item -> EventM Name UIState () scrollSelectionToMiddle list = do   case list^.listSelectedL of     Nothing -> return ()@@ -349,8 +375,8 @@ -- 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]}+reportSpecSetFutureAndForecast d fcast rspec =+    rspec{_rsQuery=simplifyQuery $ And [_rsQuery rspec, periodq, excludeforecastq fcast]}   where     periodq = Date . periodAsDateSpan . period_ $ _rsReportOpts rspec     -- Except in forecast mode, exclude future/forecast transactions.@@ -364,9 +390,9 @@ -- Vertically scroll the named list's viewport with the given number of non-empty items -- by the given positive or negative number of items (usually 1 or -1). -- The selection will be moved when necessary to keep it visible and allow the scroll.-listScrollPushingSelection :: (Ord n, Foldable t, Splittable t) => -  n -> GenericList n t e -> Int -> Int -> EventM n (GenericList n t e)-listScrollPushingSelection name list listheight scrollamt = do+listScrollPushingSelection :: Name -> Int -> Int -> EventM Name (List Name item) (GenericList Name Vector item)+listScrollPushingSelection name listheight scrollamt = do+  list <- get   viewportScroll name `vScrollBy` scrollamt   mvp <- lookupViewport name   case mvp of@@ -381,3 +407,24 @@               | otherwise = id         _ -> return list     _ -> return list++-- | Log a string to ./debug.log before returning the second argument,+-- if the global debug level is at or above a standard hledger-ui debug level.+-- Uses unsafePerformIO.+dlogUiTrace :: String -> a -> a+dlogUiTrace = dlogTraceAt uiDebugLevel++-- | Like dlogUiTrace, but within the hledger-ui brick event handler monad.+dlogUiTraceM :: String -> EventM Name UIState ()+dlogUiTraceM s = dlogUiTrace s $ return ()++-- | Log hledger-ui events at this debug level.+uiDebugLevel :: Int+uiDebugLevel = 2++-- | How many blank items to add to lists to fill the full window height.+uiNumBlankItems :: Int+uiNumBlankItems+  -- | debugLevel >= uiDebugLevel = 0    -- suppress to improve debug output.+  -- | otherwise +  = 100  -- 100 ought to be enough for anyone
hledger-ui.1 view
@@ -1,5 +1,5 @@ -.TH "HLEDGER-UI" "1" "July 2022" "hledger-ui-1.26.1 " "hledger User Manuals"+.TH "HLEDGER-UI" "1" "September 2022" "hledger-ui-1.27 " "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.26.1.+This manual is for hledger-ui 1.27. .SH SYNOPSIS .PP \f[C]hledger-ui [OPTIONS] [QUERYARGS]\f[R]@@ -557,9 +557,10 @@ reboot. .PP On Windows computers, the default value is probably-\f[C]C:\[rs]Users\[rs]MyUserName\[rs].hledger.journal\f[R].+\f[C]C:\[rs]Users\[rs]YOURNAME\[rs].hledger.journal\f[R]. You can change this by running a command like this in a powershell-window:+window (let us know if you need to be an Administrator, and if this+persists across a reboot): .IP .nf \f[C]@@ -567,8 +568,8 @@ \f[R] .fi .PP-(Let us know if you need to be an Administrator, and if this persists-across a reboot.)+Or, change it in settings: see+https://www.java.com/en/download/help/path.html. .SH FILES .PP Reads data from one or more files in hledger journal, timeclock,
hledger-ui.cabal view
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack  name:           hledger-ui-version:        1.26.1+version:        1.27 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==9.0.1+    GHC==8.10.7, GHC==9.0.2, GHC==9.2.4 extra-source-files:     CHANGES.md     README.md@@ -62,13 +62,14 @@       Paths_hledger_ui   hs-source-dirs:       ./-  ghc-options: -Wall -Wno-incomplete-uni-patterns -Wno-missing-signatures -Wno-name-shadowing -Wno-orphans -Wno-type-defaults -Wno-unused-do-bind-  cpp-options: -DVERSION="1.26.1"+  ghc-options: -Wall -Wno-incomplete-uni-patterns -Wno-missing-signatures -Wno-orphans -Wno-type-defaults -Wno-unused-do-bind+  cpp-options: -DVERSION="1.27"   build-depends:       ansi-terminal >=0.9     , async-    , base >=4.11 && <4.17-    , brick >=0.23+    , base >=4.14 && <4.17+    , breakpoint+    , brick >=1.0     , cmdargs >=0.8     , containers >=0.5.9     , data-default@@ -77,8 +78,8 @@     , extra >=1.6.3     , filepath     , fsnotify >=0.2.1.2 && <0.4-    , hledger >=1.26.1 && <1.27-    , hledger-lib >=1.26.1 && <1.27+    , hledger ==1.27.*+    , hledger-lib ==1.27.*     , megaparsec >=7.0.0 && <9.3     , 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.26.1.+tool.  This manual is for hledger-ui 1.27.     'hledger-ui [OPTIONS] [QUERYARGS]' 'hledger ui -- [OPTIONS] [QUERYARGS]'@@ -589,13 +589,14 @@    For this to take effect you might need to 'killall Dock', or reboot.     On Windows computers, the default value is probably-'C:\Users\MyUserName\.hledger.journal'.  You can change this by running-a command like this in a powershell window:+'C:\Users\YOURNAME\.hledger.journal'.  You can change this by running a+command like this in a powershell window (let us know if you need to be+an Administrator, and if this persists across a reboot):  > setx LEDGER_FILE "C:\Users\MyUserName\finance\2021.journal" -   (Let us know if you need to be an Administrator, and if this persists-across a reboot.)+   Or, change it in settings: see+https://www.java.com/en/download/help/path.html.   File: hledger-ui.info,  Node: FILES,  Next: BUGS,  Prev: ENVIRONMENT,  Up: Top@@ -639,34 +640,34 @@  Tag Table: Node: Top221-Node: OPTIONS1656-Ref: #options1754-Node: MOUSE6636-Ref: #mouse6731-Node: KEYS7013-Ref: #keys7106-Node: SCREENS11192-Ref: #screens11290-Node: Accounts screen11380-Ref: #accounts-screen11508-Node: Register screen13847-Ref: #register-screen14002-Node: Transaction screen15986-Ref: #transaction-screen16144-Node: Error screen17014-Ref: #error-screen17136-Node: TIPS17380-Ref: #tips17479-Node: Watch mode17531-Ref: #watch-mode17648-Node: Watch mode limitations18398-Ref: #watch-mode-limitations18539-Node: ENVIRONMENT19675-Ref: #environment19786-Node: FILES21094-Ref: #files21193-Node: BUGS21406-Ref: #bugs21483+Node: OPTIONS1654+Ref: #options1752+Node: MOUSE6634+Ref: #mouse6729+Node: KEYS7011+Ref: #keys7104+Node: SCREENS11190+Ref: #screens11288+Node: Accounts screen11378+Ref: #accounts-screen11506+Node: Register screen13845+Ref: #register-screen14000+Node: Transaction screen15984+Ref: #transaction-screen16142+Node: Error screen17012+Ref: #error-screen17134+Node: TIPS17378+Ref: #tips17477+Node: Watch mode17529+Ref: #watch-mode17646+Node: Watch mode limitations18396+Ref: #watch-mode-limitations18537+Node: ENVIRONMENT19673+Ref: #environment19784+Node: FILES21169+Ref: #files21268+Node: BUGS21481+Ref: #bugs21558  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.26.1.+       tool.  This manual is for hledger-ui 1.27.  SYNOPSIS        hledger-ui [OPTIONS] [QUERYARGS]@@ -489,23 +489,24 @@         For this to take effect you might need to killall Dock, or reboot. -       On Windows computers, the default value  is  probably  C:\Users\MyUser--       Name\.hledger.journal.   You  can change this by running a command like-       this in a powershell window:+       On Windows computers, the  default  value  is  probably  C:\Users\YOUR-+       NAME\.hledger.journal.   You  can change this by running a command like+       this in a powershell window (let us know if you need to be an  Adminis-+       trator, and if this persists across a reboot):                > setx LEDGER_FILE "C:\Users\MyUserName\finance\2021.journal" -       (Let us know if you need to be an Administrator, and if  this  persists-       across a reboot.)+       Or,   change   it   in   settings:   see  https://www.java.com/en/down-+       load/help/path.html.  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).@@ -513,13 +514,13 @@        -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.@@ -530,7 +531,7 @@   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)  @@ -548,4 +549,4 @@   -hledger-ui-1.26.1                  July 2022                     HLEDGER-UI(1)+hledger-ui-1.27                 September 2022                   HLEDGER-UI(1)