Browse Source

org-pcomplete: Fix tag completion

* lisp/org-pcomplete.el (org-thing-at-point): Only complete where tags
  could be located.
(pcomplete/org-mode/tag): Add paring argument to avoid looking at
other arguments on the same line.
* testing/lisp/test-org-pcomplete.el (test-org-pcomplete/tag): New test.

Reported-by: Alain.Cochard@unistra.fr
<http://lists.gnu.org/r/emacs-orgmode/2018-11/msg00154.html>
Nicolas Goaziou 7 months ago
parent
commit
8b7ba29b60
2 changed files with 40 additions and 5 deletions
  1. 7 5
      lisp/org-pcomplete.el
  2. 33 0
      testing/lisp/test-org-pcomplete.el

+ 7 - 5
lisp/org-pcomplete.el

@@ -104,10 +104,11 @@ The return value is a string naming the thing at point."
 		(char-before)))
       (cons "tex" nil))
      ;; Tags on a headline.
-     ((and (org-at-heading-p)
-	   (eq ?: (save-excursion
-		    (skip-chars-backward "[:alnum:]_@#%")
-		    (char-before))))
+     ((and (org-match-line
+	    (format "\\*+ \\(?:.+? \\)?\\(:\\)\\(\\(?::\\|%s\\)+\\)?[ \t]*$"
+		    org-tag-re))
+	   (or (org-point-in-group (point) 2)
+	       (= (point) (match-end 1))))
       (cons "tag" nil))
      ;; TODO keywords on an empty headline.
      ((and (string-match "^\\*+ +\\S-*$" line-to-here)
@@ -372,7 +373,8 @@ This needs more work, to handle headings with lots of spaces in them."
 		      (setq lst (delete tag lst)))
 		    lst))
 	  (and (string-match ".*:" pcomplete-stub)
-	       (substring pcomplete-stub (match-end 0))))))
+	       (substring pcomplete-stub (match-end 0)))
+	  t)))
 
 (defun pcomplete/org-mode/drawer ()
   "Complete a drawer name, including \"PROPERTIES\"."

+ 33 - 0
testing/lisp/test-org-pcomplete.el

@@ -125,6 +125,39 @@
 	    (pcomplete)
 	    (buffer-string)))))
 
+(ert-deftest test-org-pcomplete/tag ()
+  "Test tag completion."
+  ;; Complete at end of line, according to `org-current-tag-alist'.
+  (should
+   (equal "* H :foo:"
+	  (org-test-with-temp-text "* H :<point>"
+	    (let ((org-current-tag-alist '(("foo")))) (pcomplete))
+	    (buffer-string))))
+  (should
+   (equal "* H :foo:bar:"
+	  (org-test-with-temp-text "* H :foo:b<point>"
+	    (let ((org-current-tag-alist '(("bar")))) (pcomplete))
+	    (buffer-string))))
+  ;; If `org-current-tag-alist' is non-nil, complete against tags in
+  ;; buffer.
+  (should
+   (equal "* H1 :bar:\n* H2 :bar:"
+	  (org-test-with-temp-text "* H1 :bar:\n* H2 :<point>"
+	    (let ((org-current-tag-alist nil)) (pcomplete))
+	    (buffer-string))))
+  ;; Do not complete in the middle of a line.
+  (should
+   (equal "* H :notag: :real:tags:"
+	  (org-test-with-temp-text "* H :notag:<point> :real:tags:"
+	    (let ((org-current-tag-alist '(("foo")))) (pcomplete))
+	    (buffer-string))))
+  ;; Complete even when there's a match on the line.
+  (should
+   (equal "* foo: :foo:"
+	  (org-test-with-temp-text "* foo: :<point>"
+	    (let ((org-current-tag-alist '(("foo")))) (pcomplete))
+	    (buffer-string)))))
+
 (ert-deftest test-org-pcomplete/todo ()
   "Test TODO completion."
   (should