Browse Source

Function `org-link-search' does exact headline search

* lisp/org.el (org-link-search): Change headline search such that it
always does an exact search, ignoring spaces.

* testing/lisp/test-org.el (test-org/fuzzy-links): Test exact headline
match with spaces and cookies.

* doc/org.texi (External links): Cleanup footnote about the
`org-link-search-must-match-exact-headline' option for text searches
and add a footnote about the effect of the same option for heading
searches.

* doc/ORG-NEWS: Document changes.
Alan Schmitt 10 years ago
parent
commit
6d691e2aa8
4 changed files with 68 additions and 27 deletions
  1. 8 4
      doc/org.texi
  2. 5 0
      etc/ORG-NEWS
  3. 29 23
      lisp/org.el
  4. 26 0
      testing/lisp/test-org.el

+ 8 - 4
doc/org.texi

@@ -3561,10 +3561,14 @@ file:projects.org::some words             @r{text search in Org file}@footnote{
 The actual behavior of the search will depend on the value of
 the option @code{org-link-search-must-match-exact-headline}.  If its value
 is @code{nil}, then a fuzzy text search will be done.  If it is t, then only the
-exact headline will be matched.  If the value is @code{'query-to-create},
-then an exact headline will be searched; if it is not found, then the user
-will be queried to create it.}
-file:projects.org::*task title            @r{heading search in Org file}
+exact headline will be matched, ignoring spaces and cookies.  If the value is
+@code{query-to-create}, then an exact headline will be searched; if it is not
+found, then the user will be queried to create it.}
+file:projects.org::*task title @r{heading search in Org
+file}@footnote{ Headline searches always match the exact headline, ignoring
+spaces and cookies.  If the headline is not found and the value of the option
+@code{org-link-search-must-match-exact-headline} is @code{query-to-create},
+then the user will be queried to create it.}
 file+sys:/path/to/file                    @r{open via OS, like double-click}
 file+emacs:/path/to/file                  @r{force opening by Emacs}
 docview:papers/last.pdf::NNN              @r{open in doc-view mode at page}

+ 5 - 0
etc/ORG-NEWS

@@ -218,6 +218,11 @@ The build system has been enhanced to allow test selection with a
 regular expression by defining =BTEST_RE= during the test invocation.
 This is especially useful during bisection to find just when a
 particular test failure was introduced.
+*** Exact heading search for external links ignore spaces and cookies
+Exact heading search for links now ignore spaces and cookies. This is
+the case for links of the form ~file:projects.org::*task title~, as
+well as links of the form ~file:projects.org::some words~
+when ~org-link-search-must-match-exact-headline~ is not nil.
 * Version 8.2
 
 ** Incompatible changes

+ 29 - 23
lisp/org.el

@@ -11007,8 +11007,7 @@ visibility around point, thus ignoring
 						    org-emphasis-alist)
 					    "\\|") "\\)"))
 	(pos (point))
