`org-forward/backward-paragraph' do not error at buffer boundaries

* lisp/org.el (org-forward-paragraph):
(org-backward-paragraph): Do not error at buffer boundaries.

Reported-by: Omar Antolín Camarena <omar.antolin@gmail.com>
<http://lists.gnu.org/archive/html/emacs-orgmode/2017-07/msg00478.html>
This commit is contained in:
Nicolas Goaziou 2017-07-28 10:07:53 +02:00
parent f203d378bd
commit 808089ee04
2 changed files with 125 additions and 123 deletions

View File

@ -24432,74 +24432,74 @@ item, etc. It also provides some special moves for convenience:
- On a table or a property drawer, jump after it. - On a table or a property drawer, jump after it.
- On a verse or source block, stop after blank lines." - On a verse or source block, stop after blank lines."
(interactive) (interactive)
(when (eobp) (user-error "Cannot move further down")) (unless (eobp)
(let* ((deactivate-mark nil) (let* ((deactivate-mark nil)
(element (org-element-at-point)) (element (org-element-at-point))
(type (org-element-type element)) (type (org-element-type element))
(post-affiliated (org-element-property :post-affiliated element)) (post-affiliated (org-element-property :post-affiliated element))
(contents-begin (org-element-property :contents-begin element)) (contents-begin (org-element-property :contents-begin element))
(contents-end (org-element-property :contents-end element)) (contents-end (org-element-property :contents-end element))
(end (let ((end (org-element-property :end element)) (parent element)) (end (let ((end (org-element-property :end element)) (parent element))
(while (and (setq parent (org-element-property :parent parent)) (while (and (setq parent (org-element-property :parent parent))
(= (org-element-property :contents-end parent) end)) (= (org-element-property :contents-end parent) end))
(setq end (org-element-property :end parent))) (setq end (org-element-property :end parent)))
end))) end)))
(cond ((not element) (cond ((not element)
(skip-chars-forward " \r\t\n") (skip-chars-forward " \r\t\n")
(or (eobp) (beginning-of-line))) (or (eobp) (beginning-of-line)))
;; On affiliated keywords, move to element's beginning. ;; On affiliated keywords, move to element's beginning.
((< (point) post-affiliated) ((< (point) post-affiliated)
(goto-char post-affiliated)) (goto-char post-affiliated))
;; At a table row, move to the end of the table. Similarly, ;; At a table row, move to the end of the table. Similarly,
;; at a node property, move to the end of the property ;; at a node property, move to the end of the property
;; drawer. ;; drawer.
((memq type '(node-property table-row)) ((memq type '(node-property table-row))
(goto-char (org-element-property (goto-char (org-element-property
:end (org-element-property :parent element)))) :end (org-element-property :parent element))))
((memq type '(property-drawer table)) (goto-char end)) ((memq type '(property-drawer table)) (goto-char end))
;; Consider blank lines as separators in verse and source ;; Consider blank lines as separators in verse and source
;; blocks to ease editing. ;; blocks to ease editing.
((memq type '(src-block verse-block)) ((memq type '(src-block verse-block))
(when (eq type 'src-block) (when (eq type 'src-block)
(setq contents-end (setq contents-end
(save-excursion (goto-char end) (save-excursion (goto-char end)
(skip-chars-backward " \r\t\n") (skip-chars-backward " \r\t\n")
(line-beginning-position)))) (line-beginning-position))))
(beginning-of-line) (beginning-of-line)
(when (looking-at "[ \t]*$") (skip-chars-forward " \r\t\n")) (when (looking-at "[ \t]*$") (skip-chars-forward " \r\t\n"))
(if (not (re-search-forward "^[ \t]*$" contents-end t)) (if (not (re-search-forward "^[ \t]*$" contents-end t))
(goto-char end) (goto-char end)
(skip-chars-forward " \r\t\n") (skip-chars-forward " \r\t\n")
(if (= (point) contents-end) (goto-char end) (if (= (point) contents-end) (goto-char end)
(beginning-of-line)))) (beginning-of-line))))
;; With no contents, just skip element. ;; With no contents, just skip element.
((not contents-begin) (goto-char end)) ((not contents-begin) (goto-char end))
;; If contents are invisible, skip the element altogether. ;; If contents are invisible, skip the element altogether.
((org-invisible-p (line-end-position)) ((org-invisible-p (line-end-position))
(cl-case type (cl-case type
(headline (headline
(org-with-limited-levels (outline-next-visible-heading 1))) (org-with-limited-levels (outline-next-visible-heading 1)))
;; At a plain list, make sure we move to the next item ;; At a plain list, make sure we move to the next item
;; instead of skipping the whole list. ;; instead of skipping the whole list.
(plain-list (forward-char) (plain-list (forward-char)
(org-forward-paragraph)) (org-forward-paragraph))
(otherwise (goto-char end)))) (otherwise (goto-char end))))
((>= (point) contents-end) (goto-char end)) ((>= (point) contents-end) (goto-char end))
((>= (point) contents-begin) ((>= (point) contents-begin)
;; This can only happen on paragraphs and plain lists. ;; This can only happen on paragraphs and plain lists.
(cl-case type (cl-case type
(paragraph (goto-char end)) (paragraph (goto-char end))
;; At a plain list, try to move to second element in ;; At a plain list, try to move to second element in
;; first item, if possible. ;; first item, if possible.
(plain-list (end-of-line) (plain-list (end-of-line)
(org-forward-paragraph)))) (org-forward-paragraph))))
;; When contents start on the middle of a line (e.g. in ;; When contents start on the middle of a line (e.g. in
;; items and footnote definitions), try to reach first ;; items and footnote definitions), try to reach first
;; element starting after current line. ;; element starting after current line.
((> (line-end-position) contents-begin) ((> (line-end-position) contents-begin)
(end-of-line) (end-of-line)
(org-forward-paragraph)) (org-forward-paragraph))
(t (goto-char contents-begin))))) (t (goto-char contents-begin))))))
(defun org-backward-paragraph () (defun org-backward-paragraph ()
"Move backward to start of previous paragraph or equivalent. "Move backward to start of previous paragraph or equivalent.
@ -24514,55 +24514,55 @@ convenience:
- On a table or a property drawer, move to its beginning. - On a table or a property drawer, move to its beginning.
- On a verse or source block, stop before blank lines." - On a verse or source block, stop before blank lines."
(interactive) (interactive)
(when (bobp) (user-error "Cannot move further up")) (unless (bobp)
(let* ((deactivate-mark nil) (let* ((deactivate-mark nil)
(element (org-element-at-point)) (element (org-element-at-point))
(type (org-element-type element)) (type (org-element-type element))
(contents-begin (org-element-property :contents-begin element)) (contents-begin (org-element-property :contents-begin element))
(contents-end (org-element-property :contents-end element)) (contents-end (org-element-property :contents-end element))
(post-affiliated (org-element-property :post-affiliated element)) (post-affiliated (org-element-property :post-affiliated element))
(begin (org-element-property :begin element))) (begin (org-element-property :begin element)))
(cond (cond
((not element) (goto-char (point-min))) ((not element) (goto-char (point-min)))
((= (point) begin) ((= (point) begin)
(backward-char) (backward-char)
(org-backward-paragraph)) (org-backward-paragraph))
((<= (point) post-affiliated) (goto-char begin)) ((<= (point) post-affiliated) (goto-char begin))
((memq type '(node-property table-row)) ((memq type '(node-property table-row))
(goto-char (org-element-property (goto-char (org-element-property
:post-affiliated (org-element-property :parent element)))) :post-affiliated (org-element-property :parent element))))
((memq type '(property-drawer table)) (goto-char begin)) ((memq type '(property-drawer table)) (goto-char begin))
((memq type '(src-block verse-block)) ((memq type '(src-block verse-block))
(when (eq type 'src-block) (when (eq type 'src-block)
(setq contents-begin (setq contents-begin
(save-excursion (goto-char begin) (forward-line) (point)))) (save-excursion (goto-char begin) (forward-line) (point))))
(if (= (point) contents-begin) (goto-char post-affiliated) (if (= (point) contents-begin) (goto-char post-affiliated)
;; Inside a verse block, see blank lines as paragraph ;; Inside a verse block, see blank lines as paragraph
;; separators. ;; separators.
(let ((origin (point))) (let ((origin (point)))
(skip-chars-backward " \r\t\n" contents-begin) (skip-chars-backward " \r\t\n" contents-begin)
(when (re-search-backward "^[ \t]*$" contents-begin 'move) (when (re-search-backward "^[ \t]*$" contents-begin 'move)
(skip-chars-forward " \r\t\n" origin) (skip-chars-forward " \r\t\n" origin)
(if (= (point) origin) (goto-char contents-begin) (if (= (point) origin) (goto-char contents-begin)
(beginning-of-line)))))) (beginning-of-line))))))
((not contents-begin) (goto-char (or post-affiliated begin))) ((not contents-begin) (goto-char (or post-affiliated begin)))
((eq type 'paragraph) ((eq type 'paragraph)
(goto-char contents-begin) (goto-char contents-begin)
;; When at first paragraph in an item or a footnote definition, ;; When at first paragraph in an item or a footnote definition,
;; move directly to beginning of line. ;; move directly to beginning of line.
(let ((parent-contents (let ((parent-contents
(org-element-property (org-element-property
:contents-begin (org-element-property :parent element)))) :contents-begin (org-element-property :parent element))))
(when (and parent-contents (= parent-contents contents-begin)) (when (and parent-contents (= parent-contents contents-begin))
(beginning-of-line)))) (beginning-of-line))))
;; At the end of a greater element, move to the beginning of the ;; At the end of a greater element, move to the beginning of the
;; last element within. ;; last element within.
((>= (point) contents-end) ((>= (point) contents-end)
(goto-char (1- contents-end)) (goto-char (1- contents-end))
(org-backward-paragraph)) (org-backward-paragraph))
(t (goto-char (or post-affiliated begin)))) (t (goto-char (or post-affiliated begin))))
;; Ensure we never leave point invisible. ;; Ensure we never leave point invisible.
(when (org-invisible-p (point)) (beginning-of-visual-line)))) (when (org-invisible-p (point)) (beginning-of-visual-line)))))
(defun org-forward-element () (defun org-forward-element ()
"Move forward by one element. "Move forward by one element.

View File

@ -3259,11 +3259,12 @@ SCHEDULED: <2017-05-06 Sat>
(ert-deftest test-org/forward-paragraph () (ert-deftest test-org/forward-paragraph ()
"Test `org-forward-paragraph' specifications." "Test `org-forward-paragraph' specifications."
;; At end of buffer, return an error. ;; At end of buffer, do not return an error.
(should-error (should
(org-test-with-temp-text "Paragraph" (org-test-with-temp-text "Paragraph"
(goto-char (point-max)) (goto-char (point-max))
(org-forward-paragraph))) (org-forward-paragraph)
t))
;; Standard test. ;; Standard test.
(should (should
(org-test-with-temp-text "P1\n\nP2\n\nP3" (org-test-with-temp-text "P1\n\nP2\n\nP3"
@ -3328,10 +3329,11 @@ SCHEDULED: <2017-05-06 Sat>
(ert-deftest test-org/backward-paragraph () (ert-deftest test-org/backward-paragraph ()
"Test `org-backward-paragraph' specifications." "Test `org-backward-paragraph' specifications."
;; Error at beginning of buffer. ;; Do not error at beginning of buffer.
(should-error (should
(org-test-with-temp-text "Paragraph" (org-test-with-temp-text "Paragraph"
(org-backward-paragraph))) (org-backward-paragraph)
t))
;; Regular test. ;; Regular test.
(should (should
(org-test-with-temp-text "P1\n\nP2\n\nP3" (org-test-with-temp-text "P1\n\nP2\n\nP3"