瀏覽代碼

Make internal links in Org files search for an exact headline match

* lisp/org.el (org-link-search-must-match-exact-headline): New option.
(org-link-search-inhibit-query): New variable.
(org-link-search): Search for exact headline match in Org files
* doc/org.texi (Internal links): Document the changes in internal links.

Internal links used to do a fuzzy text search for the link text.  This
patch changes the behavior for Org files.  Here a link [[My Target]]
now searches for an exact headline match, i.e. for a headline that
does look like "* My Target", optionally with TODO keyword, priority
cookie and tags.

The new option `org-link-search-must-match-exact-headline' is
`query-to-create' by default.  This means that a failed link search
will offer to create the headline as a top-level headline at the end
of the buffer.  This corresponds to a wiki-like behavior where missing
targets are automatically created.  If you do not like this behavior,
change the option to t.
Carsten Dominik 14 年之前
父節點
當前提交
a84c8a2cba
共有 3 個文件被更改,包括 55 次插入38 次删除
  1. 7 17
      doc/org.texi
  2. 13 12
      lisp/org-exp.el
  3. 35 9
      lisp/org.el

+ 7 - 17
doc/org.texi

@@ -2806,23 +2806,13 @@ text before the first headline is usually not exported, so the first such
 target should be after the first headline, or in the line directly before the
 target should be after the first headline, or in the line directly before the
 first headline.}.
 first headline.}.
 
 
-If no dedicated target exists, Org will search for the words in the link.  In
-the above example the search would be for @samp{my target}.  Links starting
-with a star like @samp{*My Target} restrict the search to
-headlines@footnote{To insert a link targeting a headline, in-buffer
-completion can be used.  Just type a star followed by a few optional letters
-into the buffer and press @kbd{M-@key{TAB}}.  All headlines in the current
-buffer will be offered as completions.  @xref{Handling links}, for more
-commands creating links.}.  When searching, Org-mode will first try an
-exact match, but then move on to more and more lenient searches.  For
-example, the link @samp{[[*My Targets]]} will find any of the following:
-
-@example
-** My targets
-** TODO my targets are bright
-** my 20 targets are
-@end example
-
+If no dedicated target exists, Org will search for a headline that is exactly
+the link text but may also include a TODO keyword and tags@footnote{To insert
+a link targeting a headline, in-buffer completion can be used.  Just type a
+star followed by a few optional letters into the buffer and press
+@kbd{M-@key{TAB}}.  All headlines in the current buffer will be offered as
+completions.}.  In non-Org files, the search will look for the words in the
+link text, in the above example the search would be for @samp{my target}.
 
 
 Following a link pushes a mark onto Org's own mark ring.  You can
 Following a link pushes a mark onto Org's own mark ring.  You can
 return to the previous position with @kbd{C-c &}.  Using this command
 return to the previous position with @kbd{C-c &}.  Using this command

+ 13 - 12
lisp/org-exp.el

