From 54ecf8c7721eb44cd7111ae9b812cffd4b0bb0e6 Mon Sep 17 00:00:00 2001 From: "Alejandro R. Mosteo" Date: Sun, 1 Feb 2026 23:20:56 +0100 Subject: [PATCH 1/3] feat: rate-limit Activity printing --- src/simple_logging.adb | 15 ++++++++++++++- src/simple_logging.ads | 16 ++++++++++++++-- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/simple_logging.adb b/src/simple_logging.adb index c3b251e..0b3dde1 100644 --- a/src/simple_logging.adb +++ b/src/simple_logging.adb @@ -130,6 +130,7 @@ package body Simple_Logging is Statuses : Status_Sets.Set; Last_Status_Line : Unbounded_String; -- Used for cleanup + Last_Update : Duration := 0.0; Last_Spin : Duration := 0.0; Spinner : Spinner_Holders.Holder; Spinner_Pos : Integer := 0; @@ -168,6 +169,7 @@ package body Simple_Logging is Data => (Level => Level, Start => Internal_Clock, Text => To_Unbounded_String (Text)), + Period => <>, Text_Autocomplete => To_Unbounded_String (Autocomplete_Text)) do @@ -296,7 +298,8 @@ package body Simple_Logging is procedure Step (This : in out Ongoing; New_Text : String := ""; - Clear : Boolean := False) is + Clear : Boolean := False; + Keyframe : Boolean := False) is Old_Line : constant String := This.Build_Status_Line; begin -- Update status if needed @@ -306,6 +309,16 @@ package body Simple_Logging is Statuses.Insert (This.Data); end if; + -- Early exit if rate-limited. + if Internal_Clock - Last_Update < This.Period + and then not Keyframe + and then not Clear + then + return; + end if; + + Last_Update := Internal_Clock; + declare New_Line : constant String := This.Build_Status_Line; New_Len : constant Natural := Visible_Length (New_Line); diff --git a/src/simple_logging.ads b/src/simple_logging.ads index 9b11066..fede7a7 100644 --- a/src/simple_logging.ads +++ b/src/simple_logging.ads @@ -68,6 +68,11 @@ package Simple_Logging with Preelaborate is -- Time between spinner frame changes. TODO: make this a property of the -- spinner itself. + Max_Updates_Per_Second : Natural := 10; + -- Activity updates won't exceed this many per second. Set to 0 to disable + -- rate limiting. Not to confuse with Spinner_Period, which only affects + -- the spinner animation speed. + procedure Log (Message : String; Level : Levels := Info; Entity : String := Gnat.Source_Info.Enclosing_Entity; @@ -133,12 +138,14 @@ package Simple_Logging with Preelaborate is procedure Step (This : in out Ongoing; New_Text : String := ""; - Clear : Boolean := False) + Clear : Boolean := False; + Keyframe : Boolean := False) with Pre => not (Clear and then New_Text /= ""); -- Say that progress was made, which will advance the spinner. Optionally, -- update the text to display in this activity. When Clear, remove this -- status contribution (e.g., because we are nesting further and this one - -- becomes irrelevant) + -- becomes irrelevant). When Keyframe is True, force an update even if + -- Max_FPS would prevent it. procedure New_Line (This : in out Ongoing; Text : String); @@ -176,6 +183,11 @@ private -- Rest of state not needed to rebuild the status line Text_Autocomplete : Unbounded_String; + Period : Duration := + (if Max_Updates_Per_Second = 0 + then 0.0 + else 1.0 / Max_Updates_Per_Second); + -- Here so changes to Max_FPS propagate to new Ongoing instances end record; -- Note: Although activities can be nested, there is only a global spinner -- so all that state is in the body. From ac224cc041c37b367f42e7218e4c0080e3174e90 Mon Sep 17 00:00:00 2001 From: "Alejandro R. Mosteo" Date: Sun, 1 Feb 2026 23:29:00 +0100 Subject: [PATCH 2/3] self-review --- src/simple_logging.adb | 3 ++- src/simple_logging.ads | 9 +++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/simple_logging.adb b/src/simple_logging.adb index 0b3dde1..50d9b8a 100644 --- a/src/simple_logging.adb +++ b/src/simple_logging.adb @@ -310,7 +310,8 @@ package body Simple_Logging is end if; -- Early exit if rate-limited. - if Internal_Clock - Last_Update < This.Period + if This.Period > 0.0 + and then Internal_Clock - Last_Update < This.Period and then not Keyframe and then not Clear then diff --git a/src/simple_logging.ads b/src/simple_logging.ads index fede7a7..f07bf7f 100644 --- a/src/simple_logging.ads +++ b/src/simple_logging.ads @@ -70,8 +70,8 @@ package Simple_Logging with Preelaborate is Max_Updates_Per_Second : Natural := 10; -- Activity updates won't exceed this many per second. Set to 0 to disable - -- rate limiting. Not to confuse with Spinner_Period, which only affects - -- the spinner animation speed. + -- rate limiting. Not to be confused with Spinner_Period, which only + -- affects the spinner animation speed. procedure Log (Message : String; Level : Levels := Info; @@ -145,7 +145,7 @@ package Simple_Logging with Preelaborate is -- update the text to display in this activity. When Clear, remove this -- status contribution (e.g., because we are nesting further and this one -- becomes irrelevant). When Keyframe is True, force an update even if - -- Max_FPS would prevent it. + -- Max_Updates_Per_Second would prevent it. procedure New_Line (This : in out Ongoing; Text : String); @@ -187,7 +187,8 @@ private (if Max_Updates_Per_Second = 0 then 0.0 else 1.0 / Max_Updates_Per_Second); - -- Here so changes to Max_FPS propagate to new Ongoing instances + -- Here so changes to Max_Updates_Per_Second propagate to new Ongoing + -- instances end record; -- Note: Although activities can be nested, there is only a global spinner -- so all that state is in the body. From f668e5a5695d74fba9ab88b6c102a9bd2f690820 Mon Sep 17 00:00:00 2001 From: Alejandro R Mosteo Date: Sun, 1 Feb 2026 23:39:30 +0100 Subject: [PATCH 3/3] fix improbable corner case Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/simple_logging.adb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/simple_logging.adb b/src/simple_logging.adb index 50d9b8a..760eec5 100644 --- a/src/simple_logging.adb +++ b/src/simple_logging.adb @@ -130,7 +130,7 @@ package body Simple_Logging is Statuses : Status_Sets.Set; Last_Status_Line : Unbounded_String; -- Used for cleanup - Last_Update : Duration := 0.0; + Last_Update : Duration := -1.0; Last_Spin : Duration := 0.0; Spinner : Spinner_Holders.Holder; Spinner_Pos : Integer := 0;