Merge branch 'fix/bug-21072'
This commit is contained in:
commit
9a5e864de7
4 changed files with 402 additions and 43 deletions
|
@ -248,11 +248,15 @@ the same as @kbd{C-M-a} with a positive argument.
|
|||
(@code{mark-defun}), which sets the mark at the end of the current
|
||||
defun and puts point at its beginning. @xref{Marking Objects}. This
|
||||
is the easiest way to get ready to kill the defun in order to move it
|
||||
to a different place in the file. If you use the command while point
|
||||
is between defuns, it uses the following defun. If you use the
|
||||
command while the mark is already active, it sets the mark but does
|
||||
not move point; furthermore, each successive use of @kbd{C-M-h}
|
||||
extends the end of the region to include one more defun.
|
||||
to a different place in the file. If the defun is directly preceded
|
||||
by comments (with no intervening blank lines), they are marked, too.
|
||||
If you use the command while point is between defuns, it uses the
|
||||
following defun. If you use the command while the mark is already
|
||||
active, it extends the end of the region to include one more defun.
|
||||
With a prefix argument, it marks that many defuns or extends the
|
||||
region by the appropriate number of defuns. With negative prefix
|
||||
argument it marks defuns in the opposite direction and also changes
|
||||
the direction of selecting for subsequent uses of @code{mark-defun}.
|
||||
|
||||
In C mode, @kbd{C-M-h} runs the function @code{c-mark-function},
|
||||
which is almost the same as @code{mark-defun}; the difference is that
|
||||
|
|
9
etc/NEWS
9
etc/NEWS
|
@ -403,6 +403,15 @@ manner via the new mode 'auto-save-visited-mode'. Unlike
|
|||
procedure and therefore obeys saving hooks.
|
||||
'auto-save-visited-file-name' is now obsolete.
|
||||
|
||||
+++
|
||||
** New behavior of 'mark-defun' implemented
|
||||
Prefix argument selects that many (or that many more) defuns.
|
||||
Negative prefix arg flips the direction of selection. Also,
|
||||
'mark-defun' between defuns correctly selects N following defuns (or
|
||||
-N previous for negative arguments). Finally, comments preceding the
|
||||
defun are selected unless they are separated from the defun by a blank
|
||||
line.
|
||||
|
||||
|
||||
* Changes in Specialized Modes and Packages in Emacs 26.1
|
||||
|
||||
|
|
|
@ -398,6 +398,42 @@ is called as a function to find the defun's beginning."
|
|||
(goto-char (if arg-+ve floor ceiling))
|
||||
nil))))))))
|
||||
|
||||
(defun beginning-of-defun--in-emptyish-line-p ()
|
||||
"Return non-nil if the point is in an \"emptyish\" line.
|
||||
This means a line that consists entirely of comments and/or
|
||||
whitespace."
|
||||
;; See http://lists.gnu.org/archive/html/help-gnu-emacs/2016-08/msg00141.html
|
||||
(save-excursion
|
||||
(forward-line 0)
|
||||
(< (line-end-position)
|
||||
(let ((ppss (syntax-ppss)))
|
||||
(when (nth 4 ppss)
|
||||
(goto-char (nth 8 ppss)))
|
||||
(forward-comment (point-max))
|
||||
(point)))))
|
||||
|
||||
(defun beginning-of-defun-comments (&optional arg)
|
||||
"Move to the beginning of ARGth defun, including comments."
|
||||
(interactive "^p")
|
||||
(unless arg (setq arg 1))
|
||||
(beginning-of-defun arg)
|
||||
(let (first-line-p)
|
||||
(while (let ((ppss (progn (setq first-line-p (= (forward-line -1) -1))
|
||||
(syntax-ppss (line-end-position)))))
|
||||
(while (and (nth 4 ppss) ; If eol is in a line-spanning comment,
|
||||
(< (nth 8 ppss) (line-beginning-position)))
|
||||
(goto-char (nth 8 ppss)) ; skip to comment start.
|
||||
(setq ppss (syntax-ppss (line-end-position))))
|
||||
(and (not first-line-p)
|
||||
(progn (skip-syntax-backward
|
||||
"-" (line-beginning-position))
|
||||
(not (bolp))) ; Check for blank line.
|
||||
(progn (parse-partial-sexp
|
||||
(line-beginning-position) (line-end-position)
|
||||
nil t (syntax-ppss (line-beginning-position)))
|
||||
(eolp))))) ; Check for non-comment text.
|
||||
(forward-line (if first-line-p 0 1))))
|
||||
|
||||
(defvar end-of-defun-function
|
||||
(lambda () (forward-sexp 1))
|
||||
"Function for `end-of-defun' to call.
|
||||
|
@ -478,48 +514,72 @@ is called as a function to find the defun's end."
|
|||
(funcall end-of-defun-function)
|
||||
(funcall skip)))))
|
||||
|
||||
(defun mark-defun (&optional allow-extend)
|
||||
(defun mark-defun (&optional arg)
|
||||
"Put mark at end of this defun, point at beginning.
|
||||
The defun marked is the one that contains point or follows point.
|
||||
With positive ARG, mark this and that many next defuns; with negative
|
||||
ARG, change the direction of marking.
|
||||
|
||||
Interactively, if this command is repeated
|
||||
or (in Transient Mark mode) if the mark is active,
|
||||
it marks the next defun after the ones already marked."
|
||||
If the mark is active, it marks the next or previous defun(s) after
|
||||
the one(s) already marked."
|
||||
(interactive "p")
|
||||
(cond ((and allow-extend
|
||||
(or (and (eq last-command this-command) (mark t))
|
||||
(and transient-mark-mode mark-active)))
|
||||
(set-mark
|
||||
(save-excursion
|
||||
(goto-char (mark))
|
||||
(end-of-defun)
|
||||
(point))))
|
||||
(t
|
||||
(let ((opoint (point))
|
||||
beg end)
|
||||
(push-mark opoint)
|
||||
;; Try first in this order for the sake of languages with nested
|
||||
;; functions where several can end at the same place as with
|
||||
;; the offside rule, e.g. Python.
|
||||
(beginning-of-defun)
|
||||
(setq beg (point))
|
||||
(end-of-defun)
|
||||
(setq end (point))
|
||||
(while (looking-at "^\n")
|
||||
(forward-line 1))
|
||||
(if (> (point) opoint)
|
||||
(progn
|
||||
;; We got the right defun.
|
||||
(push-mark beg nil t)
|
||||
(goto-char end)
|
||||
(exchange-point-and-mark))
|
||||
;; beginning-of-defun moved back one defun
|
||||
;; so we got the wrong one.
|
||||
(goto-char opoint)
|
||||
(end-of-defun)
|
||||
(push-mark (point) nil t)
|
||||
(beginning-of-defun))
|
||||
(re-search-backward "^\n" (- (point) 1) t)))))
|
||||
(setq arg (or arg 1))
|
||||
;; There is no `mark-defun-back' function - see
|
||||
;; https://lists.gnu.org/archive/html/bug-gnu-emacs/2016-11/msg00079.html
|
||||
;; for explanation
|
||||
(when (eq last-command 'mark-defun-back)
|
||||
(setq arg (- arg)))
|
||||
(when (< arg 0)
|
||||
(setq this-command 'mark-defun-back))
|
||||
(cond ((use-region-p)
|
||||
(if (>= arg 0)
|
||||
(set-mark
|
||||
(save-excursion
|
||||
(goto-char (mark))
|
||||
;; change the dotimes below to (end-of-defun arg) once bug #24427 is fixed
|
||||
(dotimes (_ignore arg)
|
||||
(end-of-defun))
|
||||
(point)))
|
||||
(beginning-of-defun-comments (- arg))))
|
||||
(t
|
||||
(let ((opoint (point))
|
||||
beg end)
|
||||
(push-mark opoint)
|
||||
;; Try first in this order for the sake of languages with nested
|
||||
;; functions where several can end at the same place as with the
|
||||
;; offside rule, e.g. Python.
|
||||
(beginning-of-defun-comments)
|
||||
(setq beg (point))
|
||||
(end-of-defun)
|
||||
(setq end (point))
|
||||
(when (or (and (<= (point) opoint)
|
||||
(> arg 0))
|
||||
(= beg (point-min))) ; we were before the first defun!
|
||||
;; beginning-of-defun moved back one defun so we got the wrong
|
||||
;; one. If ARG < 0, however, we actually want to go back.
|
||||
(goto-char opoint)
|
||||
(end-of-defun)
|
||||
(setq end (point))
|
||||
(beginning-of-defun-comments)
|
||||
(setq beg (point)))
|
||||
(goto-char beg)
|
||||
(cond ((> arg 0)
|
||||
;; change the dotimes below to (end-of-defun arg) once bug #24427 is fixed
|
||||
(dotimes (_ignore arg)
|
||||
(end-of-defun))
|
||||
(setq end (point))
|
||||
(push-mark end nil t)
|
||||
(goto-char beg))
|
||||
(t
|
||||
(goto-char beg)
|
||||
(unless (= arg -1) ; beginning-of-defun behaves
|
||||
; strange with zero arg - see
|
||||
; https://lists.gnu.org/archive/html/bug-gnu-emacs/2017-02/msg00196.html
|
||||
(beginning-of-defun (1- (- arg))))
|
||||
(push-mark end nil t))))))
|
||||
(skip-chars-backward "[:space:]\n")
|
||||
(unless (bobp)
|
||||
(forward-line 1)))
|
||||
|
||||
(defvar narrow-to-defun-include-comments nil
|
||||
"If non-nil, `narrow-to-defun' will also show comments preceding the defun.")
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
;; Author: Aaron S. Hawley <aaron.s.hawley@gmail.com>
|
||||
;; Author: Stefan Monnier <monnier@iro.umontreal.ca>
|
||||
;; Author: Daniel Colascione <dancol@dancol.org>
|
||||
;; Author: Marcin Borkowski <mbork@mbork.pl>
|
||||
;; Keywords: internal
|
||||
|
||||
;; GNU Emacs is free software: you can redistribute it and/or modify
|
||||
|
@ -303,5 +304,290 @@
|
|||
;; abcdefghijklmnopqrstuv
|
||||
i f a scan-error)
|
||||
|
||||
;;; Helpers
|
||||
|
||||
(eval-and-compile
|
||||
(defvar elisp-test-point-position-regex "=!\\([a-zA-Z0-9-]+\\)="
|
||||
"A regexp matching placeholders for point position for
|
||||
`elisp-tests-with-temp-buffer'."))
|
||||
|
||||
;; Copied and heavily modified from `python-tests-with-temp-buffer'
|
||||
(defmacro elisp-tests-with-temp-buffer (contents &rest body)
|
||||
"Create an `emacs-lisp-mode' enabled temp buffer with CONTENTS.
|
||||
BODY is the code to be executed within the temp buffer. Point is
|
||||
always located at the beginning of buffer. CONTENTS is an
|
||||
expression that must evaluate to a string at compile time. Words
|
||||
of the form =!NAME= in CONTENTS are removed, and a for each one a
|
||||
variable called NAME is bound to the position of the word's
|
||||
start."
|
||||
(declare (indent 1) (debug (def-form body)))
|
||||
(let* ((var-pos nil)
|
||||
(text (with-temp-buffer
|
||||
(insert (eval contents))
|
||||
(goto-char (point-min))
|
||||
(while (re-search-forward elisp-test-point-position-regex nil t)
|
||||
(push (list (intern (match-string-no-properties 1))
|
||||
(match-beginning 0))
|
||||
var-pos)
|
||||
(delete-region (match-beginning 0)
|
||||
(match-end 0)))
|
||||
(buffer-string))))
|
||||
`(with-temp-buffer
|
||||
(emacs-lisp-mode)
|
||||
(insert ,text)
|
||||
(goto-char (point-min))
|
||||
(let ,var-pos
|
||||
;; Let the =!POSITION= variables be ignorable.
|
||||
,@(mapcar (lambda (v-p) `(ignore ,(car v-p))) var-pos)
|
||||
,@body))))
|
||||
|
||||
;;; mark-defun
|
||||
|
||||
(eval-and-compile
|
||||
(defvar mark-defun-test-buffer
|
||||
";; Comment header
|
||||
=!before-1=
|
||||
\(defun func-1 (arg)
|
||||
=!inside-1=\"docstring\"
|
||||
body)
|
||||
=!after-1==!before-2=
|
||||
;; Comment before a defun
|
||||
\(d=!inside-2=efun func-2 (arg)
|
||||
\"docstring\"
|
||||
body)
|
||||
=!after-2==!before-3=
|
||||
\(defun func-3 (arg)
|
||||
\"docstring\"=!inside-3=
|
||||
body)
|
||||
=!after-3==!before-4=(defun func-4 (arg)
|
||||
\"docstring\"=!inside-4=
|
||||
body)
|
||||
=!after-4=
|
||||
;; end
|
||||
"
|
||||
"Test buffer for `mark-defun'."))
|
||||
|
||||
(ert-deftest mark-defun-no-arg-region-inactive ()
|
||||
"Test `mark-defun' with no prefix argument and inactive
|
||||
region."
|
||||
(setq last-command nil)
|
||||
(elisp-tests-with-temp-buffer
|
||||
mark-defun-test-buffer
|
||||
;; mark-defun inside a defun, with comments and an empty line
|
||||
;; before
|
||||
(goto-char inside-1)
|
||||
(mark-defun)
|
||||
(should (= (point) before-1))
|
||||
(should (= (mark) after-1))
|
||||
;; mark-defun inside a defun with comments before
|
||||
(deactivate-mark)
|
||||
(goto-char inside-2)
|
||||
(mark-defun)
|
||||
(should (= (point) before-2))
|
||||
(should (= (mark) after-2))
|
||||
;; mark-defun inside a defun with empty line before
|
||||
(deactivate-mark)
|
||||
(goto-char inside-3)
|
||||
(mark-defun)
|
||||
(should (= (point) before-3))
|
||||
(should (= (mark) after-3))
|
||||
;; mark-defun inside a defun with another one right before
|
||||
(deactivate-mark)
|
||||
(goto-char inside-4)
|
||||
(mark-defun)
|
||||
(should (= (point) before-4))
|
||||
(should (= (mark) after-4))
|
||||
;; mark-defun between a comment and a defun
|
||||
(deactivate-mark)
|
||||
(goto-char before-1)
|
||||
(mark-defun)
|
||||
(should (= (point) before-1))
|
||||
(should (= (mark) after-1))
|
||||
;; mark-defun between defuns
|
||||
(deactivate-mark)
|
||||
(goto-char before-3)
|
||||
(mark-defun)
|
||||
(should (= (point) before-3))
|
||||
(should (= (mark) after-3))
|
||||
;; mark-defun in comment right before the defun
|
||||
(deactivate-mark)
|
||||
(goto-char before-2)
|
||||
(mark-defun)
|
||||
(should (= (point) before-2))
|
||||
(should (= (mark) after-2))))
|
||||
|
||||
(ert-deftest mark-defun-no-arg-region-active ()
|
||||
"Test `mark-defun' with no prefix argument and active
|
||||
region."
|
||||
(transient-mark-mode 1)
|
||||
(setq last-command nil)
|
||||
(elisp-tests-with-temp-buffer
|
||||
mark-defun-test-buffer
|
||||
;; mark-defun when a defun is marked
|
||||
(goto-char before-1)
|
||||
(set-mark after-1)
|
||||
(mark-defun)
|
||||
(should (= (point) before-1))
|
||||
(should (= (mark) after-2))
|
||||
;; mark-defun when two defuns are marked
|
||||
(deactivate-mark)
|
||||
(goto-char before-1)
|
||||
(set-mark after-2)
|
||||
(mark-defun)
|
||||
(should (= (point) before-1))
|
||||
(should (= (mark) after-3))))
|
||||
|
||||
(ert-deftest mark-defun-arg-region-active ()
|
||||
"Test `mark-defun' with a prefix arg and active region."
|
||||
(transient-mark-mode 1)
|
||||
(setq last-command nil)
|
||||
(elisp-tests-with-temp-buffer
|
||||
mark-defun-test-buffer
|
||||
;; mark-defun with positive arg when a defun is marked
|
||||
(goto-char before-1)
|
||||
(set-mark after-1)
|
||||
(mark-defun 2)
|
||||
(should (= (point) before-1))
|
||||
(should (= (mark) after-3))
|
||||
;; mark-defun with arg=-1 when a defun is marked
|
||||
(goto-char before-2)
|
||||
(set-mark after-2)
|
||||
(mark-defun -1)
|
||||
(should (= (point) before-1))
|
||||
(should (= (mark) after-2))
|
||||
;; mark-defun with arg=-2 when a defun is marked
|
||||
(goto-char before-3)
|
||||
(set-mark after-3)
|
||||
(mark-defun -2)
|
||||
(should (= (point) before-1))
|
||||
(should (= (mark) after-3))))
|
||||
|
||||
(ert-deftest mark-defun-pos-arg-region-inactive ()
|
||||
"Test `mark-defun' with positive argument and inactive
|
||||
region."
|
||||
(setq last-command nil)
|
||||
(elisp-tests-with-temp-buffer
|
||||
mark-defun-test-buffer
|
||||
;; mark-defun with positive arg inside a defun
|
||||
(goto-char inside-1)
|
||||
(mark-defun 2)
|
||||
(should (= (point) before-1))
|
||||
(should (= (mark) after-2))
|
||||
;; mark-defun with positive arg between defuns
|
||||
(deactivate-mark)
|
||||
(goto-char before-3)
|
||||
(mark-defun 2)
|
||||
(should (= (point) before-3))
|
||||
(should (= (mark) after-4))
|
||||
;; mark-defun with positive arg in a comment
|
||||
(deactivate-mark)
|
||||
(goto-char before-2)
|
||||
(mark-defun 2)
|
||||
(should (= (point) before-2))
|
||||
(should (= (mark) after-3))))
|
||||
|
||||
(ert-deftest mark-defun-neg-arg-region-inactive ()
|
||||
"Test `mark-defun' with negative argument and inactive
|
||||
region."
|
||||
(setq last-command nil)
|
||||
(elisp-tests-with-temp-buffer
|
||||
mark-defun-test-buffer
|
||||
;; mark-defun with arg=-1 inside a defun
|
||||
(goto-char inside-1)
|
||||
(mark-defun -1)
|
||||
(should (= (point) before-1))
|
||||
(should (= (mark) after-1))
|
||||
;; mark-defun with arg=-1 between defuns
|
||||
(deactivate-mark)
|
||||
(goto-char after-2)
|
||||
(mark-defun -1)
|
||||
(should (= (point) before-2))
|
||||
(should (= (mark) after-2))
|
||||
;; mark-defun with arg=-1 in a comment
|
||||
;; (this is probably not an optimal behavior...)
|
||||
(deactivate-mark)
|
||||
(goto-char before-2)
|
||||
(mark-defun -1)
|
||||
(should (= (point) before-1))
|
||||
(should (= (mark) after-1))
|
||||
;; mark-defun with arg=-2 inside a defun
|
||||
(deactivate-mark)
|
||||
(goto-char inside-4)
|
||||
(mark-defun -2)
|
||||
(should (= (point) before-3))
|
||||
(should (= (mark) after-4))
|
||||
;; mark-defun with arg=-2 between defuns
|
||||
(deactivate-mark)
|
||||
(goto-char before-3)
|
||||
(mark-defun -2)
|
||||
(should (= (point) before-1))
|
||||
(should (= (mark) after-2)))
|
||||
(elisp-tests-with-temp-buffer ; test case submitted by Drew Adams
|
||||
"(defun a ()
|
||||
nil)
|
||||
=!before-b=(defun b ()
|
||||
=!in-b= nil)
|
||||
=!after-b=;;;;
|
||||
\(defun c ()
|
||||
nil)
|
||||
"
|
||||
(setq last-command nil)
|
||||
(goto-char in-b)
|
||||
(mark-defun -1)
|
||||
(should (= (point) before-b))
|
||||
(should (= (mark) after-b))))
|
||||
|
||||
(ert-deftest mark-defun-bob ()
|
||||
"Test `mark-defun' at the beginning of buffer."
|
||||
;; Bob, comment, newline, defun
|
||||
(setq last-command nil)
|
||||
(elisp-tests-with-temp-buffer
|
||||
";; Comment at the bob
|
||||
=!before=
|
||||
\(defun func (arg)=!inside=
|
||||
\"docstring\"
|
||||
body)
|
||||
=!after="
|
||||
(goto-char inside)
|
||||
(mark-defun)
|
||||
(should (= (point) before))
|
||||
(should (= (mark) after)))
|
||||
;; Bob, newline, comment, defun
|
||||
(elisp-tests-with-temp-buffer
|
||||
"=!before=
|
||||
;; Comment before the defun
|
||||
\(defun func (arg)=!inside=
|
||||
\"docstring\"
|
||||
body)
|
||||
=!after="
|
||||
(goto-char inside)
|
||||
(mark-defun)
|
||||
(should (= (point) before))
|
||||
(should (= (mark) after)))
|
||||
;; Bob, comment, defun
|
||||
(elisp-tests-with-temp-buffer
|
||||
"=!before=;; Comment at the bob before the defun
|
||||
\(defun func (arg)=!inside=
|
||||
\"docstring\"
|
||||
body)
|
||||
=!after="
|
||||
(goto-char inside)
|
||||
(mark-defun)
|
||||
(should (= (point) before))
|
||||
(should (= (mark) after)))
|
||||
;; Bob, newline, comment, newline, defun
|
||||
(elisp-tests-with-temp-buffer
|
||||
"
|
||||
;; Comment before the defun
|
||||
=!before=
|
||||
\(defun func (arg)=!inside=
|
||||
\"docstring\"
|
||||
body)
|
||||
=!after="
|
||||
(goto-char inside)
|
||||
(mark-defun)
|
||||
(should (= (point) before))
|
||||
(should (= (mark) after))))
|
||||
|
||||
(provide 'lisp-tests)
|
||||
;;; lisp-tests.el ends here
|
||||
|
|
Loading…
Add table
Reference in a new issue