aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarcin Borkowski2017-03-31 13:06:06 +0200
committerMarcin Borkowski2017-05-12 11:36:27 +0200
commit22fc91704be4737865b3715e5278dc78029791bd (patch)
treebe0dcd1fb3fa25bbfb01467a8dac6716056da217
parent6d58dda40a0a43d14dffdd995f0cb3dcc329fa4b (diff)
downloademacs-22fc91704be4737865b3715e5278dc78029791bd.tar.gz
emacs-22fc91704be4737865b3715e5278dc78029791bd.zip
Fix Bug#21072 and rework `mark-defun'
* test/lisp/progmodes/elisp-mode-tests.el (mark-defun-test-buffer): New variable (mark-defun-no-arg-region-inactive) (mark-defun-no-arg-region-active) (mark-defun-arg-region-active) (mark-defun-pos-arg-region-inactive) (mark-defun-neg-arg-region-inactive, mark-defun-bob): Add tests for the new `mark-defun'. * lisp/emacs-lisp/lisp.el (beginning-of-defun--in-emptyish-line-p): New function. (beginning-of-defun-comments): New function. (mark-defun): Fix bug#21072, also rewrite large parts of `mark-defun' to accept a numerical prefix argument.
-rw-r--r--doc/emacs/programs.texi14
-rw-r--r--etc/NEWS9
-rw-r--r--lisp/emacs-lisp/lisp.el132
-rw-r--r--test/lisp/emacs-lisp/lisp-tests.el247
4 files changed, 359 insertions, 43 deletions
diff --git a/doc/emacs/programs.texi b/doc/emacs/programs.texi
index 1533c7ee8bb..222d1c2a4de 100644
--- a/doc/emacs/programs.texi
+++ b/doc/emacs/programs.texi
@@ -248,11 +248,15 @@ the same as @kbd{C-M-a} with a positive argument.
248(@code{mark-defun}), which sets the mark at the end of the current 248(@code{mark-defun}), which sets the mark at the end of the current
249defun and puts point at its beginning. @xref{Marking Objects}. This 249defun and puts point at its beginning. @xref{Marking Objects}. This
250is the easiest way to get ready to kill the defun in order to move it 250is the easiest way to get ready to kill the defun in order to move it
251to a different place in the file. If you use the command while point 251to a different place in the file. If the defun is directly preceded
252is between defuns, it uses the following defun. If you use the 252by comments (with no intervening blank lines), they are marked, too.
253command while the mark is already active, it sets the mark but does 253If you use the command while point is between defuns, it uses the
254not move point; furthermore, each successive use of @kbd{C-M-h} 254following defun. If you use the command while the mark is already
255extends the end of the region to include one more defun. 255active, it extends the end of the region to include one more defun.
256With a prefix argument, it marks that many defuns or extends the
257region by the appropriate number of defuns. With negative prefix
258argument it marks defuns in the opposite direction and also changes
259the direction of selecting for subsequent uses of @code{mark-defun}.
256 260
257 In C mode, @kbd{C-M-h} runs the function @code{c-mark-function}, 261 In C mode, @kbd{C-M-h} runs the function @code{c-mark-function},
258which is almost the same as @code{mark-defun}; the difference is that 262which is almost the same as @code{mark-defun}; the difference is that
diff --git a/etc/NEWS b/etc/NEWS
index 72818278781..8e628aad20d 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -383,6 +383,15 @@ Strings such as ΌΣΟΣ are now correctly converted to Όσος when
383capitalized instead of incorrect Όσοσ (compare lowercase sigma at the 383capitalized instead of incorrect Όσοσ (compare lowercase sigma at the
384end of the word). 384end of the word).
385 385
386+++
387** New behavior of 'mark-defun' implemented
388Prefix argument selects that many (or that many more) defuns.
389Negative prefix arg flips the direction of selection. Also,
390'mark-defun' between defuns correctly selects N following defuns (or
391-N previous for negative arguments). Finally, comments preceding the
392defun are selected unless they are separated from the defun by a blank
393line.
394
386 395
387* Changes in Specialized Modes and Packages in Emacs 26.1 396* Changes in Specialized Modes and Packages in Emacs 26.1
388 397
diff --git a/lisp/emacs-lisp/lisp.el b/lisp/emacs-lisp/lisp.el
index 0172e3af261..e74e2474ee9 100644
--- a/lisp/emacs-lisp/lisp.el
+++ b/lisp/emacs-lisp/lisp.el
@@ -398,6 +398,34 @@ is called as a function to find the defun's beginning."
398 (goto-char (if arg-+ve floor ceiling)) 398 (goto-char (if arg-+ve floor ceiling))
399 nil)))))))) 399 nil))))))))
400 400
401(defun beginning-of-defun--in-emptyish-line-p ()
402 "Return non-nil if the point is in an \"emptyish\" line.
403This means a line that consists entirely of comments and/or
404whitespace."
405;; See http://lists.gnu.org/archive/html/help-gnu-emacs/2016-08/msg00141.html
406 (save-excursion
407 (forward-line 0)
408 (< (line-end-position)
409 (let ((ppss (syntax-ppss)))
410 (when (nth 4 ppss)
411 (goto-char (nth 8 ppss)))
412 (forward-comment (point-max))
413 (point)))))
414
415(defun beginning-of-defun-comments (&optional arg)
416 "Move to the beginning of ARGth defun, including comments."
417 (interactive "^p")
418 (unless arg (setq arg 1))
419 (beginning-of-defun arg)
420 (let (nbobp)
421 (while (progn
422 (setq nbobp (zerop (forward-line -1)))
423 (and (not (looking-at "^\\s-*$"))
424 (beginning-of-defun--in-emptyish-line-p)
425 nbobp)))
426 (when nbobp
427 (forward-line 1))))
428
401(defvar end-of-defun-function 429(defvar end-of-defun-function
402 (lambda () (forward-sexp 1)) 430 (lambda () (forward-sexp 1))
403 "Function for `end-of-defun' to call. 431 "Function for `end-of-defun' to call.
@@ -478,48 +506,76 @@ is called as a function to find the defun's end."
478 (funcall end-of-defun-function) 506 (funcall end-of-defun-function)
479 (funcall skip))))) 507 (funcall skip)))))
480 508
481(defun mark-defun (&optional allow-extend) 509(defun mark-defun (&optional arg)
482 "Put mark at end of this defun, point at beginning. 510 "Put mark at end of this defun, point at beginning.
483The defun marked is the one that contains point or follows point. 511The defun marked is the one that contains point or follows point.
512With positive ARG, mark this and that many next defuns; with negative
513ARG, change the direction of marking.
484 514
485Interactively, if this command is repeated 515If the mark is active, it marks the next or previous defun(s) after
486or (in Transient Mark mode) if the mark is active, 516the one(s) already marked."
487it marks the next defun after the ones already marked."
488 (interactive "p") 517 (interactive "p")
489 (cond ((and allow-extend 518 (setq arg (or arg 1))
490 (or (and (eq last-command this-command) (mark t)) 519 ;; There is no `mark-defun-back' function - see
491 (and transient-mark-mode mark-active))) 520 ;; https://lists.gnu.org/archive/html/bug-gnu-emacs/2016-11/msg00079.html
492 (set-mark 521 ;; for explanation
493 (save-excursion 522 (when (eq last-command 'mark-defun-back)
494 (goto-char (mark)) 523 (setq arg (- arg)))
495 (end-of-defun) 524 (when (< arg 0)
496 (point)))) 525 (setq this-command 'mark-defun-back))
497 (t 526 (cond ((use-region-p)
498 (let ((opoint (point)) 527 (if (>= arg 0)
499 beg end) 528 (set-mark
500 (push-mark opoint) 529 (save-excursion
501 ;; Try first in this order for the sake of languages with nested 530 (goto-char (mark))
502 ;; functions where several can end at the same place as with 531 ;; change the dotimes below to (end-of-defun arg) once bug #24427 is fixed
503 ;; the offside rule, e.g. Python. 532 (dotimes (_ignore arg)
504 (beginning-of-defun) 533 (end-of-defun))
505 (setq beg (point)) 534 (point)))
506 (end-of-defun) 535 (beginning-of-defun-comments (- arg))))
507 (setq end (point)) 536 (t
508 (while (looking-at "^\n") 537 (let ((opoint (point))
509 (forward-line 1)) 538 beg end)
510 (if (> (point) opoint) 539 (push-mark opoint)
511 (progn 540 ;; Try first in this order for the sake of languages with nested
512 ;; We got the right defun. 541 ;; functions where several can end at the same place as with the
513 (push-mark beg nil t) 542 ;; offside rule, e.g. Python.
514 (goto-char end) 543 (beginning-of-defun-comments)
515 (exchange-point-and-mark)) 544 (setq beg (point))
516 ;; beginning-of-defun moved back one defun 545 (end-of-defun)
517 ;; so we got the wrong one. 546 (setq end (point))
518 (goto-char opoint) 547 (when (or (and (<= (point) opoint)
519 (end-of-defun) 548 (> arg 0))
520 (push-mark (point) nil t) 549 (= beg (point-min))) ; we were before the first defun!
521 (beginning-of-defun)) 550 ;; beginning-of-defun moved back one defun so we got the wrong
522 (re-search-backward "^\n" (- (point) 1) t))))) 551 ;; one. If ARG < 0, however, we actually want to go back.
552 (goto-char opoint)
553 (end-of-defun)
554 (setq end (point))
555 (beginning-of-defun-comments)
556 (setq beg (point)))
557 (goto-char beg)
558 (cond ((> arg 0)
559 ;; change the dotimes below to (end-of-defun arg) once bug #24427 is fixed
560 (dotimes (_ignore arg)
561 (end-of-defun))
562 (setq end (point))
563 (push-mark end nil t)
564 (goto-char beg))
565 (t
566 (goto-char beg)
567 (unless (= arg -1) ; beginning-of-defun behaves
568 ; strange with zero arg - see
569 ; https://lists.gnu.org/archive/html/bug-gnu-emacs/2017-02/msg00196.html
570 (beginning-of-defun (1- (- arg))))
571 (push-mark end nil t))))))
572 (let (nbobp)
573 (while (progn
574 (setq nbobp (zerop (forward-line -1)))
575 (and (looking-at "^\\s-*$")
576 nbobp)))
577 (when nbobp
578 (forward-line 1))))
523 579
524(defvar narrow-to-defun-include-comments nil 580(defvar narrow-to-defun-include-comments nil
525 "If non-nil, `narrow-to-defun' will also show comments preceding the defun.") 581 "If non-nil, `narrow-to-defun' will also show comments preceding the defun.")
diff --git a/test/lisp/emacs-lisp/lisp-tests.el b/test/lisp/emacs-lisp/lisp-tests.el
index f6039f78eb1..2119758bb77 100644
--- a/test/lisp/emacs-lisp/lisp-tests.el
+++ b/test/lisp/emacs-lisp/lisp-tests.el
@@ -342,5 +342,252 @@ a marker."
342 `(let ,marker-list 342 `(let ,marker-list
343 ,@body)))) 343 ,@body))))
344 344
345;;; mark-defun
346
347(defvar mark-defun-test-buffer
348 ";; Comment header
349=!before-1=
350\(defun func-1 (arg)
351 =!inside-1=\"docstring\"
352 body)
353=!after-1==!before-2=
354;; Comment before a defun
355\(d=!inside-2=efun func-2 (arg)
356 \"docstring\"
357 body)
358=!after-2==!before-3=
359\(defun func-3 (arg)
360 \"docstring\"=!inside-3=
361 body)
362=!after-3==!before-4=(defun func-4 (arg)
363 \"docstring\"=!inside-4=
364 body)
365=!after-4=
366;; end
367"
368 "Test buffer for `mark-defun'.")
369
370(ert-deftest mark-defun-no-arg-region-inactive ()
371 "Test `mark-defun' with no prefix argument and inactive
372region."
373 (setq last-command nil)
374 (elisp-tests-with-temp-buffer
375 mark-defun-test-buffer
376 ;; mark-defun inside a defun, with comments and an empty line
377 ;; before
378 (goto-char inside-1)
379 (mark-defun)
380 (should (= (point) before-1))
381 (should (= (mark) after-1))
382 ;; mark-defun inside a defun with comments before
383 (deactivate-mark)
384 (goto-char inside-2)
385 (mark-defun)
386 (should (= (point) before-2))
387 (should (= (mark) after-2))
388 ;; mark-defun inside a defun with empty line before
389 (deactivate-mark)
390 (goto-char inside-3)
391 (mark-defun)
392 (should (= (point) before-3))
393 (should (= (mark) after-3))
394 ;; mark-defun inside a defun with another one right before
395 (deactivate-mark)
396 (goto-char inside-4)
397 (mark-defun)
398 (should (= (point) before-4))
399 (should (= (mark) after-4))
400 ;; mark-defun between a comment and a defun
401 (deactivate-mark)
402 (goto-char before-1)
403 (mark-defun)
404 (should (= (point) before-1))
405 (should (= (mark) after-1))
406 ;; mark-defun between defuns
407 (deactivate-mark)
408 (goto-char before-3)
409 (mark-defun)
410 (should (= (point) before-3))
411 (should (= (mark) after-3))
412 ;; mark-defun in comment right before the defun
413 (deactivate-mark)
414 (goto-char before-2)
415 (mark-defun)
416 (should (= (point) before-2))
417 (should (= (mark) after-2))))
418
419(ert-deftest mark-defun-no-arg-region-active ()
420 "Test `mark-defun' with no prefix argument and active
421region."
422 (transient-mark-mode 1)
423 (setq last-command nil)
424 (elisp-tests-with-temp-buffer
425 mark-defun-test-buffer
426 ;; mark-defun when a defun is marked
427 (goto-char before-1)
428 (set-mark after-1)
429 (mark-defun)
430 (should (= (point) before-1))
431 (should (= (mark) after-2))
432 ;; mark-defun when two defuns are marked
433 (deactivate-mark)
434 (goto-char before-1)
435 (set-mark after-2)
436 (mark-defun)
437 (should (= (point) before-1))
438 (should (= (mark) after-3))))
439
440(ert-deftest mark-defun-arg-region-active ()
441 "Test `mark-defun' with a prefix arg and active region."
442 (transient-mark-mode 1)
443 (setq last-command nil)
444 (elisp-tests-with-temp-buffer
445 mark-defun-test-buffer
446 ;; mark-defun with positive arg when a defun is marked
447 (goto-char before-1)
448 (set-mark after-1)
449 (mark-defun 2)
450 (should (= (point) before-1))
451 (should (= (mark) after-3))
452 ;; mark-defun with arg=-1 when a defun is marked
453 (goto-char before-2)
454 (set-mark after-2)
455 (mark-defun -1)
456 (should (= (point) before-1))
457 (should (= (mark) after-2))
458 ;; mark-defun with arg=-2 when a defun is marked
459 (goto-char before-3)
460 (set-mark after-3)
461 (mark-defun -2)
462 (should (= (point) before-1))
463 (should (= (mark) after-3))))
464
465(ert-deftest mark-defun-pos-arg-region-inactive ()
466 "Test `mark-defun' with positive argument and inactive
467 region."
468 (setq last-command nil)
469 (elisp-tests-with-temp-buffer
470 mark-defun-test-buffer
471 ;; mark-defun with positive arg inside a defun
472 (goto-char inside-1)
473 (mark-defun 2)
474 (should (= (point) before-1))
475 (should (= (mark) after-2))
476 ;; mark-defun with positive arg between defuns
477 (deactivate-mark)
478 (goto-char before-3)
479 (mark-defun 2)
480 (should (= (point) before-3))
481 (should (= (mark) after-4))
482 ;; mark-defun with positive arg in a comment
483 (deactivate-mark)
484 (goto-char before-2)
485 (mark-defun 2)
486 (should (= (point) before-2))
487 (should (= (mark) after-3))))
488
489(ert-deftest mark-defun-neg-arg-region-inactive ()
490 "Test `mark-defun' with negative argument and inactive
491 region."
492 (setq last-command nil)
493 (elisp-tests-with-temp-buffer
494 mark-defun-test-buffer
495 ;; mark-defun with arg=-1 inside a defun
496 (goto-char inside-1)
497 (mark-defun -1)
498 (should (= (point) before-1))
499 (should (= (mark) after-1))
500 ;; mark-defun with arg=-1 between defuns
501 (deactivate-mark)
502 (goto-char after-2)
503 (mark-defun -1)
504 (should (= (point) before-2))
505 (should (= (mark) after-2))
506 ;; mark-defun with arg=-1 in a comment
507 ;; (this is probably not an optimal behavior...)
508 (deactivate-mark)
509 (goto-char before-2)
510 (mark-defun -1)
511 (should (= (point) before-1))
512 (should (= (mark) after-1))
513 ;; mark-defun with arg=-2 inside a defun
514 (deactivate-mark)
515 (goto-char inside-4)
516 (mark-defun -2)
517 (should (= (point) before-3))
518 (should (= (mark) after-4))
519 ;; mark-defun with arg=-2 between defuns
520 (deactivate-mark)
521 (goto-char before-3)
522 (mark-defun -2)
523 (should (= (point) before-1))
524 (should (= (mark) after-2)))
525 (elisp-tests-with-temp-buffer ; test case submitted by Drew Adams
526 "(defun a ()
527 nil)
528=!before-b=(defun b ()
529=!in-b= nil)
530=!after-b=;;;;
531\(defun c ()
532 nil)
533"
534 (setq last-command nil)
535 (goto-char in-b)
536 (mark-defun -1)
537 (should (= (point) before-b))
538 (should (= (mark) after-b))))
539
540(ert-deftest mark-defun-bob ()
541 "Test `mark-defun' at the beginning of buffer."
542 ;; Bob, comment, newline, defun
543 (setq last-command nil)
544 (elisp-tests-with-temp-buffer
545 ";; Comment at the bob
546=!before=
547\(defun func (arg)=!inside=
548 \"docstring\"
549 body)
550=!after="
551 (goto-char inside)
552 (mark-defun)
553 (should (= (point) before))
554 (should (= (mark) after)))
555 ;; Bob, newline, comment, defun
556 (elisp-tests-with-temp-buffer
557 "=!before=
558;; Comment before the defun
559\(defun func (arg)=!inside=
560 \"docstring\"
561 body)
562=!after="
563 (goto-char inside)
564 (mark-defun)
565 (should (= (point) before))
566 (should (= (mark) after)))
567 ;; Bob, comment, defun
568 (elisp-tests-with-temp-buffer
569 "=!before=;; Comment at the bob before the defun
570\(defun func (arg)=!inside=
571 \"docstring\"
572 body)
573=!after="
574 (goto-char inside)
575 (mark-defun)
576 (should (= (point) before))
577 (should (= (mark) after)))
578 ;; Bob, newline, comment, newline, defun
579 (elisp-tests-with-temp-buffer
580 "
581;; Comment before the defun
582=!before=
583\(defun func (arg)=!inside=
584 \"docstring\"
585 body)
586=!after="
587 (goto-char inside)
588 (mark-defun)
589 (should (= (point) before))
590 (should (= (mark) after))))
591
345(provide 'lisp-tests) 592(provide 'lisp-tests)
346;;; lisp-tests.el ends here 593;;; lisp-tests.el ends here