-	(pre nil) (post nil)
-	words re0 re1 re2 re3 re4_ re4 re5 re2a re2a_ reall)
+	words re0 re2 re4_ re4 re5 re2a re2a_ reall)
     (cond
      ;; First check if there are any special search functions
      ((run-hook-with-args-until-success 'org-execute-file-search-functions s))
@@ -11062,14 +11061,34 @@ visibility around point, thus ignoring
        ((derived-mode-p 'org-mode)
 	(org-occur (match-string 1 s)))
        (t (org-do-occur (match-string 1 s)))))
-     ((and (derived-mode-p 'org-mode) org-link-search-must-match-exact-headline)
-      (and (equal (string-to-char s) ?*) (setq s (substring s 1)))
+     ((and (derived-mode-p 'org-mode)
+	   (or (and (equal (string-to-char s) ?*) (setq s (substring s 1)))
+	       org-link-search-must-match-exact-headline))
+      ;; Headline search.
       (goto-char (point-min))
       (cond
        ((let (case-fold-search)
-	  (re-search-forward (format org-complex-heading-regexp-format
-				     (regexp-quote s))
-			     nil t))
+	  (re-search-forward
+	   (let* ((wspace "[ \t]")
+		  (wspaceopt (concat wspace "*"))
+		  (cookie (concat "\\(?:"
+				  wspaceopt
+				  "\\[[0-9]*\\(?:%\\|/[0-9]*\\)\\]"
+				  wspaceopt
+				  "\\)"))
+		  (sep (concat "\\(?:" wspace "+\\|" cookie "+\\)")))
+	     (concat
+	      org-outline-regexp-bol
+	      "\\(?:" org-todo-regexp "[ \t]+\\)?"
+	      "\\(?:\\[#.\\][ \t]+\\)?"
+	      sep "*"
+	      (mapconcat #'identity
+			 (org-split-string (regexp-quote s))
+			 (concat sep "+"))
+	      sep "*"
+	      (org-re "\\(?:[ \t]+:[[:alnum:]_@#%%:]+:\\)?")
+	      "[ \t]*$"))
+	   nil t))
 	;; OK, found a match
 	(setq type 'dedicated)
 	(goto-char (match-beginning 0)))
@@ -11085,11 +11104,6 @@ visibility around point, thus ignoring
 	(error "No match"))))
      (t
       ;; A normal search string
-      (when (equal (string-to-char s) ?*)
-	;; Anchor on headlines, post may include tags.
-	(setq pre "^\\*+[ \t]+\\(?:\\sw+\\)?[ \t]*"
-	      post (org-re "[ \t]*\\(?:[ \t]+:[[:alnum:]_@#%:+]:[ \t]*\\)?$")
-	      s (substring s 1)))
       (remove-text-properties
        0 (length s)
        '(face nil mouse-face nil keymap nil fontified nil) s)
@@ -11106,15 +11120,9 @@ visibility around point, thus ignoring
 					  "[^a-zA-Z_\r\n]+") "\\)[^a-zA-Z_]")
 	    re4 (concat "[^a-zA-Z_]" re4_)
 
-	    re1 (concat pre re2 post)
-	    re3 (concat pre (if pre re4_ re4) post)
-	    re5 (concat pre ".*" re4)
-	    re2 (concat pre re2)
-	    re2a (concat pre (if pre re2a_ re2a))
-	    re4 (concat pre (if pre re4_ re4))
-	    reall (concat "\\(" re0 "\\)\\|\\(" re1 "\\)\\|\\(" re2
-			  "\\)\\|\\(" re3 "\\)\\|\\(" re4 "\\)\\|\\("
-			  re5 "\\)"))
+	    re5 (concat ".*" re4)
+	    reall (concat "\\(" re0 "\\)\\|\\(" re2 "\\)\\|\\(" re4
+			  "\\)\\|\\(" re5 "\\)"))
       (cond
        ((eq type 'org-occur) (org-occur reall))
        ((eq type 'occur) (org-do-occur (downcase reall) 'cleanup))
@@ -11122,10 +11130,8 @@ visibility around point, thus ignoring
 	  (setq type 'fuzzy)
 	  (if (or (and (org-search-not-self 1 re0 nil t)
 		       (setq type 'dedicated))
-		  (org-search-not-self 1 re1 nil t)
 		  (org-search-not-self 1 re2 nil t)
 		  (org-search-not-self 1 re2a nil t)
-		  (org-search-not-self 1 re3 nil t)
 		  (org-search-not-self 1 re4 nil t)
 		  (org-search-not-self 1 re5 nil t))
 	      (goto-char (match-beginning 1))

+ 26 - 0
testing/lisp/test-org.el

@@ -1331,6 +1331,32 @@
      (goto-line 3)
      (org-open-at-point)
      (looking-at "\\* Test")))
+  ;; With a leading star in link, enforce exact heading match, even
+  ;; with `org-link-search-must-match-exact-headline' set to nil.
+  (should-error
+   (org-test-with-temp-text "* Test 1\nFoo Bar\n<point>[[*Test]]"
+     (let ((org-link-search-must-match-exact-headline nil))
+       (org-open-at-point))))
+  ;; Heading match should not care about spaces, cookies, todo
+  ;; keywords, priorities, and tags.
+  (should
+   (let ((first-line
+	  "** TODO [#A] [/]  Test [1/2] [33%] 1 \t  2 [%] :work:urgent: "))
+     (org-test-with-temp-text
+	 (concat first-line "\nFoo Bar\n<point>[[*Test 1 2]]")
+       (let ((org-link-search-must-match-exact-headline nil)
+	     (org-todo-regexp "TODO"))
+	 (org-open-at-point))
+       (looking-at (regexp-quote first-line)))))
+  ;; Heading match should still be exact.
+  (should-error
+   (let ((first-line
+	  "** TODO [#A] [/]  Test [1/2] [33%] 1 \t  2 [%] :work:urgent: "))
+     (org-test-with-temp-text
+	 (concat first-line "\nFoo Bar\n<point>[[*Test 1]]")
+       (let ((org-link-search-must-match-exact-headline nil)
+	     (org-todo-regexp "TODO"))
+	 (org-open-at-point)))))
   ;; Correctly un-hexify fuzzy links.
   (should
    (org-test-with-temp-text "* With space\n[[*With%20space][With space]]"