branch: elpa/dslide
commit fca6e33a6caa6b29a7d2009f9e33649f4d48d8f5
Author: Psionik K <73710933+psioni...@users.noreply.github.com>
Commit: Psionik K <73710933+psioni...@users.noreply.github.com>

    Properly declare generic methods, reconcile documentation
    
    Generic methods need docstrings in order to provide docstrings to 
implementors.
    
    The mentioned `slide` and `skip` values aren't yet recognized from init.
    
    Signed-off-by: Psionik K <73710933+psioni...@users.noreply.github.com>
---
 macro-slides.el | 338 +++++++++++++++++++++++++++++++++-----------------------
 1 file changed, 201 insertions(+), 137 deletions(-)

diff --git a/macro-slides.el b/macro-slides.el
index c98eb9326f..59e9c4319c 100644
--- a/macro-slides.el
+++ b/macro-slides.el
@@ -581,72 +581,154 @@ the mode and go to slides."
 
 ;; * Classes
 
-(defclass ms-progress-tracking ()
-  ((marker
-    :initform nil
-    :initarg :marker
-    :documentation "Marker used to track progress"))
-  "A utility class for other classes that track progress.
-Progress is tracked by reading and updating a marker.")
-
-(cl-defgeneric ms-marker (obj))
-
-(cl-defmethod ms-marker ((obj ms-progress-tracking)
-                         &optional pom)
-  "Set internal marker to POM or return marker position if set.
-Errors when asked for a marker before one has been set."
-  (let ((marker (or (oref obj marker)
-                    (pcase (type-of pom)
-                      ('marker pom)
-                      ('integer (set-marker (make-marker) pom))
-                      ('symbol nil)))))
-    (when (and marker pom)
-      (set-marker marker pom))
-    (if (and marker (marker-buffer marker))
-        (marker-position (oset obj marker marker))
-      (error "No marker was initialized"))))
-
-;; This is one of the most important interfaces for all hacking.  The domain
-;; model is that of a linear sequence of steps that the user traverses both
-;; forward and backward.
+;; This generic functions below are the most important interfaces for all
+;; hacking of this package.
+;;
+;; The domain model first must describe a linear sequence of steps that the 
user
+;; traverses both forward and backward.
 ;;
 ;; There are some states that may need to be set up or torn down at the
 ;; boundaries of the sequence.  These are handled by three methods, init, end,
 ;; and final.
 ;;
-;; Sub-sequences currently don't have any special support for setup or teardown
-;; when entering or exiting the sub-sequence.  Such cooperation is present but
-;; implemented ad-hoc.  First-class support will be consistent with the
-;; architecture.
-;;
-;; End is essentially init for going in reverse.  While using init and going
-;; forward to reach the end is theoretically viable, it does extra work and
-;; leads to headaches for implements.
+;; End is essentially init for going in reverse.  Usually this is the same as
+;; calling init and then stepping forward until no more progress is made.
+;; However doing it this way would be unable to avoid extra work and could even
+;; create headaches when implementing sequences that shouldn't use reverse to
+;; un-execute the forwards steps or in cases where implementing this is too
+;; complex to pay off to the user.  For these reasons, the implementation of
+;; `ms-end' is left up to the user.
 ;;
 ;; Goto essentially is just a careful use of step-forward.  If every forward
 ;; step properly reports its maximum extent of progress, we can use forward and
 ;; init to implement every goto.
 ;;
 ;; Finally, step-forward and step-backward should navigate the states between
-;; init / end and final.
+;; init / end and final.  They just return non-nil until they are done.  The
+;; caller doesn't care about the implementation, and that is why EIEIO is used.
+;;
+;; Sub-sequences can rely on the parent state to exist for their entire
+;; lifetime. The parent sequence will not call its own `ms-final' until after 
it
+;; has called the sub-sequence's `ms-final'.
+;;
+;; Sub-sequences currently don't have any first-class extensible support for
+;; entering or exiting the sub-sequence.  Such cooperation is present in 
limited
+;; amounts to limit coupling the parent and child sequences.
 ;;
 ;; A lazy implementer can forego methods by delegating them to simpler
 ;; idempotent methods, such as using an idempotent init for step-backward.  
With
 ;; a maximum of six methods and a minimum of two, just init and forward, you
 ;; have enough behavior to properly fit the user interface.
 
-;; Generics.  TODO check on the use of generics.
-(cl-defgeneric ms-init (obj))
+(cl-defgeneric ms-init (obj)
+  "Called when entering a sequence.
+Set up the state required for this sequence when going forward,
+entering the sequence from the beginning.
+
+Two explicit return values are understood:
+
+- `skip': This sequence has rejected being run.  `ms-final' will
+  not be called.
+
+- `step': Init was successful and should count as a step
+
+Any other return value, including nil, is considered implicit
+success but will not count as a step, meaning `ms-step-forward'
+will immediately be called.
+
+This method should work together with `ms-end' and `ms-final' to ensure
+consistently valid state for `ms-forward' and `ms-backward'.")
+
+(cl-defgeneric ms-end (obj)
+  "Init when going backwards.
+Set up the state required for this sequence when going backward,
+entering the sequence from the end.
+
+Two explicit return values are understood:
+
+- `skip': This sequence has rejected being run.  `ms-final' will
+  not be called.
+
+- `step': Init was successful and should count as a step
+
+Any other return value, including nil, is considered implicit
+success but will not count as a step, meaning `ms-step-forward'
+will immediately be called.
+
+
+The first job of this method is to perform setup, possibly by
+just calling init since they likely have similar side-effects.
+
+Second, this method should reach the state that is equivalent to
+if the user called forward until no more progress could be made.
+
+The default implementation calls `ms-init' and then calls
+`ms-step-forward' until no more progress can be made.  If this is
+inappropriate, it should be overridden.
+
+In cases where you don't need a real backward implementation or
+progressing backwards would have no sensible behavior, you can
+delegate this to init and possibly delegate backward to forward,
+resulting in a sequence that always starts at the beginning and
+always proceeds to the end.  For a single step sequence that has
+identical effect in both directions, this is appropriate.
+
+This method should work together with `ms-end' and `ms-final' to
+ensure consistently valid state for `ms-forward' and
+`ms-backward'")
+
+(cl-defgeneric ms-final (obj)
+  "Called when exiting a sequence.
+Implement this method to clean up any state that would interfere
+with the sequence succeeding when run again.  If your sequence
+implements real backward behavior,
+
+All side-effects and states created by steps in the sequence or
+the `ms-init' and `ms-end' methods must be cleaned up or
+otherwise managed or else `ms-step-backward' and other sequences
+of running a presentation will be brittle and likely fail when
+re-run.")
+
+(cl-defgeneric ms-step-forward (obj)
+  "Make one step forward.
+The return value has meaning to the deck:
 
-(cl-defgeneric ms-end (obj))
+- t: progress was made
 
-(cl-defgeneric ms-final (obj))
+- a point: progress was made up to a specific buffer location
 
-(cl-defgeneric ms-step-forward (obj))
+- nil: no progress could be made.
 
-(cl-defgeneric ms-step-backward (obj))
+For sequences that don't make progress in a buffer, returning t
+is fine.  Returning a point of progress is necessary for the
+default implementation of `ms-goto'.
 
-(cl-defgeneric ms-goto (obj point))
+⚠ Every sequence of `ms-step-forward' should return nil at some
+point or else infinite loops will result.")
+
+(cl-defgeneric ms-step-backward (obj)
+  "Make one step backwards and return earliest point.
+The return value has meaning to the deck:
+
+- t: progress was made
+
+- a point: progress was made up to a specific buffer location
+
+- nil: no progress could be made.
+
+For sequences that don't make progress in a buffer, returning t
+is fine.  Returning a point of progress is necessary for the
+default implementation of `ms-goto'.
+
+⚠ Every sequence of `ms-step-backward' should return nil at some
+point or else infinite loops will result.")
+
+(cl-defgeneric ms-goto (obj point)
+  "Step forward until advancing beyond POINT.
+This method can usually be implemented on top of
+`ms-step-forward' by advancing until POINT is exceeded.  The
+default implementation calls init.  You should call init if you
+override this method.")
 
 ;; ** Stateful Sequence
 (defclass ms-stateful-sequence ()
@@ -654,100 +736,80 @@ Errors when asked for a marker before one has been set."
     :initval nil
     :initarg :parent
     :documentation "Parent or root sequence.
-Usually a deck or slide."))
+Usually a deck or slide.  In the function stack analogy, this is
+the same as storing a stack pointer for returning to the caller."))
+
   "An interface definition for linear sequences of steps.
-The sequence can be traversed forwards and backward and also
-indexed into from higher level navigation commands.  Sequences
-can run as sub-sequences, where one sequence calls into another.
+This is an abstract class.
+
+The sequence can be traversed forwards and backward.  `init' and
+`foward' are conjugates of `end' and 'backward'.
+
+Because the sequence steps may rely on some setup and should
+perform necessary teardown, the stateful sequence provides `init'
+`end' and `final' methods.
 
-Because the steps may rely on some setup and teardown, the
-stateful sequence provides methods to call these functions at the
-appropriate times.
+It can also be indexed by high-level navigation commands.  The
+implementation of `ms-goto' Sequences can run as sub-sequences,
+where one sequence calls into another.
 
 Classes that wish to implement the stateful sequence interface
 just need to support a few methods and then rely on the generic
 implementations for the rest, unless they want to optimize or
-simplify their implementation.")
+simplify their implementation."
+  :abstract t)
 
-(cl-defmethod ms-init ((obj ms-stateful-sequence))
-  "Called when entering a sequence.
-Any state that must be set up for this sequence can run during
-the init method.  Init does not count as a step.  The guarantee
-from callers is that if init is called, `ms-final'
-will also be called.
-
-TODO Return is currently ignored, mainly because most init
-implementations are expected to produce side-effects rather than
-meaningful return values.
-
-Rather than implement this function in an idempotent way, work
-together with `ms-final' to make guarantees about
-initial conditions and tidyingup a sequence that has completely
-run its course.
-
-PARENT exists when the sequence is a sub-sequence.  Sub-sequences
-can rely on the parent state to exist for their entire lifetime.
-The parent sequence will not call its own `ms-final'
-until after it calls the sub-sequence's `ms-final'."
-  nil)
+(cl-defmethod ms-init ((_ ms-stateful-sequence)))
 
 (cl-defmethod ms-end ((obj ms-stateful-sequence))
-  "Init when going backwards.
-This method should be implemented so that the state is equivalent
-to having gone forward to the end of the slide.  The default
-implementation calls init and then advances to the end.  This can
-be inappropriate in a number of cases, and should be overridden.
-Re-using init is appropriate when a proper backward
-implementation is not valued.
-
-Just as init anticipates having forward called at least once, end
-should anticipate backward being called at least once.  This
-allows initial narrowing and slide behavior to be signaled
-properly to children and section actions."
-  (ms-init obj)
-  (let (extent (advanced t))
-    (while advanced
-      (when-let ((progress (ms-step-forward obj)))
-        (setq extent progress)))
-    extent))
-
-(cl-defmethod ms-final ((obj ms-stateful-sequence))
-  "Called when exiting a sequence.
-Implement this method to clean up any state that would interfere
-with the sequence succeeding when run again.  All side-effects
-and states created by steps in the sequence or the `ms-init'
-method must be cleaned up or otherwise managed or else
-`ms-step-backward' and other sequences of running a presentation
-will be brittle and likely fail when re-run."
-  nil)
-
-(cl-defmethod ms-step-forward ((obj ms-stateful-sequence))
-  "Make on step and return the point of farthest advance.
-When no progress can be made, return nil.  For steps that don't
-need to advance the point, if they make progress, they should
-return t or the point.  Every sequenece of `ms-step-forward'
-should return nil at some point."
-  nil)
-
-(cl-defmethod ms-step-backward ((obj ms-stateful-sequence))
-  "Make one step backwards and return earliest point.
-Backwards steps are considered to advance to be beginning of the
-extent they affect.  This enables forward and backward
-implementations to act as conjugates."
-  nil)
+  (unless (eq 'skip (ms-init obj))
+    (let ((progress t))
+      (while progress
+        (setq progress (ms-step-forward obj))))))
+
+(cl-defmethod ms-step-forward ((_ ms-stateful-sequence)))
+
+(cl-defmethod ms-step-backward ((_ ms-stateful-sequence)))
+
+(cl-defmethod ms-final ((_ ms-stateful-sequence)))
 
 (cl-defmethod ms-goto ((obj ms-stateful-sequence) point)
-  "Step forward until advancing beyond POINT.
-This method can usually be implemented on top of
-`ms-step-forward' by advancing until POINT is exceeded.
-`ms-init' is guaranteed to have been called."
-  (let (exceeded (advanced t))
-    (while (and advanced (not exceeded))
-      (let ((progress (ms-step-forward obj)))
-        (if (and (numberp progress)
-                 (>= progress point))
-            (setq exceeded progress)
-          (setq advanced progress))))))
+
+
+  (unless (eq 'skip (ms-init obj))
+    (let (exceeded (advanced t))
+      (while (and advanced (not exceeded))
+        (let ((progress (ms-step-forward obj)))
+          (if (and (numberp progress)
+                   (>= progress point))
+              (setq exceeded t)
+            (setq advanced progress)))))))
+
+;; ** Progress
+(defclass ms-progress-tracking ()
+  ((marker
+    :initform nil
+    :initarg :marker
+    :documentation "Marker used to track progress"))
+  "A utility class for other classes that track progress.
+Progress is tracked by reading and updating a marker.")
+
+(cl-defgeneric ms-marker (obj))
+
+(cl-defmethod ms-marker ((obj ms-progress-tracking)
+                         &optional pom)
+  "Set internal marker to POM or return marker position if set.
+Errors when asked for a marker before one has been set."
+  (let ((marker (or (oref obj marker)
+                    (pcase (type-of pom)
+                      ('marker pom)
+                      ('integer (set-marker (make-marker) pom))
+                      ('symbol nil)))))
+    (when (and marker pom)
+      (set-marker marker pom))
+    (if (and marker (marker-buffer marker))
+        (marker-position (oset obj marker marker))
+      (error "No marker was initialized"))))
 
 ;; ** Parent
 ;; TODO this class is kind of half-baked.  It was intended to wrap up the
@@ -837,7 +899,7 @@ their init."
       ;; next slide and call the `ms-after-last-slide-hook'
       (error "No slides could initialize"))))
 
-(cl-defmethod ms-end ((obj ms-deck))
+(cl-defmethod ms-end ((_ ms-deck))
   (error "Deck has no valid concept of starting at the end."))
 
 (cl-defmethod ms-final ((obj ms-deck))
@@ -1203,6 +1265,7 @@ See `ms-default-child-action'.")
     :documentation "Run child actions within the slide action.
 This is a temporary solution to support a basic form of action
 composition, Running actions as sequences within other actions."))
+
   "Slides store some local state and delegate behavior to several
 functions. The Slide is a stateful node that hydrates around a
 heading and stores actions and their states.")
@@ -1278,18 +1341,19 @@ heading and stores actions and their states.")
 ;; headings.  We can pretty much divide the likely user needs into either what
 ;; to do with the section and what to do with the child headings.  Because the
 ;; section needs to be narrowed to, and this narrowing must be performed both
-;; forwards and backwards, we also have a slide action that is run around the
-;; section and child actions.
+;; forwards and backwards, we also have a slide action that might (see
+;; `:compose') be run around the section and child actions.
 ;;
-;; It was anticipated for a time that actions might be nested in the
-;; configuration.  However, we still have a likely need for configuring just 
the
-;; section action or just the child action, and this API is not expected to 
look
-;; that different to the user whether nesting of actions is supported or not.
+;; There is a chance that it will make sense to support nested s-expressions in
+;; the property configuration.  For now, there is only an observed need for
+;; configuring either the section action or just the child action.  A property
+;; configuration API that supports nesting is not expected to look that
+;; different.  It will involve a bit more parsing.
 ;;
 ;; Both child actions and user configuration have demonstrated a large benefit
-;; from being able to slightly change the behavior of actions.  This is why the
-;; plist arguments are supported when hydrating from org properties and child
-;; actions can pass in arguments to `ms--make-slide'.
+;; from being able to slightly change the behavior of actions.  This is why
+;; `ms--make-slide' supports plist arguments when hydrating from org properties
+;; and why child actions that create slides can pass these in via `&rest'.
 
 (defun ms--make-slide (heading parent &rest args)
   "Hydrate a slide object from a HEADING element.

Reply via email to