Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package hledger-ui for openSUSE:Factory checked in at 2025-09-24 15:24:24 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/hledger-ui (Old) and /work/SRC/openSUSE:Factory/.hledger-ui.new.27445 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "hledger-ui" Wed Sep 24 15:24:24 2025 rev:41 rq:1306809 version:1.50.1 Changes: -------- --- /work/SRC/openSUSE:Factory/hledger-ui/hledger-ui.changes 2025-09-11 14:43:32.414452489 +0200 +++ /work/SRC/openSUSE:Factory/.hledger-ui.new.27445/hledger-ui.changes 2025-09-24 15:26:39.678361994 +0200 @@ -1,0 +2,9 @@ +Tue Sep 16 13:40:46 UTC 2025 - Peter Simons <[email protected]> + +- Update hledger-ui to version 1.50.1. + Upstream's change log file format is strange (too much unmodified + text at at the top). The automatic updater cannot extract the + relevant additions. You can find the file at: + http://hackage.haskell.org/package/hledger-ui-1.50.1/src/CHANGES.md + +------------------------------------------------------------------- Old: ---- hledger-ui-1.50.tar.gz New: ---- hledger-ui-1.50.1.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ hledger-ui.spec ++++++ --- /var/tmp/diff_new_pack.M9c5Be/_old 2025-09-24 15:26:40.198383888 +0200 +++ /var/tmp/diff_new_pack.M9c5Be/_new 2025-09-24 15:26:40.198383888 +0200 @@ -19,7 +19,7 @@ %global pkg_name hledger-ui %global pkgver %{pkg_name}-%{version} Name: %{pkg_name} -Version: 1.50 +Version: 1.50.1 Release: 0 Summary: Terminal interface for the hledger accounting system License: GPL-3.0-or-later @@ -72,6 +72,8 @@ BuildRequires: ghc-safe-prof BuildRequires: ghc-split-devel BuildRequires: ghc-split-prof +BuildRequires: ghc-stm-devel +BuildRequires: ghc-stm-prof BuildRequires: ghc-text-devel BuildRequires: ghc-text-prof BuildRequires: ghc-text-zipper-devel ++++++ hledger-ui-1.50.tar.gz -> hledger-ui-1.50.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hledger-ui-1.50/CHANGES.md new/hledger-ui-1.50.1/CHANGES.md --- old/hledger-ui-1.50/CHANGES.md 2025-09-03 21:03:21.000000000 +0200 +++ new/hledger-ui-1.50.1/CHANGES.md 2025-09-16 14:22:23.000000000 +0200 @@ -23,6 +23,36 @@ See also the hledger changelog. +# 1.50.1 2025-09-16 + +Fixes + +- The transaction screen and error screen now update on data changes like other screens, + eg when using the E key, g key, or --watch. + [#2014], [#2288] + +- When the journal is reloaded by the `g` key or `--watch`, the + --pivot (and --obfuscate) options are now preserved, + and spurious errors are avoided. + [#2451] + +- The Z key (and the -E command line flag) toggles zero-balance accounts again. + (Stephen Morgan, [#2454]) + +Improvements + +- Debug output has improved, eg it's easier to see changes to the screen stack. + +API + +- Hledger.UI.ErrorScreen: + uiReloadJournal -> uiReload, + uiReloadJournalIfChanged -> uiReloadIfFileChanged +- Hledger.UI.UIState: + enableForecastPreservingPeriod -> enableForecast + + + # 1.50 2025-09-03 Breaking changes diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hledger-ui-1.50/Hledger/UI/AccountsScreen.hs new/hledger-ui-1.50.1/Hledger/UI/AccountsScreen.hs --- old/hledger-ui-1.50/Hledger/UI/AccountsScreen.hs 2025-08-28 16:06:09.000000000 +0200 +++ new/hledger-ui-1.50.1/Hledger/UI/AccountsScreen.hs 2025-09-16 02:35:06.000000000 +0200 @@ -48,7 +48,7 @@ import Hledger.UI.UIUtils import Hledger.UI.UIScreens import Hledger.UI.Editor -import Hledger.UI.ErrorScreen (uiReloadJournal, uiCheckBalanceAssertions, uiReloadJournalIfChanged) +import Hledger.UI.ErrorScreen (uiCheckBalanceAssertions, uiReload, uiReloadIfFileChanged) import Hledger.UI.RegisterScreen (rsCenterSelection) import Data.Either (fromRight) import Control.Arrow ((>>>)) @@ -69,7 +69,7 @@ asDrawHelper UIState{aScreen=scr, aopts=uopts, ajournal=j, aMode=mode} ropts scrname = dbgui "asDrawHelper" $ case toAccountsLikeScreen scr of - Nothing -> dbgui "asDrawHelper" $ errorWrongScreenType "draw helper" -- PARTIAL: + Nothing -> dbgui "asDrawHelper" $ errorWrongScreenType "asDrawHelper" -- PARTIAL: Just (ALS _ ass) -> case mode of Help -> [helpDialog, maincontent] _ -> [maincontent] @@ -189,7 +189,7 @@ dbguiEv "asHandle" ui0@UIState{aScreen=scr, aMode=mode} <- get' case toAccountsLikeScreen scr of - Nothing -> dbgui "asHandle" $ errorWrongScreenType "event handler" -- PARTIAL: + Nothing -> dbgui "asHandle" $ errorWrongScreenType "asHandle" -- PARTIAL: Just als@(ALS scons ass) -> do -- save the currently selected account, in case we leave this screen and lose the selection put' ui0{aScreen=scons ass{_assSelectedAccount=asSelectedAccount ass}} @@ -231,7 +231,7 @@ -- XXX be sure we don't leave unconsumed app events piling up -- A data file has changed (or the user has pressed g): reload. e | e `elem` [AppEvent FileChange, VtyEvent (EvKey (KChar 'g') [])] -> - liftIO (uiReloadJournal copts d ui) >>= put' + uiReload copts d ui >>= put' -- The date has changed (and we are viewing a standard period which contained the old date): -- adjust the viewed period and regenerate, just in case needed. @@ -245,9 +245,9 @@ VtyEvent (EvKey k []) | k `elem` [KBS, KDel] -> modify' (resetFilter >>> regenerateScreens j d) -- run external programs: - VtyEvent (EvKey (KChar 'a') []) -> suspendAndResume $ clearScreen >> setCursorPosition 0 0 >> add (cliOptsDropArgs 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 'a') []) -> suspendAndResume $ clearScreen >> setCursorPosition 0 0 >> add (cliOptsDropArgs copts) j >> uiReloadIfFileChanged copts d j ui + VtyEvent (EvKey (KChar 'A') []) -> suspendAndResume $ void (runIadd (journalFilePath j)) >> uiReloadIfFileChanged copts d j ui + VtyEvent (EvKey (KChar 'E') []) -> suspendAndResume $ void (runEditor endPosition (journalFilePath j)) >> uiReloadIfFileChanged copts d j ui -- adjust the period displayed: VtyEvent (EvKey (KChar 'T') []) -> modify' (setReportPeriod (DayPeriod d) >>> regenerateScreens j d) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hledger-ui-1.50/Hledger/UI/ErrorScreen.hs new/hledger-ui-1.50.1/Hledger/UI/ErrorScreen.hs --- old/hledger-ui-1.50/Hledger/UI/ErrorScreen.hs 2025-09-02 08:59:50.000000000 +0200 +++ new/hledger-ui-1.50.1/Hledger/UI/ErrorScreen.hs 2025-09-16 14:20:53.000000000 +0200 @@ -11,6 +11,8 @@ ,esDraw ,esHandle ,uiCheckBalanceAssertions + ,uiReload + ,uiReloadIfFileChanged ,uiReloadJournal ,uiReloadJournalIfChanged ) @@ -18,12 +20,12 @@ import Brick -- import Brick.Widgets.Border ("border") -import Control.Monad import Control.Monad.IO.Class (liftIO) import Data.Time.Calendar (Day) import Data.Void (Void) import Graphics.Vty (Event(..),Key(..),Modifier(..)) import Lens.Micro ((^.)) +import Safe (headMay) import Text.Megaparsec import Text.Megaparsec.Char @@ -35,6 +37,10 @@ import Hledger.UI.UIScreens import Hledger.UI.Editor +-- legacy aliases +uiReloadJournal = uiReload +uiReloadJournalIfChanged = uiReloadIfFileChanged + esDraw :: UIState -> [Widget Name] esDraw UIState{aScreen=ES ESS{..} ,aMode=mode @@ -88,23 +94,41 @@ 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 _essError 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 + + -- g or file change: reload the journal and rebuild app state. + e | e `elem` [VtyEvent (EvKey (KChar 'g') []), AppEvent FileChange] -> esReload copts d ui + + -- E: run editor, reload the journal. + VtyEvent (EvKey (KChar 'E') []) -> do + suspendAndResume' $ do + let + (pos,f) = case parsewithString hledgerparseerrorpositionp _essError of + Right (f',l,c) -> (Just (l, Just c),f') + Left _ -> (endPosition, journalFilePath j) + runEditor pos f + esReloadIfFileChanged copts d j ui + VtyEvent (EvKey (KChar 'I') []) -> put' $ uiCheckBalanceAssertions d (popScreen $ toggleIgnoreBalanceAssertions ui) VtyEvent (EvKey (KChar 'l') [MCtrl]) -> redraw VtyEvent (EvKey (KChar 'z') [MCtrl]) -> suspend ui _ -> return () - _ -> errorWrongScreenType "event handler" + _ -> errorWrongScreenType "esHandle" + + where + -- Reload and fully regenerate the error screen. + -- XXX On an error screen below the transaction screen, this is tricky because of a current limitation of regenerateScreens. + -- For now we try to work around by re-entering the transaction screen. + -- This can show flicker in the UI and it's hard to handle all situations robustly. + esReload copts d ui = uiReload copts d ui >>= maybeReloadErrorScreen copts d + esReloadIfFileChanged copts d j ui = liftIO (uiReloadIfFileChanged copts d j ui) >>= maybeReloadErrorScreen copts d + maybeReloadErrorScreen copts d ui = + case headMay $ aPrevScreens ui of + Just (TS _) -> do + -- check balance assertions, exit to register screen, enter transaction screen, reload once more + put' $ popScreen $ popScreen $ uiCheckBalanceAssertions d ui + sendVtyEvents [EvKey KEnter [], EvKey (KChar 'g') []] -- XXX Might be disrupted if other events are queued ? + _ -> uiReload copts d (popScreen ui) >>= put' . uiCheckBalanceAssertions d -- | 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 @@ -130,29 +154,27 @@ ] --- | Unconditionally reload the journal, regenerating the current screen --- and all previous screens in the history as of the provided today-date. --- If reloading fails, enter the error screen, or if we're already --- on the error screen, update the error displayed. --- Defined here so it can reference the error screen. +-- Defined here so it can reference the error screen: + +-- | Reload the journal from its input files, then update the ui app state accordingly. +-- This means regenerate the entire screen stack from top level down to the current screen, using the provided today-date. +-- As a convenience (usually), if journal reloading fails, this enters the error screen, or if already there, updates its message. -- --- The provided CliOpts are used for reloading, and then saved in the --- UIState if reloading is successful (otherwise the UIState keeps its old --- CliOpts.) (XXX needed for.. ?) +-- The provided cli options can influence reloading; then if reloading succeeds they are saved in the ui state, +-- otherwise the UIState keeps its old options. (XXX needed for.. ?) -- --- Forecasted transactions are always generated, as at hledger-ui startup. --- If a forecast period is specified in the provided opts, or was specified --- at startup, it is preserved. +-- Like at hledger-ui startup, --forecast is always enabled. +-- A forecast period specified in the provided opts, or at startup, is preserved. -- -uiReloadJournal :: CliOpts -> Day -> UIState -> IO UIState -uiReloadJournal copts d ui = do +uiReload :: CliOpts -> Day -> UIState -> EventM Name UIState UIState +uiReload copts d ui = liftIO $ do ej <- - let copts' = enableForecastPreservingPeriod ui copts - in runExceptT $ journalReload copts' - -- dbg1IO "uiReloadJournal before reload" (map tdescription $ jtxns $ ajournal ui) + let copts' = enableForecast (astartupopts ui) copts + in runExceptT $ journalTransform copts' <$> journalReload copts' + -- dbg1IO "uiReload before reload" (map tdescription $ jtxns $ ajournal ui) return $ case ej of Right j -> - -- dbg1 "uiReloadJournal after reload" (map tdescription $ jtxns j) $ + -- dbg1 "uiReload after reload" (map tdescription $ jtxns j) $ regenerateScreens j d ui Left err -> case ui of @@ -167,12 +189,12 @@ -- RegisterScreen _ _ _ _ _ _ -- TransactionScreen _ _ _ _ _ _ --- | Like uiReloadJournal, but does not re-parse the journal if the file(s) --- have not changed since last loaded. Always regenerates the screens though, --- since the provided options or today-date may have changed. -uiReloadJournalIfChanged :: CliOpts -> Day -> Journal -> UIState -> IO UIState -uiReloadJournalIfChanged copts d j ui = do - let copts' = enableForecastPreservingPeriod ui copts +-- | Like uiReload, except it skips re-reading the journal if its file(s) have not changed +-- since it was last loaded. The up app state is always updated, since the options or today-date may have changed. +-- Also, this one runs in IO, suitable for suspendAndResume. +uiReloadIfFileChanged :: CliOpts -> Day -> Journal -> UIState -> IO UIState +uiReloadIfFileChanged copts d j ui = do + let copts' = enableForecast (astartupopts ui) copts ej <- runExceptT $ journalReloadIfChanged copts' d j return $ case ej of Right (j', _) -> regenerateScreens j' d ui diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hledger-ui-1.50/Hledger/UI/Main.hs new/hledger-ui-1.50.1/Hledger/UI/Main.hs --- old/hledger-ui-1.50/Hledger/UI/Main.hs 2025-09-02 08:59:57.000000000 +0200 +++ new/hledger-ui-1.50.1/Hledger/UI/Main.hs 2025-09-16 02:35:06.000000000 +0200 @@ -118,7 +118,7 @@ _ | boolopt "man" rawopts -> runManForTopic "hledger-ui" Nothing _ | boolopt "version" rawopts -> putStrLn prognameandversion -- _ | boolopt "binary-filename" rawopts -> putStrLn (binaryfilename progname) - _ -> withJournalDo copts' (runBrickUi opts) + _ -> withJournal copts' (runBrickUi opts) when (ghcDebugMode == GDPauseAtEnd) $ ghcDebugPause' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hledger-ui-1.50/Hledger/UI/MenuScreen.hs new/hledger-ui-1.50.1/Hledger/UI/MenuScreen.hs --- old/hledger-ui-1.50/Hledger/UI/MenuScreen.hs 2025-08-28 16:06:09.000000000 +0200 +++ new/hledger-ui-1.50.1/Hledger/UI/MenuScreen.hs 2025-09-16 02:35:06.000000000 +0200 @@ -33,7 +33,7 @@ import Hledger.UI.UIState import Hledger.UI.UIUtils import Hledger.UI.UIScreens -import Hledger.UI.ErrorScreen (uiReloadJournal, uiCheckBalanceAssertions, uiReloadJournalIfChanged) +import Hledger.UI.ErrorScreen (uiCheckBalanceAssertions, uiReload, uiReloadIfFileChanged) import Hledger.UI.Editor (runIadd, runEditor, endPosition) import Brick.Widgets.Edit (getEditContents, handleEditorEvent) import Control.Arrow ((>>>)) @@ -87,7 +87,7 @@ ,("q", str "quit") ] -msDraw _ = dbgui "msDraw" $ errorWrongScreenType "draw function" -- PARTIAL: +msDraw _ = dbgui "msDraw" $ errorWrongScreenType "msDraw" -- PARTIAL: -- msDrawItem :: (Int,Int) -> Bool -> MenuScreenItem -> Widget Name -- msDrawItem (_acctwidth, _balwidth) _selected MenuScreenItem{..} = @@ -155,12 +155,11 @@ 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' + e | e `elem` [VtyEvent (EvKey (KChar 'g') []), AppEvent FileChange] -> uiReload copts d ui >>= put' VtyEvent (EvKey (KChar 'I') []) -> put' $ uiCheckBalanceAssertions d (toggleIgnoreBalanceAssertions ui) - VtyEvent (EvKey (KChar 'a') []) -> suspendAndResume $ clearScreen >> setCursorPosition 0 0 >> add (cliOptsDropArgs 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 'a') []) -> suspendAndResume $ clearScreen >> setCursorPosition 0 0 >> add (cliOptsDropArgs copts) j >> uiReloadIfFileChanged copts d j ui + VtyEvent (EvKey (KChar 'A') []) -> suspendAndResume $ void (runIadd (journalFilePath j)) >> uiReloadIfFileChanged copts d j ui + VtyEvent (EvKey (KChar 'E') []) -> suspendAndResume $ void (runEditor endPosition (journalFilePath j)) >> uiReloadIfFileChanged copts d j ui -- VtyEvent (EvKey (KChar 'B') []) -> put' $ regenerateScreens j d $ toggleConversionOp ui -- VtyEvent (EvKey (KChar 'V') []) -> put' $ regenerateScreens j d $ toggleValue ui @@ -257,7 +256,7 @@ MouseUp{} -> return () AppEvent _ -> return () - _ -> dbguiEv "msHandle" >> errorWrongScreenType "event handler" + _ -> dbguiEv "msHandle" >> errorWrongScreenType "msHandle" msEnterScreen :: Day -> ScreenName -> UIState -> EventM Name UIState () msEnterScreen d scrname ui@UIState{ajournal=j, aopts=uopts} = do diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hledger-ui-1.50/Hledger/UI/RegisterScreen.hs new/hledger-ui-1.50.1/Hledger/UI/RegisterScreen.hs --- old/hledger-ui-1.50/Hledger/UI/RegisterScreen.hs 2025-08-28 16:06:09.000000000 +0200 +++ new/hledger-ui-1.50.1/Hledger/UI/RegisterScreen.hs 2025-09-16 02:35:06.000000000 +0200 @@ -43,13 +43,13 @@ import Hledger.UI.UIUtils import Hledger.UI.UIScreens import Hledger.UI.Editor -import Hledger.UI.ErrorScreen (uiReloadJournal, uiCheckBalanceAssertions, uiReloadJournalIfChanged) +import Hledger.UI.ErrorScreen (uiCheckBalanceAssertions, uiReload, uiReloadIfFileChanged) rsDraw :: UIState -> [Widget Name] rsDraw UIState{aopts=_uopts@UIOpts{uoCliOpts=copts@CliOpts{reportspec_=rspec}} ,aScreen=RS RSS{..} ,aMode=mode - } = dbgui "rsDraw 1" $ + } = dbgui "rsDraw" $ case mode of Help -> [helpDialog, maincontent] _ -> [maincontent] @@ -156,7 +156,7 @@ -- ,("q", "quit") ] -rsDraw _ = dbgui "rsDraw 2" $ errorWrongScreenType "draw function" -- PARTIAL: +rsDraw _ = dbgui "rsDraw" $ errorWrongScreenType "rsDraw" -- PARTIAL: rsDrawItem :: (Int,Int,Int,Int,Int) -> Bool -> RegisterScreenItem -> Widget Name rsDrawItem (datewidth,descwidth,acctswidth,changewidth,balwidth) selected RegisterScreenItem{..} = @@ -187,7 +187,7 @@ rsHandle :: BrickEvent Name AppEvent -> EventM Name UIState () rsHandle ev = do ui0 <- get' - dbguiEv "rsHandle 1" + dbguiEv "rsHandle" case ui0 of ui@UIState{ aScreen=RS sst@RSS{..} @@ -245,14 +245,13 @@ where p = reportPeriod ui - e | e `elem` [AppEvent FileChange, VtyEvent (EvKey (KChar 'g') [])] -> - liftIO (uiReloadJournal copts d ui) >>= put' + e | e `elem` [AppEvent FileChange, VtyEvent (EvKey (KChar 'g') [])] -> uiReload copts d ui >>= put' VtyEvent (EvKey (KChar 'I') []) -> put' $ uiCheckBalanceAssertions d (toggleIgnoreBalanceAssertions ui) - VtyEvent (EvKey (KChar 'a') []) -> suspendAndResume $ clearScreen >> setCursorPosition 0 0 >> add (cliOptsDropArgs copts) j >> uiReloadJournalIfChanged copts d j ui - VtyEvent (EvKey (KChar 'A') []) -> suspendAndResume $ void (runIadd (journalFilePath j)) >> uiReloadJournalIfChanged copts d j ui + VtyEvent (EvKey (KChar 'a') []) -> suspendAndResume $ clearScreen >> setCursorPosition 0 0 >> add (cliOptsDropArgs copts) j >> uiReloadIfFileChanged copts d j ui + VtyEvent (EvKey (KChar 'A') []) -> suspendAndResume $ void (runIadd (journalFilePath j)) >> uiReloadIfFileChanged 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 + VtyEvent (EvKey (KChar 'E') []) -> suspendAndResume $ void (runEditor pos f) >> uiReloadIfFileChanged copts d j ui where (pos,f) = case listSelectedElement _rssList of Nothing -> (endPosition, journalFilePath j) @@ -332,7 +331,7 @@ MouseUp{} -> return () AppEvent _ -> return () - _ -> dbgui "rsHandle 2" $ errorWrongScreenType "event handler" + _ -> errorWrongScreenType "rsHandle" isBlankElement mel = ((rsItemDate . snd) <$> mel) == Just "" @@ -354,8 +353,4 @@ rsEnterTransactionScreen :: AccountName -> [NumberedTransaction] -> NumberedTransaction -> UIState -> EventM Name UIState () rsEnterTransactionScreen acct nts nt ui = do dbguiEv "rsEnterTransactionScreen" - put' $ - pushScreen (tsNew acct nts nt) - ui - - + put' $ pushScreen (tsNew acct nts nt) ui diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hledger-ui-1.50/Hledger/UI/TransactionScreen.hs new/hledger-ui-1.50.1/Hledger/UI/TransactionScreen.hs --- old/hledger-ui-1.50/Hledger/UI/TransactionScreen.hs 2025-09-02 08:59:50.000000000 +0200 +++ new/hledger-ui-1.50.1/Hledger/UI/TransactionScreen.hs 2025-09-16 02:35:06.000000000 +0200 @@ -12,14 +12,15 @@ ,tsHandle ) where -import Control.Monad +import Brick +import Brick.Widgets.Edit (editorText, renderEditor) +import Brick.Widgets.List (listMoveTo) import Control.Monad.IO.Class (liftIO) import Data.List import Data.Maybe import qualified Data.Text as T import Graphics.Vty (Event(..),Key(..),Modifier(..), Button (BLeft)) -import Brick -import Brick.Widgets.List (listMoveTo) +import System.Exit (ExitCode (..)) import Hledger import Hledger.Cli hiding (mode, prices, progname,prognameandversion) @@ -29,8 +30,8 @@ import Hledger.UI.UIUtils import Hledger.UI.UIScreens import Hledger.UI.Editor -import Brick.Widgets.Edit (editorText, renderEditor) -import Hledger.UI.ErrorScreen (uiReloadJournalIfChanged, uiCheckBalanceAssertions, uiReloadJournal) +import Hledger.UI.ErrorScreen (uiCheckBalanceAssertions, uiReload, uiReloadIfFileChanged) +import Hledger.UI.RegisterScreen (rsHandle) tsDraw :: UIState -> [Widget Name] tsDraw UIState{aopts=UIOpts{uoCliOpts=copts@CliOpts{reportspec_=rspec@ReportSpec{_rsReportOpts=ropts}}} @@ -95,7 +96,7 @@ -- ,("q", "quit") ] -tsDraw _ = errorWrongScreenType "draw function" -- PARTIAL: +tsDraw _ = errorWrongScreenType "tsDraw" -- PARTIAL: -- Render a transaction suitably for the transaction screen. showTxn :: ReportOpts -> ReportSpec -> Journal -> Transaction -> T.Text @@ -137,23 +138,30 @@ 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') + + -- g or file change: reload the journal and rebuild app state. + e | e `elem` [VtyEvent (EvKey (KChar 'g') []), AppEvent FileChange] -> + tsReload copts d ui + + -- for debugging; leaving these here because they were hard to find + -- \u -> dbguiEv (pshow u) >> put' u -- doesn't log + -- \UIState{aScreen=TS tss} -> error' $ pshow $ _tssTransaction tss + + -- E: run editor, reload the journal. + VtyEvent (EvKey (KChar 'E') []) -> do + suspendAndResume' $ do + let (pos,f) = case tsourcepos t of (SourcePos f' l1 c1,_) -> (Just (unPos l1, Just $ unPos c1),f') + exitcode <- runEditor pos f + case exitcode of + ExitSuccess -> return () + ExitFailure c -> error' $ "running the text editor failed with exit code " ++ show c + tsReloadIfFileChanged copts d j ui + AppEvent (DateChange old _) | isStandardPeriod p && p `periodContainsDate` old -> put' $ regenerateScreens j d $ setReportPeriod (DayPeriod d) ui where p = reportPeriod ui - -- Reload. Warning, this updates parent screens but not the transaction screen itself (see tsUpdate). - -- To see the updated transaction, one must exit and re-enter the transaction screen. - e | e `elem` [VtyEvent (EvKey (KChar 'g') []), AppEvent FileChange] -> - liftIO (uiReloadJournal copts d ui) >>= put' - -- debugging.. leaving these here because they were hard to find - -- \u -> dbguiEv (pshow u) >> put' u -- doesn't log - -- \UIState{aScreen=TS tss} -> error' $ pshow $ _tssTransaction tss - VtyEvent (EvKey (KChar 'I') []) -> put' $ uiCheckBalanceAssertions d (toggleIgnoreBalanceAssertions ui) -- for toggles that may change the current/prev/next transactions, @@ -176,7 +184,54 @@ VtyEvent (EvKey (KChar 'z') [MCtrl]) -> suspend ui _ -> return () - _ -> errorWrongScreenType "event handler" + _ -> errorWrongScreenType "tsHandle" + + where + -- Reload and fully regenerate the transaction screen. + -- XXX On transaction screen or below, this is tricky because of a current limitation of regenerateScreens. + -- For now we try to work around by re-entering the screen(s). + -- This can show flicker in the UI and it's hard to handle all situations robustly. + tsReload copts d ui = uiReload copts d ui >>= reEnterTransactionScreen copts d + tsReloadIfFileChanged copts d j ui = liftIO (uiReloadIfFileChanged copts d j ui) >>= reEnterTransactionScreen copts d + + reEnterTransactionScreen _copts d ui = do + -- 1. If uiReload (or checking balance assertions) moved us to the error screen, save that, and return to the transaction screen. + let + (merrscr, uiTxn) = case aScreen $ uiCheckBalanceAssertions d ui of + s@(ES _) -> (Just s, popScreen ui) + _ -> (Nothing, ui) + -- 2. Exit to register screen + let uiReg = popScreen uiTxn + put' uiReg + -- 3. Re-enter the transaction screen + rsHandle (VtyEvent (EvKey KEnter [])) -- PARTIAL assumes we are on the register screen. + -- 4. Return to the error screen (below the transaction screen) if there was one. + -- Next events will be handled by esHandle. Error repair will return to the transaction screen. + maybe (return ()) (put' . flip pushScreen uiTxn) merrscr + -- doesn't uiTxn have old state from before step 3 ? seems to work + + -- XXX some problem: + -- 4. Reload once more, possibly re-entering the error screen, by sending a g event. + -- sendVtyEvents [EvKey (KChar 'g') []] -- XXX Might be disrupted if other events are queued + + -- XXX doesn't update on non-error change: + -- 4. Reload once more, possibly re-entering the error screen. + -- uiTxnOrErr <- uiReload copts d uiTxn + -- uiReloadIfChanged ? + -- uiCheckBalanceAssertions ? seems unneeded + -- put' uiTxnOrErr + + -- XXX not working right: + -- -- 1. If uiReload (or checking balance assertions) moved us to the error screen, exit to the transaction screen. + -- let + -- uiTxn = case aScreen $ uiCheckBalanceAssertions d ui of + -- ES _ -> popScreen ui + -- _ -> ui + -- -- 2. Exit to register screen + -- put' $ popScreen uiTxn + -- -- 3. Re-enter the transaction screen, and reload once more. + -- sendVtyEvents [EvKey KEnter [], EvKey (KChar 'g') []] -- XXX Might be disrupted if other events are queued + -- | Select a new transaction and update the previous register screen tsSelect :: Integer -> Transaction -> UIState -> UIState diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hledger-ui-1.50/Hledger/UI/UIState.hs new/hledger-ui-1.50.1/Hledger/UI/UIState.hs --- old/hledger-ui-1.50/Hledger/UI/UIState.hs 2025-09-02 08:59:50.000000000 +0200 +++ new/hledger-ui-1.50.1/Hledger/UI/UIState.hs 2025-09-16 02:35:06.000000000 +0200 @@ -35,7 +35,7 @@ ,resetDepth ,popScreen ,pushScreen -,enableForecastPreservingPeriod +,enableForecast ,resetFilter ,resetScreens ,regenerateScreens @@ -60,6 +60,7 @@ import Hledger.UI.UITypes import Hledger.UI.UIOptions (UIOpts(uoCliOpts)) import Hledger.UI.UIScreens (screenUpdate) +import Hledger.UI.UIUtils (showScreenId, showScreenStack) -- | Make an initial UI state with the given options, journal, -- parent screen stack if any, and starting screen. @@ -201,20 +202,19 @@ where newForecast = case ui^.forecast of Just _ -> Nothing - Nothing -> enableForecastPreservingPeriod ui (ui^.cliOpts) ^. forecast + Nothing -> enableForecast (astartupopts ui) (ui^.cliOpts) ^. forecast --- | Ensure this CliOpts enables forecasted transactions. --- If a forecast period was specified in the old CliOpts, --- or in the provided UIState's startup options, --- it is preserved. -enableForecastPreservingPeriod :: UIState -> CliOpts -> CliOpts -enableForecastPreservingPeriod ui copts = set forecast mforecast copts +-- | Enable forecasting in this CliOpts. +-- If it previously specified a forecast period, or else if the given ui startup options did, +-- preserve that as the forecast period. +enableForecast :: UIOpts -> CliOpts -> CliOpts +enableForecast startopts currentopts = set forecast mforecast currentopts where - mforecast = asum [mprovidedforecastperiod, mstartupforecastperiod, mdefaultforecastperiod] + mforecast = asum [mcurrentforecastperiod, mstartupforecastperiod, mdefaultforecastperiod] where - mprovidedforecastperiod = copts ^. forecast - mstartupforecastperiod = astartupopts ui ^. forecast - mdefaultforecastperiod = Just nulldatespan + mcurrentforecastperiod = currentopts ^. forecast + mstartupforecastperiod = startopts ^. forecast + mdefaultforecastperiod = Just nulldatespan -- | Toggle between showing all and showing only real (non-virtual) items. toggleReal :: UIState -> UIState @@ -340,12 +340,16 @@ setMode m ui = ui{aMode=m} pushScreen :: Screen -> UIState -> UIState -pushScreen scr ui = ui{aPrevScreens=(aScreen ui:aPrevScreens ui) - ,aScreen=scr - } +pushScreen scr ui = + dbg1Msg ("pushing screen " <> showScreenId scr <> ". " <> showScreenStack "" showScreenId ui1) + ui1 + where ui1 = ui{aPrevScreens=aScreen ui:aPrevScreens ui, aScreen=scr } popScreen :: UIState -> UIState -popScreen ui@UIState{aPrevScreens=s:ss} = ui{aScreen=s, aPrevScreens=ss} +popScreen ui@UIState{aPrevScreens = s : ss} = + dbg1Msg ("popping screen " <> showScreenId (aScreen ui) <> ". " <> showScreenStack "" showScreenId ui1) + ui1 + where ui1 = ui{aPrevScreens = ss ,aScreen = s } popScreen ui = ui -- | Reset options to their startup values, discard screen navigation history, @@ -361,7 +365,9 @@ -- then regenerate the content of all screens in the stack -- (using the ui state's current options), preserving the screen navigation history. -- Note, does not save the reporting date. +-- +-- XXX Currently this does not properly regenerate the transaction screen or error screen, +-- which depend on state from their parent(s); those screens' handlers must do additional work, which is fragile. regenerateScreens :: Journal -> Day -> UIState -> UIState regenerateScreens j d ui@UIState{aopts=opts, aScreen=s,aPrevScreens=ss} = ui{ajournal=j, aScreen=screenUpdate opts d j s, aPrevScreens=map (screenUpdate opts d j) ss} - diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hledger-ui-1.50/Hledger/UI/UIUtils.hs new/hledger-ui-1.50.1/Hledger/UI/UIUtils.hs --- old/hledger-ui-1.50/Hledger/UI/UIUtils.hs 2025-09-02 08:04:18.000000000 +0200 +++ new/hledger-ui-1.50.1/Hledger/UI/UIUtils.hs 2025-09-16 02:35:06.000000000 +0200 @@ -42,6 +42,7 @@ ,mapScreens ,uiNumBlankItems ,showScreenStack + ,sendVtyEvents ) where @@ -51,6 +52,7 @@ import Brick.Widgets.Dialog import Brick.Widgets.Edit import Brick.Widgets.List (List, listSelectedL, listNameL, listItemHeightL, listSelected, listMoveDown, listMoveUp, GenericList, listElements) +import Control.Concurrent.STM (atomically, writeTChan) -- GHC only import Control.Monad.IO.Class import Data.Bifunctor (second) import Data.List @@ -59,6 +61,7 @@ import Graphics.Vty (Event(..),Key(..),Modifier(..),Vty(..),Color,Attr,currentAttr,refresh, displayBounds -- ,Output(displayBounds,mkDisplayContext),DisplayContext(..) + ,Vty (inputIface), InternalEvent (InputEvent), Input (eventChannel) ) import Lens.Micro.Platform @@ -530,3 +533,10 @@ -- | debugLevel >= uiDebugLevel = 0 -- suppress to improve debug output. -- | otherwise = 100 -- 100 ought to be enough for anyone + +-- Send some events to vty, atomically so they won't have other events interleaved. +-- (But there may be events already in the channel ahead of them.) +sendVtyEvents :: [Event] -> EventM n s () +sendVtyEvents evs = do + input <- eventChannel . inputIface <$> getVtyHandle + liftIO $ atomically $ mapM_ (writeTChan input . InputEvent) evs diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hledger-ui-1.50/hledger-ui.1 new/hledger-ui-1.50.1/hledger-ui.1 --- old/hledger-ui-1.50/hledger-ui.1 2025-09-03 21:06:35.000000000 +0200 +++ new/hledger-ui-1.50.1/hledger-ui.1 2025-09-16 14:20:53.000000000 +0200 @@ -1,5 +1,5 @@ -.TH "HLEDGER\-UI" "1" "September 2025" "hledger-ui-1.50 " "hledger User Manuals" +.TH "HLEDGER\-UI" "1" "September 2025" "hledger-ui-1.50.1 " "hledger User Manuals" @@ -17,7 +17,7 @@ .PD \f[CR]hledger ui [OPTS] [QUERYARGS]\f[R] .SH DESCRIPTION -This manual is for hledger\[aq]s terminal interface, version 1.50. +This manual is for hledger\[aq]s terminal interface, version 1.50.1. See also the hledger manual for common concepts and file formats. .PP hledger is a robust, user\-friendly, cross\-platform set of programs for diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hledger-ui-1.50/hledger-ui.cabal new/hledger-ui-1.50.1/hledger-ui.cabal --- old/hledger-ui-1.50/hledger-ui.cabal 2025-09-03 21:06:35.000000000 +0200 +++ new/hledger-ui-1.50.1/hledger-ui.cabal 2025-09-16 14:20:53.000000000 +0200 @@ -1,11 +1,11 @@ cabal-version: 2.2 --- This file has been generated from package.yaml by hpack version 0.38.2. +-- This file has been generated from package.yaml by hpack version 0.38.1. -- -- see: https://github.com/sol/hpack name: hledger-ui -version: 1.50 +version: 1.50.1 synopsis: Terminal interface for the hledger accounting system description: A simple terminal user interface for the hledger accounting system. It can be a more convenient way to browse your accounts than the CLI. @@ -74,7 +74,7 @@ hs-source-dirs: ./ ghc-options: -Wall -Wno-incomplete-uni-patterns -Wno-missing-signatures -Wno-orphans -Wno-type-defaults -Wno-unused-do-bind - cpp-options: -DVERSION="1.50" -DVERSION="1.50" + cpp-options: -DVERSION="1.50.1" -DVERSION="1.50.1" build-depends: ansi-terminal >=0.9 , async @@ -89,8 +89,8 @@ , filepath , fsnotify >=0.4.2.0 && <0.5 , githash >=0.1.6.2 - , hledger ==1.50.* - , hledger-lib ==1.50.* + , hledger >=1.50.1 && <1.51 + , hledger-lib >=1.50.1 && <1.51 , megaparsec >=7.0.0 && <9.8 , microlens >=0.4 , microlens-platform >=0.2.3.1 @@ -98,6 +98,7 @@ , process >=1.2 , safe >=0.3.20 , split >=0.1 + , stm , text >=1.2.4.1 , text-zipper >=0.4 , time >=1.5 @@ -124,7 +125,7 @@ hs-source-dirs: app ghc-options: -Wall -Wno-incomplete-uni-patterns -Wno-missing-signatures -Wno-orphans -Wno-type-defaults -Wno-unused-do-bind -with-rtsopts=-T - cpp-options: -DVERSION="1.50" + cpp-options: -DVERSION="1.50.1" build-depends: base >=4.18 && <4.22 , hledger-ui diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hledger-ui-1.50/hledger-ui.info new/hledger-ui-1.50.1/hledger-ui.info --- old/hledger-ui-1.50/hledger-ui.info 2025-09-03 21:06:35.000000000 +0200 +++ new/hledger-ui-1.50.1/hledger-ui.info 2025-09-16 14:20:53.000000000 +0200 @@ -18,7 +18,7 @@ or 'hledger ui [OPTS] [QUERYARGS]' - This manual is for hledger's terminal interface, version 1.50. See + This manual is for hledger's terminal interface, version 1.50.1. See also the hledger manual for common concepts and file formats. hledger is a robust, user-friendly, cross-platform set of programs @@ -565,22 +565,22 @@ Tag Table: Node: Top221 -Node: OPTIONS1867 -Node: MOUSE8753 -Node: KEYS9085 -Node: SCREENS14089 -Node: Menu screen14829 -Node: Cash accounts screen15145 -Node: Balance sheet accounts screen15506 -Node: Income statement accounts screen15842 -Node: All accounts screen16227 -Node: Register screen16590 -Node: Transaction screen19033 -Node: Error screen20608 -Node: WATCH MODE20974 -Node: --watch problems21872 -Node: ENVIRONMENT23225 -Node: BUGS23458 +Node: OPTIONS1869 +Node: MOUSE8755 +Node: KEYS9087 +Node: SCREENS14091 +Node: Menu screen14831 +Node: Cash accounts screen15147 +Node: Balance sheet accounts screen15508 +Node: Income statement accounts screen15844 +Node: All accounts screen16229 +Node: Register screen16592 +Node: Transaction screen19035 +Node: Error screen20610 +Node: WATCH MODE20976 +Node: --watch problems21874 +Node: ENVIRONMENT23227 +Node: BUGS23460 End Tag Table diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hledger-ui-1.50/hledger-ui.txt new/hledger-ui-1.50.1/hledger-ui.txt --- old/hledger-ui-1.50/hledger-ui.txt 2025-09-03 21:06:35.000000000 +0200 +++ new/hledger-ui-1.50.1/hledger-ui.txt 2025-09-16 14:20:53.000000000 +0200 @@ -11,7 +11,7 @@ hledger ui [OPTS] [QUERYARGS] DESCRIPTION - This manual is for hledger's terminal interface, version 1.50. See + This manual is for hledger's terminal interface, version 1.50.1. See also the hledger manual for common concepts and file formats. hledger is a robust, user-friendly, cross-platform set of programs for @@ -471,4 +471,4 @@ SEE ALSO hledger(1), hledger-ui(1), hledger-web(1), ledger(1) -hledger-ui-1.50 September 2025 HLEDGER-UI(1) +hledger-ui-1.50.1 September 2025 HLEDGER-UI(1)
