Fix `C-a' with visual lines and arguments

* lisp/org.el (org-beginning-of-line): Move to beginning of visual line
  when appropriate.  Fix docstring.

* testing/lisp/test-org.el (test-org/beginning-of-line): Add tests.
This commit is contained in:
Nicolas Goaziou 2016-10-15 11:24:16 +02:00
parent 5c85409464
commit 8d2f0a4411
2 changed files with 103 additions and 41 deletions

View file

@ -23710,45 +23710,50 @@ package ox-bibtex by Taru Karttunen."
;;;; Functions extending outline functionality ;;;; Functions extending outline functionality
(defun org-beginning-of-line (&optional arg) (defun org-beginning-of-line (&optional n)
"Go to the beginning of the current line. "Go to the beginning of the current visible line.
If that is invisible, continue to a visible line beginning.
If this is a headline, and `org-special-ctrl-a/e' is set, ignore If this is a headline, and `org-special-ctrl-a/e' is set, ignore
tags on the first attempt, and only move to after the tags when tags on the first attempt, and only move to after the tags when
the cursor is already beyond the end of the headline." the cursor is already beyond the end of the headline.
(interactive "P")
(let ((pos (point)) With argument N not nil or 1, move forward N - 1 lines first."
(interactive "^p")
(let ((origin (point))
(special (pcase org-special-ctrl-a/e (special (pcase org-special-ctrl-a/e
(`(,C-a . _) C-a) (`(,C-a . _) C-a) (_ org-special-ctrl-a/e)))
(C-a C-a)))
deactivate-mark) deactivate-mark)
;; First move to a visible line.
(if (bound-and-true-p visual-line-mode) (if (bound-and-true-p visual-line-mode)
(call-interactively #'beginning-of-visual-line) (beginning-of-visual-line n)
(call-interactively #'move-beginning-of-line) (move-beginning-of-line n)
;; `move-beginning-of-line' may leave point after invisible ;; `move-beginning-of-line' may leave point after invisible
;; characters if line starts with such of these (e.g., with ;; characters if line starts with such of these (e.g., with
;; a link at column 0). Really move to the beginning of the ;; a link at column 0). Really move to the beginning of the
;; current visible line. ;; current visible line.
(beginning-of-line)) (beginning-of-line))
(cond (cond
((or arg (not special))) ;; No special behavior. Point is already at the beginning of
((and (looking-at org-complex-heading-regexp) ;; a line, logical or visual.
(eq (char-after (match-end 1)) ?\s)) ((not special))
;; `beginning-of-visual-line' left point before logical beginning
;; of line: point is at the beginning of a visual line. Bail
;; out.
((and (bound-and-true-p visual-line-mode) (not (bolp))))
((looking-at org-complex-heading-regexp)
;; At a headline, special position is before the title, but
;; after any TODO keyword or priority cookie.
(let ((refpos (min (1+ (or (match-end 3) (match-end 2) (match-end 1))) (let ((refpos (min (1+ (or (match-end 3) (match-end 2) (match-end 1)))
(line-end-position)))) (line-end-position)))
(goto-char (bol (point)))
(if (eq special t) (if (eq special 'reversed)
(cond ((> pos refpos) refpos) (when (and (= origin bol) (eq last-command this-command))
((= pos (point)) refpos) (goto-char refpos))
(t (point))) (when (or (> origin refpos) (= origin bol))
(cond ((> pos (point)) (point)) (goto-char refpos)))))
((not (eq last-command this-command)) (point))
(t refpos))))))
((and (looking-at org-list-full-item-re) ((and (looking-at org-list-full-item-re)
(save-match-data (memq (org-element-type (org-element-at-point)) (memq (org-element-type (save-match-data (org-element-at-point)))
'(item plain-list)))) '(item plain-list)))
;; Set special position at first white space character after ;; Set special position at first white space character after
;; bullet, and check-box, if any. ;; bullet, and check-box, if any.
(let ((after-bullet (let ((after-bullet
@ -23756,15 +23761,13 @@ the cursor is already beyond the end of the headline."
(cond ((not box) (match-end 1)) (cond ((not box) (match-end 1))
((eq (char-after box) ?\s) (1+ box)) ((eq (char-after box) ?\s) (1+ box))
(t box))))) (t box)))))
;; Special case: Move point to special position when currently (if (eq special 'reversed)
;; after it or at beginning of line. (when (and (= (point) origin) (eq last-command this-command))
(if (eq special t)
(when (or (> pos after-bullet) (= (point) pos))
(goto-char after-bullet)) (goto-char after-bullet))
;; Reversed case: Move point to special position when point (when (or (> origin after-bullet) (= (point) origin))
;; was already at beginning of line and command is repeated. (goto-char after-bullet)))))
(when (and (= (point) pos) (eq last-command this-command)) ;; No special context. Point is already at beginning of line.
(goto-char after-bullet)))))))) (t nil))))
(defun org-end-of-line (&optional n) (defun org-end-of-line (&optional n)
"Go to the end of the line, but before ellipsis, if any. "Go to the end of the line, but before ellipsis, if any.

View file

@ -2467,24 +2467,78 @@ http://article.gmane.org/gmane.emacs.orgmode/21459/"
(ert-deftest test-org/beginning-of-line () (ert-deftest test-org/beginning-of-line ()
"Test `org-beginning-of-line' specifications." "Test `org-beginning-of-line' specifications."
;; Standard test. ;; Move to beginning of line. If current line in invisible, move to
;; beginning of visible line instead.
(should (should
(org-test-with-temp-text "Some text\nSome other text<point>" (org-test-with-temp-text "Some text\nSome other text<point>"
(progn (org-beginning-of-line) (bolp)))) (org-beginning-of-line)
;; Standard test with `visual-line-mode'. (bolp)))
(should
(org-test-with-temp-text "* H1\n** H2<point>"
(org-overview)
(org-beginning-of-line)
(= (line-beginning-position) 1)))
;; With `visual-line-mode' active, move to beginning of visual line.
(should-not (should-not
(org-test-with-temp-text "A <point>long line of text\nSome other text" (org-test-with-temp-text "A <point>long line of text\nSome other text"
(progn (visual-line-mode) (visual-line-mode)
(dotimes (i 1000) (insert "very ")) (dotimes (i 1000) (insert "very "))
(org-beginning-of-line) (org-beginning-of-line)
(bolp)))) (bolp)))
;; At an headline with special movement. ;; In a wide headline, with `visual-line-mode', prefer going to the
;; beginning of a visual line than to the logical beginning of line,
;; even if special movement is active.
(should-not
(org-test-with-temp-text "* A <point>long headline"
(visual-line-mode)
(dotimes (i 1000) (insert "very "))
(goto-char (point-max))
(org-beginning-of-line)
(bobp)))
(should-not
(org-test-with-temp-text "* A <point>long headline"
(visual-line-mode)
(dotimes (i 1000) (insert "very "))
(goto-char (point-max))
(let ((org-special-ctrl-a/e t)) (org-beginning-of-line))
(bobp)))
;; At an headline with special movement, first move at beginning of
;; title, then at the beginning of line, rinse, repeat.
(should (should
(org-test-with-temp-text "* TODO Headline<point>" (org-test-with-temp-text "* TODO Headline<point>"
(let ((org-special-ctrl-a/e t)) (let ((org-special-ctrl-a/e t))
(and (progn (org-beginning-of-line) (looking-at "Headline")) (and (progn (org-beginning-of-line) (looking-at "Headline"))
(progn (org-beginning-of-line) (bolp)) (progn (org-beginning-of-line) (bolp))
(progn (org-beginning-of-line) (looking-at "Headline")))))) (progn (org-beginning-of-line) (looking-at "Headline"))))))
(should
(org-test-with-temp-text "* TODO [#A] Headline<point>"
(let ((org-special-ctrl-a/e t))
(org-beginning-of-line)
(looking-at "Headline"))))
;; At an headline with reversed movement, first move to beginning of
;; line, then to the beginning of title.
(should
(org-test-with-temp-text "* TODO Headline<point>"
(let ((org-special-ctrl-a/e 'reversed)
(this-command last-command))
(and (progn (org-beginning-of-line) (bolp))
(progn (org-beginning-of-line) (looking-at "Headline"))))))
;; At an item with special movement, first move after to beginning
;; of title, then to the beginning of line, rinse, repeat.
(should
(org-test-with-temp-text "- [ ] Item<point>"
(let ((org-special-ctrl-a/e t))
(and (progn (org-beginning-of-line) (looking-at "Item"))
(progn (org-beginning-of-line) (bolp))
(progn (org-beginning-of-line) (looking-at "Item"))))))
;; At an item with reversed movement, first move to beginning of
;; line, then to the beginning of title.
(should
(org-test-with-temp-text "- [X] Item<point>"
(let ((org-special-ctrl-a/e 'reversed)
(this-command last-command))
(and (progn (org-beginning-of-line) (bolp))
(progn (org-beginning-of-line) (looking-at "Item"))))))
;; Leave point before invisible characters at column 0. ;; Leave point before invisible characters at column 0.
(should (should
(org-test-with-temp-text "[[http://orgmode.org]]<point>" (org-test-with-temp-text "[[http://orgmode.org]]<point>"
@ -2496,6 +2550,11 @@ http://article.gmane.org/gmane.emacs.orgmode/21459/"
(let ((org-special-ctrl-a/e t)) (let ((org-special-ctrl-a/e t))
(org-beginning-of-line) (org-beginning-of-line)
(bolp)))) (bolp))))
(should
(org-test-with-temp-text "[[http<point>://orgmode.org]]"
(visual-line-mode)
(org-beginning-of-line)
(bolp)))
;; Special case: Do not error when the buffer contains only a single ;; Special case: Do not error when the buffer contains only a single
;; asterisk. ;; asterisk.
(should (should