aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBasil L. Contovounesios2018-04-29 15:37:45 +0100
committerNoam Postavsky2018-05-02 20:18:07 -0400
commitf2c74543edc7e8d07655b459ba8898eec9b6d4e8 (patch)
treeb6612a1370f9c20399e8fa32ff50be643a0ecabd
parent05e9477ab5d5dba1b960415d60b9957caa90da48 (diff)
downloademacs-f2c74543edc7e8d07655b459ba8898eec9b6d4e8.tar.gz
emacs-f2c74543edc7e8d07655b459ba8898eec9b6d4e8.zip
Fix off-by-one history pruning (bug#31211)
* lisp/subr.el (add-to-history): Clarify docstring. Protect against negative history-length and unnecessary variable modification, as per read_minibuf. * lisp/ido.el (ido-record-command): * lisp/international/mule-cmds.el (deactivate-input-method): (set-language-environment-input-method): * lisp/isearch.el (isearch-done): * lisp/minibuffer.el (read-file-name-default): * lisp/net/eww.el (eww-save-history): * lisp/simple.el (edit-and-eval-command, repeat-complex-command): (command-execute, kill-new, push-mark): * src/callint.c (Fcall_interactively): * src/minibuf.c (read_minibuf): Delegate to add-to-history. * test/lisp/simple-tests.el (command-execute-prune-command-history): * test/src/callint-tests.el (call-interactively-prune-command-history): New tests.
-rw-r--r--lisp/ido.el7
-rw-r--r--lisp/international/mule-cmds.el13
-rw-r--r--lisp/isearch.el13
-rw-r--r--lisp/minibuffer.el14
-rw-r--r--lisp/net/eww.el10
-rw-r--r--lisp/simple.el49
-rw-r--r--lisp/subr.el8
-rw-r--r--src/callint.c27
-rw-r--r--src/minibuf.c40
-rw-r--r--test/lisp/simple-tests.el11
-rw-r--r--test/src/callint-tests.el8
11 files changed, 68 insertions, 132 deletions
diff --git a/lisp/ido.el b/lisp/ido.el
index 7ff3d6820b4..705e7dd6301 100644
--- a/lisp/ido.el
+++ b/lisp/ido.el
@@ -1793,11 +1793,8 @@ is enabled then some keybindings are changed in the keymap."
1793 1793
1794(defun ido-record-command (command arg) 1794(defun ido-record-command (command arg)
1795 "Add (COMMAND ARG) to `command-history' if `ido-record-commands' is non-nil." 1795 "Add (COMMAND ARG) to `command-history' if `ido-record-commands' is non-nil."
1796 (if ido-record-commands ; FIXME: use `when' instead of `if'? 1796 (when ido-record-commands
1797 (let ((cmd (list command arg))) 1797 (add-to-history 'command-history (list command arg))))
1798 (if (or (not command-history) ; FIXME: ditto
1799 (not (equal cmd (car command-history))))
1800 (setq command-history (cons cmd command-history))))))
1801 1798
1802(defun ido-make-prompt (item prompt) 1799(defun ido-make-prompt (item prompt)
1803 ;; Make the prompt for ido-read-internal 1800 ;; Make the prompt for ido-read-internal
diff --git a/lisp/international/mule-cmds.el b/lisp/international/mule-cmds.el
index 6c49b8fa6a0..c0b329bbaea 100644
--- a/lisp/international/mule-cmds.el
+++ b/lisp/international/mule-cmds.el
@@ -1464,12 +1464,7 @@ If INPUT-METHOD is nil, deactivate any current input method."
1464(defun deactivate-input-method () 1464(defun deactivate-input-method ()
1465 "Turn off the current input method." 1465 "Turn off the current input method."
1466 (when current-input-method 1466 (when current-input-method
1467 (if input-method-history 1467 (add-to-history 'input-method-history current-input-method)
1468 (unless (string= current-input-method (car input-method-history))
1469 (setq input-method-history
1470 (cons current-input-method
1471 (delete current-input-method input-method-history))))
1472 (setq input-method-history (list current-input-method)))
1473 (unwind-protect 1468 (unwind-protect
1474 (progn 1469 (progn
1475 (setq input-method-function nil 1470 (setq input-method-function nil
@@ -2022,10 +2017,8 @@ See `set-language-info-alist' for use in programs."
2022 (let ((input-method (get-language-info language-name 'input-method))) 2017 (let ((input-method (get-language-info language-name 'input-method)))
2023 (when input-method 2018 (when input-method
2024 (setq default-input-method input-method) 2019 (setq default-input-method input-method)
2025 (if input-method-history 2020 (when input-method-history
2026 (setq input-method-history 2021 (add-to-history 'input-method-history input-method)))))
2027 (cons input-method
2028 (delete input-method input-method-history)))))))
2029 2022
2030(defun set-language-environment-nonascii-translation (language-name) 2023(defun set-language-environment-nonascii-translation (language-name)
2031 "Do unibyte/multibyte translation setup for language environment LANGUAGE-NAME." 2024 "Do unibyte/multibyte translation setup for language environment LANGUAGE-NAME."
diff --git a/lisp/isearch.el b/lisp/isearch.el
index 5cbb4c941a5..feadf10e8b7 100644
--- a/lisp/isearch.el
+++ b/lisp/isearch.el
@@ -1049,13 +1049,12 @@ For a failing search, NOPUSH is t.
1049For going to the minibuffer to edit the search string, 1049For going to the minibuffer to edit the search string,
1050NOPUSH is t and EDIT is t." 1050NOPUSH is t and EDIT is t."
1051 1051
1052 (if isearch-resume-in-command-history 1052 (when isearch-resume-in-command-history
1053 (let ((command `(isearch-resume ,isearch-string ,isearch-regexp 1053 (add-to-history 'command-history
1054 ,isearch-regexp-function ,isearch-forward 1054 `(isearch-resume ,isearch-string ,isearch-regexp
1055 ,isearch-message 1055 ,isearch-regexp-function ,isearch-forward
1056 ',isearch-case-fold-search))) 1056 ,isearch-message
1057 (unless (equal (car command-history) command) 1057 ',isearch-case-fold-search)))
1058 (setq command-history (cons command command-history)))))
1059 1058
1060 (remove-hook 'pre-command-hook 'isearch-pre-command-hook) 1059 (remove-hook 'pre-command-hook 'isearch-pre-command-hook)
1061 (remove-hook 'post-command-hook 'isearch-post-command-hook) 1060 (remove-hook 'post-command-hook 'isearch-post-command-hook)
diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el
index f1cbdc0cc36..a7e6a8761ff 100644
--- a/lisp/minibuffer.el
+++ b/lisp/minibuffer.el
@@ -2722,17 +2722,9 @@ See `read-file-name' for the meaning of the arguments."
2722 (if (string= val1 (cadr file-name-history)) 2722 (if (string= val1 (cadr file-name-history))
2723 (pop file-name-history) 2723 (pop file-name-history)
2724 (setcar file-name-history val1))) 2724 (setcar file-name-history val1)))
2725 (if add-to-history 2725 (when add-to-history
2726 ;; Add the value to the history--but not if it matches 2726 (add-to-history 'file-name-history
2727 ;; the last value already there. 2727 (minibuffer-maybe-quote-filename val))))
2728 (let ((val1 (minibuffer-maybe-quote-filename val)))
2729 (unless (and (consp file-name-history)
2730 (equal (car file-name-history) val1))
2731 (setq file-name-history
2732 (cons val1
2733 (if history-delete-duplicates
2734 (delete val1 file-name-history)
2735 file-name-history)))))))
2736 val)))) 2728 val))))
2737 2729
2738(defun internal-complete-buffer-except (&optional buffer) 2730(defun internal-complete-buffer-except (&optional buffer)
diff --git a/lisp/net/eww.el b/lisp/net/eww.el
index e74f661ac75..97fdabd72bd 100644
--- a/lisp/net/eww.el
+++ b/lisp/net/eww.el
@@ -1813,13 +1813,9 @@ If CHARSET is nil then use UTF-8."
1813(defun eww-save-history () 1813(defun eww-save-history ()
1814 (plist-put eww-data :point (point)) 1814 (plist-put eww-data :point (point))
1815 (plist-put eww-data :text (buffer-string)) 1815 (plist-put eww-data :text (buffer-string))
1816 (push eww-data eww-history) 1816 (let ((history-delete-duplicates nil))
1817 (setq eww-data (list :title "")) 1817 (add-to-history 'eww-history eww-data eww-history-limit t))
1818 ;; Don't let the history grow infinitely. We store quite a lot of 1818 (setq eww-data (list :title "")))
1819 ;; data per page.
1820 (when-let* ((tail (and eww-history-limit
1821 (nthcdr eww-history-limit eww-history))))
1822 (setcdr tail nil)))
1823 1819
1824(defvar eww-current-buffer) 1820(defvar eww-current-buffer)
1825 1821
diff --git a/lisp/simple.el b/lisp/simple.el
index 5446159d319..9fde9a5c90a 100644
--- a/lisp/simple.el
+++ b/lisp/simple.el
@@ -1646,13 +1646,10 @@ the minibuffer, then read and evaluate the result."
1646 'command-history) 1646 'command-history)
1647 ;; If command was added to command-history as a string, 1647 ;; If command was added to command-history as a string,
1648 ;; get rid of that. We want only evaluable expressions there. 1648 ;; get rid of that. We want only evaluable expressions there.
1649 (if (stringp (car command-history)) 1649 (when (stringp (car command-history))
1650 (setq command-history (cdr command-history))))))) 1650 (pop command-history))))))
1651 1651
1652 ;; If command to be redone does not match front of history, 1652 (add-to-history 'command-history command)
1653 ;; add it to the history.
1654 (or (equal command (car command-history))
1655 (setq command-history (cons command command-history)))
1656 (eval command))) 1653 (eval command)))
1657 1654
1658(defun repeat-complex-command (arg) 1655(defun repeat-complex-command (arg)
@@ -1682,13 +1679,10 @@ to get different commands to edit and resubmit."
1682 ;; If command was added to command-history as a 1679 ;; If command was added to command-history as a
1683 ;; string, get rid of that. We want only 1680 ;; string, get rid of that. We want only
1684 ;; evaluable expressions there. 1681 ;; evaluable expressions there.
1685 (if (stringp (car command-history)) 1682 (when (stringp (car command-history))
1686 (setq command-history (cdr command-history)))))) 1683 (pop command-history)))))
1687 1684
1688 ;; If command to be redone does not match front of history, 1685 (add-to-history 'command-history newcmd)
1689 ;; add it to the history.
1690 (or (equal newcmd (car command-history))
1691 (setq command-history (cons newcmd command-history)))
1692 (apply #'funcall-interactively 1686 (apply #'funcall-interactively
1693 (car newcmd) 1687 (car newcmd)
1694 (mapcar (lambda (e) (eval e t)) (cdr newcmd)))) 1688 (mapcar (lambda (e) (eval e t)) (cdr newcmd))))
@@ -1905,11 +1899,8 @@ a special event, so ignore the prefix argument and don't clear it."
1905 ;; If requested, place the macro in the command history. For 1899 ;; If requested, place the macro in the command history. For
1906 ;; other sorts of commands, call-interactively takes care of this. 1900 ;; other sorts of commands, call-interactively takes care of this.
1907 (when record-flag 1901 (when record-flag
1908 (push `(execute-kbd-macro ,final ,prefixarg) command-history) 1902 (add-to-history
1909 ;; Don't keep command history around forever. 1903 'command-history `(execute-kbd-macro ,final ,prefixarg) nil t))
1910 (when (and (numberp history-length) (> history-length 0))
1911 (let ((cell (nthcdr history-length command-history)))
1912 (if (consp cell) (setcdr cell nil)))))
1913 (execute-kbd-macro final prefixarg)) 1904 (execute-kbd-macro final prefixarg))
1914 (t 1905 (t
1915 ;; Pass `cmd' rather than `final', for the backtrace's sake. 1906 ;; Pass `cmd' rather than `final', for the backtrace's sake.
@@ -4408,9 +4399,8 @@ argument should still be a \"useful\" string for such uses."
4408 (equal-including-properties string (car kill-ring))) 4399 (equal-including-properties string (car kill-ring)))
4409 (if (and replace kill-ring) 4400 (if (and replace kill-ring)
4410 (setcar kill-ring string) 4401 (setcar kill-ring string)
4411 (push string kill-ring) 4402 (let ((history-delete-duplicates nil))
4412 (if (> (length kill-ring) kill-ring-max) 4403 (add-to-history 'kill-ring string kill-ring-max t))))
4413 (setcdr (nthcdr (1- kill-ring-max) kill-ring) nil))))
4414 (setq kill-ring-yank-pointer kill-ring) 4404 (setq kill-ring-yank-pointer kill-ring)
4415 (if interprogram-cut-function 4405 (if interprogram-cut-function
4416 (funcall interprogram-cut-function string))) 4406 (funcall interprogram-cut-function string)))
@@ -5724,10 +5714,11 @@ purposes. See the documentation of `set-mark' for more information.
5724 5714
5725In Transient Mark mode, activate mark if optional third arg ACTIVATE non-nil." 5715In Transient Mark mode, activate mark if optional third arg ACTIVATE non-nil."
5726 (unless (null (mark t)) 5716 (unless (null (mark t))
5727 (setq mark-ring (cons (copy-marker (mark-marker)) mark-ring)) 5717 (let ((old (nth mark-ring-max mark-ring))
5728 (when (> (length mark-ring) mark-ring-max) 5718 (history-delete-duplicates nil))
5729 (move-marker (car (nthcdr mark-ring-max mark-ring)) nil) 5719 (add-to-history 'mark-ring (copy-marker (mark-marker)) mark-ring-max t)
5730 (setcdr (nthcdr (1- mark-ring-max) mark-ring) nil))) 5720 (when old
5721 (set-marker old nil))))
5731 (set-marker (mark-marker) (or location (point)) (current-buffer)) 5722 (set-marker (mark-marker) (or location (point)) (current-buffer))
5732 ;; Now push the mark on the global mark ring. 5723 ;; Now push the mark on the global mark ring.
5733 (if (and global-mark-ring 5724 (if (and global-mark-ring
@@ -5735,10 +5726,12 @@ In Transient Mark mode, activate mark if optional third arg ACTIVATE non-nil."
5735 ;; The last global mark pushed was in this same buffer. 5726 ;; The last global mark pushed was in this same buffer.
5736 ;; Don't push another one. 5727 ;; Don't push another one.
5737 nil 5728 nil
5738 (setq global-mark-ring (cons (copy-marker (mark-marker)) global-mark-ring)) 5729 (let ((old (nth global-mark-ring-max global-mark-ring))
5739 (when (> (length global-mark-ring) global-mark-ring-max) 5730 (history-delete-duplicates nil))
5740 (move-marker (car (nthcdr global-mark-ring-max global-mark-ring)) nil) 5731 (add-to-history
5741 (setcdr (nthcdr (1- global-mark-ring-max) global-mark-ring) nil))) 5732 'global-mark-ring (copy-marker (mark-marker)) global-mark-ring-max t)
5733 (when old
5734 (set-marker old nil))))
5742 (or nomsg executing-kbd-macro (> (minibuffer-depth) 0) 5735 (or nomsg executing-kbd-macro (> (minibuffer-depth) 0)
5743 (message "Mark set")) 5736 (message "Mark set"))
5744 (if (or activate (not transient-mark-mode)) 5737 (if (or activate (not transient-mark-mode))
diff --git a/lisp/subr.el b/lisp/subr.el
index 9f6cade0f71..35e220a10ee 100644
--- a/lisp/subr.el
+++ b/lisp/subr.el
@@ -1798,7 +1798,7 @@ variable. The possible values of maximum length have the same meaning as
1798the values of `history-length'. 1798the values of `history-length'.
1799Remove duplicates of NEWELT if `history-delete-duplicates' is non-nil. 1799Remove duplicates of NEWELT if `history-delete-duplicates' is non-nil.
1800If optional fourth arg KEEP-ALL is non-nil, add NEWELT to history even 1800If optional fourth arg KEEP-ALL is non-nil, add NEWELT to history even
1801if it is empty or a duplicate." 1801if it is empty or duplicates the most recent entry in the history."
1802 (unless maxelt 1802 (unless maxelt
1803 (setq maxelt (or (get history-var 'history-length) 1803 (setq maxelt (or (get history-var 'history-length)
1804 history-length))) 1804 history-length)))
@@ -1814,12 +1814,12 @@ if it is empty or a duplicate."
1814 (setq history (delete newelt history))) 1814 (setq history (delete newelt history)))
1815 (setq history (cons newelt history)) 1815 (setq history (cons newelt history))
1816 (when (integerp maxelt) 1816 (when (integerp maxelt)
1817 (if (= 0 maxelt) 1817 (if (>= 0 maxelt)
1818 (setq history nil) 1818 (setq history nil)
1819 (setq tail (nthcdr (1- maxelt) history)) 1819 (setq tail (nthcdr (1- maxelt) history))
1820 (when (consp tail) 1820 (when (consp tail)
1821 (setcdr tail nil))))) 1821 (setcdr tail nil))))
1822 (set history-var history))) 1822 (set history-var history))))
1823 1823
1824 1824
1825;;;; Mode hooks. 1825;;;; Mode hooks.
diff --git a/src/callint.c b/src/callint.c
index 08a8bba4646..fd44494cfee 100644
--- a/src/callint.c
+++ b/src/callint.c
@@ -262,7 +262,7 @@ to the function `interactive' at the top level of the function body.
262See `interactive'. 262See `interactive'.
263 263
264Optional second arg RECORD-FLAG non-nil 264Optional second arg RECORD-FLAG non-nil
265means unconditionally put this command in the command-history. 265means unconditionally put this command in the variable `command-history'.
266Otherwise, this is done only if an arg is read using the minibuffer. 266Otherwise, this is done only if an arg is read using the minibuffer.
267 267
268Optional third arg KEYS, if given, specifies the sequence of events to 268Optional third arg KEYS, if given, specifies the sequence of events to
@@ -328,18 +328,8 @@ invoke it. If KEYS is omitted or nil, the return value of
328 and turn them into things we can eval. */ 328 and turn them into things we can eval. */
329 Lisp_Object values = quotify_args (Fcopy_sequence (specs)); 329 Lisp_Object values = quotify_args (Fcopy_sequence (specs));
330 fix_command (input, values); 330 fix_command (input, values);
331 Lisp_Object this_cmd = Fcons (function, values); 331 call4 (intern ("add-to-history"), intern ("command-history"),
332 if (history_delete_duplicates) 332 Fcons (function, values), Qnil, Qt);
333 Vcommand_history = Fdelete (this_cmd, Vcommand_history);
334 Vcommand_history = Fcons (this_cmd, Vcommand_history);
335
336 /* Don't keep command history around forever. */
337 if (INTEGERP (Vhistory_length) && XINT (Vhistory_length) > 0)
338 {
339 Lisp_Object teml = Fnthcdr (Vhistory_length, Vcommand_history);
340 if (CONSP (teml))
341 XSETCDR (teml, Qnil);
342 }
343 } 333 }
344 334
345 Vthis_command = save_this_command; 335 Vthis_command = save_this_command;
@@ -768,15 +758,8 @@ invoke it. If KEYS is omitted or nil, the return value of
768 visargs[i] = (varies[i] > 0 758 visargs[i] = (varies[i] > 0
769 ? list1 (intern (callint_argfuns[varies[i]])) 759 ? list1 (intern (callint_argfuns[varies[i]]))
770 : quotify_arg (args[i])); 760 : quotify_arg (args[i]));
771 Vcommand_history = Fcons (Flist (nargs - 1, visargs + 1), 761 call4 (intern ("add-to-history"), intern ("command-history"),
772 Vcommand_history); 762 Flist (nargs - 1, visargs + 1), Qnil, Qt);
773 /* Don't keep command history around forever. */
774 if (INTEGERP (Vhistory_length) && XINT (Vhistory_length) > 0)
775 {
776 Lisp_Object teml = Fnthcdr (Vhistory_length, Vcommand_history);
777 if (CONSP (teml))
778 XSETCDR (teml, Qnil);
779 }
780 } 763 }
781 764
782 /* If we used a marker to hold point, mark, or an end of the region, 765 /* If we used a marker to hold point, mark, or an end of the region,
diff --git a/src/minibuf.c b/src/minibuf.c
index c41958d85f9..e18c99bef28 100644
--- a/src/minibuf.c
+++ b/src/minibuf.c
@@ -702,44 +702,8 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt,
702 histstring = Qnil; 702 histstring = Qnil;
703 703
704 /* Add the value to the appropriate history list, if any. */ 704 /* Add the value to the appropriate history list, if any. */
705 if (!NILP (Vhistory_add_new_input) 705 if (! (NILP (Vhistory_add_new_input) || NILP (histstring)))
706 && SYMBOLP (Vminibuffer_history_variable) 706 call2 (intern ("add-to-history"), Vminibuffer_history_variable, histstring);
707 && !NILP (histstring))
708 {
709 /* If the caller wanted to save the value read on a history list,
710 then do so if the value is not already the front of the list. */
711
712 /* The value of the history variable must be a cons or nil. Other
713 values are unacceptable. We silently ignore these values. */
714
715 if (NILP (histval)
716 || (CONSP (histval)
717 /* Don't duplicate the most recent entry in the history. */
718 && (NILP (Fequal (histstring, Fcar (histval))))))
719 {
720 Lisp_Object length;
721
722 if (history_delete_duplicates) Fdelete (histstring, histval);
723 histval = Fcons (histstring, histval);
724 Fset (Vminibuffer_history_variable, histval);
725
726 /* Truncate if requested. */
727 length = Fget (Vminibuffer_history_variable, Qhistory_length);
728 if (NILP (length)) length = Vhistory_length;
729 if (INTEGERP (length))
730 {
731 if (XINT (length) <= 0)
732 Fset (Vminibuffer_history_variable, Qnil);
733 else
734 {
735 Lisp_Object temp;
736
737 temp = Fnthcdr (Fsub1 (length), histval);
738 if (CONSP (temp)) Fsetcdr (temp, Qnil);
739 }
740 }
741 }
742 }
743 707
744 /* If Lisp form desired instead of string, parse it. */ 708 /* If Lisp form desired instead of string, parse it. */
745 if (expflag) 709 if (expflag)
diff --git a/test/lisp/simple-tests.el b/test/lisp/simple-tests.el
index 64b341bd469..7a10df20587 100644
--- a/test/lisp/simple-tests.el
+++ b/test/lisp/simple-tests.el
@@ -448,6 +448,17 @@ See Bug#21722."
448 (call-interactively #'eval-expression) 448 (call-interactively #'eval-expression)
449 (should (equal (current-message) "66 (#o102, #x42, ?B)")))))) 449 (should (equal (current-message) "66 (#o102, #x42, ?B)"))))))
450 450
451(ert-deftest command-execute-prune-command-history ()
452 "Check that Bug#31211 is fixed."
453 (let ((history-length 1)
454 (command-history ()))
455 (dotimes (_ (1+ history-length))
456 (command-execute "" t))
457 (should (= (length command-history) history-length))))
458
459
460;;; `line-number-at-pos'
461
451(ert-deftest line-number-at-pos-in-widen-buffer () 462(ert-deftest line-number-at-pos-in-widen-buffer ()
452 (let ((target-line 3)) 463 (let ((target-line 3))
453 (with-temp-buffer 464 (with-temp-buffer
diff --git a/test/src/callint-tests.el b/test/src/callint-tests.el
index 9a812223ad0..feee9b692b7 100644
--- a/test/src/callint-tests.el
+++ b/test/src/callint-tests.el
@@ -43,4 +43,12 @@
43 (list a b)))) 43 (list a b))))
44 '("a" "b")))) 44 '("a" "b"))))
45 45
46(ert-deftest call-interactively-prune-command-history ()
47 "Check that Bug#31211 is fixed."
48 (let ((history-length 1)
49 (command-history ()))
50 (dotimes (_ (1+ history-length))
51 (call-interactively #'ignore t))
52 (should (= (length command-history) history-length))))
53
46;;; callint-tests.el ends here 54;;; callint-tests.el ends here