diff options
| author | Lars Ingebrigtsen | 2022-08-08 15:52:19 +0200 |
|---|---|---|
| committer | Lars Ingebrigtsen | 2022-08-08 15:53:41 +0200 |
| commit | ffc81ebc4b5d6cfc827e6a08679da55134f73fb5 (patch) | |
| tree | 0d285330a12d247433e1e5805ede15a9c5a0b62f | |
| parent | 498c5d26bb6360eda5c6cedbcf027e2cc67120ff (diff) | |
| download | emacs-ffc81ebc4b5d6cfc827e6a08679da55134f73fb5.tar.gz emacs-ffc81ebc4b5d6cfc827e6a08679da55134f73fb5.zip | |
Allow specifying how args are to be stored in `command-history'
* doc/lispref/functions.texi (Declare Form): Document
`interactive-args'
* lisp/replace.el (replace-string): Store the correct interactive
arguments (bug#45607).
* lisp/emacs-lisp/byte-run.el (byte-run--set-interactive-args):
New function.
(defun-declarations-alist): Use it.
* src/callint.c (fix_command): Remove the old hack (which now
longer works since interactive specs are byte-compiled) and
instead rely on `interactive-args'.
| -rw-r--r-- | doc/lispref/functions.texi | 4 | ||||
| -rw-r--r-- | lisp/emacs-lisp/byte-run.el | 17 | ||||
| -rw-r--r-- | lisp/replace.el | 5 | ||||
| -rw-r--r-- | src/callint.c | 113 | ||||
| -rw-r--r-- | test/src/callint-tests.el | 13 |
5 files changed, 73 insertions, 79 deletions
diff --git a/doc/lispref/functions.texi b/doc/lispref/functions.texi index 8e8cc5fd9c0..8265e58210e 100644 --- a/doc/lispref/functions.texi +++ b/doc/lispref/functions.texi | |||
| @@ -2498,6 +2498,10 @@ the current buffer. | |||
| 2498 | Specify that this command is meant to be applicable for @var{modes} | 2498 | Specify that this command is meant to be applicable for @var{modes} |
| 2499 | only. | 2499 | only. |
| 2500 | 2500 | ||
| 2501 | @item (interactive-args @var{arg} ...) | ||
| 2502 | Specify the arguments that should be stored for @code{repeat-command}. | ||
| 2503 | Each @var{arg} is on the form @code{@var{argument-name} @var{form}}. | ||
| 2504 | |||
| 2501 | @item (pure @var{val}) | 2505 | @item (pure @var{val}) |
| 2502 | If @var{val} is non-@code{nil}, this function is @dfn{pure} | 2506 | If @var{val} is non-@code{nil}, this function is @dfn{pure} |
| 2503 | (@pxref{What Is a Function}). This is the same as the @code{pure} | 2507 | (@pxref{What Is a Function}). This is the same as the @code{pure} |
diff --git a/lisp/emacs-lisp/byte-run.el b/lisp/emacs-lisp/byte-run.el index 9370bd3a097..4a2860cd43d 100644 --- a/lisp/emacs-lisp/byte-run.el +++ b/lisp/emacs-lisp/byte-run.el | |||
| @@ -236,6 +236,20 @@ The return value of this function is not used." | |||
| 236 | (list 'function-put (list 'quote f) | 236 | (list 'function-put (list 'quote f) |
| 237 | ''command-modes (list 'quote val)))) | 237 | ''command-modes (list 'quote val)))) |
| 238 | 238 | ||
| 239 | (defalias 'byte-run--set-interactive-args | ||
| 240 | #'(lambda (f args &rest val) | ||
| 241 | (setq args (remove '&optional (remove '&rest args))) | ||
| 242 | (list 'function-put (list 'quote f) | ||
| 243 | ''interactive-args | ||
| 244 | (list | ||
| 245 | 'quote | ||
| 246 | (mapcar | ||
| 247 | (lambda (elem) | ||
| 248 | (cons | ||
| 249 | (seq-position args (car elem)) | ||
| 250 | (cadr elem))) | ||
| 251 | val))))) | ||
| 252 | |||
| 239 | ;; Add any new entries to info node `(elisp)Declare Form'. | 253 | ;; Add any new entries to info node `(elisp)Declare Form'. |
| 240 | (defvar defun-declarations-alist | 254 | (defvar defun-declarations-alist |
| 241 | (list | 255 | (list |
| @@ -255,7 +269,8 @@ If `error-free', drop calls even if `byte-compile-delete-errors' is nil.") | |||
| 255 | (list 'indent #'byte-run--set-indent) | 269 | (list 'indent #'byte-run--set-indent) |
| 256 | (list 'speed #'byte-run--set-speed) | 270 | (list 'speed #'byte-run--set-speed) |
| 257 | (list 'completion #'byte-run--set-completion) | 271 | (list 'completion #'byte-run--set-completion) |
| 258 | (list 'modes #'byte-run--set-modes)) | 272 | (list 'modes #'byte-run--set-modes) |
| 273 | (list 'interactive-args #'byte-run--set-interactive-args)) | ||
| 259 | "List associating function properties to their macro expansion. | 274 | "List associating function properties to their macro expansion. |
| 260 | Each element of the list takes the form (PROP FUN) where FUN is | 275 | Each element of the list takes the form (PROP FUN) where FUN is |
| 261 | a function. For each (PROP . VALUES) in a function's declaration, | 276 | a function. For each (PROP . VALUES) in a function's declaration, |
diff --git a/lisp/replace.el b/lisp/replace.el index ab9ac17ed9c..cac0edf43ac 100644 --- a/lisp/replace.el +++ b/lisp/replace.el | |||
| @@ -664,7 +664,10 @@ which will run faster and will not set the mark or print anything. | |||
| 664 | \(You may need a more complex loop if FROM-STRING can match the null string | 664 | \(You may need a more complex loop if FROM-STRING can match the null string |
| 665 | and TO-STRING is also null.)" | 665 | and TO-STRING is also null.)" |
| 666 | (declare (interactive-only | 666 | (declare (interactive-only |
| 667 | "use `search-forward' and `replace-match' instead.")) | 667 | "use `search-forward' and `replace-match' instead.") |
| 668 | (interactive-args | ||
| 669 | (start (if (use-region-p) (region-beginning))) | ||
| 670 | (end (if (use-region-p) (region-end))))) | ||
| 668 | (interactive | 671 | (interactive |
| 669 | (let ((common | 672 | (let ((common |
| 670 | (query-replace-read-args | 673 | (query-replace-read-args |
diff --git a/src/callint.c b/src/callint.c index ffa3b231eb5..dfc479284c0 100644 --- a/src/callint.c +++ b/src/callint.c | |||
| @@ -161,10 +161,8 @@ check_mark (bool for_region) | |||
| 161 | xsignal0 (Qmark_inactive); | 161 | xsignal0 (Qmark_inactive); |
| 162 | } | 162 | } |
| 163 | 163 | ||
| 164 | /* If the list of args INPUT was produced with an explicit call to | 164 | /* If FUNCTION has an `interactive-args' spec, replace relevant |
| 165 | `list', look for elements that were computed with | 165 | elements in VALUES with those forms instead. |
| 166 | (region-beginning) or (region-end), and put those expressions into | ||
| 167 | VALUES instead of the present values. | ||
| 168 | 166 | ||
| 169 | This function doesn't return a value because it modifies elements | 167 | This function doesn't return a value because it modifies elements |
| 170 | of VALUES to do its job. */ | 168 | of VALUES to do its job. */ |
| @@ -172,62 +170,24 @@ check_mark (bool for_region) | |||
| 172 | static void | 170 | static void |
| 173 | fix_command (Lisp_Object input, Lisp_Object function, Lisp_Object values) | 171 | fix_command (Lisp_Object input, Lisp_Object function, Lisp_Object values) |
| 174 | { | 172 | { |
| 175 | /* FIXME: Instead of this ugly hack, we should provide a way for an | 173 | /* Quick exit if there's no values to alter. */ |
| 176 | interactive spec to return an expression/function that will re-build the | 174 | if (!CONSP (values)) |
| 177 | args without user intervention. */ | 175 | return; |
| 178 | if (CONSP (input)) | 176 | |
| 177 | Lisp_Object reps = Fget (function, Qinteractive_args); | ||
| 178 | |||
| 179 | if (!NILP (reps) && CONSP (reps)) | ||
| 179 | { | 180 | { |
| 180 | Lisp_Object car; | 181 | int i = 0; |
| 182 | Lisp_Object vals = values; | ||
| 181 | 183 | ||
| 182 | car = XCAR (input); | 184 | while (!NILP (vals)) |
| 183 | /* Skip through certain special forms. */ | ||
| 184 | while (EQ (car, Qlet) || EQ (car, Qletx) | ||
| 185 | || EQ (car, Qsave_excursion) | ||
| 186 | || EQ (car, Qprogn)) | ||
| 187 | { | 185 | { |
| 188 | while (CONSP (XCDR (input))) | 186 | Lisp_Object rep = Fassq (make_fixnum (i), reps); |
| 189 | input = XCDR (input); | 187 | if (!NILP (rep)) |
| 190 | input = XCAR (input); | 188 | Fsetcar (vals, XCDR (rep)); |
| 191 | if (!CONSP (input)) | 189 | vals = XCDR (vals); |
| 192 | break; | 190 | ++i; |
| 193 | car = XCAR (input); | ||
| 194 | } | ||
| 195 | if (EQ (car, Qlist)) | ||
| 196 | { | ||
| 197 | Lisp_Object intail, valtail; | ||
| 198 | for (intail = Fcdr (input), valtail = values; | ||
| 199 | CONSP (valtail); | ||
| 200 | intail = Fcdr (intail), valtail = XCDR (valtail)) | ||
| 201 | { | ||
| 202 | Lisp_Object elt; | ||
| 203 | elt = Fcar (intail); | ||
| 204 | if (CONSP (elt)) | ||
| 205 | { | ||
| 206 | Lisp_Object presflag, carelt; | ||
| 207 | carelt = XCAR (elt); | ||
| 208 | /* If it is (if X Y), look at Y. */ | ||
| 209 | if (EQ (carelt, Qif) | ||
| 210 | && NILP (Fnthcdr (make_fixnum (3), elt))) | ||
| 211 | elt = Fnth (make_fixnum (2), elt); | ||
| 212 | /* If it is (when ... Y), look at Y. */ | ||
| 213 | else if (EQ (carelt, Qwhen)) | ||
| 214 | { | ||
| 215 | while (CONSP (XCDR (elt))) | ||
| 216 | elt = XCDR (elt); | ||
| 217 | elt = Fcar (elt); | ||
| 218 | } | ||
| 219 | |||
| 220 | /* If the function call we're looking at | ||
| 221 | is a special preserved one, copy the | ||
| 222 | whole expression for this argument. */ | ||
| 223 | if (CONSP (elt)) | ||
| 224 | { | ||
| 225 | presflag = Fmemq (Fcar (elt), preserved_fns); | ||
| 226 | if (!NILP (presflag)) | ||
| 227 | Fsetcar (valtail, Fcar (intail)); | ||
| 228 | } | ||
| 229 | } | ||
| 230 | } | ||
| 231 | } | 191 | } |
| 232 | } | 192 | } |
| 233 | 193 | ||
| @@ -235,31 +195,28 @@ fix_command (Lisp_Object input, Lisp_Object function, Lisp_Object values) | |||
| 235 | optional, remove them from the list. This makes navigating the | 195 | optional, remove them from the list. This makes navigating the |
| 236 | history less confusing, since it doesn't contain a lot of | 196 | history less confusing, since it doesn't contain a lot of |
| 237 | parameters that aren't used. */ | 197 | parameters that aren't used. */ |
| 238 | if (CONSP (values)) | 198 | Lisp_Object arity = Ffunc_arity (function); |
| 199 | /* We don't want to do this simplification if we have an &rest | ||
| 200 | function, because (cl-defun foo (a &optional (b 'zot)) ..) | ||
| 201 | etc. */ | ||
| 202 | if (FIXNUMP (XCAR (arity)) && FIXNUMP (XCDR (arity))) | ||
| 239 | { | 203 | { |
| 240 | Lisp_Object arity = Ffunc_arity (function); | 204 | Lisp_Object final = Qnil; |
| 241 | /* We don't want to do this simplification if we have an &rest | 205 | ptrdiff_t final_i = 0, i = 0; |
| 242 | function, because (cl-defun foo (a &optional (b 'zot)) ..) | 206 | for (Lisp_Object tail = values; |
| 243 | etc. */ | 207 | CONSP (tail); |
| 244 | if (FIXNUMP (XCAR (arity)) && FIXNUMP (XCDR (arity))) | 208 | tail = XCDR (tail), ++i) |
| 245 | { | 209 | { |
| 246 | Lisp_Object final = Qnil; | 210 | if (!NILP (XCAR (tail))) |
| 247 | ptrdiff_t final_i = 0, i = 0; | ||
| 248 | for (Lisp_Object tail = values; | ||
| 249 | CONSP (tail); | ||
| 250 | tail = XCDR (tail), ++i) | ||
| 251 | { | 211 | { |
| 252 | if (!NILP (XCAR (tail))) | 212 | final = tail; |
| 253 | { | 213 | final_i = i; |
| 254 | final = tail; | ||
| 255 | final_i = i; | ||
| 256 | } | ||
| 257 | } | 214 | } |
| 258 | |||
| 259 | /* Chop the trailing optional values. */ | ||
| 260 | if (final_i > 0 && final_i >= XFIXNUM (XCAR (arity)) - 1) | ||
| 261 | XSETCDR (final, Qnil); | ||
| 262 | } | 215 | } |
| 216 | |||
| 217 | /* Chop the trailing optional values. */ | ||
| 218 | if (final_i > 0 && final_i >= XFIXNUM (XCAR (arity)) - 1) | ||
| 219 | XSETCDR (final, Qnil); | ||
| 263 | } | 220 | } |
| 264 | } | 221 | } |
| 265 | 222 | ||
| @@ -950,4 +907,6 @@ use `event-start', `event-end', and `event-click-count'. */); | |||
| 950 | defsubr (&Scall_interactively); | 907 | defsubr (&Scall_interactively); |
| 951 | defsubr (&Sfuncall_interactively); | 908 | defsubr (&Sfuncall_interactively); |
| 952 | defsubr (&Sprefix_numeric_value); | 909 | defsubr (&Sprefix_numeric_value); |
| 910 | |||
| 911 | DEFSYM (Qinteractive_args, "interactive-args"); | ||
| 953 | } | 912 | } |
diff --git a/test/src/callint-tests.el b/test/src/callint-tests.el index d964fc3c1f3..5a633fdc2bd 100644 --- a/test/src/callint-tests.el +++ b/test/src/callint-tests.el | |||
| @@ -52,4 +52,17 @@ | |||
| 52 | (call-interactively #'ignore t)) | 52 | (call-interactively #'ignore t)) |
| 53 | (should (= (length command-history) history-length)))) | 53 | (should (= (length command-history) history-length)))) |
| 54 | 54 | ||
| 55 | (defun callint-test-int-args (foo bar &optional zot) | ||
| 56 | (declare (interactive-args | ||
| 57 | (bar 10) | ||
| 58 | (zot 11))) | ||
| 59 | (interactive (list 1 1 1)) | ||
| 60 | (+ foo bar zot)) | ||
| 61 | |||
| 62 | (ert-deftest test-interactive-args () | ||
| 63 | (let ((history-length 1) | ||
| 64 | (command-history ())) | ||
| 65 | (should (= (call-interactively 'callint-test-int-args t) 3)) | ||
| 66 | (should (equal command-history '((callint-test-int-args 1 10 11)))))) | ||
| 67 | |||
| 55 | ;;; callint-tests.el ends here | 68 | ;;; callint-tests.el ends here |