From 74379eaaa2443f7a9fe62e04606c49613f5e6204 Mon Sep 17 00:00:00 2001 From: Nicolas Goaziou Date: Sat, 15 Apr 2017 16:51:33 +0200 Subject: [PATCH] Fix `org-forward-heading-same-level' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * lisp/org.el (org-forward-heading-same-level): Do not fail when heading is at the beginning of the buffer. * testing/lisp/test-org.el (test-org/forward-heading-same-level): New test. Reported-by: Rafael Laboissière --- lisp/org.el | 47 +++++++++++++++----------------- testing/lisp/test-org.el | 58 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 26 deletions(-) diff --git a/lisp/org.el b/lisp/org.el index dd9c43664..17dd46b75 100644 --- a/lisp/org.el +++ b/lisp/org.el @@ -24367,32 +24367,27 @@ Stop at the first and last subheadings of a superior heading. Normally this only looks at visible headings, but when INVISIBLE-OK is non-nil it will also look at invisible ones." (interactive "p") - (if (not (ignore-errors (org-back-to-heading invisible-ok))) - (if (and arg (< arg 0)) - (goto-char (point-min)) - (outline-next-heading)) - (org-at-heading-p) - (let ((level (- (match-end 0) (match-beginning 0) 1)) - (f (if (and arg (< arg 0)) - 're-search-backward - 're-search-forward)) - (count (if arg (abs arg) 1)) - (result (point))) - (while (and (prog1 (> count 0) - (forward-char (if (and arg (< arg 0)) -1 1))) - (funcall f org-outline-regexp-bol nil 'move)) - (let ((l (- (match-end 0) (match-beginning 0) 1))) - (cond ((< l level) (setq count 0)) - ((and (= l level) - (or invisible-ok - (progn - (goto-char (line-beginning-position)) - (not (outline-invisible-p))))) - (setq count (1- count)) - (when (eq l level) - (setq result (point))))))) - (goto-char result)) - (beginning-of-line 1))) + (let ((backward? (and arg (< arg 0)))) + (if (org-before-first-heading-p) + (if backward? (goto-char (point-min)) (outline-next-heading)) + (org-back-to-heading invisible-ok) + (unless backward? (end-of-line)) ;do not match current headline + (let ((level (- (match-end 0) (match-beginning 0) 1)) + (f (if backward? #'re-search-backward #'re-search-forward)) + (count (if arg (abs arg) 1)) + (result (point))) + (while (and (> count 0) + (funcall f org-outline-regexp-bol nil 'move)) + (let ((l (- (match-end 0) (match-beginning 0) 1))) + (cond ((< l level) (setq count 0)) + ((and (= l level) + (or invisible-ok + (not (outline-invisible-p + (line-beginning-position))))) + (cl-decf count) + (when (= l level) (setq result (point))))))) + (goto-char result)) + (beginning-of-line)))) (defun org-backward-heading-same-level (arg &optional invisible-ok) "Move backward to the ARG'th subheading at same level as this one. diff --git a/testing/lisp/test-org.el b/testing/lisp/test-org.el index 79d768f85..3b146be14 100644 --- a/testing/lisp/test-org.el +++ b/testing/lisp/test-org.el @@ -2533,6 +2533,64 @@ http://article.gmane.org/gmane.emacs.orgmode/21459/" ;;; Navigation +(ert-deftest test-org/forward-heading-same-level () + "Test `org-forward-heading-same-level' specifications." + ;; Test navigation at top level, forward and backward. + (should + (equal "* H2" + (org-test-with-temp-text "* H1\n* H2" + (org-forward-heading-same-level 1) + (buffer-substring-no-properties (point) (line-end-position))))) + (should + (equal "* H1" + (org-test-with-temp-text "* H1\n* H2" + (org-forward-heading-same-level -1) + (buffer-substring-no-properties (point) (line-end-position))))) + ;; Test navigation in a sub-tree, forward and backward. + (should + (equal "* H2" + (org-test-with-temp-text "* H1\n** H11\n** H12\n* H2" + (org-forward-heading-same-level 1) + (buffer-substring-no-properties (point) (line-end-position))))) + (should + (equal "* H1" + (org-test-with-temp-text "* H1\n** H11\n** H12\n* H2" + (org-forward-heading-same-level -1) + (buffer-substring-no-properties (point) (line-end-position))))) + ;; Stop at first or last sub-heading. + (should-not + (equal "* H2" + (org-test-with-temp-text "* H1\n** H11\n** H12\n* H2" + (org-forward-heading-same-level 1) + (buffer-substring-no-properties (point) (line-end-position))))) + (should-not + (equal "* H2" + (org-test-with-temp-text "* H1\n** H11\n** H12\n* H2" + (org-forward-heading-same-level -1) + (buffer-substring-no-properties (point) (line-end-position))))) + ;; Allow multiple moves. + (should + (equal "* H3" + (org-test-with-temp-text "* H1\n* H2\n* H3" + (org-forward-heading-same-level 2) + (buffer-substring-no-properties (point) (line-end-position))))) + (should + (equal "* H1" + (org-test-with-temp-text "* H1\n* H2\n* H3" + (org-forward-heading-same-level -2) + (buffer-substring-no-properties (point) (line-end-position))))) + ;; Ignore spurious moves when first (or last) sibling is reached. + (should + (equal "** H3" + (org-test-with-temp-text "* First\n** H1\n** H2\n** H3\n* Last" + (org-forward-heading-same-level 100) + (buffer-substring-no-properties (point) (line-end-position))))) + (should + (equal "** H1" + (org-test-with-temp-text "* First\n** H1\n** H2\n** H3\n* Last" + (org-forward-heading-same-level -100) + (buffer-substring-no-properties (point) (line-end-position)))))) + (ert-deftest test-org/end-of-meta-data () "Test `org-end-of-meta-data' specifications." ;; Skip planning line.