emacs-super-duper-yes-or-no

Replace the yes-or-no function with an even more demanding yes or no prompt, rather than with y-or-n.
Log | Files | Refs | LICENSE

commit 4094cc228cc3a9f3e79e6eaae47808050abb01a3
parent 9cb390c90fec53e5855beba5db0fa60dda91a86c
Author: Yuval Langer <yuvallangerontheroad@gmail.com>
Date:   Fri,  6 Oct 2023 00:16:16 +0300

Add some docstrings.  Also `cl-loop`ify my loops.

Diffstat:
A.dir-locals.el | 2++
Msuper-duper-yes-or-no.el | 219+++++++++++++++++++++++++++++++++++++++++--------------------------------------
2 files changed, 117 insertions(+), 104 deletions(-)

diff --git a/.dir-locals.el b/.dir-locals.el @@ -0,0 +1,2 @@ +((emacs-lisp-mode + (indent-tabs-mode nil diff --git a/super-duper-yes-or-no.el b/super-duper-yes-or-no.el @@ -57,13 +57,16 @@ (* 0 0))) (defun sd--randint (minimum maximum) - "Returns an integer between MINIMUM (inclusive) and MAXIMUM (exclusive)." + "Return an integer between MINIMUM (inclusive) and MAXIMUM (exclusive)." (+ minimum (random (- maximum minimum)))) (defun sd--make-random-input-string (wanted-number-of-words) + "Make a list of words, WANTED-NUMBER-OF-WORDS long. + +Used in the `super-duper-yes-or-no-yes-or-no-words-p' function." (let ((words ()) (wordlist-size (seq-length sd-words))) (while (not (= (seq-length words) @@ -76,6 +79,9 @@ words)) (defun sd--make-random-yes-or-no-input-pair (number-of-words) + "Make two different word lists, each NUMBER-OF-WORDS long. + +Used in super-duper-yes-or-no-words-p function." (let ((yes-input (sd--make-random-input-string number-of-words))) (named-let loop ((no-input (sd--make-random-input-string number-of-words))) @@ -87,6 +93,15 @@ no-input)))))) (defun sd--list-intersperse (input-list intersperser) + "Return INPUT-LIST interspersed with INTERSPERSER. + +Example: + +\(sd--list-intersperse '(a b c) 'd) + +Will result with: + +'(a d b d c)" (cond ((null input-list) '()) @@ -98,7 +113,10 @@ (sd--list-intersperse (cdr input-list) intersperser)))))) -(defun sd-yes-or-no-p (prompt) +(defun sd-yes-or-no-words-p (prompt) + "Ask user a yes or no question. + +Display in minibuffer PROMPT followed by two sequences of words, one for yes and the other for no." (let* ((wanted-yes-or-no (sd--make-random-yes-or-no-input-pair sd-number-of-words)) (wanted-yes (car wanted-yes-or-no)) @@ -140,6 +158,15 @@ (upcase our-char)))) (defun sd--randomly-toggle-string-case (input-string number-of-chars-to-toggle) + "Toggle a number of chars' case in a string. + +A NUMBER-OF-CHARS-TO-TOGGLE chars will be selected at random from +INPUT-STRING. Then function returns INPUT-STRING, with the +selected chars' case toggled. + +NUMBERS-OF-CHARS-TO-TOGGLE MUST be less or equal to the number of +English ASCII chars in INPUT-STRING - other chars do not count in +the toggle count AND they are not toggled." (let* ((input-length (length input-string)) (our-positions (number-sequence 0 (1- input-length))) (our-shuffled-positions nil)) @@ -174,70 +201,62 @@ input-char))))) (defun sd--string-only-uppercase (input-string) - (concat - (cl-loop for input-char - across input-string - when (char-uppercase-p input-char) - collect input-char))) + "Return INPUT-STRING with only the uppercase chars." + (cl-loop + for input-char + across input-string + when (char-uppercase-p input-char) + concat (char-to-string input-char))) (defun sd-yes-or-no-toggle-case-p (prompt) - (let* ((wanted-yes-prompt - (sd--randomly-toggle-string-case - sd-upper-case-phrase-for-yes - sd-number-of-case-toggle-characters)) - (wanted-no-prompt - (sd--randomly-toggle-string-case - sd-upper-case-phrase-for-no - sd-number-of-case-toggle-characters)) - (wanted-input-yes - (sd--string-only-uppercase - wanted-yes-prompt)) - (wanted-input-no - (sd--string-only-uppercase - wanted-no-prompt)) - ) - (while (equal wanted-input-yes - wanted-input-no) - (setq wanted-yes-prompt - (sd--randomly-toggle-string-case - sd-upper-case-phrase-for-yes - sd-number-of-case-toggle-characters)) - (setq wanted-no-prompt - (sd--randomly-toggle-string-case - sd-upper-case-phrase-for-no - sd-number-of-case-toggle-characters)) - (setq wanted-input-yes - (sd--string-only-uppercase - wanted-yes-prompt)) - (setq wanted-input-no - (sd--string-only-uppercase - wanted-no-prompt))) - (let ((user-input - (read-from-minibuffer - (concat prompt - wanted-yes-prompt - "\n" - wanted-no-prompt - ":\n")))) - (while (not (or (equal user-input - wanted-input-yes) - (equal user-input - wanted-input-no))) - (setq user-input - (read-from-minibuffer - (concat prompt - wanted-yes-prompt - "\n" - wanted-no-prompt - ":\n")))) - (equal user-input - wanted-input-yes)))) + "Ask user a yes or no question, but expect the uppercase letters. + +Display in minibuffer PROMPT followed by two lines, one for yes +and the other for no. Each of those two lines have a sequence of +uppercase letters sprinkled inside. If you want to answer yes, +write in sequence the uppercase letters of the first line, otherwise, the uppercase letters of the second." + (cl-loop + for wanted-yes-prompt = + (sd--randomly-toggle-string-case + sd-upper-case-phrase-for-yes + sd-number-of-case-toggle-characters) + for wanted-no-prompt = + (sd--randomly-toggle-string-case + sd-upper-case-phrase-for-no + sd-number-of-case-toggle-characters) + for wanted-input-yes = + (sd--string-only-uppercase + wanted-yes-prompt) + for wanted-input-no = + (sd--string-only-uppercase + wanted-no-prompt) + while (equal wanted-input-yes + wanted-input-no) + finally return + (cl-loop + for user-input = + (read-from-minibuffer + (concat prompt + wanted-yes-prompt + "\n" + wanted-no-prompt + ":\n")) + until (or (equal user-input + wanted-input-yes) + (equal user-input + wanted-input-no)) + finally return + (equal user-input + wanted-input-yes)))) (defun sd--make-arithmetic-problem-number () + "Return a random integer number between the two variables. +`sd-arithmetic-problem-minimum' and `sd-arithmetic-problem-maximum'." (sd--randint sd-arithmetic-problem-minimum (1+ sd-arithmetic-problem-maximum))) (defun sd--make-arithmetic-problem-arguments (argument-list) + "Return ARGUMENT-LIST in an arithmetic problem with actual integer values." (cond ((null argument-list) '()) @@ -251,58 +270,50 @@ (cdr argument-list)))))) (defun sd--make-arithmetic-problem (arithmetic-expression-template) + "Make an arithmetic problem using an ARITHMETIC-EXPRESSION-TEMPLATE." (cond ((null arithmetic-expression-template) '()) (t (cons (car arithmetic-expression-template) (sd--make-arithmetic-problem-arguments (cdr arithmetic-expression-template)))))) (defun sd--yes-or-no-arithmetic-problem-p (prompt) - (let* ((wanted-yes-prompt - (sd--make-arithmetic-problem - sd--arithmetic-problem-template)) - (wanted-no-prompt - (sd--make-arithmetic-problem - sd--arithmetic-problem-template)) - (wanted-input-yes - (eval wanted-yes-prompt)) - (wanted-input-no - (eval wanted-no-prompt))) - (while (= wanted-input-yes - wanted-input-no) - (setq wanted-yes-prompt - (sd--make-arithmetic-problem - sd--arithmetic-problem-template)) - (setq wanted-no-prompt - (sd--make-arithmetic-problem - sd--arithmetic-problem-template)) - (setq wanted-input-yes - (eval wanted-yes-prompt)) - (setq wanted-input-no - (eval wanted-no-prompt))) - (let ((user-input - (string-to-number - (read-from-minibuffer - (concat prompt - "Please answer " - (format "%S" wanted-yes-prompt) - " for \"yes\" and " - (format "%S" wanted-no-prompt) - " for \"no\": "))))) - (while (not (or (= user-input - wanted-input-yes) - (= user-input - wanted-input-no))) - (setq user-input - (string-to-number - (read-from-minibuffer - (concat prompt - "Please answer " - (format "%S" wanted-yes-prompt) - " for \"yes\" and " - (format "%S" wanted-no-prompt) - " for \"no\": "))))) - (= user-input - wanted-input-yes)))) + "Ask user a yes or no question, but as an answer to an arithmetic expression. + +Display in minibuffer PROMPT followed by two arithmetic +expressions, one for yes and the other for no. If you want to +answer yes, write the number that is the answer to the first +problem, otherwise, the answer to the second problem." + (cl-loop + for wanted-yes-prompt = + (sd--make-arithmetic-problem + sd--arithmetic-problem-template) + for wanted-no-prompt = + (sd--make-arithmetic-problem + sd--arithmetic-problem-template) + for wanted-input-yes = + (eval wanted-yes-prompt) + for wanted-input-no = + (eval wanted-no-prompt) + while (= wanted-input-yes + wanted-input-no) + finally return + (cl-loop + for user-input = + (string-to-number + (read-from-minibuffer + (concat prompt + "Please answer " + (format "%S" wanted-yes-prompt) + " for \"yes\" and " + (format "%S" wanted-no-prompt) + " for \"no\": "))) + until (or (= user-input + wanted-input-yes) + (= user-input + wanted-input-no)) + finally return + (= user-input + wanted-input-yes)))) ;; Local Variables: ;; read-symbol-shorthands: (("sd-" . "super-duper-yes-or-no-"))