* this post in Org format: [https://github.com/tonyaldon/posts](https://github.com/tonyaldon/posts)
* email me: [aldon.tony@gmail.com](mailto:aldon.tony@gmail.com)
* message me: [https://twitter.com/tonyaldon](https://twitter.com/tonyaldon)
───────────────────────────────────────────
Hey Emacser,
I hope you are doing well :)
In [this post (2022-04-04)](https://www.reddit.com/r/emacs/comments/tw3fpu/search_options_in_file_links_link_abbreviations/) we talked about search options in file links, link abbreviations and the implementation of the command `org-open-at-point`.
My goal was to walk the path from pressing `RET` (if you have `org-return-follows-link` set to `t`) or `C-c C-o` on top of the abbreviated org link
[[emacs:lisp/simple.el::(defun next-error (&optional]]
to the file `lisp/simple.el` in the Emacs source code (cloned locally) with the point at the beginning of the function `next-error`.
We have seen that this path follows the following "call stack":
org-open-at-point
│
└> org-link-open
│
└> org-link-open-as-file
│
└> org-open-file
│
└> org-link-search
As the post has become bigger than I expected, I decided not to talk about the last call to the function `org-link-search`, despite my desire to do so.
Some time later, I decided to give it a chance. I looked `org-link-search` in the eyes and I saw that I won't be able to give a clear explanation without talking a least in one post about the catch/throw pattern in Elisp. So, I postponed it and wrote the post [A tour of the catch/throw pattern in the Emacs source code (2022-04-13)](https://www.reddit.com/r/emacs/comments/u2u229/dont_explain_show_me_examples_a_tour_of_the/) in which we discuss the catch/throw pattern.
And today I'm trying to give a clear explanation of its implementation and I realized that the way to do it is not by showing bits of code and talking about those bits.
There is too much details to bring some value doing it like this (at least, I still haven't figured out how to do it that way for that function).
So let's try another approach that may give you some "tools" to examine its implementation if you are interested.
First, we look at a "full" example that features org links that, in the end, use `org-link-search` to make the jump when we call `org-open-at-point` on them.
Next, we give some practical examples demonstrating part of the Elisp API dealing with strings and regexps functions that are used in `org-link-search`.
With that done, just between us, let's say we are done with `org-link-search` function and we've completed the walk inside `org-open-at-point` function :)
## Full example of org links that use org-link-search when followed
The following example features org links that, in the end, use `org-link-search` to make the jump when we call `org-open-at-point` on them.
Notes on the example:
For three of those links to work you need to cloned the org-mode repository under the directory `/tmp/org-mode/`. You can do this by running the following command:
cd /tmp/ && git clone git://git.sv.gnu.org/emacs.git
The example is written inside a unique source block (org-mode), so you have several options to take advantage of it:
1. if you are reading this post on Reddit, copy/paste the block in an org-mode buffer and start playing with it,
2. if you are reading this post from inside Emacs (using this document [https://github.com/tonyaldon/posts](https://github.com/tonyaldon/posts)) with the point inside the source block you can hit `C-c '` (`org-edit-special` by default) and start playing with the example in a org-mode buffer.
For convenience and to make the following example almost selfcontained, we use for instance the link `[[#custom-id-2]]` that is of type `custom-id` instead of demonstrating the use of `org-link-search` with a link like this `[[/path-to-file.org::#custom-id-2]]` which is of type `file` with the search option component equal to `#custom-id-2`.
But it doesn't matter much because, in the end, both are treated the same way by the same function `org-link-search`, and the exact last call in both cases is `(org-link-search "#custom-id-2")`.
In the example, the links are presented in the same order as they are treated in the `cond` special form in the body of `org-link-search`. If you finally decide to look at the implementation of `org-link-search`, you'll be able to follow the "flow" in the implementation following the "flow" of the example or vice-versa, maybe side by side using two different buffers (by the way this is how I built the example, side by side :-)).
Lastly, the info node related to this example are the following:
* `M-x eval-expression RET (info "(org)Internal links")`,
* `M-x eval-expression RET (info "(org)Search Options")`,
* `M-x eval-expression RET (info "(org)Literal Examples")`,
* `M-x eval-expression RET (info "(org)Org Syntax")`.
Here is the example:
* headline 1
With [[#custom-id-2]] link we jump (with ~org-open-at-point~) in this
document to the heading with the ~:CUSTOM_ID~ property equal to
~custom-id-2~.
With [[(coderef)]] link we jump (with ~org-open-at-point~) to ~(ref:coderef)~ in the source block below:
#+NAME: a named source block
#+BEGIN_SRC emacs-lisp
(let ((x 2)) (coderef)
(1+ x))
#+END_SRC
Let assume we have the org repository cloned under the directory
~/tmp/org-mode/~.
With [[/tmp/org-mode/lisp/ol.el::/org-link-search/]] we jump (with
~org-open-at-point~) to an Occur buffer like this one
#+BEGIN_SRC text
6 matches for "org-link-search" in buffer: ol.el
340:(defcustom org-link-search-must-match-exact-headline 'query-to-create
1093: (org-link-search
1132:(defun org-link-search (s &optional avoid-pos stealth)
1245: ;; `org-link-search-must-match-exact-headline'.
1247: (eq org-link-search-must-match-exact-headline 'query-to-create)
1257: (or starred org-link-search-must-match-exact-headline))
#+END_SRC
that matches the occurences of ~org-link-search~ (the text surrounded by
two slashes ~/~ after the two colons ~::~ in the previous link) in the
file ~/tmp/org-mode/lisp/ol.el~.
With [[target]] link we jump (with ~org-open-at-point~) to the target
~<<target>>~ that is located in the first paragraph of the section
~headline 2~.
With [[a named source block]] link we jump (with ~org-open-at-point~) to the
previous source block that is named ~a named source block~ via the
statment ~#+NAME: a named source block~ (because there is no target ~<<a
named source block>>~ in the document).
With [[headline 2]] link we jump (with ~org-open-at-point~) to the next
headline ~headline 2~, whatever the value of the variable
~org-link-search-must-match-exact-headline~, because:
1) this headline exists,
2) there is no target ~<<headline 2>>~ in the document,
3) there is no named block, named paragagraph, etc. (see Affiliated
Keywords in the org syntax) named with the statment ~#+NAME headline
2~.
#+NAME: headline 3
This paragagraph named ~headline 3~ contains a target <<headline 3>>, so
we can't use the link ~[[headline 3]]~ to jump to the existing headline
~headline 3~. But, we can use the following link [[* headline 3]] (starting
with a star ~*~) to jump to the existing headline ~headline 3~. And this
work whatever the value of the variable
~org-link-search-must-match-exact-headline~.
When ~org-link-search-must-match-exact-headline~ is set to
~query-to-create~ (which is the default value)
#+BEGIN_SRC emacs-lisp
(setq org-link-search-must-match-exact-headline 'query-to-create)
#+END_SRC
calling ~org-open-at-point~ on [[headline 4]] link offers to create a new
headline ~headline 4~ at the end of this org document. If we choose to
add it we will jump to that new headline and if not nothing happens
and we don't move.
When ~org-link-search-must-match-exact-headline~ is set to something
other than ~query-to-create~, for instance ~t~ or ~nil~ like this:
#+BEGIN_SRC emacs-lisp
(setq org-link-search-must-match-exact-headline nil)
;; or
(setq org-link-search-must-match-exact-headline t)
#+END_SRC
calling ~org-open-at-point~ on the link [[* headline 5]] (starting with a
star ~*~) that points to a non existing headline raises the following
error:
: No match for fuzzy expression: * headline 5
When ~org-link-search-must-match-exact-headline~ is set ~t~
#+BEGIN_SRC emacs-lisp
(setq org-link-search-must-match-exact-headline t)
#+END_SRC
calling ~org-open-at-point~ on the link [[headline 5]] (and there is no
existing headline ~headline 5~, there is no target ~<<headline 5>>~ and
there is no named element ~headline 5~ in the document) raises the
following error:
: No match for fuzzy expression: * headline 5
When ~org-link-search-must-match-exact-headline~ is set ~nil~
#+BEGIN_SRC emacs-lisp
(setq org-link-search-must-match-exact-headline nil)
#+END_SRC
calling ~org-open-at-point~ on the link [[headline 5]] (and there is no
existing headline ~headline 5~, there is no target ~<<headline 5>>~ and
there is no named element ~headline 5~ in the document) jumps to first
occurence of ~headline 5~ in the current document.
We still assume we have the org repository cloned under the directory
~/tmp/org-mode/~.
With [[/tmp/org-mode/lisp/ol.el::org-link-search]] we jump (with
~org-open-at-point~) to the first occurence of ~org-link-search~ in the
file ~/tmp/org-mode/lisp/ol.el~ which happens to be on the variable
~org-link-search-must-match-exact-headline~:
#+BEGIN_SRC emacs-lisp
(defcustom org-link-search-must-match-exact-headline 'query-to-create
...)
#+END_SRC
Note that as the file ~/tmp/org-mode/lisp/ol.el~ is not "open" in
org-mode, ~org-link-search~ does a fuzzy text search and doesn't look
for target, named elements or headlines.
If the search option (or internal links) we've used doesn't "match"
one of the previous search, ~org-link-search~ raises an error.
We still assume we have the org repository cloned under the directory
~/tmp/org-mode/~.
With [[/tmp/org-mode/lisp/ol.el::5]] we jump (with ~org-open-at-point~) to
jump to the line ~5~ in the file ~/tmp/org-mode/lisp/ol.el~.
Although this link is of type ~file~ with its search option equal to ~5~,
the "jump" isn't done by ~org-link-search~ but by ~org-goto-line~ in the
function ~org-open-file~.
* headline 2
:PROPERTIES:
:CUSTOM_ID: custom-id-2
:END:
I'm the <<target>>!
* headline 3
# Elisp API dealing with strings and regexps
Remember that when we are working with Elisp we can obtain information about any symbols with the command `describe-symbol` bound by default to `C-h o`.
In `org-link-search`, the variable `case-fold-search` is set `t` which means that searches and matches should ignore case:
(let ((case-fold-search t))
(string-match "FOO" "foo")) ; 0
(let ((case-fold-search nil))
(string-match "FOO" "foo")) ; nil
In `org-link-search`, the search string `s` provided can contain newlines followed by any numbers of spaces or tabs. Those patterns are replaced by one space. This is done using the function `replace-regexp-in-string` like this:
(let ((s "search \n \t\t option"))
(replace-regexp-in-string "\n[ \t]*" " " s))
;; "search option"
The search option string `s` given to `org-link-search` can start:
1. with a star `*` when we search specifically a headline (for instance in the link `[[* headline 3]]` used in the above example) or,
2. with a hash `#` when we search a custom id (for instance in the link `[[#custom-id-2]]` used in the above example)
The function `string-to-char` returns the first character of a string and we can use like this:
(string-to-char "*foo") ; 42
?* ; 42
(string-to-char "#bar") ; 35
?# ; 35
(eq (string-to-char "* headline 3") ?*) ; t
(eq (string-to-char "#custom-id-2") ?#) ; t
In `org-link-search`, the searches are not done directly against its given string `s` but against different regexps depending on the context built from `s`.
The function `split-string` is used to split one of the string into substrings bounded by whitespace like thins:
(split-string "foo bar baz") ; ("foo" "bar" "baz")
When the given string `s` starts with a star `*`, the star is removed using the function `substring` like this:
(substring "*foo" 1) ; "foo"
The searches are done using the function `re-search-forward` that searches in the current buffer for regular expression. So, we have to be careful when we give it a string to search for, that string must be a regexp.
We can ensure that the string we give to `re-search-forward` is a regexp using the function `regexp-quote` that returns a regexp string which matches exactly the string we gave it and this is how `org-link-search` does it.
For instance, the characters `.`, `?`, `+` and `*` are "special" in regexps, so if we want to match them in a regexp we must escape them and we can do it using the function `regexp-quote` like this:
(regexp-quote "foo.bar") ; "foo\\.bar"
(regexp-quote "foo+bar") ; "foo\\+bar"
(regexp-quote "foo?bar") ; "foo\\?bar"
(regexp-quote "foo*bar") ; "foo\\*bar"
We can go on and on but we won't, I think that's enough to get started :)
WE ARE DONE !!!
# Acknowledgments
I take the opportunity of this post to thank Ihor Radchenko for his work on org-mode.
In addition to his contributions he always answers quickly in [https://orgmode.org/worg/org-mailing-list.html](https://orgmode.org/worg/org-mailing-list.html).
Thank you Ihor Radchenko.