Browse Source

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.
Nicolas Goaziou 3 years ago
parent
commit
8d2f0a4411
2 changed files with 103 additions and 41 deletions
  1. 36 33
      lisp/org.el
  2. 67 8
      testing/lisp/test-org.el

+ 36 - 33
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.

+ 67 - 8
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<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