summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicolas Goaziou <mail@nicolasgoaziou.fr>2020-04-30 16:00:18 +0200
committerNicolas Goaziou <mail@nicolasgoaziou.fr>2020-06-13 17:22:48 +0200
commite2b62b4da8839106bbfedeb778e4ad4b40164964 (patch)
treeae47bfee8fa6daf9c2e1bfab3d127290a3e09ed6
parent6882478cac36dd71bbb605fb62b9f043ab6bd6f1 (diff)
downloadorg-mode-e2b62b4da8839106bbfedeb778e4ad4b40164964.tar.gz
Rewrite `org-forward-paragraph' and `org-backward-paragraph'
* lisp/org.el (org-forward-paragraph): (org-backward-paragraph): Rewrite functions. Add repeat argument. Mimic more closely regular `forward|backward-paragraph' functions. (org--forward-paragraph-once): (org--backward-paragraph-once): (org--paragraph-at-point): New functions. * testing/lisp/test-org.el (test-org/forward-paragraph): (test-org/backward-paragraph): Update tests. Add some. Signed-off-by: Nicolas Goaziou <mail@nicolasgoaziou.fr>
-rw-r--r--etc/ORG-NEWS9
-rw-r--r--lisp/org.el440
-rw-r--r--testing/lisp/test-org.el255
3 files changed, 480 insertions, 224 deletions
diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS
index f1b071f..6b137c7 100644
--- a/etc/ORG-NEWS
+++ b/etc/ORG-NEWS
@@ -385,6 +385,15 @@ From ~org-enable-priority-commands~ to ~org-priority-enable-commands~.
From ~org-show-priority~ to ~org-priority-show~.
** Miscellaneous
+*** Forward/backward paragraph functions in line with the rest of Emacs
+~org-forward-paragraph~ and ~org-backward-paragraph~, bound to
+~<C-UP>~ and ~<C-DOWN>~ functions mimic more closely behaviour of
+~forward-paragraph~ and ~backward-paragraph~ functions when
+available.
+
+They also accept an optional argument for multiple calls.
+
+See their docstring for details.
*** ~org-table-to-lisp~ no longer checks if point is at a table
The caller is now responsible for the check. It can use, e.g.,
~org-at-table-p~.
diff --git a/lisp/org.el b/lisp/org.el
index f3202a2..0e02e2c 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -20512,156 +20512,300 @@ With ARG, repeats or can move forward if negative."
(interactive "p")
(org-next-visible-heading (- arg)))
-(defun org-forward-paragraph ()
- "Move forward to beginning of next paragraph or equivalent.
-
-The function moves point to the beginning of the next visible
-structural element, which can be a paragraph, a table, a list
-item, etc. It also provides some special moves for convenience:
-
- - On an affiliated keyword, jump to the beginning of the
- relative element.
- - On an item or a footnote definition, move to the second
- element inside, if any.
- - On a table or a property drawer, jump after it.
- - On a verse or source block, stop after blank lines."
+(defun org-forward-paragraph (&optional arg)
+ "Move forward by a paragraph, or equivalent, unit.
+
+With argument ARG, do it ARG times;
+a negative argument ARG = -N means move backward N paragraphs.
+
+The function moves point between two structural
+elements (paragraphs, tables, lists, etc.).
+
+It also provides the following special moves for convenience:
+
+ - on a table or a property drawer, move to its beginning;
+ - on comment, example, export, source and verse blocks, stop
+ at blank lines;
+ - skip consecutive clocks, diary S-exps, and keywords."
+ (interactive "^p")
+ (unless arg (setq arg 1))
+ (if (< arg 0) (org-backward-paragraph (- arg))
+ (while (and (> arg 0) (not (eobp)))
+ (org--forward-paragraph-once)
+ (cl-decf arg))
+ ;; Return moves left.
+ arg))
+
+(defun org-backward-paragraph (&optional arg)
+ "Move backward by a paragraph, or equivalent, unit.
+
+With argument ARG, do it ARG times;
+a negative argument ARG = -N means move forward N paragraphs.
+
+The function moves point between two structural
+elements (paragraphs, tables, lists, etc.).
+
+It also provides the following special moves for convenience:
+
+ - on a table or a property drawer, move to its beginning;
+ - on comment, example, export, source and verse blocks, stop
+ at blank lines;
+ - skip consecutive clocks, diary S-exps, and keywords."
+ (interactive "^p")
+ (unless arg (setq arg 1))
+ (if (< arg 0) (org-forward-paragraph (- arg))
+ (while (and (> arg 0) (not (bobp)))
+ (org--backward-paragraph-once)
+ (cl-decf arg))
+ ;; Return moves left.
+ arg))
+
+(defun org--paragraph-at-point ()
+ "Return paragraph, or equivalent, element at point.
+
+Paragraph element at point is the element at point, with the
+following special cases:
+
+- treat table rows (resp. node properties) as the table
+ \(resp. property drawer) containing them.
+
+- treat plain lists with an item every line as a whole.
+
+- treat consecutive keywords, clocks, and diary-sexps as a single
+ block.
+
+Function may return a real element, or a pseudo-element with type
+`pseudo-paragraph'."
+ (let* ((e (org-element-at-point))
+ (type (org-element-type e))
+ ;; If we need to fake a new pseudo-element, triplet is
+ ;;
+ ;; (BEG END PARENT)
+ ;;
+ ;; where BEG and END are element boundaries, and PARENT the
+ ;; element containing it, or nil.
+ (triplet
+ (cond
+ ((memq type '(table property-drawer))
+ (list (org-element-property :begin e)
+ (org-element-property :end e)
+ (org-element-property :parent e)))
+ ((memq type '(node-property table-row))
+ (let ((e (org-element-property :parent e)))
+ (list (org-element-property :begin e)
+ (org-element-property :end e)
+ (org-element-property :parent e))))
+ ((memq type '(clock diary-sexp keyword))
+ (let* ((regexp (pcase type
+ (`clock org-clock-line-re)
+ (`diary-sexp "%%(")
+ (_ org-keyword-regexp)))
+ (end (if (< 0 (org-element-property :post-blank e))
+ (org-element-property :end e)
+ (org-with-wide-buffer
+ (forward-line)
+ (while (looking-at regexp) (forward-line))
+ (skip-chars-forward " \t\n")
+ (line-beginning-position))))
+ (begin (org-with-point-at (org-element-property :begin e)
+ (while (and (not (bobp)) (looking-at regexp))
+ (forward-line -1))
+ ;; We may have gotten one line too far.
+ (if (looking-at regexp)
+ (point)
+ (line-beginning-position 2)))))
+ (list begin end (org-element-property :parent e))))
+ ;; Find the full plain list containing point, the check it
+ ;; contains exactly one line per item.
+ ((let ((l (org-element-lineage e '(plain-list) t)))
+ (while (memq (org-element-type (org-element-property :parent l))
+ '(item plain-list))
+ (setq l (org-element-property :parent l)))
+ (and l
+ (org-with-point-at (org-element-property :post-affiliated l)
+ (forward-line (length (org-element-property :structure l)))
+ (= (point) (org-element-property :contents-end l)))
+ ;; Return value.
+ (list (org-element-property :begin l)
+ (org-element-property :end l)
+ (org-element-property :parent l)))))
+ (t nil)))) ;no triplet: return element
+ (pcase triplet
+ (`(,b ,e ,p)
+ (org-element-create
+ 'pseudo-paragraph
+ (list :begin b :end e :parent p :post-blank 0 :post-affiliated b)))
+ (_ e))))
+
+(defun org--forward-paragraph-once ()
+ "Move forward to end of paragraph or equivalent, once.
+See `org-forward-paragraph'."
(interactive)
- (unless (eobp)
- (let* ((deactivate-mark nil)
- (element (org-element-at-point))
- (type (org-element-type element))
- (post-affiliated (org-element-property :post-affiliated element))
- (contents-begin (org-element-property :contents-begin element))
- (contents-end (org-element-property :contents-end element))
- (end (let ((end (org-element-property :end element)) (parent element))
- (while (and (setq parent (org-element-property :parent parent))
- (= (org-element-property :contents-end parent) end))
- (setq end (org-element-property :end parent)))
- end)))
- (cond ((not element)
- (skip-chars-forward " \r\t\n")
- (or (eobp) (beginning-of-line)))
- ;; On affiliated keywords, move to element's beginning.
- ((< (point) post-affiliated)
- (goto-char post-affiliated))
- ;; At a table row, move to the end of the table. Similarly,
- ;; at a node property, move to the end of the property
- ;; drawer.
- ((memq type '(node-property table-row))
- (goto-char (org-element-property
- :end (org-element-property :parent element))))
- ((memq type '(property-drawer table)) (goto-char end))
- ;; Consider blank lines as separators in verse and source
- ;; blocks to ease editing.
- ((memq type '(src-block verse-block))
- (when (eq type 'src-block)
- (setq contents-end
- (save-excursion (goto-char end)
- (skip-chars-backward " \r\t\n")
- (line-beginning-position))))
- (beginning-of-line)
- (when (looking-at "[ \t]*$") (skip-chars-forward " \r\t\n"))
- (if (not (re-search-forward "^[ \t]*$" contents-end t))
- (goto-char end)
- (skip-chars-forward " \r\t\n")
- (if (= (point) contents-end) (goto-char end)
- (beginning-of-line))))
- ;; With no contents, just skip element.
- ((not contents-begin) (goto-char end))
- ;; If contents are invisible, skip the element altogether.
- ((org-invisible-p (line-end-position))
- (cl-case type
- (headline
- (org-with-limited-levels (outline-next-visible-heading 1)))
- ;; At a plain list, make sure we move to the next item
- ;; instead of skipping the whole list.
- (plain-list (forward-char)
- (org-forward-paragraph))
- (otherwise (goto-char end))))
- ((>= (point) contents-end) (goto-char end))
- ((>= (point) contents-begin)
- ;; This can only happen on paragraphs and plain lists.
- (cl-case type
- (paragraph (goto-char end))
- ;; At a plain list, try to move to second element in
- ;; first item, if possible.
- (plain-list (end-of-line)
- (org-forward-paragraph))))
- ;; When contents start on the middle of a line (e.g. in
- ;; items and footnote definitions), try to reach first
- ;; element starting after current line.
- ((> (line-end-position) contents-begin)
- (end-of-line)
- (org-forward-paragraph))
- (t (goto-char contents-begin))))))
-
-(defun org-backward-paragraph ()
- "Move backward to start of previous paragraph or equivalent.
-
-The function moves point to the beginning of the current
-structural element, which can be a paragraph, a table, a list
-item, etc., or to the beginning of the previous visible one if
-point is already there. It also provides some special moves for
-convenience:
-
- - On an affiliated keyword, jump to the first one.
- - On a table or a property drawer, move to its beginning.
- - On comment, example, export, src and verse blocks, stop
- before blank lines."
+ (save-restriction
+ (widen)
+ (skip-chars-forward " \t\n")
+ (cond
+ ((eobp) nil)
+ ;; When inside a folded part, move out of it.
+ ((pcase (get-char-property-and-overlay (point) 'invisible)
+ (`(,(or `outline `org-hide-block `org-hide-drawer) . ,o)
+ (goto-char (overlay-end o))
+ (forward-line)
+ t)
+ (_ nil)))
+ (t
+ (let* ((element (org--paragraph-at-point))
+ (type (org-element-type element))
+ (contents-begin (org-element-property :contents-begin element))
+ (end (org-element-property :end element))
+ (post-affiliated (org-element-property :post-affiliated element)))
+ (cond
+ ((eq type 'plain-list)
+ (forward-char)
+ (org--forward-paragraph-once))
+ ;; If the element is folded, skip it altogether.
+ ((pcase (org-with-point-at post-affiliated
+ (get-char-property-and-overlay (line-end-position)
+ 'invisible))
+ (`(,(or `outline `org-hide-block `org-hide-drawer) . ,o)
+ (goto-char (overlay-end o))
+ (forward-line)
+ t)
+ (_ nil)))
+ ;; At a greater element, move inside.
+ ((and contents-begin
+ (> contents-begin (point))
+ (not (eq type 'paragraph)))
+ (goto-char contents-begin)
+ ;; Items and footnote definitions contents may not start at
+ ;; the beginning of the line. In this case, skip until the
+ ;; next paragraph.
+ (cond
+ ((not (bolp)) (org--forward-paragraph-once))
+ ((org-previous-line-empty-p) (forward-line -1))
+ (t nil)))
+ ;; Move between empty lines in some blocks.
+ ((memq type '(comment-block example-block export-block src-block
+ verse-block))
+ (let ((contents-start
+ (org-with-point-at post-affiliated
+ (line-beginning-position 2))))
+ (if (< (point) contents-start)
+ (goto-char contents-start)
+ (let ((contents-end
+ (org-with-point-at end
+ (skip-chars-backward " \t\n")
+ (line-beginning-position))))
+ (cond
+ ((>= (point) contents-end)
+ (goto-char end)
+ (skip-chars-backward " \t\n")
+ (forward-line))
+ ((re-search-forward "^[ \t]*\n" contents-end :move)
+ (forward-line -1))
+ (t nil))))))
+ (t
+ ;; Move to element's end.
+ (goto-char end)
+ (skip-chars-backward " \t\n")
+ (forward-line))))))))
+
+(defun org--backward-paragraph-once ()
+ "Move backward to start of paragraph or equivalent, once.
+See `org-backward-paragraph'."
(interactive)
- (unless (bobp)
- (let* ((deactivate-mark nil)
- (element (org-element-at-point))
- (type (org-element-type element))
- (contents-end (org-element-property :contents-end element))
- (post-affiliated (org-element-property :post-affiliated element))
- (begin (org-element-property :begin element))
- (special? ;blocks handled specially
- (memq type '(comment-block example-block export-block src-block
- verse-block)))
- (contents-begin
- (if special?
- ;; These types have no proper contents. Fake line
- ;; below the block opening line as contents beginning.
- (save-excursion (goto-char begin) (line-beginning-position 2))
- (org-element-property :contents-begin element))))
- (cond
- ((not element) (goto-char (point-min)))
- ((= (point) begin)
- (backward-char)
- (org-backward-paragraph))
- ((<= (point) post-affiliated) (goto-char begin))
- ;; Special behavior: on a table or a property drawer, move to
- ;; its beginning.
- ((memq type '(node-property table-row))
- (goto-char (org-element-property
- :post-affiliated (org-element-property :parent element))))
- (special?
- (if (<= (point) contents-begin) (goto-char post-affiliated)
- ;; Inside a verse block, see blank lines as paragraph
- ;; separators.
- (let ((origin (point)))
- (skip-chars-backward " \r\t\n" contents-begin)
- (when (re-search-backward "^[ \t]*$" contents-begin 'move)
- (skip-chars-forward " \r\t\n" origin)
- (if (= (point) origin) (goto-char contents-begin)
- (beginning-of-line))))))
- ((eq type 'paragraph) (goto-char contents-begin)
- ;; When at first paragraph in an item or a footnote definition,
- ;; move directly to beginning of line.
- (let ((parent-contents
- (org-element-property
- :contents-begin (org-element-property :parent element))))
- (when (and parent-contents (= parent-contents contents-begin))
- (beginning-of-line))))
- ;; At the end of a greater element, move to the beginning of
- ;; the last element within.
- ((and contents-end (>= (point) contents-end))
- (goto-char (1- contents-end))
- (org-backward-paragraph))
- (t (goto-char (or post-affiliated begin))))
- ;; Ensure we never leave point invisible.
- (when (org-invisible-p (point)) (beginning-of-visual-line)))))
+ (save-restriction
+ (widen)
+ (cond
+ ((bobp) nil)
+ ;; Blank lines at the beginning of the buffer.
+ ((and (org-match-line "^[ \t]*$")
+ (save-excursion (skip-chars-backward " \t\n") (bobp)))
+ (goto-char (point-min)))
+ ;; When inside a folded part, move out of it.
+ ((pcase (get-char-property-and-overlay (1- (point)) 'invisible)
+ (`(,(or `outline `org-hide-block `org-hide-drawer) . ,o)
+ (goto-char (1- (overlay-start o)))
+ (org--backward-paragraph-once)
+ t)
+ (_ nil)))
+ (t
+ (let* ((element (org--paragraph-at-point))
+ (type (org-element-type element))
+ (begin (org-element-property :begin element))
+ (post-affiliated (org-element-property :post-affiliated element))
+ (contents-end (org-element-property :contents-end element))
+ (end (org-element-property :end element))
+ (parent (org-element-property :parent element))
+ (reach
+ ;; Move to the visible empty line above position P, or
+ ;; to position P. Return t.
+ (lambda (p)
+ (goto-char p)
+ (when (and (org-previous-line-empty-p)
+ (let ((end (line-end-position 0)))
+ (or (= end (point-min))
+ (not (org-invisible-p (1- end))))))
+ (forward-line -1))
+ t)))
+ (cond
+ ;; Already at the beginning of an element.
+ ((= begin (point))
+ (cond
+ ;; There is a blank line above. Move there.
+ ((and (org-previous-line-empty-p)
+ (not (org-invisible-p (1- (line-end-position 0)))))
+ (forward-line -1))
+ ;; At the beginning of the first element within a greater
+ ;; element. Move to the beginning of the greater element.
+ ((and parent (= begin (org-element-property :contents-begin parent)))
+ (funcall reach (org-element-property :begin parent)))
+ ;; Since we have to move anyway, find the beginning
+ ;; position of the element above.
+ (t
+ (forward-char -1)
+ (org--backward-paragraph-once))))
+ ;; Skip paragraphs at the very beginning of footnote
+ ;; definitions or items.
+ ((and (eq type 'paragraph)
+ (org-with-point-at begin (not (bolp))))
+ (funcall reach (progn (goto-char begin) (line-beginning-position))))
+ ;; If the element is folded, skip it altogether.
+ ((org-with-point-at post-affiliated
+ (org-invisible-p (line-end-position) t))
+ (funcall reach begin))
+ ;; At the end of a greater element, move inside.
+ ((and contents-end
+ (<= contents-end (point))
+ (not (eq type 'paragraph)))
+ (cond
+ ((memq type '(footnote-definition plain-list))
+ (skip-chars-backward " \t\n")
+ (org--backward-paragraph-once))
+ ((= contents-end (point))
+ (forward-char -1)
+ (org--backward-paragraph-once))
+ (t
+ (goto-char contents-end))))
+ ;; Move between empty lines in some blocks.
+ ((and (memq type '(comment-block example-block export-block src-block
+ verse-block))
+ (let ((contents-start
+ (org-with-point-at post-affiliated
+ (line-beginning-position 2))))
+ (when (> (point) contents-start)
+ (let ((contents-end
+ (org-with-point-at end
+ (skip-chars-backward " \t\n")
+ (line-beginning-position))))
+ (if (> (point) contents-end)
+ (progn (goto-char contents-end) t)
+ (skip-chars-backward " \t\n" begin)
+ (re-search-backward "^[ \t]*\n" contents-start :move)
+ t))))))
+ ;; Move to element's start.
+ (t
+ (funcall reach begin))))))))
(defun org-forward-element ()
"Move forward by one element.
diff --git a/testing/lisp/test-org.el b/testing/lisp/test-org.el
index 21fc323..375e1a7 100644
--- a/testing/lisp/test-org.el
+++ b/testing/lisp/test-org.el
@@ -3700,45 +3700,46 @@ SCHEDULED: <2017-05-06 Sat>
t))
;; Standard test.
(should
- (org-test-with-temp-text "P1\n\nP2\n\nP3"
- (org-forward-paragraph)
- (looking-at "P2")))
- ;; Ignore depth.
+ (= 2
+ (org-test-with-temp-text "P1\n\nP2"
+ (org-forward-paragraph)
+ (org-current-line))))
(should
- (org-test-with-temp-text "#+BEGIN_CENTER\nP1\n#+END_CENTER\nP2"
- (org-forward-paragraph)
- (looking-at "P1")))
+ (= 2
+ (org-test-with-temp-text "P1\n\nP2\n\nP3"
+ (org-forward-paragraph)
+ (org-current-line))))
+ ;; Enter greater elements.
+ (should
+ (= 2
+ (org-test-with-temp-text "#+begin_center\nP1\n#+end_center\nP2"
+ (org-forward-paragraph)
+ (org-current-line))))
;; Do not enter elements with invisible contents.
(should
- (org-test-with-temp-text "#+BEGIN_CENTER\nP1\n\nP2\n#+END_CENTER\nP3"
- (org-hide-block-toggle)
- (org-forward-paragraph)
- (looking-at "P3")))
- ;; On an affiliated keyword, jump to the beginning of the element.
+ (= 4
+ (org-test-with-temp-text "* H1\n P1\n\n* H2"
+ (org-cycle)
+ (org-forward-paragraph)
+ (org-current-line))))
(should
- (org-test-with-temp-text "#+name: para\n#+caption: caption\nPara"
- (org-forward-paragraph)
- (looking-at "Para")))
- ;; On an item or a footnote definition, move to the second element
+ (= 6
+ (org-test-with-temp-text "#+begin_center\nP1\n\nP2\n#+end_center\nP3"
+ (org-hide-block-toggle)
+ (org-forward-paragraph)
+ (org-current-line))))
+ ;; On an item or a footnote definition, move past the first element
;; inside, if any.
(should
- (org-test-with-temp-text "- Item1\n\n Paragraph\n- Item2"
- (org-forward-paragraph)
- (looking-at " Paragraph")))
- (should
- (org-test-with-temp-text "[fn:1] Def1\n\nParagraph\n\n[fn:2] Def2"
- (org-forward-paragraph)
- (looking-at "Paragraph")))
- ;; On an item, or a footnote definition, when the first line is
- ;; empty, move to the first item.
- (should
- (org-test-with-temp-text "- \n\n Paragraph\n- Item2"
- (org-forward-paragraph)
- (looking-at " Paragraph")))
+ (= 2
+ (org-test-with-temp-text "- Item1\n\n Paragraph\n- Item2"
+ (org-forward-paragraph)
+ (org-current-line))))
(should
- (org-test-with-temp-text "[fn:1]\n\nParagraph\n\n[fn:2] Def2"
- (org-forward-paragraph)
- (looking-at "Paragraph")))
+ (= 2
+ (org-test-with-temp-text "[fn:1] Def1\n\nParagraph\n\n[fn:2] Def2"
+ (org-forward-paragraph)
+ (org-current-line))))
;; On a table (resp. a property drawer) do not move through table
;; rows (resp. node properties).
(should
@@ -3750,15 +3751,59 @@ SCHEDULED: <2017-05-06 Sat>
"* H\n<point>:PROPERTIES:\n:prop: value\n:END:\nParagraph"
(org-forward-paragraph)
(looking-at "Paragraph")))
- ;; On a verse or source block, stop after blank lines.
+ ;; Skip consecutive keywords, clocks and diary S-exps.
(should
- (org-test-with-temp-text "#+BEGIN_VERSE\nL1\n\nL2\n#+END_VERSE"
+ (org-test-with-temp-text "#+key: val\n #+key2: val\n#+key3: val\n"
(org-forward-paragraph)
- (looking-at "L2")))
+ (eobp)))
(should
- (org-test-with-temp-text "#+BEGIN_SRC\nL1\n\nL2\n#+END_SRC"
+ (org-test-with-temp-text "CLOCK: val\n CLOCK: val\nCLOCK: val\n"
(org-forward-paragraph)
- (looking-at "L2"))))
+ (eobp)))
+ (should
+ (org-test-with-temp-text "%%(foo)\n%%(bar)\n%%(baz)\n"
+ (org-forward-paragraph)
+ (eobp)))
+ (should-not
+ (org-test-with-temp-text "#+key: val\n #+key2: val\n\n#+key3: val\n"
+ (org-forward-paragraph)
+ (eobp)))
+ (should-not
+ (org-test-with-temp-text "#+key: val\nCLOCK: ...\n"
+ (org-forward-paragraph)
+ (eobp)))
+ ;; In a plain list with one item every line, skip the whole list,
+ ;; even with point in the middle of the list.
+ (should
+ (org-test-with-temp-text "- A\n - B\n- C\n"
+ (org-forward-paragraph)
+ (eobp)))
+ (should
+ (org-test-with-temp-text "- A\n - <point>B\n- C\n"
+ (org-forward-paragraph)
+ (eobp)))
+ ;; On a comment, verse or source block, stop at "contents"
+ ;; boundaries and blank lines.
+ (should
+ (= 2
+ (org-test-with-temp-text "#+begin_src emacs-lisp\nL1\n\nL2\n#+end_src"
+ (org-forward-paragraph)
+ (org-current-line))))
+ (should
+ (= 3
+ (org-test-with-temp-text "#+begin_verse\n<point>L1\n\nL2\n#+end_verse"
+ (org-forward-paragraph)
+ (org-current-line))))
+ (should
+ (= 5
+ (org-test-with-temp-text "#+begin_comment\nL1\n\n<point>L2\n#+end_comment"
+ (org-forward-paragraph)
+ (org-current-line))))
+ ;; Being on an affiliated keyword shouldn't make any difference.
+ (should
+ (org-test-with-temp-text "#+name: para\n#+caption: caption\nPara"
+ (org-forward-paragraph)
+ (eobp))))
(ert-deftest test-org/backward-paragraph ()
"Test `org-backward-paragraph' specifications."
@@ -3767,44 +3812,65 @@ SCHEDULED: <2017-05-06 Sat>
(org-test-with-temp-text "Paragraph"
(org-backward-paragraph)
t))
- ;; Regular test.
+ ;; At blank lines at the very beginning of a buffer, move to
+ ;; point-min.
(should
- (org-test-with-temp-text "P1\n\nP2\n\nP3<point>"
+ (org-test-with-temp-text "\n\n<point>\n\nParagraph"
(org-backward-paragraph)
- (looking-at "P3")))
+ (bobp)))
+ ;; Regular test.
(should
- (org-test-with-temp-text "P1\n\nP2\n\n<point>P3"
- (org-backward-paragraph)
- (looking-at-p "P2")))
- ;; Ignore depth.
+ (= 2
+ (org-test-with-temp-text "P1\n\nP2<point>"
+ (org-backward-paragraph)
+ (org-current-line))))
(should
- (org-test-with-temp-text "P1\n\n#+BEGIN_CENTER\nP2\n#+END_CENTER\n<point>P3"
- (org-backward-paragraph)
- (looking-at-p "P2")))
- ;; Ignore invisible elements.
+ (= 4
+ (org-test-with-temp-text "P1\n\nP2\n\nP3<point>"
+ (org-backward-paragraph)
+ (org-current-line))))
+ ;; Try to move on the line above current element.
+ (should
+ (= 2
+ (org-test-with-temp-text "\n\n<point>Paragraph"
+ (org-backward-paragraph)
+ (org-current-line))))
+ ;; Do not leave point in an invisible area.
(should
- (org-test-with-temp-text "* H1\n P1\n* H2"
+ (org-test-with-temp-text "* H1\n P1\n\n* H2"
(org-cycle)
(goto-char (point-max))
(beginning-of-line)
(org-backward-paragraph)
(bobp)))
- ;; On an affiliated keyword, jump to the first one.
(should
- (org-test-with-temp-text
- "P1\n#+name: n\n#+caption: c1\n#+caption: <point>c2\nP2"
+ (org-test-with-temp-text "#+begin_center\nP1\n\nP2\n#+end_center\n"
+ (org-hide-block-toggle)
+ (goto-char (point-max))
(org-backward-paragraph)
- (looking-at-p "#\\+name")))
- ;; On the second element in an item or a footnote definition, jump
- ;; to item or the definition.
+ (bobp)))
+ ;; On the first element in an item or a footnote definition, jump
+ ;; before the footnote or the item.
(should
- (org-test-with-temp-text "- line1\n\n<point> line2"
+ (org-test-with-temp-text "- line1<point>"
(org-backward-paragraph)
- (looking-at-p "- line1")))
+ (bobp)))
(should
- (org-test-with-temp-text "[fn:1] line1\n\n<point> line2"
+ (org-test-with-temp-text "[fn:1] line1n<point>"
(org-backward-paragraph)
- (looking-at-p "\\[fn:1\\] line1")))
+ (bobp)))
+ ;; On the second element in an item or a footnote definition, jump
+ ;; to item or the definition.
+ (should
+ (= 2
+ (org-test-with-temp-text "- line1\n\n<point> line2"
+ (org-backward-paragraph)
+ (org-current-line))))
+ (should
+ (= 2
+ (org-test-with-temp-text "[fn:1] line1\n\n<point> line2"
+ (org-backward-paragraph)
+ (org-current-line))))
;; On a table (resp. a property drawer), ignore table rows
;; (resp. node properties).
(should
@@ -3812,38 +3878,75 @@ SCHEDULED: <2017-05-06 Sat>
(org-backward-paragraph)
(bobp)))
(should
- (org-test-with-temp-text "* H\n:PROPERTIES:\n:prop: value\n:END:\n<point>P1"
+ (= 2
+ (org-test-with-temp-text
+ "* H\n:PROPERTIES:\n:prop: value\n:END:\n<point>P1"
+ (org-backward-paragraph)
+ (org-current-line))))
+ ;; In a plain list with one item every line, skip the whole list,
+ ;; even with point in the middle of the list.
+ (should
+ (org-test-with-temp-text "- A\n - B\n- C\n<point>"
(org-backward-paragraph)
- (looking-at-p ":PROPERTIES:")))
- ;; On a comment, example, src and verse blocks, stop before blank
- ;; lines.
+ (bobp)))
(should
- (org-test-with-temp-text "#+BEGIN_VERSE\nL1\n\nL2\n\n<point>L3\n#+END_VERSE"
+ (org-test-with-temp-text "- A\n - B\n- <point>C\n"
(org-backward-paragraph)
- (looking-at-p "L2")))
+ (bobp)))
+ ;; Skip consecutive keywords, clocks and diary S-exps.
(should
- (org-test-with-temp-text "#+BEGIN_SRC\nL1\n\nL2\n\n<point>L3#+END_SRC"
+ (org-test-with-temp-text "#+key: val\n #+key2: val\n#+key3: val\n<point>"
(org-backward-paragraph)
- (looking-at-p "L2")))
- ;; In comment, example, export, src and verse blocks, stop below
- ;; opening line when called from within the block.
+ (bobp)))
(should
- (org-test-with-temp-text "#+BEGIN_VERSE\nL1\nL2<point>\n#+END_VERSE"
+ (org-test-with-temp-text "CLOCK: val\n CLOCK: val\nCLOCK: val\n<point>"
(org-backward-paragraph)
- (looking-at-p "L1")))
+ (bobp)))
(should
- (org-test-with-temp-text "#+BEGIN_EXAMPLE\nL1\nL2<point>\n#+END_EXAMPLE"
+ (org-test-with-temp-text "%%(foo)\n%%(bar)\n%%(baz)\n<point>"
+ (org-backward-paragraph)
+ (bobp)))
+ (should-not
+ (org-test-with-temp-text "#+key: val\n #+key2: val\n\n#+key3: val\n<point>"
+ (org-backward-paragraph)
+ (bobp)))
+ (should-not
+ (org-test-with-temp-text "#+key: val\nCLOCK: ...\n<point>"
(org-backward-paragraph)
- (looking-at-p "L1")))
+ (bobp)))
+ ;; On a comment, example, source and verse blocks, stop at blank
+ ;; lines.
+ (should
+ (= 1
+ (org-test-with-temp-text
+ "#+begin_comment\n<point>L1\n\nL2\n\nL3\n#+end_comment"
+ (org-backward-paragraph)
+ (org-current-line))))
+ (should
+ (= 2
+ (org-test-with-temp-text
+ "#+begin_verse\nL1\n\n<point>L2\n\nL3\n#+end_verse"
+ (org-backward-paragraph)
+ (org-current-line))))
+ (should
+ (= 3
+ (org-test-with-temp-text
+ "#+begin_src emacs-lisp\nL1\n\nL2\n\n<point>L3\n#+end_src"
+ (org-backward-paragraph)
+ (org-current-line))))
;; When called from the opening line itself, however, move to
;; beginning of block.
(should
- (org-test-with-temp-text "#+BEGIN_<point>EXAMPLE\nL1\n#+END_EXAMPLE"
+ (org-test-with-temp-text "#+begin_<point>example\nL1\n#+end_example"
+ (org-backward-paragraph)
+ (bobp)))
+ ;; On an empty heading, move above it.
+ (should
+ (org-test-with-temp-text "\n* <point>"
(org-backward-paragraph)
(bobp)))
- ;; Pathological case: on an empty heading, move to its beginning.
(should
- (org-test-with-temp-text "* <point>H"
+ (org-test-with-temp-text "\n* \n<point>"
(org-backward-paragraph)
(bobp))))