Sometimes, the execution progress of long-running operations has to be tracked and reported. For any particular instance of the problem, a simple hack is a sufficient solution - and there is indeed lots of code printing dots or even drawing progress bars (sometimes even with ANSI SGR escapes) from within the respective operations. Of course, such hacks are a poor solution when separation of concerns, reusability or flexibility are taken into account.
The user-interface.progress system, in combination with the
=more-conditions= system, tries to improve the situation.
As a first step, progress tracking can be separated from reporting
using the Common Lisp condition system: when there is progress to be
tracked, any code can cl:signal a
more-conditions:progress-condition. The signaling code does not
have to care or even know whether the condition will be handled
somewhere up the callstack, nor does it depend on anything more than
the more-conditions systems.
On the other hand, a piece of code wanting to report progress can do so without having to know if and where progress conditions will be signaled.
Note that the following snippets will not have any observable effect unless the signaled conditions are handled (This is usually done by setting up progress reporting).
The simplest way to report progress is calling the
more-conditions:progress function:
(more-conditions:progress :my-operation 0.5
"Processing element ~A" :some-element)There are also convenience macros for common situations:
(more-conditions:with-trivial-progress
(:my-operation "Frobbing ~A" :some-object)
CODE)This will signal a progress of 0 % before the execution of CODE and a progress of 100 % after the execution.
(more-conditions:with-sequence-progress (:my-operation my-sequence)
(dolist (my-element my-sequence)
(more-conditions:progress "Processing element ~A" my-element)
CODE))This will signal a progress condition for each element of the sequence, automatically computing the corresponding progress.
The macro user-interface.progress:with-progress-report setup up
collecting and reporting of progress information. It takes a
stream (or some other target) to which the report should be
written and style object which selects the kind of report:
(user-interface.progress:with-progress-report
(*standard-output*
:style (make-instance 'user-interface.progress::vertical))
CODE)Progress conditions signaled from CODE will result in a report
in style “vertical” being written to *standard-output*.