Browse Source

Fix `C-e' with visible lines and arguments

* lisp/org.el (org-end-of-line): Correctly go to the end of visible
  line, when appropriate.
* testing/lisp/test-org.el (test-org/end-of-line): Add tests.
Nicolas Goaziou 8 years ago
parent
commit
45048eb783
2 changed files with 120 additions and 49 deletions
  1. 54 33
      lisp/org.el
  2. 66 16
      testing/lisp/test-org.el

+ 54 - 33
lisp/org.el

@@ -23766,42 +23766,63 @@ the cursor is already beyond the end of the headline."
 	  (when (and (= (point) pos) (eq last-command this-command))
 	    (goto-char after-bullet))))))))
 
-(defun org-end-of-line (&optional arg)
-  "Go to the end of the line.
+(defun org-end-of-line (&optional n)
+  "Go to the end of the line, but before ellipsis, if any.
+
 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 ((special (if (consp org-special-ctrl-a/e) (cdr org-special-ctrl-a/e)
-		   org-special-ctrl-a/e))
-	(move-fun (cond ((bound-and-true-p visual-line-mode)
-			 'end-of-visual-line)
-			((fboundp 'move-end-of-line) 'move-end-of-line)
-			(t 'end-of-line)))
+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-e) C-e) (_ org-special-ctrl-a/e)))
 	deactivate-mark)
-    (if (or (not special) arg) (call-interactively move-fun)
-      (let* ((element (save-excursion (beginning-of-line)
-				      (org-element-at-point)))
-	     (type (org-element-type element)))
-	(cond
-	 ((memq type '(headline inlinetask))
-	  (let ((pos (point)))
-	    (beginning-of-line 1)
-	    (if (looking-at ".*?\\(?:\\([ \t]*\\)\\(:[[:alnum:]_@#%:]+:\\)?[ \t]*\\)?$")
-		(if (eq special t)
-		    (if (or (< pos (match-beginning 1)) (= pos (match-end 0)))
-			(goto-char (match-beginning 1))
-		      (goto-char (match-end 0)))
-		  (if (or (< pos (match-end 0))
-			  (not (eq this-command last-command)))
-		      (goto-char (match-end 0))
-		    (goto-char (match-beginning 1))))
-	      (call-interactively move-fun))))
-	 ((outline-invisible-p (line-end-position))
-	  ;; If element is hidden, `move-end-of-line' would put point
-	  ;; after it.  Use `end-of-line' to stay on current line.
-	  (call-interactively 'end-of-line))
-	 (t (call-interactively move-fun))))))
+    ;; First move to a visible line.
+    (if (bound-and-true-p visual-line-mode)
+	(beginning-of-visual-line n)
+      (move-beginning-of-line n))
+    (cond
+     ;; At a headline, with tags.
+     ((and special
+	   (save-excursion
+	     (beginning-of-line)
+	     (looking-at org-complex-heading-regexp))
+	   (match-end 5))
+      (let ((tags (save-excursion
+		    (goto-char (match-beginning 5))
+		    (skip-chars-backward " \t")
+		    (point)))
+	    (visual-end (and (bound-and-true-p visual-line-mode)
+			     (save-excursion
+			       (end-of-visual-line)
+			       (point)))))
+	;; If `end-of-visual-line' brings us before end of line or
+	;; even tags, i.e., the headline spans over multiple visual
+	;; lines, move there.
+	(cond ((and visual-end
+		    (< visual-end tags)
+		    (<= origin visual-end))
+	       (goto-char visual-end))
+	      ((eq special 'reversed)
+	       (if (and (= origin (line-end-position))
+			(eq this-command last-command))
+		   (goto-char tags)
+		 (end-of-line)))
+	      (t
+	       (if (or (< origin tags) (= origin (line-end-position)))
+		   (goto-char tags)
+		 (end-of-line))))))
+     ((bound-and-true-p visual-line-mode)
+      (let ((bol (line-beginning-position)))
+	(end-of-visual-line)
+	;; If `end-of-visual-line' gets us past the ellipsis at the
+	;; end of a line, backtrack and use `end-of-line' instead.
+	(when (/= bol (line-beginning-position))
+	  (goto-char bol)
+	  (end-of-line))))
+     (t (end-of-line))))
   (setq disable-point-adjustment
         (or (not (invisible-p (point)))
             (not (invisible-p (max (point-min) (1- (point))))))))

+ 66 - 16
testing/lisp/test-org.el

@@ -2511,35 +2511,80 @@ http://article.gmane.org/gmane.emacs.orgmode/21459/"
   (should
    (org-test-with-temp-text "Some text\nSome other text"
      (progn (org-end-of-line) (eolp))))
