unofficial mirror of help-gnu-emacs@gnu.org
 help / color / mirror / Atom feed
* Compiled vs. interpreted ERT test
@ 2020-02-27 22:19 Joost Kremers
  2020-02-28  1:48 ` Noam Postavsky
  2020-02-28  2:18 ` Stefan Monnier
  0 siblings, 2 replies; 5+ messages in thread
From: Joost Kremers @ 2020-02-27 22:19 UTC (permalink / raw)
  To: help-gnu-emacs

Hi list,

I learned about the package `with-simulated-input`[1] which 
provides a macro of the same name that's intended for writing 
automated tests for code that takes user input. When trying to 
apply this in some ERT test, I found that it works fine if the 
tests are interpreted (e.g., loaded with `eval-defun`), but not 
when the test is byte-compiled. Byte-compiled tests fail with the 
following error:

```
(wrong-type-argument listp
                         #[nil "\300\207"
                               [t]
                               1])
```

I opened an issue on the package's Github page but the maintainer 
says he doesn't know how to debug this.[2] So I'm coming here in 
the hopes someone has an idea or a suggestion how to debug this.

TIA

Joost



Footnotes: 
[1]  https://github.com/DarwinAwardWinner/with-simulated-input

[2] 
https://github.com/DarwinAwardWinner/with-simulated-input/issues/6

-- 
Joost Kremers
Life has its moments



^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Compiled vs. interpreted ERT test
  2020-02-27 22:19 Compiled vs. interpreted ERT test Joost Kremers
@ 2020-02-28  1:48 ` Noam Postavsky
  2020-02-28  2:18 ` Stefan Monnier
  1 sibling, 0 replies; 5+ messages in thread
From: Noam Postavsky @ 2020-02-28  1:48 UTC (permalink / raw)
  To: Joost Kremers; +Cc: Help Gnu Emacs mailing list

On Thu, 27 Feb 2020 at 17:19, Joost Kremers <joostkremers@fastmail.fm> wrote:

> I opened an issue on the package's Github page but the maintainer
> says he doesn't know how to debug this.[2] So I'm coming here in
> the hopes someone has an idea or a suggestion how to debug this.

