aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLars Ingebrigtsen2022-08-08 15:52:19 +0200
committerLars Ingebrigtsen2022-08-08 15:53:41 +0200
commitffc81ebc4b5d6cfc827e6a08679da55134f73fb5 (patch)
tree0d285330a12d247433e1e5805ede15a9c5a0b62f
parent498c5d26bb6360eda5c6cedbcf027e2cc67120ff (diff)
downloademacs-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.texi4
-rw-r--r--lisp/emacs-lisp/byte-run.el17
-rw-r--r--lisp/replace.el5
-rw-r--r--src/callint.c113
-rw-r--r--test/src/callint-tests.el13
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.
2498Specify that this command is meant to be applicable for @var{modes} 2498Specify that this command is meant to be applicable for @var{modes}
2499only. 2499only.
2500 2500
2501@item (interactive-args @var{arg} ...)
2502Specify the arguments that should be stored for @code{repeat-command}.
2503Each @var{arg} is on the form @code{@var{argument-name} @var{form}}.
2504
2501@item (pure @var{val}) 2505@item (pure @var{val})
2502If @var{val} is non-@code{nil}, this function is @dfn{pure} 2506If @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.
260Each element of the list takes the form (PROP FUN) where FUN is 275Each element of the list takes the form (PROP FUN) where FUN is
261a function. For each (PROP . VALUES) in a function's declaration, 276a 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
665and TO-STRING is also null.)" 665and 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)
172static void 170static void
173fix_command (Lisp_Object input, Lisp_Object function, Lisp_Object values) 171fix_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