From a84c8a2cba8c510acfa0c14487f6c993f664a406 Mon Sep 17 00:00:00 2001 From: Carsten Dominik Date: Fri, 6 Aug 2010 08:34:33 +0200 Subject: [PATCH] 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. --- doc/org.texi | 24 +++++++----------------- lisp/org-exp.el | 25 +++++++++++++------------ lisp/org.el | 44 +++++++++++++++++++++++++++++++++++--------- 3 files changed, 55 insertions(+), 38 deletions(-) diff --git a/doc/org.texi b/doc/org.texi index 628978e11..301d46ac6 100644 --- a/doc/org.texi +++ b/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 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 return to the previous position with @kbd{C-c &}. Using this command diff --git a/lisp/org-exp.el b/lisp/org-exp.el index 6fc5109fb..36f820254 100644 --- a/lisp/org-exp.el +++ b/lisp/org-exp.el @@ -1289,18 +1289,19 @@ the current file." (string-match "^\\." link)) nil) (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 (set-match-data md) (goto-char (match-beginning 1)) diff --git a/lisp/org.el b/lisp/org.el index d2c1fdf47..09fa36098 100644 --- a/lisp/org.el +++ b/lisp/org.el @@ -1439,6 +1439,17 @@ Changing this requires a restart of Emacs to work correctly." :group 'org-link-follow :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 '((vm . vm-visit-folder-other-frame) (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)") +(defvar org-link-search-inhibit-query nil) ;; dynamically scoped (defun org-link-search (s &optional type avoid-pos) "Search for a link search option. 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) words re0 re1 re2 re3 re4_ re4 re5 re2a re2a_ reall) (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)) ;; Now try the builtin stuff ((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) ;; (grep (concat "grep -n -e '" (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 - ;; A normal search strings + ;; A normal search string (when (equal (string-to-char s) ?*) ;; Anchor on headlines, post may include tags. (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 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)) type))