I'm not really sure, but it could help to reduce the example a bit.
Neither ert nor ebib are necessary to trigger this:

    (defun foo ()
      (with-simulated-input "RET"
        "xxx"))
    (byte-compile 'foo)
    (foo)

I guess the problem lies in some of the tricks with-simulated-input is
playing with closures and environments and such.



^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Compiled vs. interpreted ERT test
  2020-02-27 22:19 Compiled vs. interpreted ERT test Joost Kremers
  2020-02-28  1:48 ` Noam Postavsky
@ 2020-02-28  2:18 ` Stefan Monnier
  2020-02-28  3:16   ` Stefan Monnier
  1 sibling, 1 reply; 5+ messages in thread
From: Stefan Monnier @ 2020-02-28  2:18 UTC (permalink / raw)
  To: help-gnu-emacs

> I opened an issue on the package's Github page but the maintainer says he
> doesn't know how to debug this.[2] So I'm coming here in the hopes someone
> has an idea or a suggestion how to debug this.

I haven't looked at the rest of the code, but the first thing I saw is:

    (defmacro wsi-current-lexical-environment ()
      "Return the current lexical environment.
    
    If `lexical-binding' is not enabled, return nil.
    
    This macro expands to a Lisp form that evaluates to the current
    lexical environment. It works by creating a closure and then
    extracting and returning its lexical environment.
    
    This can be used to manually construct closures in that
    environment."
      `(let ((temp-closure (lambda () t)))
         (when (eq (car temp-closure) 'closure)
           (cadr temp-closure))))

which clearly assumes the code is interpreted since once compiled,
`temp-closure` is not going to be a list any more (and you won't be
able to extract the lexical env from it).


        Stefan




^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Compiled vs. interpreted ERT test
  2020-02-28  2:18 ` Stefan Monnier
@ 2020-02-28  3:16   ` Stefan Monnier
  2020-02-28  8:07     ` Joost Kremers
  0 siblings, 1 reply; 5+ messages in thread
From: Stefan Monnier @ 2020-02-28  3:16 UTC (permalink / raw)
  To: help-gnu-emacs

>> I opened an issue on the package's Github page but the maintainer says he
>> doesn't know how to debug this.[2] So I'm coming here in the hopes someone
>> has an idea or a suggestion how to debug this.
> I haven't looked at the rest of the code, but the first thing I saw is:

Maybe the 100% untested patch below is a good starting point.


        Stefan


diff --git a/README.md b/README.md
index f3f95e7..e85ef05 100644
--- a/README.md
+++ b/README.md
@@ -31,7 +31,7 @@ enter "world" after entering "hello" via key sequence:
 
 ```elisp
 (with-simulated-input
-    '("hello SPC" (insert "world") "RET")
+    ("hello SPC" (insert "world") "RET")
   (read-string "Say hello: "))
 ```
 
@@ -47,7 +47,7 @@ will return `"hello world"`.
 (run-with-idle-timer 500 nil 'insert "world")
 (with-simulated-input
     ;; Type "hello ", then "wait" 501 seconds, then type "RET"
-    '("hello SPC" (wsi-simulate-idle-time 501) "RET")
+    ("hello SPC" (wsi-simulate-idle-time 501) "RET")
   (read-string "Enter a string: "))
 ```
 
diff --git a/with-simulated-input.el b/with-simulated-input.el
index 0f344f1..c44f2a3 100644
--- a/with-simulated-input.el
+++ b/with-simulated-input.el
@@ -12,17 +12,6 @@
 
 ;; This file is NOT part of GNU Emacs.
 
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;
-;;; Commentary:
-
-;; This package provides a single macro, `with-simulated-input', which
-;; evaluates one or more forms while simulating a sequence of input
-;; events for those forms to read. The result is the same as if you
-;; had evaluated the forms and then manually typed in the same input.
-;; This macro is useful for non-interactive testing of normally
-;; interactive commands and functions, such as `completing-read'.
-
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;
 ;; This program is free software: you can redistribute it and/or modify
@@ -40,88 +29,18 @@
 ;;
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;
-;;; Code:
-
-(require 'cl-lib)
-
-(cl-defun wsi-key-bound-p (key)
-  "Return non-nil if KEY is bound in any keymap.
-
-This function checks every keymap in `obarray' for a binding for
-KEY, and returns t if it finds and and nil otherwise. Note that
-this checks ALL keymaps, not just currently active ones."
-  (catch 'bound
-    (mapatoms
-     (lambda (sym)
-       (let ((keymap
-              (when (boundp sym)
-                (symbol-value sym))))
-         (when (keymapp keymap)
-           (let ((binding (lookup-key keymap (kbd key))))
-             (when binding
-               (throw 'bound t)))))))
-    (throw 'bound nil)))
-
-(cl-defun wsi-get-unbound-key
-    (&optional (modifiers '("C-M-A-s-H-" "C-M-A-s-" "C-M-A-H-"))
-               (keys "abcdefghijklmnopqrstuvwxyz0123456789"))
-  "Return a key binding that is not bound in any known keymap.
-
-This function will check every letter from a to z and every
-number from 0 through 9 with several combinations of multiple
-modifiers (i.e. control, meta, alt, super, hyper). For each such
-key combination, it will check for bindings in all known keymaps,
-and return the first combination for which no such bindings
-exist. Thus, it should be safe to bind this key in a new keymap
-without interfering with any existing keymap.
-
-Optional arguments MODIFIERS and KEYS can be used the change the
-search space. MODIFIERS is a list of strings representing
-modifier combinations, e.g.:
-
-    '(\"C-\" \"M-\" \"C-M-\")
-
-for control, meta, or both. KEYS is a string containing all keys
-to check.
-"
-  (declare (advertised-calling-convention (&optional modifiers keys) nil))
-  (when (stringp modifiers)
-    (setq modifiers (list modifiers)))
-  (when (listp keys)
-    (setq keys (apply #'concat keys)))
-  (cl-loop
-   named findkey
-   for modifier in modifiers
-   do (cl-loop
-       for char across keys
-       for bind = (concat modifier (string char))
-       when (not (wsi-key-bound-p bind))
-       do (cl-return-from findkey bind))
-   finally do (error "Could not find an unbound key with the specified modifiers")))
-
-(defmacro wsi-current-lexical-environment ()
-  "Return the current lexical environment.
-
-If `lexical-binding' is not enabled, return nil.
-
-This macro expands to a Lisp form that evaluates to the current
-lexical environment. It works by creating a closure and then
-extracting and returning its lexical environment.
+;;; Commentary:
 
-This can be used to manually construct closures in that
-environment."
-  `(let ((temp-closure (lambda () t)))
-     (when (eq (car temp-closure) 'closure)
-       (cadr temp-closure))))
+;; This package provides a single macro, `with-simulated-input', which
+;; evaluates one or more forms while simulating a sequence of input
+;; events for those forms to read. The result is the same as if you
+;; had evaluated the forms and then manually typed in the same input.
+;; This macro is useful for non-interactive testing of normally
+;; interactive commands and functions, such as `completing-read'.
 
-(defun wsi-make-closure (expr env)
-  "Construct a closure from EXPR and ENV.
+;;; Code:
 
-Returns a zero-argument function that, when called, evaluates
-EXPR in lexical environment ENV and returns the result."
-  (if env
-      `(closure ,env () ,expr)
-    `(lambda () ,expr)))
+(require 'cl-lib)
 
 (defconst wsi--canary-sym (cl-gensym "wsi-canary-")
   "A unique symbol.")
@@ -139,7 +58,7 @@ keys after initiating evaluation of BODY.
 KEYS should be a string representing a sequence of key presses,
 in the format understood by `kbd'. In the most common case of
 typing in some text and pressing RET, KEYS would be something
-like `\"hello RET\"'. Note that spaced must be indicated
+like `\"hello RET\"'. Note that spaces must be indicated
 explicitly using `SPC', e.g. `\"hello SPC world RET\"'.
 
 KEYS can also be a list. In this case, each element should either
@@ -161,86 +80,74 @@ are propagated normally.
 
 The return value is the last form in BODY, as if it was wrapped
 in `progn'."
-  (declare (indent 1))
-  `(cl-letf*
-       ((lexenv (wsi-current-lexical-environment))
-        (correct-current-buffer (current-buffer))
-        (next-action-key (wsi-get-unbound-key))
-        (result wsi--canary-sym)
-        (thrown-error nil)
-        (body-form
-         '(throw 'wsi-body-finished (progn ,@body)))
-        (end-of-actions-form
-         (list 'throw
-               '(quote wsi-body-finished)
-               (list 'quote wsi--canary-sym)))
-        ;; Ensure KEYS is a list, and put the body form as the first
-        ;; item and `C-g' as the last item
-        (keylist ,keys)
-        (keylist (if (listp keylist)
-                     keylist
-                   (list keylist)))
-        ;; Build the full action list, which includes everything in
-        ;; KEYS, as well as some additional setup beforehand and
-        ;; cleanup afterward.
-        (action-list
-         (nconc
-          (list
-           ;; First we switch back to the correct buffer (since
-           ;; `execute-kbd-macro' switches to the wrong one).
-           (list 'switch-to-buffer correct-current-buffer)
-           ;; Then we run the body form
-           body-form)
-          ;; Then we run each of the actions specified in KEYS
+  (declare (indent 1)) ;; Add `debug' spec?
+  ;; Old usage convention where KEYS was evaluated at run time.
+  (when (eq 'quote (car-safe keys)) (setq keys (cadr keys)))
+  ;; Ensure KEYS is a list.
+  (unless (listp keys) (setq keys (list keys)))
+  `(with--simulated-input
+       ;; Wrap each action in a lexical closure so it can refer to
+       ;; variables from the caller.
+       (list ,@(mapcar (lambda (x) (if (stringp x) x `(lambda () ,x))) keys))
+     (lambda () ,@body)))
+
+(defun with--simulated-input (keys body-fun)
+  (let* ((current-buffer (current-buffer))
+         (next-action-key 'wsi--next-action-key)
+         (result wsi--canary-sym)
+         (thrown-error nil)
+         (real-body-fun
+          (lambda ()
+            (with-current-buffer current-buffer
+              (throw 'wsi-body-finished (funcall body-fun)))))
+         (end-of-actions-fun
+          (lambda () (throw 'wsi-body-finished wsi--canary-sym)))
+         ;; Build the full action list, which includes everything in
+         ;; KEYS, as well as some additional setup beforehand and
+         ;; cleanup afterward.
+         (action-list
+          `(,real-body-fun
+            ;; Then we run each of the actions specified in KEYS
+            ,@(cl-loop
+               for action in keys
+               if (not (stringp action))
+               collect action)
+            ;; Finally we throw the canary if we read past the end of
+            ;; the input.
+            ,end-of-actions-fun))
+         ;; Replace non-strings with `next-action-key' and concat
+         ;; everything together
+         (full-key-sequence
           (cl-loop
-           for action in keylist
-           if (not (stringp action))
-           collect action)
-          ;; Finally we throw the canary if we read past the end of
-          ;; the input.
-          (list end-of-actions-form)))
-        ;; Wrap each action in a lexical closure so it can refer to
-        ;; variables from the caller.
-        (action-closures
-         (cl-loop
-          for action in action-list
-          collect (wsi-make-closure action lexenv)))
-        ;; Replace non-strings with `next-action-key' and concat
-        ;; everything together
-        (full-key-sequence
-         (cl-loop
-          for action in keylist
-          if (stringp action)
-          collect action into key-sequence-list
-          else
-          collect next-action-key into key-sequence-list
-          finally return
-          ;; Prepend and append `next-action-key' as appropriate to
-          ;; switch buffer, run body, and throw canary.
-          (concat
-           ;; Switch to correct buffer
-           next-action-key " "
-           ;; Start executing body
-           next-action-key " "
-           ;; Execute the actual key sequence
-           (mapconcat #'identity key-sequence-list " ")
-           ;; Throw the canary if BODY reads past the provided input
-           " " next-action-key)))
-        ;; Define the next action command with lexical scope so it can
-        ;; access `action-closures'.
-        ((symbol-function 'wsi-run-next-action)
-         (lambda ()
-           (interactive)
-           (condition-case err
-               (if action-closures
-                   (let ((next-action (pop action-closures)))
-                     (funcall next-action))
-                 (error "`with-simulated-input' reached end of action list without returning"))
-             (error (throw 'wsi-threw-error err)))))
-        ;; Set up the temporary keymap
-        (action-map (make-sparse-keymap)))
+           for action in keys
+           
+           collect (if (stringp action) action next-action-key)
+           into key-sequence-list
+           finally return
+           ;; Prepend and append `next-action-key' as appropriate to
+           ;; switch buffer, run body, and throw canary.
+           (concat
+            ;; Start executing body
+            next-action-key " "
+            ;; Execute the actual key sequence
+            (mapconcat #'identity key-sequence-list " ")
+            ;; Throw the canary if BODY reads past the provided input
+            " " next-action-key)))
+         ;; Define the next action command with lexical scope so it can
+         ;; access `action-closures'.
+         (wsi-run-next-action
+          (lambda ()
+            (interactive)
+            (condition-case err
+                (if action-list
+                    (let ((next-action (pop action-list)))
+                      (funcall next-action))
+                  (error "`with-simulated-input' reached end of action list without returning"))
+              (error (throw 'wsi-threw-error err)))))
+         ;; Set up the temporary keymap
+         (action-map (make-sparse-keymap)))
      ;; Finish setting up the keymap for the temp command
-     (define-key action-map (kbd next-action-key) 'wsi-run-next-action)
+     (define-key action-map (kbd next-action-key) wsi-run-next-action)
      (setq
       thrown-error
       (catch 'wsi-threw-error




^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: Compiled vs. interpreted ERT test
  2020-02-28  3:16   ` Stefan Monnier
@ 2020-02-28  8:07     ` Joost Kremers
  0 siblings, 0 replies; 5+ messages in thread
From: Joost Kremers @ 2020-02-28  8:07 UTC (permalink / raw)
  To: help-gnu-emacs

Hi Stefan and Noam,

Thank you both for your replies. I've passed them on to the 
maintainer of `with-simulated-input`.

Joost


On Fri, Feb 28 2020, Stefan Monnier wrote:
>>> I opened an issue on the package's Github page but the 
>>> maintainer says he
>>> doesn't know how to debug this.[2] So I'm coming here in the 
>>> hopes someone
>>> has an idea or a suggestion how to debug this.
>> I haven't looked at the rest of the code, but the first thing I 
>> saw is:
>
> Maybe the 100% untested patch below is a good starting point.
>
>
>         Stefan
>
>
> diff --git a/README.md b/README.md
> index f3f95e7..e85ef05 100644
> --- a/README.md
> +++ b/README.md
> @@ -31,7 +31,7 @@ enter "world" after entering "hello" via key 
> sequence:
>  
>  ```elisp
>  (with-simulated-input
> -    '("hello SPC" (insert "world") "RET")
> +    ("hello SPC" (insert "world") "RET")
>    (read-string "Say hello: "))
>  ```
>  
> @@ -47,7 +47,7 @@ will return `"hello world"`.
>  (run-with-idle-timer 500 nil 'insert "world")
>  (with-simulated-input
>      ;; Type "hello ", then "wait" 501 seconds, then type "RET"
> -    '("hello SPC" (wsi-simulate-idle-time 501) "RET")
> +    ("hello SPC" (wsi-simulate-idle-time 501) "RET")
>    (read-string "Enter a string: "))
>  ```
>  
> diff --git a/with-simulated-input.el b/with-simulated-input.el
> index 0f344f1..c44f2a3 100644
> --- a/with-simulated-input.el
> +++ b/with-simulated-input.el
> @@ -12,17 +12,6 @@
>  
>  ;; This file is NOT part of GNU Emacs.
>  
> -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
> -;;
> -;;; Commentary:
> -
> -;; This package provides a single macro, 
> `with-simulated-input', which
> -;; evaluates one or more forms while simulating a sequence of 
> input
> -;; events for those forms to read. The result is the same as if 
> you
> -;; had evaluated the forms and then manually typed in the same 
> input.
> -;; This macro is useful for non-interactive testing of normally
> -;; interactive commands and functions, such as 
> `completing-read'.
> -
>  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
>  ;;
>  ;; This program is free software: you can redistribute it 
>  and/or modify
> @@ -40,88 +29,18 @@
>  ;;
>  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
>  ;;
> -;;; Code:
> -
> -(require 'cl-lib)
> -
> -(cl-defun wsi-key-bound-p (key)
> -  "Return non-nil if KEY is bound in any keymap.
> -
> -This function checks every keymap in `obarray' for a binding 
> for
> -KEY, and returns t if it finds and and nil otherwise. Note that
> -this checks ALL keymaps, not just currently active ones."
> -  (catch 'bound
> -    (mapatoms
> -     (lambda (sym)
> -       (let ((keymap
> -              (when (boundp sym)
> -                (symbol-value sym))))
> -         (when (keymapp keymap)
> -           (let ((binding (lookup-key keymap (kbd key))))
> -             (when binding
> -               (throw 'bound t)))))))
> -    (throw 'bound nil)))
> -
> -(cl-defun wsi-get-unbound-key
> -    (&optional (modifiers '("C-M-A-s-H-" "C-M-A-s-" 
> "C-M-A-H-"))
> -               (keys "abcdefghijklmnopqrstuvwxyz0123456789"))
> -  "Return a key binding that is not bound in any known keymap.
> -
> -This function will check every letter from a to z and every
> -number from 0 through 9 with several combinations of multiple
> -modifiers (i.e. control, meta, alt, super, hyper). For each 
> such
> -key combination, it will check for bindings in all known 
> keymaps,
> -and return the first combination for which no such bindings
> -exist. Thus, it should be safe to bind this key in a new keymap
> -without interfering with any existing keymap.
> -
> -Optional arguments MODIFIERS and KEYS can be used the change 
> the
> -search space. MODIFIERS is a list of strings representing
> -modifier combinations, e.g.:
> -
> -    '(\"C-\" \"M-\" \"C-M-\")
> -
> -for control, meta, or both. KEYS is a string containing all 
> keys
> -to check.
> -"
> -  (declare (advertised-calling-convention (&optional modifiers 
> keys) nil))
> -  (when (stringp modifiers)
> -    (setq modifiers (list modifiers)))
> -  (when (listp keys)
> -    (setq keys (apply #'concat keys)))
> -  (cl-loop
> -   named findkey
> -   for modifier in modifiers
> -   do (cl-loop
> -       for char across keys
> -       for bind = (concat modifier (string char))
> -       when (not (wsi-key-bound-p bind))
> -       do (cl-return-from findkey bind))
> -   finally do (error "Could not find an unbound key with the 
> specified modifiers")))
> -
> -(defmacro wsi-current-lexical-environment ()
> -  "Return the current lexical environment.
> -
> -If `lexical-binding' is not enabled, return nil.
> -
> -This macro expands to a Lisp form that evaluates to the current
> -lexical environment. It works by creating a closure and then
> -extracting and returning its lexical environment.
> +;;; Commentary:
>  
> -This can be used to manually construct closures in that
> -environment."
> -  `(let ((temp-closure (lambda () t)))
> -     (when (eq (car temp-closure) 'closure)
> -       (cadr temp-closure))))
> +;; This package provides a single macro, 
> `with-simulated-input', which
> +;; evaluates one or more forms while simulating a sequence of 
> input
> +;; events for those forms to read. The result is the same as if 
> you
> +;; had evaluated the forms and then manually typed in the same 
> input.
> +;; This macro is useful for non-interactive testing of normally
> +;; interactive commands and functions, such as 
> `completing-read'.
>  
> -(defun wsi-make-closure (expr env)
> -  "Construct a closure from EXPR and ENV.
> +;;; Code:
>  
> -Returns a zero-argument function that, when called, evaluates
> -EXPR in lexical environment ENV and returns the result."
> -  (if env
> -      `(closure ,env () ,expr)
> -    `(lambda () ,expr)))
> +(require 'cl-lib)
>  
>  (defconst wsi--canary-sym (cl-gensym "wsi-canary-")
>    "A unique symbol.")
> @@ -139,7 +58,7 @@ keys after initiating evaluation of BODY.
>  KEYS should be a string representing a sequence of key presses,
>  in the format understood by `kbd'. In the most common case of
>  typing in some text and pressing RET, KEYS would be something
> -like `\"hello RET\"'. Note that spaced must be indicated
> +like `\"hello RET\"'. Note that spaces must be indicated
>  explicitly using `SPC', e.g. `\"hello SPC world RET\"'.
>  
>  KEYS can also be a list. In this case, each element should 
>  either
> @@ -161,86 +80,74 @@ are propagated normally.
>  
>  The return value is the last form in BODY, as if it was wrapped
>  in `progn'."
> -  (declare (indent 1))
> -  `(cl-letf*
> -       ((lexenv (wsi-current-lexical-environment))
> -        (correct-current-buffer (current-buffer))
> -        (next-action-key (wsi-get-unbound-key))
> -        (result wsi--canary-sym)
> -        (thrown-error nil)
> -        (body-form
> -         '(throw 'wsi-body-finished (progn ,@body)))
> -        (end-of-actions-form
> -         (list 'throw
> -               '(quote wsi-body-finished)
> -               (list 'quote wsi--canary-sym)))
> -        ;; Ensure KEYS is a list, and put the body form as the 
> first
> -        ;; item and `C-g' as the last item
> -        (keylist ,keys)
> -        (keylist (if (listp keylist)
> -                     keylist
> -                   (list keylist)))
> -        ;; Build the full action list, which includes 
> everything in
> -        ;; KEYS, as well as some additional setup beforehand 
> and
> -        ;; cleanup afterward.
> -        (action-list
> -         (nconc
> -          (list
> -           ;; First we switch back to the correct buffer (since
> -           ;; `execute-kbd-macro' switches to the wrong one).
> -           (list 'switch-to-buffer correct-current-buffer)
> -           ;; Then we run the body form
> -           body-form)
> -          ;; Then we run each of the actions specified in KEYS
> +  (declare (indent 1)) ;; Add `debug' spec?
> +  ;; Old usage convention where KEYS was evaluated at run time.
> +  (when (eq 'quote (car-safe keys)) (setq keys (cadr keys)))
> +  ;; Ensure KEYS is a list.
> +  (unless (listp keys) (setq keys (list keys)))
> +  `(with--simulated-input
> +       ;; Wrap each action in a lexical closure so it can refer 
> to
> +       ;; variables from the caller.
> +       (list ,@(mapcar (lambda (x) (if (stringp x) x `(lambda 
> () ,x))) keys))
> +     (lambda () ,@body)))
> +
> +(defun with--simulated-input (keys body-fun)
> +  (let* ((current-buffer (current-buffer))
> +         (next-action-key 'wsi--next-action-key)
> +         (result wsi--canary-sym)
> +         (thrown-error nil)
> +         (real-body-fun
> +          (lambda ()
> +            (with-current-buffer current-buffer
> +              (throw 'wsi-body-finished (funcall body-fun)))))
> +         (end-of-actions-fun
> +          (lambda () (throw 'wsi-body-finished 
> wsi--canary-sym)))
> +         ;; Build the full action list, which includes 
> everything in
> +         ;; KEYS, as well as some additional setup beforehand 
> and
> +         ;; cleanup afterward.
> +         (action-list
> +          `(,real-body-fun
> +            ;; Then we run each of the actions specified in 
> KEYS
> +            ,@(cl-loop
> +               for action in keys
> +               if (not (stringp action))
> +               collect action)
> +            ;; Finally we throw the canary if we read past the 
> end of
> +            ;; the input.
> +            ,end-of-actions-fun))
> +         ;; Replace non-strings with `next-action-key' and 
> concat
> +         ;; everything together
> +         (full-key-sequence
>            (cl-loop
> -           for action in keylist
> -           if (not (stringp action))
> -           collect action)
> -          ;; Finally we throw the canary if we read past the 
> end of
> -          ;; the input.
> -          (list end-of-actions-form)))
> -        ;; Wrap each action in a lexical closure so it can 
> refer to
> -        ;; variables from the caller.
> -        (action-closures
> -         (cl-loop
> -          for action in action-list
> -          collect (wsi-make-closure action lexenv)))
> -        ;; Replace non-strings with `next-action-key' and 
> concat
> -        ;; everything together
> -        (full-key-sequence
> -         (cl-loop
> -          for action in keylist
> -          if (stringp action)
> -          collect action into key-sequence-list
> -          else
> -          collect next-action-key into key-sequence-list
> -          finally return
> -          ;; Prepend and append `next-action-key' as 
> appropriate to
> -          ;; switch buffer, run body, and throw canary.
> -          (concat
> -           ;; Switch to correct buffer
> -           next-action-key " "
> -           ;; Start executing body
> -           next-action-key " "
> -           ;; Execute the actual key sequence
> -           (mapconcat #'identity key-sequence-list " ")
> -           ;; Throw the canary if BODY reads past the provided 
> input
> -           " " next-action-key)))
> -        ;; Define the next action command with lexical scope so 
> it can
> -        ;; access `action-closures'.
> -        ((symbol-function 'wsi-run-next-action)
> -         (lambda ()
> -           (interactive)
> -           (condition-case err
> -               (if action-closures
> -                   (let ((next-action (pop action-closures)))
> -                     (funcall next-action))
> -                 (error "`with-simulated-input' reached end of 
> action list without returning"))
> -             (error (throw 'wsi-threw-error err)))))
> -        ;; Set up the temporary keymap
> -        (action-map (make-sparse-keymap)))
> +           for action in keys
> +           
> +           collect (if (stringp action) action next-action-key)
> +           into key-sequence-list
> +           finally return
> +           ;; Prepend and append `next-action-key' as 
> appropriate to
> +           ;; switch buffer, run body, and throw canary.
> +           (concat
> +            ;; Start executing body
> +            next-action-key " "
> +            ;; Execute the actual key sequence
> +            (mapconcat #'identity key-sequence-list " ")
> +            ;; Throw the canary if BODY reads past the provided 
> input
> +            " " next-action-key)))
> +         ;; Define the next action command with lexical scope 
> so it can
> +         ;; access `action-closures'.
> +         (wsi-run-next-action
> +          (lambda ()
> +            (interactive)
> +            (condition-case err
> +                (if action-list
> +                    (let ((next-action (pop action-list)))
> +                      (funcall next-action))
> +                  (error "`with-simulated-input' reached end of 
> action list without returning"))
> +              (error (throw 'wsi-threw-error err)))))
> +         ;; Set up the temporary keymap
> +         (action-map (make-sparse-keymap)))
>       ;; Finish setting up the keymap for the temp command
> -     (define-key action-map (kbd next-action-key) 
> 'wsi-run-next-action)
> +     (define-key action-map (kbd next-action-key) 
> wsi-run-next-action)
>       (setq
>        thrown-error
>        (catch 'wsi-threw-error


-- 
Joost Kremers
Life has its moments



^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2020-02-28  8:07 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2020-02-27 22:19 Compiled vs. interpreted ERT test Joost Kremers
2020-02-28  1:48 ` Noam Postavsky
2020-02-28  2:18 ` Stefan Monnier
2020-02-28  3:16   ` Stefan Monnier
2020-02-28  8:07     ` Joost Kremers

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).