Function: org-link-search

org-link-search is a byte-compiled function defined in ol.el.gz.

Signature

(org-link-search S &optional AVOID-POS STEALTH NEW-HEADING-CONTAINER)

Documentation

Search for a search string S in the accessible part of the buffer.

If S starts with "#", it triggers a custom ID search.

If S is enclosed within parenthesis, it initiates a coderef search.

If S is surrounded by forward slashes, it is interpreted as a regular expression. In Org mode files, this will create an org-occur sparse tree. In ordinary files, occur will be used to list matches. If the current buffer is in dired-mode, grep will be used to search in all files.

When AVOID-POS is given, ignore matches near that position.

When optional argument STEALTH is non-nil, do not modify visibility around point, thus ignoring org-show-context-detail variable.

When optional argument NEW-HEADING-CONTAINER is an element, any new heading that is created (see org-link-search-must-match-exact-headline) will be added as a subheading of NEW-HEADING-CONTAINER. Otherwise, new headings are created at level 1 at the end of the accessible part of the buffer.

Search is case-insensitive and ignores white spaces. Return type of matched result, which is either dedicated or fuzzy. Search respects buffer narrowing.

Source Code

;; Defined in /usr/src/emacs/lisp/org/ol.el.gz
(defun org-link-search (s &optional avoid-pos stealth new-heading-container)
  "Search for a search string S in the accessible part of the buffer.

If S starts with \"#\", it triggers a custom ID search.

If S is enclosed within parenthesis, it initiates a coderef
search.

If S is surrounded by forward slashes, it is interpreted as
a regular expression.  In Org mode files, this will create an
`org-occur' sparse tree.  In ordinary files, `occur' will be used
to list matches.  If the current buffer is in `dired-mode', grep
will be used to search in all files.

When AVOID-POS is given, ignore matches near that position.

When optional argument STEALTH is non-nil, do not modify
visibility around point, thus ignoring `org-show-context-detail'
variable.

When optional argument NEW-HEADING-CONTAINER is an element, any
new heading that is created (see
`org-link-search-must-match-exact-headline') will be added as a
subheading of NEW-HEADING-CONTAINER.  Otherwise, new headings are
created at level 1 at the end of the accessible part of the
buffer.

Search is case-insensitive and ignores white spaces.  Return type
of matched result, which is either `dedicated' or `fuzzy'.  Search
respects buffer narrowing."
  (unless (org-string-nw-p s) (error "Invalid search string \"%s\"" s))
  (let* ((case-fold-search t)
	 (origin (point))
	 (normalized (replace-regexp-in-string "\n[ \t]*" " " s))
	 (starred (eq (string-to-char normalized) ?*))
	 (words (split-string (if starred (substring s 1) s)))
	 (s-multi-re (mapconcat #'regexp-quote words "\\(?:[ \t\n]+\\)"))
	 (s-single-re (mapconcat #'regexp-quote words "[ \t]+"))
	 type)
    (cond
     ;; Check if there are any special search functions.
     ((run-hook-with-args-until-success 'org-execute-file-search-functions s))
     ((eq (string-to-char s) ?#)
      ;; Look for a custom ID S if S starts with "#".
      (let* ((id (substring normalized 1))
	     (match (org-find-property "CUSTOM_ID" id)))
	(if match (progn (goto-char match) (setf type 'dedicated))
	  (error "No match for custom ID: %s" id))))
     ((string-match "\\`(\\(.*\\))\\'" normalized)
      ;; Look for coderef targets if S is enclosed within parenthesis.
      (let ((coderef (match-string-no-properties 1 normalized))
	    (re (substring s-single-re 1 -1)))
	(goto-char (point-min))
	(catch :coderef-match
	  (while (re-search-forward re nil t)
	    (let ((element (org-element-at-point)))
	      (when (and (org-element-type-p element '(example-block src-block))
			 (org-match-line
			  (concat ".*?" (org-src-coderef-regexp
					 (org-src-coderef-format element)
					 coderef))))
		(setq type 'dedicated)
		(goto-char (match-beginning 2))
		(throw :coderef-match nil))))
	  (goto-char origin)
	  (error "No match for coderef: %s" coderef))))
     ((string-match "\\`/\\(.*\\)/\\'" normalized)
      ;; Look for a regular expression.
      (funcall (if (derived-mode-p 'org-mode) #'org-occur #'org-do-occur)
	       (match-string 1 s)))
     ;; From here, we handle fuzzy links.
     ;;
     ;; Look for targets, only if not in a headline search.
     ((and (not starred)
	   (let ((target (format "<<%s>>" s-multi-re)))
	     (catch :target-match
	       (goto-char (point-min))
	       (while (re-search-forward target nil t)
		 (backward-char)
		 (let ((context (org-element-context)))
		   (when (org-element-type-p context 'target)
		     (setq type 'dedicated)
		     (goto-char (org-element-begin context))
		     (throw :target-match t))))
	       nil))))
     ;; Look for elements named after S, only if not in a headline
     ;; search.
     ((and (not starred)
	   (let ((name (format "^[ \t]*#\\+NAME: +%s[ \t]*$" s-single-re)))
	     (catch :name-match
	       (goto-char (point-min))
	       (while (re-search-forward name nil t)
		 (let* ((element (org-element-at-point))
			(name (org-element-property :name element)))
		   (when (and name (equal (mapcar #'upcase words) (mapcar #'upcase (split-string name))))
		     (setq type 'dedicated)
		     (forward-line 0)
		     (throw :name-match t))))
	       nil))))
     ;; Regular text search.  Prefer headlines in Org mode buffers.
     ;; Ignore COMMENT keyword, TODO keywords, priority cookies,
     ;; statistics cookies and tags.
     ((and (derived-mode-p 'org-mode)
	   (let ((title-re
		  (format "%s.*\\(?:%s[ \t]\\)?.*%s"
			  org-outline-regexp-bol
			  org-comment-string
			  (mapconcat #'regexp-quote words ".+"))))
	     (goto-char (point-min))
	     (catch :found
	       (while (re-search-forward title-re nil t)
		 (when (equal (mapcar #'upcase words)
                              (mapcar #'upcase
                                      (split-string
                                       (org-link--normalize-string
                                        (org-get-heading t t t t)))))
		   (throw :found t)))
	       nil)))
      (forward-line 0)
      (setq type 'dedicated))
     ;; Offer to create non-existent headline depending on
     ;; `org-link-search-must-match-exact-headline'.
     ((and (derived-mode-p 'org-mode)
	   (eq org-link-search-must-match-exact-headline 'query-to-create)
	   (yes-or-no-p "No match - create this as a new heading? "))
      (let* ((container-ok (and new-heading-container
                                (org-element-type-p new-heading-container '(headline))))
             (new-heading-position (if container-ok
                                       (- (org-element-end new-heading-container) 1)
                                     (point-max)))
             (new-heading-level (if container-ok
                                    (+ 1 (org-element-property :level new-heading-container))
                                  1)))
        ;; Need to widen when target is outside accessible portion of
        ;; buffer, since the we want the user to end up there.
        (unless (and (<= (point-min) new-heading-position)
                     (>= (point-max) new-heading-position))
          (widen))
        (goto-char new-heading-position)
        (unless (bolp) (newline))
        (org-insert-heading nil t new-heading-level)
        (insert (if starred (substring s 1) s) "\n")
        (forward-line -1)))
     ;; Only headlines are looked after.  No need to process
     ;; further: throw an error.
     ((and (derived-mode-p 'org-mode)
	   (or starred org-link-search-must-match-exact-headline))
      (goto-char origin)
      (error "No match for fuzzy expression: %s" normalized))
     ;; Regular text search.
     ((catch :fuzzy-match
	(goto-char (point-min))
	(while (re-search-forward s-multi-re nil t)
	  ;; Skip match if it contains AVOID-POS or it is included in
	  ;; a link with a description but outside the description.
	  (unless (or (and avoid-pos
			   (<= (match-beginning 0) avoid-pos)
			   (> (match-end 0) avoid-pos))
		      (and (save-match-data
			     (org-in-regexp org-link-bracket-re))
			   (match-beginning 3)
			   (or (> (match-beginning 3) (point))
			       (<= (match-end 3) (point)))
			   (org-element-lineage
			    (save-match-data (org-element-context))
			    'link t)))
	    (goto-char (match-beginning 0))
	    (setq type 'fuzzy)
	    (throw :fuzzy-match t)))
	nil))
     ;; All failed.  Throw an error.
     (t (goto-char origin)
	(error "No match for fuzzy expression: %s" normalized)))
    ;; Disclose surroundings of match, if appropriate.
    (when (and (derived-mode-p 'org-mode) (not stealth))
      (org-fold-show-context 'link-search))
    type))