diff --git a/lisp/dabbrev.el b/lisp/dabbrev.el index 7b6cbb78cef..f9f9fc8bb27 100644 --- a/lisp/dabbrev.el +++ b/lisp/dabbrev.el @@ -464,8 +464,21 @@ dabbrev-expand See also `dabbrev-abbrev-char-regexp' and \\[dabbrev-completion]." (interactive "*P") - (let (abbrev record-case-pattern - expansion old direction (orig-point (point))) + ;; There are three possible sources of the expansion, which we need to + ;; check in a specific order: + (let ((buf (cond ((window-minibuffer-p) + ;; If we invoked dabbrev-expand in the minibuffer, + ;; this is the buffer from which we entered the + ;; minibuffer. + (window-buffer (get-mru-window))) + ;; Otherwise, if we found the expansion in another + ;; buffer, use that buffer for further expansions. + (dabbrev--last-buffer-found dabbrev--last-buffer-found) + ;; Otherwise, use the buffer where we invoked + ;; dabbrev-expand. + (t (current-buffer)))) + abbrev record-case-pattern expansion old direction + (orig-point (point))) ;; abbrev -- the abbrev to expand ;; expansion -- the expansion found (eventually) or nil until then ;; old -- the text currently in the buffer @@ -480,6 +493,7 @@ dabbrev-expand (point))))) ;; Find a different expansion for the same abbrev as last time. (progn + (setq dabbrev--last-buffer-found nil) (setq abbrev dabbrev--last-abbreviation) (setq old dabbrev--last-expansion) (setq direction dabbrev--last-direction)) @@ -488,7 +502,14 @@ dabbrev-expand (if (and (eq (preceding-char) ?\s) (markerp dabbrev--last-abbrev-location) (marker-position dabbrev--last-abbrev-location) - (= (point) (1+ dabbrev--last-abbrev-location))) + ;; Comparing with point only makes sense in the buffer + ;; where we called dabbrev-exand, but if that differs + ;; from the buffer containing the expansion, we want to + ;; get the next word in the latter buffer, so we skip + ;; the comparison. + (if (eq buf (current-buffer)) + (= (point) (1+ dabbrev--last-abbrev-location)) + t)) (progn ;; The "abbrev" to expand is just the space. (setq abbrev " ") @@ -549,29 +570,43 @@ dabbrev-expand (if old " further" "") abbrev)) (t (if (not (or (eq dabbrev--last-buffer dabbrev--last-buffer-found) - (minibuffer-window-active-p (selected-window)))) + ;; If we are in the minibuffer and an expansion has + ;; been found but dabbrev--last-buffer-found is not + ;; yet set, we need to set it now. + (and dabbrev--last-buffer-found + (minibuffer-window-active-p (selected-window))))) (progn (when (buffer-name dabbrev--last-buffer) (message "Expansion found in `%s'" (buffer-name dabbrev--last-buffer))) (setq dabbrev--last-buffer-found dabbrev--last-buffer)) (message nil)) - (if (and (or (eq (current-buffer) dabbrev--last-buffer) - (null dabbrev--last-buffer) - (buffer-live-p dabbrev--last-buffer)) - (numberp dabbrev--last-expansion-location) - (and (> dabbrev--last-expansion-location (point)))) - (setq dabbrev--last-expansion-location - (copy-marker dabbrev--last-expansion-location))) + ;; To get correct further expansions we have to be sure to use the + ;; buffer containing the already found expansions. + (when dabbrev--last-buffer-found + (setq buf dabbrev--last-buffer-found)) + ;; If the buffer where we called dabbrev-expand differs from the + ;; buffer containing the expansion, make sure copy-marker is + ;; called in the latter buffer. + (with-current-buffer buf + (if (and (or (eq (current-buffer) dabbrev--last-buffer) + (null dabbrev--last-buffer) + (buffer-live-p dabbrev--last-buffer)) + (numberp dabbrev--last-expansion-location) + (and (> dabbrev--last-expansion-location (point)))) + (setq dabbrev--last-expansion-location + (copy-marker dabbrev--last-expansion-location)))) ;; Success: stick it in and return. (setq buffer-undo-list (cons orig-point buffer-undo-list)) (setq expansion (dabbrev--substitute-expansion old abbrev expansion record-case-pattern)) - ;; Save state for re-expand. - (setq dabbrev--last-expansion expansion) - (setq dabbrev--last-abbreviation abbrev) - (setq dabbrev--last-abbrev-location (point-marker)))))) + ;; Save state for re-expand (making sure it's the state of the + ;; buffer containing the already found expansions). + (with-current-buffer buf + (setq dabbrev--last-expansion expansion) + (setq dabbrev--last-abbreviation abbrev) + (setq dabbrev--last-abbrev-location (point-marker))))))) ;;---------------------------------------------------------------- ;; Local functions diff --git a/test/lisp/dabbrev-tests.el b/test/lisp/dabbrev-tests.el index c7574403949..987106aa5af 100644 --- a/test/lisp/dabbrev-tests.el +++ b/test/lisp/dabbrev-tests.el @@ -25,6 +25,7 @@ ;;; Code: (require 'ert) +(require 'ert-x) (require 'dabbrev) (ert-deftest dabbrev-expand-test () @@ -68,4 +69,210 @@ dabbrev-completion-test-with-argument (execute-kbd-macro (kbd "C-u C-u C-M-/"))) (should (string= (buffer-string) "abc\na"))))) +(defmacro with-dabbrev-test (&rest body) + "Set up an isolated `dabbrev' test environment." + (declare (debug (body))) + `(ert-with-temp-directory dabbrev-test-home + (let* (;; Since we change HOME, clear this to avoid a conflict + ;; e.g. if Emacs runs within the user's home directory. + (abbreviated-home-dir nil) + (process-environment (cons (format "HOME=%s" dabbrev-test-home) + process-environment)) + (dabbrev-directory (ert-resource-directory))) + (unwind-protect + (progn ,@body) + ;; Restore pre-test-run state of test files. + (dolist (f (directory-files dabbrev-directory)) + (let ((buf (get-file-buffer f))) + (when buf + (with-current-buffer buf + (restore-buffer-modified-p nil) + (kill-buffer))))) + (dabbrev--reset-global-variables))))) + +(ert-deftest dabbrev-expand-test-same-buffer-1 () + "Test expanding a string twice within a single buffer. +The first expansion should expand the input (a prefix-string) to a +string in the buffer containing no whitespace character, the second +expansion, after adding a space to the first expansion, should extend +the string with the following string in the buffer up to the next +whitespace character." + (with-dabbrev-test + (find-file (ert-resource-file "INSTALL_BEGIN")) + (goto-char (point-max)) + (terpri) + (execute-kbd-macro (kbd "Ind M-/")) + (should (string= (buffer-substring (pos-bol) (pos-eol)) "Indic")) + (execute-kbd-macro (kbd "SPC M-/")) + (should (string= (buffer-substring (pos-bol) (pos-eol)) "Indic and")))) + +(ert-deftest dabbrev-expand-test-same-buffer-2 () + "Test expanding a string plus space twice within a single buffer. +Each expansion should extend the string with the following string in the +buffer up to the next whitespace character." + (with-dabbrev-test + (find-file (ert-resource-file "INSTALL_BEGIN")) + (goto-char (point-max)) + (terpri) + (execute-kbd-macro (kbd "Indic SPC M-/")) + (should (string= (buffer-substring (pos-bol) (pos-eol)) "Indic and")) + (execute-kbd-macro (kbd "SPC M-/")) + (should (string= (buffer-substring (pos-bol) (pos-eol)) "Indic and Khmer")))) + +(ert-deftest dabbrev-expand-test-same-buffer-3 () + "Test replacing an expansion within a single buffer." + (with-dabbrev-test + (find-file (ert-resource-file "INSTALL_BEGIN")) + (goto-char (point-max)) + (terpri) + (insert-file-contents (ert-resource-file "dabbrev-expand.el")) + (goto-char (point-max)) + (terpri) + (execute-kbd-macro (kbd "Ind M-/")) + (should (string= (buffer-substring (pos-bol) (pos-eol)) "Indicate")) + (kill-whole-line) + (execute-kbd-macro (kbd "Ind M-/ M-/")) + (should (string= (buffer-substring (pos-bol) (pos-eol)) "Indic")) + (execute-kbd-macro (kbd "SPC M-/")) + (should (string= (buffer-substring (pos-bol) (pos-eol)) "Indic and")))) + +(ert-deftest dabbrev-expand-test-same-buffer-4 () + "Test expanding a string in a narrowed-region." + (with-dabbrev-test + (let (disabled-command-function) ; Enable narrow-to-region. + (find-file (ert-resource-file "INSTALL_BEGIN")) + (goto-char (point-min)) + (execute-kbd-macro (kbd "C-s Ind M-a C-SPC M-} C-x n n")) + (goto-char (point-max)) + (terpri) + (execute-kbd-macro (kbd "Ind M-/")) + (should (string= (buffer-substring (pos-bol) (pos-eol)) "Indic")) + (execute-kbd-macro (kbd "SPC M-/")) + (should (string= (buffer-substring (pos-bol) (pos-eol)) "Indic and"))))) + +(ert-deftest dabbrev-expand-test-other-buffer-1 () + "Test expanding a prefix string to a string from another buffer." + (with-dabbrev-test + (find-file (ert-resource-file "INSTALL_BEGIN")) + (switch-to-buffer (get-buffer-create "a" t)) + (execute-kbd-macro (kbd "Ind M-/")) + (should (string= (buffer-string) "Indic")) + (execute-kbd-macro (kbd "SPC M-/")) + (should (string= (buffer-string) "Indic and")) + (kill-buffer "a"))) + +(ert-deftest dabbrev-expand-test-other-buffer-2 () + "Test expanding a string + space to a string from another buffer." + (with-dabbrev-test + (find-file (ert-resource-file "INSTALL_BEGIN")) + (switch-to-buffer (get-buffer-create "a" t)) + (execute-kbd-macro (kbd "Indic SPC M-/")) + (should (string= (buffer-string) "Indic and")) + (execute-kbd-macro (kbd "SPC M-/")) + (should (string= (buffer-string) "Indic and Khmer")) + (kill-buffer "a"))) + +(ert-deftest dabbrev-expand-test-other-buffer-3 () + "Test replacing an expansion with three different buffers. +A prefix string in a buffer should find the first expansion in a +different buffer and then find a replacement expansion is yet another +buffer." + (with-dabbrev-test + (find-file (ert-resource-file "INSTALL_BEGIN")) + (find-file (ert-resource-file "dabbrev-expand.el")) + (switch-to-buffer (get-buffer-create "a" t)) + (emacs-lisp-mode) + (execute-kbd-macro (kbd "Ind M-/")) + (should (string= (buffer-string) "Indicate")) + (erase-buffer) + (execute-kbd-macro (kbd "Ind M-/ M-/")) + (should (string= (buffer-string) "Indic")) + (execute-kbd-macro (kbd "SPC M-/")) + (should (string= (buffer-string) "Indic and")) + (kill-buffer "a"))) + +(ert-deftest dabbrev-expand-test-other-buffer-4 () + "Test expanding a string using another narrowed buffer." + (with-dabbrev-test + (let (disabled-command-function) ; Enable narrow-to-region. + (find-file (ert-resource-file "INSTALL_BEGIN")) + (goto-char (point-min)) + (execute-kbd-macro (kbd "C-s Ind M-a C-SPC M-} C-x n n")) + (switch-to-buffer (get-buffer-create "a" t)) + (execute-kbd-macro (kbd "Ind M-/")) + (should (string= (buffer-string) "Indic")) + (execute-kbd-macro (kbd "SPC M-/")) + (should (string= (buffer-string) "Indic and")) + (kill-buffer "a")))) + +(ert-deftest dabbrev-expand-test-minibuffer-1 () + "Test expanding a prefix string twice in the minibuffer. +Both expansions should come from the buffer from which the minibuffer +was entered." + (with-dabbrev-test + (find-file (ert-resource-file "INSTALL_BEGIN")) + (with-selected-window (minibuffer-window) + (insert "Ind") + (dabbrev-expand nil) + (should (string= (minibuffer-contents) "Indic")) + (insert " ") + (dabbrev-expand nil) + (should (string= (minibuffer-contents) "Indic and")) + (delete-minibuffer-contents)))) + +(ert-deftest dabbrev-expand-test-minibuffer-2 () + "Test expanding a string + space in the minibuffer. +The expansions should come from the buffer from which the minibuffer was +entered." + (with-dabbrev-test + (find-file (ert-resource-file "INSTALL_BEGIN")) + (with-selected-window (minibuffer-window) + (insert "Indic ") + (dabbrev-expand nil) + (should (string= (minibuffer-contents) "Indic and")) + (insert " ") + (dabbrev-expand nil) + (should (string= (buffer-string) "Indic and Khmer")) + (delete-minibuffer-contents)))) + +;; FIXME: Why is dabbrev--reset-global-variables needed here? +(ert-deftest dabbrev-expand-test-minibuffer-3 () + "Test replacing an expansion in the minibuffer using two buffers. +The first expansion should befound in the buffer from which the +minibuffer was entered, the replacement should found in another buffer." + (with-dabbrev-test + (find-file (ert-resource-file "INSTALL_BEGIN")) + (find-file (ert-resource-file "dabbrev-expand.el")) + (with-selected-window (minibuffer-window) + (insert "Ind") + (dabbrev-expand nil) + (should (string= (minibuffer-contents) "Indicate")) + (kill-whole-line) + (dabbrev--reset-global-variables) + (insert "Ind") + (dabbrev-expand nil) + (dabbrev-expand nil) + (should (string= (minibuffer-contents) "Indic")) + (dabbrev--reset-global-variables) + (insert " ") + (dabbrev-expand nil) + (should (string= (minibuffer-contents) "Indic and")) + (delete-minibuffer-contents)))) + +(ert-deftest dabbrev-expand-test-minibuffer-4 () + "Test expansion in the minibuffer using another narrowed buffer." + (with-dabbrev-test + (let (disabled-command-function) ; Enable narrow-to-region. + (find-file (ert-resource-file "INSTALL_BEGIN")) + (goto-char (point-min)) + (execute-kbd-macro (kbd "C-s Ind M-a C-SPC M-} C-x n n"))) + (with-selected-window (minibuffer-window) + (insert "Ind") + (dabbrev-expand nil) + (should (string= (minibuffer-contents) "Indic")) + (insert " ") + (dabbrev-expand nil) + (should (string= (minibuffer-contents) "Indic and")) + (delete-minibuffer-contents)))) + ;;; dabbrev-tests.el ends here