@@ -1289,18 +1289,19 @@ the current file."
 		   (string-match "^\\." link))
 		   (string-match "^\\." link))
 	       nil)
 	       nil)
 	      (t
 	      (t
-	       (save-excursion
-		 (setq found (condition-case nil (org-link-search link)
-			       (error nil)))
-		 (when (and found
-			    (or (org-on-heading-p)
-				(not (eq found 'dedicated))))
-		   (or (get-text-property (point) 'target)
-		       (get-text-property
-			(max (point-min)
-			     (1- (or (previous-single-property-change
-				      (point) 'target) 0)))
-			'target))))))))
+	       (let ((org-link-search-inhibit-query t))
+		 (save-excursion
+		   (setq found (condition-case nil (org-link-search link)
+				 (error nil)))
+		   (when (and found
+			      (or (org-on-heading-p)
+				  (not (eq found 'dedicated))))
+		     (or (get-text-property (point) 'target)
+			 (get-text-property
+			  (max (point-min)
+			       (1- (or (previous-single-property-change
+					(point) 'target) 0)))
+			  'target)))))))))
        (when target
        (when target
 	 (set-match-data md)
 	 (set-match-data md)
 	 (goto-char (match-beginning 1))
 	 (goto-char (match-beginning 1))

+ 35 - 9
lisp/org.el

@@ -1439,6 +1439,17 @@ Changing this requires a restart of Emacs to work correctly."
   :group 'org-link-follow
   :group 'org-link-follow
   :type 'integer)
   :type 'integer)
 
 
+(defcustom org-link-search-must-match-exact-headline 'query-to-create
+  "Non-nil means internal links in Org files must exactly match a headline.
+When nil, the link search tries to match a phrase will all words
+in the search text."
+  :group 'org-link-follow
+  :type '(choice
+	  (const :tag "Use fuzy text search" nil)
+	  (const :tag "Match only exact headline" t)
+	  (const :tag "Match extact headline or query to create it"
+		 query-to-create)))
+
 (defcustom org-link-frame-setup
 (defcustom org-link-frame-setup
   '((vm . vm-visit-folder-other-frame)
   '((vm . vm-visit-folder-other-frame)
     (gnus . org-gnus-no-new-news)
     (gnus . org-gnus-no-new-news)
@@ -9300,6 +9311,7 @@ the window configuration before `org-open-at-point' was called using:
 
 
     (set-window-configuration org-window-config-before-follow-link)")
     (set-window-configuration org-window-config-before-follow-link)")
 
 
+(defvar org-link-search-inhibit-query nil) ;; dynamically scoped
 (defun org-link-search (s &optional type avoid-pos)
 (defun org-link-search (s &optional type avoid-pos)
   "Search for a link search option.
   "Search for a link search option.
 If S is surrounded by forward slashes, it is interpreted as a
 If S is surrounded by forward slashes, it is interpreted as a
@@ -9317,7 +9329,7 @@ in all files.  If AVOID-POS is given, ignore matches near that position."
 	(pre nil) (post nil)
 	(pre nil) (post nil)
 	words re0 re1 re2 re3 re4_ re4 re5 re2a re2a_ reall)
 	words re0 re1 re2 re3 re4_ re4 re5 re2a re2a_ reall)
     (cond
     (cond
-     ;; First check if there are any special
+     ;; First check if there are any special search functions
      ((run-hook-with-args-until-success 'org-execute-file-search-functions s))
      ((run-hook-with-args-until-success 'org-execute-file-search-functions s))
      ;; Now try the builtin stuff
      ;; Now try the builtin stuff
      ((and (equal (string-to-char s0) ?#)
      ((and (equal (string-to-char s0) ?#)
@@ -9362,8 +9374,28 @@ in all files.  If AVOID-POS is given, ignore matches near that position."
        ;;((eq major-mode 'dired-mode)
        ;;((eq major-mode 'dired-mode)
        ;; (grep (concat "grep -n -e '" (match-string 1 s) "' *")))
        ;; (grep (concat "grep -n -e '" (match-string 1 s) "' *")))
        (t (org-do-occur (match-string 1 s)))))
        (t (org-do-occur (match-string 1 s)))))
+     ((and (org-mode-p) org-link-search-must-match-exact-headline)
+      (and (equal (string-to-char s) ?*) (setq s (substring s 1)))
+      (goto-char (point-min))
+      (cond
+       ((let (case-fold-search)
+	  (re-search-forward (format org-complex-heading-regexp-format
+				     (regexp-quote s))
+			     nil t))
+	;; OK, found a match
+	(goto-char (match-beginning 0)))
+       ((and (not org-link-search-inhibit-query)
+	     (eq org-link-search-must-match-exact-headline 'query-to-create)
+	     (y-or-n-p "No match - create this as a new heading? "))
+	(goto-char (point-max))
+	(or (bolp) (newline))
+	(insert "* " s "\n")
+	(beginning-of-line 0))
+       (t
+	(goto-char pos)
+	(error "No match"))))
      (t
      (t
-      ;; A normal search strings
+      ;; A normal search string
       (when (equal (string-to-char s) ?*)
       (when (equal (string-to-char s) ?*)
 	;; Anchor on headlines, post may include tags.
 	;; Anchor on headlines, post may include tags.
 	(setq pre "^\\*+[ \t]+\\(?:\\sw+\\)?[ \t]*"
 	(setq pre "^\\*+[ \t]+\\(?:\\sw+\\)?[ \t]*"
@@ -9408,13 +9440,7 @@ in all files.  If AVOID-POS is given, ignore matches near that position."
 		  )
 		  )
 	      (goto-char (match-beginning 1))
 	      (goto-char (match-beginning 1))
 	    (goto-char pos)
 	    (goto-char pos)
-	    (error "No match")))))
-     (t
-      ;; Normal string-search
-      (goto-char (point-min))
-      (if (search-forward s nil t)
-	  (goto-char (match-beginning 0))
-	(error "No match"))))
+	    (error "No match"))))))
     (and (org-mode-p) (org-show-context 'link-search))
     (and (org-mode-p) (org-show-context 'link-search))
     type))
     type))