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
(defun org-beginning-of-line (&optional arg)
"Go to the beginning of the current line.
If that is invisible, continue to a visible line beginning.
(defun org-beginning-of-line (&optional n)
"Go to the beginning of the current visible line.
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
the cursor is already beyond the end of the headline."
(interactive "P")
(let ((pos (point))
the cursor is already beyond the end of the headline.
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
(`(,C-a . _) C-a)
(C-a C-a)))
(`(,C-a . _) C-a) (_ org-special-ctrl-a/e)))
deactivate-mark)
;; First move to a visible line.
(if (bound-and-true-p visual-line-mode)
(call-interactively #'beginning-of-visual-line)
(call-interactively #'move-beginning-of-line)
(beginning-of-visual-line n)
(move-beginning-of-line n)
;; `move-beginning-of-line' may leave point after invisible
;; characters if line starts with such of these (e.g., with
;; a link at column 0). Really move to the beginning of the
;; current visible line.
(beginning-of-line))
(cond
((or arg (not special)))
((and (looking-at org-complex-heading-regexp)
(eq (char-after (match-end 1)) ?\s))
;; No special behavior. Point is already at the beginning of
;; a line, logical or visual.
((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)))
(line-end-position))))
(goto-char
(if (eq special t)
(cond ((> pos refpos) refpos)
((= pos (point)) refpos)
(t (point)))
(cond ((> pos (point)) (point))
((not (eq last-command this-command)) (point))
(t refpos))))))
(line-end-position)))
(bol (point)))
(if (eq special 'reversed)
(when (and (= origin bol) (eq last-command this-command))
(goto-char refpos))
(when (or (> origin refpos) (= origin bol))
(goto-char refpos)))))
((and (looking-at org-list-full-item-re)
(save-match-data (memq (org-element-type (org-element-at-point))
'(item plain-list))))
(memq (org-element-type (save-match-data (org-element-at-point)))
'(item plain-list)))
;; Set special position at first white space character after
;; bullet, and check-box, if any.
(let ((after-bullet
@ -23756,15 +23761,13 @@ the cursor is already beyond the end of the headline."
(cond ((not box) (match-end 1))
((eq (char-after box) ?\s) (1+ box))
(t box)))))
;; Special case: Move point to special position when currently
;; after it or at beginning of line.
(if (eq special t)
(when (or (> pos after-bullet) (= (point) pos))
(if (eq special 'reversed)
(when (and (= (point) origin) (eq last-command this-command))
(goto-char after-bullet))
;; Reversed case: Move point to special position when point
;; was already at beginning of line and command is repeated.
(when (and (= (point) pos) (eq last-command this-command))
(goto-char after-bullet))))))))
(when (or (> origin after-bullet) (= (point) origin))
(goto-char after-bullet)))))
;; No special context. Point is already at beginning of line.
(t nil))))
(defun org-end-of-line (&optional n)
"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 ()
"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
(org-test-with-temp-text "Some text\nSome other text<point>"
(progn (org-beginning-of-line) (bolp))))
;; Standard test with `visual-line-mode'.
(org-beginning-of-line)
(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
(org-test-with-temp-text "A <point>long line of text\nSome other text"
(progn (visual-line-mode)
(dotimes (i 1000) (insert "very "))
(org-beginning-of-line)
(bolp))))
;; At an headline with special movement.
(visual-line-mode)
(dotimes (i 1000) (insert "very "))
(org-beginning-of-line)
(bolp)))
;; 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
(org-test-with-temp-text "* TODO Headline<point>"
(let ((org-special-ctrl-a/e t))
(and (progn (org-beginning-of-line) (looking-at "Headline"))
(progn (org-beginning-of-line) (bolp))
(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.
(should
(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))
(org-beginning-of-line)
(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
;; asterisk.
(should