From 8d2f0a441174c703ed0ed570e2f0eaf0da5d6aeb Mon Sep 17 00:00:00 2001 From: Nicolas Goaziou Date: Sat, 15 Oct 2016 11:24:16 +0200 Subject: [PATCH] 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. --- lisp/org.el | 69 +++++++++++++++++++++++--------------------- testing/lisp/test-org.el | 75 ++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 103 insertions(+), 41 deletions(-) diff --git a/lisp/org.el b/lisp/org.el index 99ffdf9d6..732f7e01f 100644 --- a/lisp/org.el +++ b/lisp/org.el @@ -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. diff --git a/testing/lisp/test-org.el b/testing/lisp/test-org.el index 08ce4d8b1..915d22939 100644 --- a/testing/lisp/test-org.el +++ b/testing/lisp/test-org.el @@ -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" - (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" + (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 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 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 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" (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" + (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" + (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" + (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" + (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]]" @@ -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://orgmode.org]]" + (visual-line-mode) + (org-beginning-of-line) + (bolp))) ;; Special case: Do not error when the buffer contains only a single ;; asterisk. (should -- 2.11.4.GIT