-  ;; Standard test with `visual-line-mode'.
+  ;; With `visual-line-mode' active, move to end of visible line.
+  ;; However, never go past ellipsis.
   (should-not
-   (org-test-with-temp-text "A long line of text\nSome other text"
-     (progn (visual-line-mode)
-	    (forward-char 2)
-	    (dotimes (i 1000) (insert "very "))
-	    (goto-char (point-min))
-	    (org-end-of-line)
-	    (eolp))))
-  ;; At an headline with special movement.
+   (org-test-with-temp-text "A <point>long line of text\nSome other text"
+     (visual-line-mode)
+     (dotimes (i 1000) (insert "very "))
+     (goto-char (point-min))
+     (org-end-of-line)
+     (eolp)))
+  (should-not
+   (org-test-with-temp-text "* A short headline\nSome contents"
+     (visual-line-mode)
+     (org-overview)
+     (org-end-of-line)
+     (eobp)))
+  ;; In a wide headline, with `visual-line-mode', prefer going to end
+  ;; of visible line if tags, or end of line, are farther.
+  (should-not
+   (org-test-with-temp-text "* A <point>long headline"
+     (visual-line-mode)
+     (dotimes (i 1000) (insert "very "))
+     (goto-char (point-min))
+     (org-end-of-line)
+     (eolp)))
+  (should-not
+   (org-test-with-temp-text "* A <point>long headline :tag:"
+     (visual-line-mode)
+     (dotimes (i 1000) (insert "very "))
+     (goto-char (point-min))
+     (org-end-of-line)
+     (eolp)))
+  ;; At an headline without special movement, go to end of line.
+  ;; However, never go past ellipsis.
+  (should
+   (org-test-with-temp-text "* Headline2b :tag:\n"
+     (let ((org-special-ctrl-a/e nil))
+       (and (progn (org-end-of-line) (eolp))
+	    (progn (org-end-of-line) (eolp))))))
+  (should
+   (org-test-with-temp-text "* Headline2a :tag:\n** Sub"
+     (org-overview)
+     (let ((org-special-ctrl-a/e nil))
+       (org-end-of-line)
+       (= 1 (line-beginning-position)))))
+  ;; At an headline with special movement, first move before tags,
+  ;; then at the end of line, rinse, repeat.  However, never go past
+  ;; ellipsis.
   (should
    (org-test-with-temp-text "* Headline1 :tag:\n"
      (let ((org-special-ctrl-a/e t))
        (and (progn (org-end-of-line) (looking-at " :tag:"))
 	    (progn (org-end-of-line) (eolp))
 	    (progn (org-end-of-line) (looking-at " :tag:"))))))
-  ;; At an headline without special movement.
   (should
-   (org-test-with-temp-text "* Headline2 :tag:\n"
-     (let ((org-special-ctrl-a/e nil))
-       (and (progn (org-end-of-line) (eolp))
-	    (progn (org-end-of-line) (eolp))))))
-  ;; At an headline, with reversed movement.
+   (org-test-with-temp-text "* Headline2a :tag:\n** Sub"
+     (org-overview)
+     (let ((org-special-ctrl-a/e t))
+       (org-end-of-line)
+       (org-end-of-line)
+       (= 1 (line-beginning-position)))))
+  ;; At an headline, with reversed movement, first go to end of line,
+  ;; then before tags.  However, never go past ellipsis.
   (should
    (org-test-with-temp-text "* Headline3 :tag:\n"
      (let ((org-special-ctrl-a/e 'reversed)
 	   (this-command last-command))
        (and (progn (org-end-of-line) (eolp))
 	    (progn (org-end-of-line) (looking-at " :tag:"))))))
+  (should
+   (org-test-with-temp-text "* Headline2a :tag:\n** Sub"
+     (org-overview)
+     (let ((org-special-ctrl-a/e 'reversed))
+       (org-end-of-line)
+       (= 1 (line-beginning-position)))))
   ;; At a block without hidden contents.
   (should
    (org-test-with-temp-text "#+BEGIN_CENTER\nContents\n#+END_CENTER"
@@ -2550,7 +2595,12 @@ http://article.gmane.org/gmane.emacs.orgmode/21459/"
      (let ((org-special-ctrl-a/e t))
        (org-hide-block-toggle)
        (org-end-of-line)
-       (eobp)))))
+       (eobp))))
+  ;; Get past invisible characters at the end of line.
+  (should
+   (org-test-with-temp-text "[[http://orgmode.org]]"
+     (org-end-of-line)
+     (eolp))))
 
 (ert-deftest test-org/open-line ()
   "Test `org-open-line' specifications."