Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add next-source-p and previous-source-p. #24

Merged
merged 2 commits into from
Oct 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 30 additions & 12 deletions prompter.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -269,23 +269,41 @@ when STEPS is positive (resp. negative)."
(defun empty-source-p (source)
(not (suggestions source)))

(export-always 'adjacent-source)
(defun adjacent-source (prompter &key (steps 1) (source (current-source prompter)))
"Return non-nil when PROMPTER has a non-empty source STEPS away from SOURCE.

When STEPS is 0, do nothing.
When STEPS is negative, go backward."
(sera:and-let* ((_ (not (= steps 0)))
(nonempty-sources (remove-if #'empty-source-p (sources prompter)))
(current-source-index (or (position source nonempty-sources)
0))
(new-source-index (alex:clamp (+ steps current-source-index)
0
(1- (length nonempty-sources))))
(_ (not (= current-source-index new-source-index))))
(nth new-source-index nonempty-sources)))

(export-always 'next-source-p)
(defun next-source-p (prompter)
"Returns non-nil when PROMPTER has a non-empty next source."
(adjacent-source prompter :steps 1))

(export-always 'previous-source-p)
(defun previous-source-p (prompter)
"Returns non-nil when PROMPTER has a non-empty previous source."
(adjacent-source prompter :steps -1))

(export-always 'next-source)
(defun next-source (prompter &optional (steps 1))
"Set `current-suggestion' after traversing STEPS non-empty sources.
When STEPS is 0, do nothing.
The `current-suggestion' is set to be the topmost of the destination source.

The `current-suggestion' is set to be the topmost of the destination source.
See also `previous-source'."
(unless (= 0 steps)
(sera:and-let* ((nonempty-sources (remove-if #'empty-source-p (sources prompter)))
(source-index (or (position (current-source prompter)
nonempty-sources)
0))
(new-source (nth (alex:clamp (+ steps source-index)
0
(1- (length nonempty-sources)))
nonempty-sources)))
(setf (current-suggestion prompter) (list new-source 0)))))
(alex:if-let ((new-source (adjacent-source prompter :steps steps)))
(setf (current-suggestion prompter) (list new-source 0))
(current-suggestion prompter)))

(export-always 'previous-source)
(defun previous-source (prompter &optional (steps 1))
Expand Down
41 changes: 41 additions & 0 deletions tests/tests.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,47 @@
(current-suggestion-value)))
(prompter:all-ready-p prompter))))

(define-test next-and-prev-source ()
(with-report-dangling-threads
(let ((prompter (prompter:make
:sources (list (make-instance 'prompter:source
:name "Test empty source")
(make-instance 'prompter:source
:name "Test source"
:constructor '("foo" "bar"))
(make-instance 'prompter:source
:name "Test empty source")
(make-instance 'prompter:source
:name "Test source 2"
:constructor '("100 foo" "200"))
(make-instance 'prompter:source
:name "Test source 3"
:constructor '("foo" "bar" "baz"))
(make-instance 'prompter:source
:name "Test empty source")))))
(prompter:all-ready-p prompter)
(assert-false (prompter::adjacent-source prompter :steps 0))
(assert-false (prompter::adjacent-source prompter :steps -2))
(assert-false (prompter:previous-source-p prompter))
(assert-true (prompter:next-source-p prompter))
(prompter:previous-source prompter)
(assert-false (prompter:previous-source-p prompter))
(assert-true (prompter:next-source-p prompter))
(prompter:next-source prompter)
(assert-true (prompter:next-source-p prompter))
(assert-true (prompter:previous-source-p prompter))
(prompter:next-source prompter)
(assert-false (prompter:next-source-p prompter))
(assert-true (prompter:previous-source-p prompter))
(prompter:next-source prompter)
(assert-false (prompter:next-source-p prompter))
(assert-true (prompter:previous-source-p prompter))
(prompter:previous-source prompter)
(assert-true (prompter:next-source-p prompter))
(assert-true (prompter:previous-source-p prompter))
(prompter:all-ready-p prompter))))


(define-test set-current-suggestion-all-empty-sources ()
(with-report-dangling-threads
(let* ((first-empty-source (make-instance 'prompter:source
Expand Down