# -*- coding: utf-8 -*- #+TITLE: Posts #+AUTHOR: Tony aldon #+DATE: <2022-05-07 Sat 08:25> # This document is meant to be read inside Emacs. To make the internal # local links to Emacs and Org-mode repositories work correctly, # make sure to clone those repositories under this directory: # # git clone git://git.sv.gnu.org/emacs.git # git clone https://git.savannah.gnu.org/git/emacs/org-mode.git #+LINK: emacs ./emacs/ #+LINK: org-mode ./org-mode/ * [2022-05-11 Wed] org-mode links in 2022: implementation, packages, articles and videos | THIS IS EMACS :posts: :PROPERTIES: :CUSTOM_ID: /2022-05-11-org-mode-links-in-2022-implementation-packages-articles-and-videos-this-is-emacs/ :REDDIT_POST: https://www.reddit.com/r/emacs/comments/une2d8/orgmode_links_in_2022_implementation_packages/ :COMMIT_ORG_MODE: 407104459b86a40e03f5f1f323d28ad935721ef3 :END: Hey Emacsers, I hope you are doing well. In the last few weeks, we have dedicated 6 posts to org-mode links. Our interest was not only to show the specific features of org-mode links but also to see how they are implemented. In this post, the last one about org-mode links, we list ALL the videos, articles and packages related to org-mode links. It is unrealistic to think that it is possible to do so. But, we can try. And If you think about any video, article or package that should be part of that list, please do share it in the comment section below. Let's go! ** Implementation In the post [[#/2022-04-04-search-options-link-abbreviations-and-org-open-at-point/][Search options in file links | link abbreviations | COME WITH ME on this JOURNEY into the heart of the command org-open-at-point]], we looked at the implementation of ~org-open-at-point~. To do so, we also looked at the functions: ~org-element-context~, ~org-element--object-lex~, ~org-element-link-parser~, ~org-link-expand-abbrev~, ~org-link-open~, ~org-link-open-as-file~ and ~org-open-file~. In the post [[#/2022-04-09-org-links-in-property-drawers-are-not-links/][Did you know that Org links in property drawers are not links?]], we looked at the functions ~org-element-link-parser~, ~org-link-make-regexps~, ~org-open-at-point~ and also the variable ~org-link-any-re~. In the post [[#/2022-04-27-full-example-of-org-mode-links-internal-links-and-search-options/][FULL example of org-mode links: internal links and search options]], instead of "explaining" the implementation of the function ~org-link-search~ we gave an full example that demonstrates its behavior and provided some Elisp examples of the functions and variables used by ~org-link-search~: ~case-fold-search~, ~replace-regexp-in-string~, ~string-to-char~, ~split-string~, ~substring~ and ~regexp-quote~. In the post [[#/2022-04-29-link-to-a-git-commit-from-org-mode-using-magit-this-is-emacs/][Link to a git commit from Org mode using Magit | THIS IS EMACS]], we looked at the functions ~org-link-open~, ~org-link-get-parameter~ and ~org-link--open-elisp~. In the post [[#/2022-05-04-org-mode-links-everywhere-not-only-in-org-mode-buffers-this-is-emacs/][org-mode links everywhere, not only in org-mode buffers | THIS IS EMACS]], we looked at ~org-open-at-point-global~ and ~org-link-open-from-string~. In the post [[#/2022-05-06-org-store-link-powerful-and-flexible-this-is-emacs/][org-store-link... powerful and flexible | THIS IS EMACS]], we looked at ~org-store-link~ and ~org-link--store-help~. ** Videos *** Zaiste Programming In his series [[https://www.youtube.com/watch?v=rCMh7srOqvw&list=PLhXZp00uXBk4np17N39WvB80zgxlZfVwj&ab_channel=ZaisteProgramming][DoomCasts: Emacs Doom Screencasts]] Zaiste dedicates the episode 10, 11 and 12 to org-mode links. In [[https://www.youtube.com/watch?v=BRqjaN4-gGQ&ab_channel=ZaisteProgramming][Emacs Doom E10: Org Mode - Links, Hyperlinks and more]], the focus is on internal links to headline, file links, links to url, elisp link type (use for instance to open org agenda with ~org-agenda~), ~shell~ link type (to list files in a directory) and ~org-insert-link~. In [[https://www.youtube.com/watch?v=Febe4lUK5G4&ab_channel=ZaisteProgramming][Emacs Doom E11: Org Mode - Custom Link Types]] Zaist describes how to add a new link type using ~org-add-link-type~. Note that ~org-add-link-type~ is obsolete since org-mode ~9.0~ but still supported. Since ~9.0~ we use ~org-link-set-parameters~ to add the links: #+BEGIN_SRC emacs-lisp ;; from the video (defun make-youtube-link (youtube_id) (browse-url (concat "https://www.youtube.com/embed/" youtube_id))) (org-add-link-type "yt" #'make-youtube-link) ; obsolete since 9.0 ;; since 9.0 (org-link-set-parameters "yt" :follow #'make-youtube-link) #+END_SRC This allows us to open the following link ~[[yt:Febe4lUK5G4]]~ in the browser with ~org-open-at-point~ (bound to ~C-c C-o~ by default). In [[https://www.youtube.com/watch?v=aU1EV8gzZb8&ab_channel=ZaisteProgramming][Emacs Doom E12: Org Mode - Linking to words & Bookmarks]] the focus is on using command ~org-store-link~ on an org-mode headline and the behavior of ~org-open-at-point~ when we modify the variable ~org-link-search-must-match-exact-headline~. *** Rainer König In his series [[https://www.youtube.com/watch?v=sQS06Qjnkcc&list=PLVtKhBrRV_ZkPnBtt_TD1Cs9PJlU0IIdE&ab_channel=RainerK%C3%B6nig][OrgMode tutorial]] Rainer König dedicates the episode ~E05S01~ and ~E05S02~ to org-mode links. In [[https://www.youtube.com/watch?v=eoIfLS4zMa8&ab_channel=RainerK%C3%B6nig][OrgMode E05S01: Linking (internal)]] the focus is on ~org-insert-link~, link to named target (~#+NAME:~), link to headline with ~CUSTOM_ID~ property, radio targets (enclosed by triple angular brackets like this ~<<>>~). At some point he says that we are responsible for the ~CUSTOM_ID~ to make them unique through out the document which is true. We take the opportunity to mention the command ~org-lint~ that checks the current Org buffer for syntax mistakes. This command reports if there are duplicated ~CUSTOM_ID~ property. For instance, if we call the command ~org-lint~ in the following Org buffer (with two ~CUSTOM_ID~ with the value ~foo~): #+BEGIN_SRC org ,* headline 1 :PROPERTIES: :CUSTOM_ID: foo :END: ,* headline 2 :PROPERTIES: :CUSTOM_ID: foo :END: #+END_SRC the buffer ~*Org Lint*~ pops up with the following content: #+BEGIN_SRC text 3 nil Duplicate CUSTOM_ID property "foo" 7 nil Duplicate CUSTOM_ID property "foo" #+END_SRC In [[https://www.youtube.com/watch?v=0TS3pTNGFIA&ab_channel=RainerK%C3%B6nig][OrgMode E05S02: Linking (external)]] Rainer talks about links of type file, url, bbdb. He shows how to use ~id~ links (~org-id-get-create~ and some custom functions). He also demonstrates how ~id~ links does not break when we archive a subtree with the command ~org-archive-subtree-default~ (bound to ~C-c C-x C-a~ by default). *** John Kitchin In [[https://www.youtube.com/watch?v=5haX95nk02E&ab_channel=JohnKitchin][New link features in org-mode v9.0]] John Kitchin demonstrates how to defines new link types using the function ~org-link-set-parameters~: 1) the ~:face~ property is used to make links green, 2) the ~:display~ property is used to show "all" the link, 3) the ~:help-echo~ is used to produce a dynamic tooltip for some links, 4) the ~:follow~ property is used to define a function to open ~file~ type link that let you choose the command to use to open a file with completion using ~helm~, 5) the ~:keymap~ property is used to define a keymap only active on some links, 6) the properties ~:complete~, ~:store~ and ~:activate-func~ are also demonstrated. This video is completed with this article [[https://kitchingroup.cheme.cmu.edu/blog/2016/11/04/New-link-features-in-org-9/][New link features in org 9]]. ** Articles In [[https://kitchingroup.cheme.cmu.edu/blog/2016/11/04/New-link-features-in-org-9/][New link features in org 9]] John Kitchin demonstrates how to defines new link types using the function ~org-link-set-parameters~. This article is completed with this videos [[https://www.youtube.com/watch?v=5haX95nk02E&ab_channel=JohnKitchin][New link features in org-mode v9.0]]. Kaushal Modi wrote two articles about org-mode links: [[https://scripter.co/linking-and-exporting-org-info-links/][Linking and Exporting Org "info:" links]] with the following abstract: #+BEGIN_SRC text Journey of Org links from copying (storing) them, inserting them in Org documents and exporting them. The focus of this post is on the info: type Org links, but the same concept would apply to any Org link type. #+END_SRC and [[https://scripter.co/improving-ox-hugo-exported-org-info-links/][Improving ox-hugo exported Org "info:" links]] with the following abstract: #+BEGIN_SRC text In my previous post, I talked about how info: Org link export support got added to ox-hugo. This post is about making those exported links a tiny 🤏 bit better. #+END_SRC In [[https://www.eigenbahn.com/2021/11/14/custom-org-link-abbrevs][Custom Org-mode link abbrevs]] Eigenbahn "blends" link abbreviations and link types to have fast copy/paste experience with link abbreviations. He uses ~org-link-set-parameters~ and advices the command ~org-yank~ to do so. In [[https://localauthor.github.io/posts/aw-select.html][Emacs: With ace-window and link-hint, open links exactly where you want them]] Grant Rosson describes, as said in the abstract: #+BEGIN_SRC text how the Emacs package link-hint can be combined with ace-window to allow you to select, on-the-fly, which window a link will open in—instead of letting fate, or custom, or Emacs decide for you. #+END_SRC ** Packages *** org-contrib The repository https://git.sr.ht/~bzg/org-contrib contains many add-ons to Org. Doing a recursive grep inside the repository for the function ~org-link-set-parameters~ we can find the following link types. **** ol-mew.el This file implements links to Mew messages from within Org-mode. It defines the link type [[https://git.sr.ht/~bzg/org-contrib/tree/15b03d5090cd6ef46bdfdd14a3f9e20c3dab60da/lisp/ol-mew.el#L152][mew]]. **** ol-vm.el This file implements links to VM messages and folders from within Org-mode. It defines the link types [[https://git.sr.ht/~bzg/org-contrib/tree/15b03d5090cd6ef46bdfdd14a3f9e20c3dab60da/lisp/ol-vm.el#L59][vm]] and [[https://git.sr.ht/~bzg/org-contrib/tree/15b03d5090cd6ef46bdfdd14a3f9e20c3dab60da/lisp/ol-vm.el#L60][vm-imap]]. **** org-mairix.el Support for hooking mairix search into Org for different MUAs. It defines the link type [[https://git.sr.ht/~bzg/org-contrib/tree/15b03d5090cd6ef46bdfdd14a3f9e20c3dab60da/lisp/org-mairix.el#L85][mairix]]. **** ol-notmuch.el Links to notmuch messages. It defines the link types [[https://git.sr.ht/~bzg/org-contrib/tree/15b03d5090cd6ef46bdfdd14a3f9e20c3dab60da/lisp/ol-notmuch.el#L70][notmuch]], [[https://git.sr.ht/~bzg/org-contrib/tree/15b03d5090cd6ef46bdfdd14a3f9e20c3dab60da/lisp/ol-notmuch.el#L103][notmuch-search]] and [[https://git.sr.ht/~bzg/org-contrib/tree/15b03d5090cd6ef46bdfdd14a3f9e20c3dab60da/lisp/ol-notmuch.el#L129][notmuch-tree]]. **** ol-elisp-symbol.el Links to Emacs-lisp symbols. It defines the link type [[https://git.sr.ht/~bzg/org-contrib/tree/15b03d5090cd6ef46bdfdd14a3f9e20c3dab60da/lisp/ol-elisp-symbol.el#L81][elisp-symbol]]. **** org-mac-link.el Insert org-mode links to items selected in various Mac apps. It defines the link types [[https://git.sr.ht/~bzg/org-contrib/tree/15b03d5090cd6ef46bdfdd14a3f9e20c3dab60da/lisp/org-mac-link.el#L498][x-together-item]], [[https://git.sr.ht/~bzg/org-contrib/tree/15b03d5090cd6ef46bdfdd14a3f9e20c3dab60da/lisp/org-mac-link.el#L557][addressbook]], [[https://git.sr.ht/~bzg/org-contrib/tree/15b03d5090cd6ef46bdfdd14a3f9e20c3dab60da/lisp/org-mac-link.el#L592][skim]], [[https://git.sr.ht/~bzg/org-contrib/tree/15b03d5090cd6ef46bdfdd14a3f9e20c3dab60da/lisp/org-mac-link.el#L651][acrobat]], [[https://git.sr.ht/~bzg/org-contrib/tree/15b03d5090cd6ef46bdfdd14a3f9e20c3dab60da/lisp/org-mac-link.el#L701][mac-outlook]], [[https://git.sr.ht/~bzg/org-contrib/tree/15b03d5090cd6ef46bdfdd14a3f9e20c3dab60da/lisp/org-mac-link.el#L813][mac-evernote]], [[https://git.sr.ht/~bzg/org-contrib/tree/15b03d5090cd6ef46bdfdd14a3f9e20c3dab60da/lisp/org-mac-link.el#L874][x-devonthink-item]] and [[https://git.sr.ht/~bzg/org-contrib/tree/15b03d5090cd6ef46bdfdd14a3f9e20c3dab60da/lisp/org-mac-link.el#L922][message]]. **** ol-git-link.el Links to specific file version. It defines the link types [[https://git.sr.ht/~bzg/org-contrib/tree/15b03d5090cd6ef46bdfdd14a3f9e20c3dab60da/lisp/ol-git-link.el#L74][gitbare]] and [[https://git.sr.ht/~bzg/org-contrib/tree/15b03d5090cd6ef46bdfdd14a3f9e20c3dab60da/lisp/ol-git-link.el#L97][git]]. **** ob-smiles.el Org-mode Babel support for SMILES. It defines the link type [[https://git.sr.ht/~bzg/org-contrib/tree/15b03d5090cd6ef46bdfdd14a3f9e20c3dab60da/lisp/ob-smiles.el#L56][molecule]]. **** ol-wl.el Links to Wanderlust messages. It defines the link type [[https://git.sr.ht/~bzg/org-contrib/tree/15b03d5090cd6ef46bdfdd14a3f9e20c3dab60da/lisp/ol-wl.el#L114][wl]]. **** org-contacts.el This file contains the code for managing your contacts into Org-mode. It defines the link types [[https://git.sr.ht/~bzg/org-contrib/tree/15b03d5090cd6ef46bdfdd14a3f9e20c3dab60da/lisp/org-contacts.el#L1120][tel]] and [[https://git.sr.ht/~bzg/org-contrib/tree/15b03d5090cd6ef46bdfdd14a3f9e20c3dab60da/lisp/org-contacts.el#L1160][org-contact]]. **** ol-bookmark.el Links to bookmarks. It defines the link type [[https://git.sr.ht/~bzg/org-contrib/tree/15b03d5090cd6ef46bdfdd14a3f9e20c3dab60da/lisp/ol-bookmark.el#L52][bookmark]]. *** On GitHub By doing a search for the function ~org-link-set-parameters~ (used to define new Org link types) using https://grep.app/ we can find "all" the org link types defined in GitHub public repositories. There are more than 150 of them. I tried some of them, read the source of others. One of these Org link types may meet your needs or you can read their implementations to get inspired to write your own. So here is the list of Org link types I found (the urls point to the definition on GitHub of these links pinned to the last revision while I was writing this post). **** calibredb.el https://github.com/chenyanming/calibredb.el: Yet another [[https://calibre-ebook.com/][calibre]] client for emacs. ~calibredb.el~ defines the link type [[https://github.com/chenyanming/calibredb.el/blob/232fa1cf3af08200af439d1cbb5a131f38286183/calibredb-org.el#L70][calibredb]]. **** deft https://github.com/jrblevin/deft: Deft is an Emacs mode for quickly browsing, filtering, and editing directories of plain text notes, inspired by Notational Velocity. ~deft~ defines the link type [[https://github.com/jrblevin/deft/blob/28be94d89bff2e1c7edef7244d7c5ba0636b1296/deft.el#L1793][deft]]. **** doomemacs https://github.com/doomemacs/doomemacs: Doom is a configuration framework for [GNU Emacs] tailored for Emacs bankruptcy veterans who want less framework in their frameworks, a modicum of stability (and reproducibility) from their package manager, and the performance of a hand rolled config (or better). ~doomemacs~ defines the link types [[https://github.com/doomemacs/doomemacs/blob/8a27eb99bec5a955833b6d23f431c5cc39e91f7f/modules/lang/org/config.el#L579][kbd]], [[https://github.com/doomemacs/doomemacs/blob/8a27eb99bec5a955833b6d23f431c5cc39e91f7f/modules/lang/org/config.el#L584][doom-package]] and [[https://github.com/doomemacs/doomemacs/blob/8a27eb99bec5a955833b6d23f431c5cc39e91f7f/modules/lang/org/config.el#L588][doom-module]]. **** ebdb https://github.com/girzel/ebdb: EBDB is a contact management/addressbook package for Emacs. ~ebdb~ defines the link type [[https://github.com/girzel/ebdb/blob/321caee9be5bc432dbef5ea4250b09dbaf1a0c2b/ebdb-org.el#L65][ebdb]]. **** ebib https://github.com/joostkremers/ebib: Ebib is a BibTeX database manager that runs in GNU Emacs. ~ebib~ defines the link type [[https://github.com/joostkremers/ebib/blob/0e243a78f435038dda31953c5b48cbddf2a89e27/org-ebib.el#L76][ebib]]. **** ejira https://github.com/nyyManni/ejira: JIRA integration to Emacs org-mode ~ejira~ defines the link type [[https://github.com/nyyManni/ejira/blob/1dc2176ec49ec19516e68130552aae34cda4886c/ejira-core.el#L815][jirauser]]. **** el-easydraw https://github.com/misohena/el-easydraw: Emacs Easy Draw is a drawing tool that runs inside Emacs. ~el-easydraw~ defines the link type [[https://github.com/misohena/el-easydraw/blob/701bfaaebca2a0f557de2bdfe65f4464c7837d12/edraw-org.el#L127][edraw]]. **** elfeed https://github.com/skeeto/elfeed: Elfeed is an extensible web feed reader for Emacs, supporting both Atom and RSS. ~elfeed~ defines the link type [[https://github.com/skeeto/elfeed/blob/162d7d545ed41c27967d108c04aa31f5a61c8e16/elfeed-link.el#L75][elfeed]]. **** elpher https://github.com/emacsmirror/elpher: Elpher aims to provide a full-featured combination gopher and gemini client for GNU Emacs. ~elpher~ defines the link types [[https://github.com/emacsmirror/elpher/blob/bf0dd36eb2f5b339c6b561dbe3ee9693565b484b/elpher.el#L2127][elpher]], [[https://github.com/emacsmirror/elpher/blob/bf0dd36eb2f5b339c6b561dbe3ee9693565b484b/elpher.el#L2133][gemini]], [[https://github.com/emacsmirror/elpher/blob/bf0dd36eb2f5b339c6b561dbe3ee9693565b484b/elpher.el#L2138][gopher]] and [[https://github.com/emacsmirror/elpher/blob/bf0dd36eb2f5b339c6b561dbe3ee9693565b484b/elpher.el#L2143][finger]]. **** emacs-application-framework https://github.com/emacs-eaf/emacs-application-framework: The vision of the Emacs Application Framework (EAF) project is, while fully retaining the rich history, culture, and ecosystem of Emacs and Emacs Lisp, to open up completely new doors to the ecosystems of Python, Qt6, and even JavaScript. EAF extends Emacs to the world of modern graphics, but still preserving the extensibility and customizability of Emacs. It will be the key to ultimately Live in Emacs ~emacs-application-framework~ defines the link types ~docview~, ~pdfview~ and ~pdftools~ in the [[https://github.com/emacs-eaf/emacs-application-framework/blob/71f18a6ccda910a6b8da7bb21c1e24be5e05cde7/extension/eaf-org.el#L92][extension/eaf-org.el#L92]] and also [[https://github.com/emacs-eaf/emacs-application-framework/blob/71f18a6ccda910a6b8da7bb21c1e24be5e05cde7/extension/eaf-org.el#L181][eaf]]. **** emacs-document https://github.com/lujun9972/emacs-document: Translate emacs documents to Chinese for convenient reference. ~emacs-document~ defines the link type [[https://github.com/lujun9972/emacs-document/blob/63b37f7cf8166065f3d162080dd74e7ac61ccc27/auto_publish.el#L68][video]]. **** emacs-webkit https://github.com/akirakyle/emacs-webkit: A successor to xwidget-webkit. ~emacs-webkit~ defines the link type [[https://github.com/akirakyle/emacs-webkit/blob/96a4850676b74ffa55b52ff8e9824f7537df6a47/webkit.el#L542][webkit]]. **** fanyi.el https://github.com/condy0919/fanyi.el: fanyi.el is a simple yet powerful multi-dictionaries interface for Emacs. ~fanyi.el~ defines the link type [[https://github.com/condy0919/fanyi.el/blob/b01cb24209d223ae0e7281c279daab87800ee7f4/ol-fanyi.el#L34][fanyi]]. **** holy-books https://github.com/alhassy/holy-books: An Emacs interface to the Quran and the Bible: Org-mode links, tooltips, and Lisp look-ups. ~holy-books~ defines the link types [[https://github.com/alhassy/holy-books/blob/02c2956d36631d3d8c8b4bacdcf0a5cdd1f3136d/holy-books.el#L198][quran]], [[https://github.com/alhassy/holy-books/blob/02c2956d36631d3d8c8b4bacdcf0a5cdd1f3136d/holy-books.el#L237][Quran]], [[https://github.com/alhassy/holy-books/blob/02c2956d36631d3d8c8b4bacdcf0a5cdd1f3136d/holy-books.el#L368][bible]], [[https://github.com/alhassy/holy-books/blob/02c2956d36631d3d8c8b4bacdcf0a5cdd1f3136d/holy-books.el#L408][Bible]] and [[https://github.com/alhassy/holy-books/blob/02c2956d36631d3d8c8b4bacdcf0a5cdd1f3136d/holy-books.el#L81][basmala]]. **** mu https://github.com/djcb/mu: mu is a tool for dealing with e-mail messages stored in the Maildir-format. ~mu~ defines the link type [[https://github.com/djcb/mu/blob/9062b9d67f99220c1543d5515f3efe36c7448a08/mu4e/mu4e-org.el#L143][mu4e]]. **** mu4e-dashboard https://github.com/rougier/mu4e-dashboard: mu4e dashboard provides a new =mu4e= org link type that allows to execute various mu4e queries when clicked. ~mu4e-dashboard~ defines the link type [[https://github.com/rougier/mu4e-dashboard/blob/3e080af2b86d73bc3757c5e55a4676f33ce02b3b/mu4e-dashboard.el#L60][mu]]. **** org-annotate https://github.com/girzel/org-annotate: Provides a new link type for Org that allows you to create annotations on arbitrary chunks of text. ~org-annotate~ defines the link type [[https://github.com/girzel/org-annotate/blob/0297290f1cb1d31b264632e3f4cb4013956b5b94/org-annotate.el#L60][note]]. **** org-brain https://github.com/Kungsgeten/org-brain: org-brain implements a variant of [[https://en.wikipedia.org/wiki/Concept_map][concept mapping]] in Emacs, using [[http://orgmode.org/][org-mode]]. ~org-brain~ defines the link types [[https://github.com/Kungsgeten/org-brain/blob/46ca9f766322cff31279ecdf02251ff24a0e9431/org-brain.el#L3373][brain]], [[https://github.com/Kungsgeten/org-brain/blob/46ca9f766322cff31279ecdf02251ff24a0e9431/org-brain.el#L3378][brain-child]], [[https://github.com/Kungsgeten/org-brain/blob/46ca9f766322cff31279ecdf02251ff24a0e9431/org-brain.el#L3382][brain-parent]], [[https://github.com/Kungsgeten/org-brain/blob/46ca9f766322cff31279ecdf02251ff24a0e9431/org-brain.el#L3386][brain-friend]] and [[https://github.com/Kungsgeten/org-brain/blob/46ca9f766322cff31279ecdf02251ff24a0e9431/org-brain.el#L3416][brainswitch]]. **** org-jira https://github.com/ahungry/org-jira: Use Jira in Emacs org-mode. ~org-jira~ defines the link type [[https://github.com/ahungry/org-jira/blob/c0c0086419b4e68bb45bf609931916d6d8ae48a2/org-jira.el#L2382][jira]]. **** org-krita https://github.com/lepisma/org-krita: Minor mode for working with [[https://krita.org/en/][krita]] notes, sketches etc. in org mode. ~org-krita~ defines the link type [[https://github.com/lepisma/org-krita/blob/7e334951b8de8f2f1c8cbe5068e7dfe6b9e9808f/org-krita.el#L38][krita]]. **** org-music https://github.com/tecosaur/org-music: This adds a new link type, =[[music:artist:song]]=. ~org-music~ defines the link types [[https://github.com/tecosaur/org-music/blob/2257cf850cbdb103495c22932daecd405504084b/org-music.el#L379][music]] and [[https://github.com/tecosaur/org-music/blob/2257cf850cbdb103495c22932daecd405504084b/org-music.el#L384][Music]]. **** org-pdftools https://github.com/fuxialexander/org-pdftools: A custom org link type for pdf-tools. ~org-pdftools~ defines the link type [[https://github.com/fuxialexander/org-pdftools/blob/967f48fb5038bba32915ee9da8dc4e8b10ba3376/org-pdftools.el#L391][pdf]]. **** org-ql https://github.com/alphapapa/org-ql: This package provides a query language for Org files. It offers two syntax styles: Lisp-like sexps and search engine-like keywords. ~org-ql~ defines the link type [[https://github.com/alphapapa/org-ql/blob/46f523d94a376b168176c75bbd0e3e0d00e61170/org-ql-view.el#L587][org-ql-search]]. **** org-ref https://github.com/jkitchin/org-ref: org-ref makes it easy to insert citations, cross-references, indexes and glossaries as hyper-functional links into org files. The links are fontified so you can tell them apart from other links, and each link is clickable to access functions like opening a pdf, notes or url associated with the link. Each link also can be exported to LaTeX to build a PDF. For citations, export to other formats is supported by [[https://github.com/andras-simonyi/citeproc-el][citeproc]] for high quality export to HTML, markdown, plain text, or stand-alone (i.e. independent of a bibtex file) LaTeX. ~org-ref~ defines the link types: - [[https://github.com/jkitchin/org-ref/blob/0d2355d1eb4dcac1095a03d885788a12fe566610/doi-utils.el#L1368][doi]] in ~doi-utils.el~, - [[https://github.com/jkitchin/org-ref/blob/0d2355d1eb4dcac1095a03d885788a12fe566610/nist-webbook.el#L32][nist-wb-name]] in ~nist-webbook.el~, - [[https://github.com/jkitchin/org-ref/blob/0d2355d1eb4dcac1095a03d885788a12fe566610/org-ref-arxiv.el#L51][arxiv]] in ~org-ref-arxiv.el~, - [[https://github.com/jkitchin/org-ref/blob/0d2355d1eb4dcac1095a03d885788a12fe566610/org-ref-bibliography-links.el#L207][bibliography]] [[https://github.com/jkitchin/org-ref/blob/0d2355d1eb4dcac1095a03d885788a12fe566610/org-ref-bibliography-links.el#L217][nobibliography]], [[https://github.com/jkitchin/org-ref/blob/0d2355d1eb4dcac1095a03d885788a12fe566610/org-ref-bibliography-links.el#L237][printbibliography]] and [[https://github.com/jkitchin/org-ref/blob/0d2355d1eb4dcac1095a03d885788a12fe566610/org-ref-bibliography-links.el#L304][bibliographystyle]] in ~org-ref-bibliography-links.el~, - [[https://github.com/jkitchin/org-ref/blob/0d2355d1eb4dcac1095a03d885788a12fe566610/org-ref-label-link.el#L36][label]] in ~org-ref-label-link.el~, - [[https://github.com/jkitchin/org-ref/blob/0d2355d1eb4dcac1095a03d885788a12fe566610/org-ref-misc-links.el#L146][list-of-tables]], [[https://github.com/jkitchin/org-ref/blob/0d2355d1eb4dcac1095a03d885788a12fe566610/org-ref-misc-links.el#L160][index]], [[https://github.com/jkitchin/org-ref/blob/0d2355d1eb4dcac1095a03d885788a12fe566610/org-ref-misc-links.el#L230][printindex]] and [[https://github.com/jkitchin/org-ref/blob/0d2355d1eb4dcac1095a03d885788a12fe566610/org-ref-misc-links.el#L89][list-of-figures]] in ~org-ref-misc-links.el~, - [[https://github.com/jkitchin/org-ref/blob/0d2355d1eb4dcac1095a03d885788a12fe566610/org-ref-pubmed.el#L167][pmcid]] [[https://github.com/jkitchin/org-ref/blob/0d2355d1eb4dcac1095a03d885788a12fe566610/org-ref-pubmed.el#L185][nihmsid]], [[https://github.com/jkitchin/org-ref/blob/0d2355d1eb4dcac1095a03d885788a12fe566610/org-ref-pubmed.el#L220][pubmed-search]], [[https://github.com/jkitchin/org-ref/blob/0d2355d1eb4dcac1095a03d885788a12fe566610/org-ref-pubmed.el#L249][pubmed-clinical]] and [[https://github.com/jkitchin/org-ref/blob/0d2355d1eb4dcac1095a03d885788a12fe566610/org-ref-pubmed.el#L56][pmid]] in ~org-ref-pubmed.el~, - [[https://github.com/jkitchin/org-ref/blob/0d2355d1eb4dcac1095a03d885788a12fe566610/org-ref-ref-links.el#L369][ref]] [[https://github.com/jkitchin/org-ref/blob/0d2355d1eb4dcac1095a03d885788a12fe566610/org-ref-ref-links.el#L381][pageref]], [[https://github.com/jkitchin/org-ref/blob/0d2355d1eb4dcac1095a03d885788a12fe566610/org-ref-ref-links.el#L394][nameref]], [[https://github.com/jkitchin/org-ref/blob/0d2355d1eb4dcac1095a03d885788a12fe566610/org-ref-ref-links.el#L405][eqref]], [[https://github.com/jkitchin/org-ref/blob/0d2355d1eb4dcac1095a03d885788a12fe566610/org-ref-ref-links.el#L416][autoref]], [[https://github.com/jkitchin/org-ref/blob/0d2355d1eb4dcac1095a03d885788a12fe566610/org-ref-ref-links.el#L430][cref]], [[https://github.com/jkitchin/org-ref/blob/0d2355d1eb4dcac1095a03d885788a12fe566610/org-ref-ref-links.el#L440][Cref]], [[https://github.com/jkitchin/org-ref/blob/0d2355d1eb4dcac1095a03d885788a12fe566610/org-ref-ref-links.el#L472][crefrange]] and [[https://github.com/jkitchin/org-ref/blob/0d2355d1eb4dcac1095a03d885788a12fe566610/org-ref-ref-links.el#L481][Crefrange]] in ~org-ref-ref-links.el~, - [[https://github.com/jkitchin/org-ref/blob/0d2355d1eb4dcac1095a03d885788a12fe566610/org-ref-sci-id.el#L32][orcid]] [[https://github.com/jkitchin/org-ref/blob/0d2355d1eb4dcac1095a03d885788a12fe566610/org-ref-sci-id.el#L42][researcherid]], [[https://github.com/jkitchin/org-ref/blob/0d2355d1eb4dcac1095a03d885788a12fe566610/org-ref-scopus.el#L162][eid]], [[https://github.com/jkitchin/org-ref/blob/0d2355d1eb4dcac1095a03d885788a12fe566610/org-ref-scopus.el#L176][scopus-search]], [[https://github.com/jkitchin/org-ref/blob/0d2355d1eb4dcac1095a03d885788a12fe566610/org-ref-scopus.el#L190][scopus-advanced-search]] and [[https://github.com/jkitchin/org-ref/blob/0d2355d1eb4dcac1095a03d885788a12fe566610/org-ref-scopus.el#L204][scopusid]] in ~org-ref-sci-id.el~, - [[https://github.com/jkitchin/org-ref/blob/0d2355d1eb4dcac1095a03d885788a12fe566610/org-ref-wos.el#L31][wos]], [[https://github.com/jkitchin/org-ref/blob/0d2355d1eb4dcac1095a03d885788a12fe566610/org-ref-wos.el#L44][wos-search]] in ~org-ref-wos.el~, - ~cite~, ~nocite~, ~citet~, ~citet*~, ~citep~, ~citep*~, ~citealt~, ~citealt*~, ~citealp~, ~citealp*~, ~citenum~, ~citetext~, ~citeauthor~, ~citeauthor*~, ~citeyear~, ~citeyearpar~, ~Citet~, ~Citep~, ~Citealt~, ~Citealp~, ~Citeauthor~, ~Citet*~, ~Citep*~, ~Citealt*~, ~Citealp*~, ~Citeauthor*~ in [[https://github.com/jkitchin/org-ref/blob/0d2355d1eb4dcac1095a03d885788a12fe566610/org-ref-citation-links.el#L780][org-ref-citation-links.el#L780]], - ~cites~, ~Cites~, ~parencites~, ~Parencites~, ~footcites~, ~footcitetexts~, ~smartcites~, ~Smartcites~, ~textcites~, ~Textcites~, ~supercites~, ~autocites~, ~Autocites~ in [[https://github.com/jkitchin/org-ref/blob/0d2355d1eb4dcac1095a03d885788a12fe566610/org-ref-citation-links.el#L791][org-ref-citation-links.el#L791]], - [[https://github.com/jkitchin/org-ref/blob/0d2355d1eb4dcac1095a03d885788a12fe566610/org-ref-citation-links.el#L801][bibentry]] in ~org-ref-citation-links.el~, - ~gls~, ~glspl~, ~Gls~, ~Glspl~, ~glssymbol~, ~Glssymbol~, ~glsdesc~, ~Glsdesc~ in [[https://github.com/jkitchin/org-ref/blob/0d2355d1eb4dcac1095a03d885788a12fe566610/org-ref-glossary.el#L331][org-ref-glossary.el#L331]], - [[https://github.com/jkitchin/org-ref/blob/0d2355d1eb4dcac1095a03d885788a12fe566610/org-ref-glossary.el#L343][glslink]] and [[https://github.com/jkitchin/org-ref/blob/0d2355d1eb4dcac1095a03d885788a12fe566610/org-ref-glossary.el#L384][printglossaries]] in ~org-ref-glossary.el~, - ~glslink~, ~printglossaries~, ~acrshort~, ~acrshortpl~, ~Acrshort~, ~Acrshortpl~, ~ACRshort~, ~ACRshortpl~, ~acrlong~, ~acrlongpl~, ~Acrlong~, ~Acrlongpl~, ~ACRlong~, ~ACRlongpl~, ~acrfull~, ~acrfullpl~, ~Acrfull~, ~Acrfullpl~, ~ACRfull~ and ~ACRfullpl~ in [[https://github.com/jkitchin/org-ref/blob/0d2355d1eb4dcac1095a03d885788a12fe566610/org-ref-glossary.el#L588][org-ref-glossary.el#L588]]. **** org-roam-server https://github.com/org-roam/org-roam-server: Server for org-roam v1. ~org-roam-server~ defines the link types: [[https://github.com/org-roam/org-roam-server/blob/a207ecd36e29dad55eb66431f041e39144130ee5/org-roam-server.el#L508][server]], [[https://github.com/org-roam/org-roam-server/blob/a207ecd36e29dad55eb66431f041e39144130ee5/org-roam-server.el#L509][file]] and [[https://github.com/org-roam/org-roam-server/blob/a207ecd36e29dad55eb66431f041e39144130ee5/org-roam-server.el#L510][image]]. **** org-roam https://github.com/org-roam/org-roam: Org-roam is a plain-text knowledge management system. It brings some of Roam's more powerful features into the Org-mode ecosystem. ~org-roam~ defines the link type [[https://github.com/org-roam/org-roam/blob/455f139d3ee389eacaa2b61a42ade00fde6d899d/org-roam-id.el#L91][id]] and [[https://github.com/org-roam/org-roam/blob/455f139d3ee389eacaa2b61a42ade00fde6d899d/org-roam-node.el#L710][roam]]. **** org-sketch https://github.com/yuchen-lea/org-sketch: Xournal, Draw.io support for Org Mode. ~org-sketch~ defines the link type [[https://github.com/yuchen-lea/org-sketch/blob/faa3988c41a367e6cf7478ab9629f80206232a9b/org-sketch.el#L405][xournal]] and [[https://github.com/yuchen-lea/org-sketch/blob/faa3988c41a367e6cf7478ab9629f80206232a9b/org-sketch.el#L409][drawio]]. **** org-yt https://github.com/TobiasZawada/org-yt: Youtube links in org-mode. ~org-yt~ defines the link type [[https://github.com/TobiasZawada/org-yt/blob/40cc1ac76d741055cbefa13860d9f070a7ade001/org-yt.el#L135][yt]]. **** org2jekyll https://github.com/ardumont/org2jekyll: Blogging with org-mode and jekyll without alien yaml headers. ~org2jekyll~ defines the link type [[https://github.com/ardumont/org2jekyll/blob/32f6cfc7265cf24ebb5361264e8c1b61a07e74df/org2jekyll.el#L719][local]]. **** orgit-forge https://github.com/magit/orgit-forge: This package defines the Org link type orgit-topic, which can be used to link to Forge topic buffers. This is similar to the orgit package, which links to various Magit buffers. ~orgit-forge~ defines the link type [[https://github.com/magit/orgit-forge/blob/8baf1dee795f026d4555687022487fab89c9bcdf/orgit-forge.el#L58][orgit-topic]]. **** orgit https://github.com/magit/orgit: This package defines several Org link types, which can be used to link to certain Magit buffers. ~orgit~ defines the link types [[https://github.com/magit/orgit/blob/b33b916915db5f91d2c9da4cb1a2457ccbb09332/orgit.el#L258][orgit]], [[https://github.com/magit/orgit/blob/b33b916915db5f91d2c9da4cb1a2457ccbb09332/orgit.el#L294][orgit-log]] and [[https://github.com/magit/orgit/blob/b33b916915db5f91d2c9da4cb1a2457ccbb09332/orgit.el#L358][orgit-rev]]. **** orgqda https://github.com/andersjohansson/orgqda: ~orgqda~ defines a minor mode and several commands for making coding, listing of codes, and collection of coded extracts possible in text written in org mode. ~orgqda~ defines the link types [[https://github.com/andersjohansson/orgqda/blob/02b0daf0e18fad3c107d8880ebdc71bc17173921/orgqda-transcript.el#L385][oqdats]], [[https://github.com/andersjohansson/orgqda/blob/02b0daf0e18fad3c107d8880ebdc71bc17173921/orgqda.el#L1224][opbm]] and [[https://github.com/andersjohansson/orgqda/blob/02b0daf0e18fad3c107d8880ebdc71bc17173921/orgqda.el#L1521][otag]]. **** orgstrap https://github.com/tgbugs/orgstrap: orgstrap allows Org files to describe their own requirements and define their own functionality, making them self-contained standalone computational artifacts dependent only on Emacs or other implementations of the Org Babel protocol in the future. ~orgstrap~ defines the link types [[https://github.com/tgbugs/orgstrap/blob/bc981b957967be8d872c08be9ba7f2dbde5caf1d/ow.el#L1090][FOLD-HEADLINE]], [[https://github.com/tgbugs/orgstrap/blob/bc981b957967be8d872c08be9ba7f2dbde5caf1d/ow.el#L1096][RUN]], [[https://github.com/tgbugs/orgstrap/blob/bc981b957967be8d872c08be9ba7f2dbde5caf1d/ow.el#L1103][RUNPREV]], [[https://github.com/tgbugs/orgstrap/blob/bc981b957967be8d872c08be9ba7f2dbde5caf1d/ow.el#L1110][RUNC]], [[https://github.com/tgbugs/orgstrap/blob/bc981b957967be8d872c08be9ba7f2dbde5caf1d/ow.el#L1118][TEXT-LARGER]], [[https://github.com/tgbugs/orgstrap/blob/bc981b957967be8d872c08be9ba7f2dbde5caf1d/ow.el#L1123][TEXT-SMALLER]] and [[https://github.com/tgbugs/orgstrap/blob/bc981b957967be8d872c08be9ba7f2dbde5caf1d/ow.el#L1128][TEXT-RESET]]. **** orly https://github.com/abo-abo/orly: Additional Org-mode link types and completion for them. ~orly~ defines the link types [[https://github.com/abo-abo/orly/blob/5d840bd9d1ffb0136150e3b3ddfddc1148be9546/orly-code.el#L200][code]], [[https://github.com/abo-abo/orly/blob/5d840bd9d1ffb0136150e3b3ddfddc1148be9546/orly.el#L55][el]], [[https://github.com/abo-abo/orly/blob/5d840bd9d1ffb0136150e3b3ddfddc1148be9546/orly.el#L56][man]], [[https://github.com/abo-abo/orly/blob/5d840bd9d1ffb0136150e3b3ddfddc1148be9546/orly.el#L57][pdf]] and [[https://github.com/abo-abo/orly/blob/5d840bd9d1ffb0136150e3b3ddfddc1148be9546/orly.el#L58][exe]]. **** osm https://github.com/minad/osm: Osm.el is a tile-based map viewer, with a responsive moveable and zoomable display. The map can be controlled with the keyboard or with the mouse. The viewer fetches the map tiles in parallel from tile servers via the =curl= program. The package comes with a list of multiple preconfigured tile servers. You can bookmark your favorite locations using regular Emacs bookmarks or create links from Org files to locations. Furthermore the package provides commands to search for locations by name and to open and display GPX tracks. ~osm~ defines the link type [[https://github.com/minad/osm/blob/471f21f0f8bffc078d5ccfd86610a83e5269c2a0/osm-ol.el#L33][osm]]. **** ox-ipynb https://github.com/jkitchin/ox-ipynb: This module allows you to export an org-file to an Ipython notebook. Python and R notebooks are currently supported. It is not currently possible to mix these languages. ~ox-ipynb~ defines the link type [[https://github.com/jkitchin/ox-ipynb/blob/cdd2bd0f188e6515f2235946b7a2a06165c035db/ox-ipynb.el#L297][image]]. **** paperless https://github.com/atgreen/paperless: Emacs assisted PDF document filing. ~paperless~ defines the link type [[https://github.com/atgreen/paperless/blob/2db39586a2914f78f345379511d0e8cea4c96b86/org-paperless.el#L36][paperless]]. **** papis.el https://github.com/papis/papis.el: The main motivation of this package is to make it easy to interact with org-mode and papis. ~papis.el~ defines the link types [[https://github.com/papis/papis.el/blob/c56c31b3c6d96e80445d2781846df04d9998c194/papis.el#L217][papis+doi]] and [[https://github.com/papis/papis.el/blob/c56c31b3c6d96e80445d2781846df04d9998c194/papis.el#L241][papis]]. **** scimax https://github.com/jkitchin/scimax: Scimax is an Emacs starterkit for scientists and engineers. It provides a comprehensive configuration of Emacs for scientific programming and publishing. ~scimax~ defines the link types [[https://github.com/jkitchin/scimax/blob/f38f9e7c5c4e1f4ec66dd215e918f4d204418879/kitchingroup/kitchingroup.el#L352][box]], [[https://github.com/jkitchin/scimax/blob/f38f9e7c5c4e1f4ec66dd215e918f4d204418879/kitchingroup/scimax-nersc.el#L322][nersc]], [[https://github.com/jkitchin/scimax/blob/f38f9e7c5c4e1f4ec66dd215e918f4d204418879/scimax-contacts.el#L261][contact]], [[https://github.com/jkitchin/scimax/blob/f38f9e7c5c4e1f4ec66dd215e918f4d204418879/scimax-elfeed.el#L198][elfeed]], [[https://github.com/jkitchin/scimax/blob/f38f9e7c5c4e1f4ec66dd215e918f4d204418879/scimax-inkscape.el#L163][inkscape]], [[https://github.com/jkitchin/scimax/blob/f38f9e7c5c4e1f4ec66dd215e918f4d204418879/scimax-link-thumbnails.el#L44][file]], [[https://github.com/jkitchin/scimax/blob/f38f9e7c5c4e1f4ec66dd215e918f4d204418879/scimax-org.el#L494][pydoc]], [[https://github.com/jkitchin/scimax/blob/f38f9e7c5c4e1f4ec66dd215e918f4d204418879/scimax-org.el#L520][attachfile]], [[https://github.com/jkitchin/scimax/blob/f38f9e7c5c4e1f4ec66dd215e918f4d204418879/scimax-org.el#L530][altmetric]], [[https://github.com/jkitchin/scimax/blob/f38f9e7c5c4e1f4ec66dd215e918f4d204418879/scimax-org.el#L557][man]] and [[https://github.com/jkitchin/scimax/blob/f38f9e7c5c4e1f4ec66dd215e918f4d204418879/subfiles/scimax-org-subfiles.el#L40][subfile]]. **** sx.el https://github.com/vermiculus/sx.el: SX is a full-featured Stack Exchange mode for GNU Emacs 24+. Using the official API, it provides a versatile experience for the Stack Exchange network within Emacs itself. ~sx.el~ defines the link type [[https://github.com/vermiculus/sx.el/blob/e9d1093c97507a6d7b4f4710ef65200dae725e5f/sx-interaction.el#L235][sx]]. **** telega.el https://github.com/zevlg/telega.el: telega.el is full featured unofficial client for https://telegram.org platform for GNU Emacs. ~telega.el~ defines the link type [[https://github.com/zevlg/telega.el/blob/29010616931f52e3a5aa9d155c14873c09c7306b/contrib/ol-telega.el#L101][telega]]. **** treemacs https://github.com/Alexander-Miller/treemacs: A tree layout file explorer for Emacs. ~treemacs~ defines the link type [[https://github.com/Alexander-Miller/treemacs/blob/dca83bd42918b510173759df4cc8b3663d5d480d/src/elisp/treemacs-compatibility.el#L142][treemacs]]. **** weblorg https://github.com/emacs-love/weblorg: A Static HTML Generator entirely written in Emacs-Lisp. ~weblorg~ defines the link types [[https://github.com/emacs-love/weblorg/blob/b2bb79ed2c532cad5b03455d8cae887ddb803db3/weblorg.el#L784][anchor]], [[https://github.com/emacs-love/weblorg/blob/b2bb79ed2c532cad5b03455d8cae887ddb803db3/weblorg.el#L788][url_for]] and [[https://github.com/emacs-love/weblorg/blob/b2bb79ed2c532cad5b03455d8cae887ddb803db3/weblorg.el#L792][url_for_img]]. **** zotxt-emacs https://github.com/egh/zotxt-emacs: zotxt-emacs works with https://github.com/egh/zotxt to provide Emacs integration with Zotero, allowing you to manage citation keys for pandoc markdown documents as well as org mode links to items in your Zotero collection. ~zotxt-emacs~ defines the link type [[https://github.com/egh/zotxt-emacs/blob/0d5570ec5db79f0a665a29038e6239bdbcef29fc/org-zotxt.el#L198][zotero]]. ** WE ARE DONE!!! * [2022-05-06 Fri] org-store-link... powerful and flexible | THIS IS EMACS :PROPERTIES: :CUSTOM_ID: /2022-05-06-org-store-link-powerful-and-flexible-this-is-emacs/ :REDDIT_POST: https://www.reddit.com/r/emacs/comments/ujpq3s/orgstorelink_powerful_and_flexible_this_is_emacs/ :COMMIT_ORG_MODE: 407104459b86a40e03f5f1f323d28ad935721ef3 :END: Hey org-mode fans, In [[#/2022-05-04-org-mode-links-everywhere-not-only-in-org-mode-buffers-this-is-emacs/][this post]] we saw that we can open org-mode links everywhere, not only in org-mode buffers, thanks to the command [[org-mode:lisp/org.el::(defun org-open-at-point-global][org-open-at-point-global]]. Specifically, we demonstrated it with: 1) the link ~[[help:pcase]]~ which links to the help buffer describing the macro ~pcase~ and, 2) the link ~[[info:elisp#Current Buffer]]~ which to the elisp info node "Current Buffer". We left aside two things: 1) How can we store a link in the org syntax to the specific "place" (in Emacs) we are visiting? 2) When we've stored an org mode link with the command provided by org-mode, how can we insert that link in our current buffer? The answers to those questions are: 1) using the command [[org-mode:lisp/ol.el::(defun org-store-link (arg][org-store-link]] (everywhere) to store the link, 2) using either the command [[org-mode:lisp/ol.el::(defun org-insert-link (&optional][org-insert-link]] if we want to insert the link in an Org buffer or the command [[org-mode:lisp/ol.el::(defun org-insert-link-global][org-insert-link-global]] if we want to insert the link in a any buffer. For the rest of this post, we assume that we have cloned the org-mode repository under the directory ~/tmp/org-mode/~. Let's go! ** [[org-mode:lisp/ol.el::(defun org-store-link (arg][org-store-link]] and [[org-mode:lisp/ol.el::(defcustom org-link-keep-stored-after-insertion][org-link-keep-stored-after-insertion]] The command [[org-mode:lisp/ol.el::(defun org-store-link (arg][org-store-link]] is "smart enough" to know what to do in many cases (~dired-mode~, visiting an ~org-mode~ file, visiting other type of files). For instance, if we are visiting the directory ~/tmp/org-mode/~ in ~dired-mode~ with the point under the the file ~CONTRIBUTE~: #+BEGIN_SRC text /tmp/org-mode: doc etc .git lisp mk testing CONTRIBUTE COPYING .dir-locals.el .gitignore .gitmodules Makefile README README_ELPA request-assign-future.txt #+END_SRC calling ~M-x org-store-link~ stores the link ~file:/tmp/org-mode/CONTRIBUTE~ in the variable [[org-mode:lisp/ol.el::(defvar org-stored-links][org-stored-links]] like this (assuming [[org-mode:lisp/ol.el::(defvar org-stored-links][org-stored-links]] was ~nil~ before calling [[org-mode:lisp/ol.el::(defun org-store-link (arg][org-store-link]]) #+BEGIN_SRC emacs-lisp (("file:/tmp/org-mode/CONTRIBUTE" "file:/tmp/org-mode/CONTRIBUTE")) #+END_SRC Now, let's say we want to insert that link into an org-mode buffer. We call the function [[org-mode:lisp/ol.el::(defun org-insert-link (&optional][org-insert-link]] (bound to ~C-c C-l~ by default), and we select the stored link ~file:/tmp/org-mode/CONTRIBUTE~ which insert the following in our org buffer: #+BEGIN_SRC org - [[file:/tmp/org-mode/CONTRIBUTE][file:/tmp/org-mode/CONTRIBUTE]] #+END_SRC Let's try to insert it again. We call the command [[org-mode:lisp/ol.el::(defun org-insert-link (&optional][org-insert-link]] but the link ~file:/tmp/org-mode/CONTRIBUTE~ is no longer stored. We can inspect the variable [[org-mode:lisp/ol.el::(defvar org-stored-links][org-stored-links]] and see that its value is ~nil~. This is normal because by default the variable [[org-mode:lisp/ol.el::(defcustom org-link-keep-stored-after-insertion][org-link-keep-stored-after-insertion]] is set to ~nil~ and so once a link that was in [[org-mode:lisp/ol.el::(defvar org-stored-links][org-stored-links]] is inserted, it is removed from [[org-mode:lisp/ol.el::(defvar org-stored-links][org-stored-links]]. If we prefer to keep the links stored even after inserting them, we can set [[org-mode:lisp/ol.el::(defcustom org-link-keep-stored-after-insertion][org-link-keep-stored-after-insertion]] to ~t~ (default is ~nil~) like this: #+BEGIN_SRC emacs-lisp (setq org-link-keep-stored-after-insertion t) #+END_SRC ** [[org-mode:lisp/ol.el::(defun org-store-link (arg][org-store-link]] and [[org-mode:lisp/ol.el::(defcustom org-link-context-for-files][org-link-context-for-files]] Let's continue with the default value. Another example. If we are visiting the file ~ol.el~ with the point on the line defining the variable [[org-mode:lisp/ol.el::(defcustom org-link-context-for-files][org-link-context-for-files]]: #+BEGIN_SRC emacs-lisp (defcustom org-link-context-for-files t "Non-nil means file links from `org-store-link' contain context. ...") #+END_SRC calling ~M-x org-store-link~ stores the link ~"file:~/work/tmp/org-mode/lisp/ol.el::(defcustom org-link-context-for-files t"~ with its description being ~nil~ in the variable [[org-mode:lisp/ol.el::(defvar org-stored-links][org-stored-links]] like this #+BEGIN_SRC emacs-lisp ((#("file:~/work/tmp/org-mode/lisp/ol.el::(defcustom org-link-context-for-files t" ...) nil)) #+END_SRC Now, in the same Org buffer as above, in another list item, we call the function [[org-mode:lisp/ol.el::(defun org-insert-link (&optional][org-insert-link]] (bound to ~C-c C-l~ by default): 1) then we select the stored link ~"file:~/work/tmp/org-mode/lisp/ol.el::(defcustom org-link-context-for-files t"~ 2) then we are asked in the minibuffer to provide a description for the link and we provide the description ~org-link-context-for-files~ 3) finally the link is inserted as follow: #+BEGIN_SRC org - [[file:/tmp/org-mode/CONTRIBUTE][file:/tmp/org-mode/CONTRIBUTE]] - [[file:/tmp/org-mode/lisp/ol.el::(defcustom org-link-context-for-files t][org-link-context-for-files]] #+END_SRC At the previous step ~1)~ we could have remove the search option (everything after the two colons ~::~) and the two colons ~::~ if we only wanted to link to the file. If we never want the search option (the context) when storing a link to a file, we can set the variable [[org-mode:lisp/ol.el::(defcustom org-link-context-for-files][org-link-context-for-files]] to ~nil~ (default value is ~t~) like this: #+BEGIN_SRC emacs-lisp (setq org-link-context-for-files nil) #+END_SRC Note that in that case when we insert the file, we are not asked to provide a description. ** [[org-mode:lisp/ol.el::(defun org-store-link (arg][org-store-link]] in org-mode files If we use [[org-mode:lisp/ol.el::(defun org-store-link (arg][org-store-link]] in an org-mode file, the search option of the link to that file is picked "wisely" by the command depending of the position of the point (see [[info:org#Search Options]]). ** built-in link types The support of [[org-mode:lisp/ol.el::(defun org-store-link (arg][org-store-link]] for the links we've described so far are hard coded in [[org-mode:lisp/ol.el::(defun org-store-link (arg][org-store-link]]. Beside the hard coded support for storing link, org-mode provides a mechanism to add new link types via the function [[org-mode:lisp/ol.el::(defun org-link-set-parameters][org-link-set-parameters]] (we already talk about that subject in the post [[#/2022-04-29-link-to-a-git-commit-from-org-mode-using-magit-this-is-emacs/][Link to a git commit from Org mode using Magit]]). And by providing a specific function for storing links depending on the Emacs context we can use the function [[org-mode:lisp/ol.el::(defun org-store-link (arg][org-store-link]] to store those new link types. Org provides various built-in link types with support for storing them. We can list them (if you have added other link types with store functions, they will be listed too) by evaluating the following s-exp (which is just a variant of the function [[org-mode:lisp/ol.el::(defun org-store-link-functions][org-store-link-functions]]): #+BEGIN_SRC emacs-lisp (cl-loop for link in org-link-parameters if (org-link-get-parameter (car link) :store) collect (car link)) ;; ("eww" "rmail" "mhe" "irc" "info" "gnus" "docview" "bibtex" "bbdb" "w3m" "help") #+END_SRC ** ~help~ link type and the mechanism of [[org-mode:lisp/ol.el::(defun org-store-link (arg][org-store-link]] Now we focus on the ~help~ link type. It is define using the function [[org-mode:lisp/ol.el::(defun org-link-set-parameters][org-link-set-parameters]] like this: #+BEGIN_SRC emacs-lisp (org-link-set-parameters "help" :follow #'org-link--open-help :store #'org-link--store-help) #+END_SRC Specifically [[org-mode:lisp/ol.el::(defun org-link--store-help][org-link--store-help]] is the function responsible to produce the links to help buffers when we call [[org-mode:lisp/ol.el::(defun org-store-link (arg][org-store-link]] in help buffers. How does this work? Let's say we are visiting the following help buffer of the macro ~pcase~ and we call ~M-x org-store-link~: #+BEGIN_SRC text pcase is a Lisp macro. (pcase EXP &rest CASES) Probably introduced at or before Emacs version 24.1. Evaluate EXP to get EXPVAL; try passing control to one of CASES. ...MORE HERE... #+END_SRC As we call the command [[org-mode:lisp/ol.el::(defun org-store-link (arg][org-store-link]] without 2 or 3 universal arguments (~C-u C-u~ nor ~C-u C-u C-u~), the command [[org-mode:lisp/ol.el::(defun org-store-link (arg][org-store-link]] does the following. First, [[org-mode:lisp/ol.el::(defun org-store-link (arg][org-store-link]] calls all the defined store functions (~(org-store-link-functions)~) and then 1) if they all return ~nil~, try to store the link with the hard coded support, 2) if only one, which we call ~F~, returns a non-nil value, use the ~:link~ value and ~:description~ value of the variable [[org-mode:lisp/ol.el::(defvar org-store-link-plist][org-store-link-plist]] (which has been set by the call to ~F~) to set the local variable ~link~ and ~desc~, 3) if more than one returns a non-nil value (that means that various functions can handle/produce the link for the buffer (Emacs context) we are visiting), ask us via the minibuffer to choose the one we want to use and then use it to set the local variable ~link~ and ~desc~. As we are visiting the help buffer of the macro ~pcase~ (and we only use the built-in link type offer by org-mode) we are in the second case. Specifically, the function [[org-mode:lisp/ol.el::(defun org-link--store-help][org-link--store-help]] is the only one that returned a non-nil value. So the values of the variable ~link~ and ~desc~ is produced by the function [[org-mode:lisp/ol.el::(defun org-link--store-help][org-link--store-help]] by setting the variable [[org-mode:lisp/ol.el::(defvar org-store-link-plist][org-store-link-plist]]. Finally, as we've never stored that link before, the function [[org-mode:lisp/ol.el::(defun org-store-link (arg][org-store-link]] add it to the global variable [[org-mode:lisp/ol.el::(defvar org-stored-links][org-stored-links]] evaluating the following s-exp: #+BEGIN_SRC emacs-lisp (push (list link desc) org-stored-links) #+END_SRC Here are the parts of [[org-mode:lisp/ol.el::(defun org-store-link (arg][org-store-link]] we've just discussed: #+BEGIN_SRC emacs-lisp (defun org-store-link (arg &optional interactive?) "..." (interactive "P\np") (org-load-modules-maybe) (if (and (equal arg '(64)) (org-region-active-p)) ... (setq org-store-link-plist nil) (let (link cpltxt desc search custom-id agenda-link) (cond ((and (not (equal arg '(16))) (let ((results-alist nil)) (dolist (f (org-store-link-functions)) (when (funcall f) (push (cons f (copy-sequence org-store-link-plist)) results-alist))) (pcase results-alist (`nil nil) (`((,_ . ,_)) t) ;single choice: nothing to do (`((,name . ,_) . ,_) (apply #'org-link-store-props (cdr (assoc-string (completing-read (format "Store link with (default %s): " name) (mapcar #'car results-alist) nil t nil nil (symbol-name name)) results-alist))) t)))) (setq link (plist-get org-store-link-plist :link)) (setq desc (if (plist-member org-store-link-plist :description) (plist-get org-store-link-plist :description) link))) ... (t (setq link nil))) ... ;; Store and return the link (if (not (and interactive? link)) ... (if (member (list link desc) org-stored-links) (message "This link has already been stored") (push (list link desc) org-stored-links) (message "Stored: %s" (or desc link)) ...) (car org-stored-links))))) #+END_SRC Now we just have to look at the function [[org-mode:lisp/ol.el::(defun org-link--store-help][org-link--store-help]] which returns a non-nil value only if we call it when we are in ~help-mode~ buffers as we can see below: #+BEGIN_SRC emacs-lisp (defun org-link--store-help () "Store \"help\" type link." (when (eq major-mode 'help-mode) (let ((symbol (save-excursion (goto-char (point-min)) ;; In case the help is about the key-binding, store the ;; function instead. (search-forward "runs the command " (line-end-position) t) (read (current-buffer))))) (org-link-store-props :type "help" :link (format "help:%s" symbol) :description nil)))) #+END_SRC What is important to note is the call to the function [[org-mode:lisp/ol.el::(defun org-link-store-props (&rest][org-link-store-props]] which set the variable [[org-mode:lisp/ol.el::(defvar org-store-link-plist][org-store-link-plist]] (by side effect) which is then used in the body of [[org-mode:lisp/ol.el::(defun org-store-link (arg][org-store-link]]. Specifically, calling [[org-mode:lisp/ol.el::(defun org-link--store-help][org-link--store-help]] in the help buffer of the macro ~pcase~ set the variable [[org-mode:lisp/ol.el::(defvar org-store-link-plist][org-store-link-plist]] to the following plist: #+BEGIN_SRC emacs-lisp (:type "help" :link "help:pcase" :description nil) #+END_SRC Finally to to store the link in the same Org buffer as above, in another list item, we call the function [[org-mode:lisp/ol.el::(defun org-insert-link (&optional][org-insert-link]] selecting the link to the ~pcase~ help buffer leaving blanck the description, and we get: #+BEGIN_SRC org - [[file:/tmp/org-mode/CONTRIBUTE][file:/tmp/org-mode/CONTRIBUTE]] - [[file:/tmp/org-mode/lisp/ol.el::(defcustom org-link-context-for-files t][org-link-context-for-files]] - [[help:pcase]] #+END_SRC ** Browse all the built-in link types that provide a store function If we want to look at the implementation of all the built-in link types that provide a store function, we can use the built-in command ~rgrep~ in org-mode repository (in our case cloned under the directory ~/tmp/org-mode/~) searching for the regexp ~:store~ which pops up the following ~grep~ buffer that we can navigate with the keys ~n~ and ~p~: #+BEGIN_SRC text -*- mode: grep; default-directory: "/tmp/org-mode/" -*- Grep started at Fri May 6 16:17:31 find [...] -exec grep --color=auto -nH --null -e \:store \{\} + ./lisp/ol.el:98:are, in this order, `:follow', `:export', and `:store', described ./lisp/ol.el:121:`:store' ./lisp/ol.el:1012:The functions are defined in the `:store' property of ./lisp/ol.el:1033: do (setq store-func (org-link-get-parameter (car link) :store)) ./lisp/ol.el:1389: :store #'org-link--store-help) ./lisp/ol-w3m.el:49:(org-link-set-parameters "w3m" :store #'org-w3m-store-link) ./lisp/ol-eww.el:55: :store #'org-eww-store-link) ./lisp/ol-irc.el:78: :store #'org-irc-store-link ./lisp/ol-bibtex.el:487: :store #'org-bibtex-store-link) ./lisp/ol-docview.el:56: :store #'org-docview-store-link) ./lisp/ol-eshell.el:34: :store #'org-eshell-store-link) ./lisp/ol-mhe.el:77:(org-link-set-parameters "mhe" :follow #'org-mhe-open :store #'org-mhe-store-link) ./lisp/ol-rmail.el:48: :store #'org-rmail-store-link) ./lisp/ol-bbdb.el:222: :store #'org-bbdb-store-link) ./lisp/ol-info.el:46: :store #'org-info-store-link) ./lisp/ol-man.el:32: :store #'org-man-store-link) ./lisp/ol-gnus.el:84: :store #'org-gnus-store-link) Grep finished with 17 matches found at Fri May 6 16:17:31 #+END_SRC WE ARE DONE!!! * [2022-05-04 Wed] org-mode links everywhere, not only in org-mode buffers | THIS IS EMACS :PROPERTIES: :CUSTOM_ID: /2022-05-04-org-mode-links-everywhere-not-only-in-org-mode-buffers-this-is-emacs/ :REDDIT_POST: https://www.reddit.com/r/emacs/comments/ui8eiq/orgmode_links_everywhere_not_only_in_orgmode/ :COMMIT_ORG_MODE: 407104459b86a40e03f5f1f323d28ad935721ef3 :END: Hey Emacsers, What a great day to talk about EMACS :) Let's set the scene! We've just written a new command named ~my-new-command~ that we've added to our init file ~~/.emacs.d/init.el~ (or whatever init file your are using except org-mode file). To write our new command we had to look at the help of the macro [[emacs:lisp/emacs-lisp/pcase.el::(defmacro pcase (exp][pcase]] and the elisp info node "Current Buffer". Now let's say we want to keep the links to these help and info buffers in comments near to the definition of our new command. Is is possible? How do we do that? ** Org links everywhere One way to do that is to use the org-mode link syntax using the identifier ~info~ and ~elisp~ (described in the info node [[info:org#Link Abbreviations]]) like this: #+BEGIN_SRC emacs-lisp ;; [[help:pcase]] ;; [[info:elisp#Current Buffer]] (defun my-new-command () "..." ...) #+END_SRC Now we can use the command [[org-mode:lisp/org.el::(defun org-open-at-point-global][org-open-at-point-global]] to open those links. For instance, running ~M-x org-open-at-point-global~ on top of the link ~[[help:pcase]]~ (in the previous ~emacs-lisp~ buffer) pops up the following help buffer: #+BEGIN_SRC text pcase is a Lisp macro. (pcase EXP &rest CASES) Probably introduced at or before Emacs version 24.1. Evaluate EXP to get EXPVAL; try passing control to one of CASES. CASES is a list of elements of the form (PATTERN CODE...). For the first CASE whose PATTERN "matches" EXPVAL, evaluate its CODE..., and return the value of the last form. If no CASE has a PATTERN that matches, return nil. ...MORE HERE... #+END_SRC For instance, running ~M-x org-open-at-point-global~ on top of the link ~[[info:elisp#Current Buffer]]~ (in the previous ~emacs-lisp~ buffer) pops up the following info buffer: #+BEGIN_SRC text File: elisp.info, Node: Current Buffer, Next: Buffer Names, Prev: Buffer Basics, Up: Buffers 28.2 The Current Buffer ======================= There are, in general, many buffers in an Emacs session. At any time, one of them is designated the “current buffer”—the buffer in which most editing takes place. Most of the primitives for examining or changing text operate implicitly on the current buffer (*note Text::). Normally, the buffer displayed in the selected window is the current buffer, but this is not always so: a Lisp program can temporarily designate any buffer as current in order to operate on its contents, without changing what is displayed on the screen. The most basic function for designating a current buffer is ‘set-buffer’. ...MORE HERE... #+END_SRC Thanks to [[org-mode:lisp/org.el::(defun org-open-at-point-global][org-open-at-point-global]] we have org-mode links everywhere! What do you think about that "feature"? Isn't it super cool!? ** How does [[org-mode:lisp/org.el::(defun org-open-at-point-global][org-open-at-point-global]] work? To understand the mechanism of [[org-mode:lisp/org.el::(defun org-open-at-point-global][org-open-at-point-global]], let's see what happens when we call [[org-mode:lisp/org.el::(defun org-open-at-point-global][org-open-at-point-global]] on top of the link ~[[help:pcase]]~ in the previous ~emacs-lisp~ buffer? The function [[org-mode:lisp/org.el::(defun org-open-at-point-global][org-open-at-point-global]] checks if point is inside a match of the regexp [[org-mode:lisp/ol.el::(defvar org-link-any-re][org-link-any-re]] (regular expression matching any link) using the function [[org-mode:lisp/org-macs.el::(defun org-in-regexp (regexp][org-in-regexp]]. As it is true (the regexp [[org-mode:lisp/ol.el::(defvar org-link-any-re][org-link-any-re]] is matched in the string ~[[help:pcase]]~), the function [[org-mode:lisp/org.el::(defun org-open-at-point-global][org-open-at-point-global]] returns with the call: #+BEGIN_SRC emacs-lisp (org-link-open-from-string "[[help:pcase]]") #+END_SRC Here are the parts of [[org-mode:lisp/org.el::(defun org-open-at-point-global][org-open-at-point-global]] we've just discussed: #+BEGIN_SRC emacs-lisp (defun org-open-at-point-global () "..." (interactive) (let (...) (cond ((org-in-regexp org-link-any-re) (org-link-open-from-string (match-string-no-properties 0))) ... (t (user-error "No link found"))))) #+END_SRC So far, nothing impressive. We match something at point and apply a function on that thing. True! The magic happens in the function [[org-mode:lisp/ol.el::(defun org-link-open-from-string][org-link-open-from-string]] where we use a temporary buffer in org-mode where we insert the string ~[[help:case]]~ and leverage the Org parser calling the function [[org-mode:lisp/org-element.el::(defun org-element-link-parser (][org-element-link-parser]] at the beginning of the inserted link which parses the link and returns this org object link: #+BEGIN_SRC emacs-lisp (link (:type "help" :path "pcase" :format bracket :raw-link "help:pcase" :application nil :search-option nil :begin 1 :end 15 :contents-begin nil :contents-end nil :post-blank 0)) #+END_SRC which is passed to the function [[org-mode:lisp/ol.el::(defun org-link-open (link][org-link-open]] (which returns) like this: #+BEGIN_SRC emacs-lisp (org-link-open '(link (:type "help" :path "pcase" :format bracket :raw-link "help:pcase" ...))) #+END_SRC And the function [[org-mode:lisp/ol.el::(defun org-link-open (link][org-link-open]] "takes care" to open the help buffer describing the macro [[emacs:lisp/emacs-lisp/pcase.el::(defmacro pcase (exp][pcase]]. Here is the definition of the function [[org-mode:lisp/ol.el::(defun org-link-open-from-string][org-link-open-from-string]]: #+BEGIN_SRC emacs-lisp (defun org-link-open-from-string (s &optional arg) "Open a link in the string S, as if it was in Org mode. Optional argument is passed to `org-open-file' when S is a \"file\" link." (interactive "sLink: \nP") (pcase (with-temp-buffer (let ((org-inhibit-startup nil)) (insert s) (org-mode) (goto-char (point-min)) (org-element-link-parser))) (`nil (user-error "No valid link in %S" s)) (link (org-link-open link arg)))) #+END_SRC ** [[org-mode:lisp/org.el::(defun org-open-at-point-global][org-open-at-point-global]] and link abbreviations As we've seen in the post [[#/2022-04-09-org-links-in-property-drawers-are-not-links/][Did you know that Org links in property drawers are not links?]], in org-mode "links" matched by the regexp [[org-mode:lisp/ol.el::(defvar org-link-any-re][org-link-any-re]] are not always treated as link by the Org parser. For instance, the URL ~https://orgmode.org/worg/~ in the property drawer of the following org-mode buffer is not a link for the Org parser but the ~:value~ of a ~node-property~ element: #+BEGIN_SRC org ,* Heading :PROPERTIES: :MY_URL: https://orgmode.org/worg/ :END: #+END_SRC And so when we call the function [[org-mode:lisp/org.el::(defun org-open-at-point (&optional arg][org-open-at-point]] (~C-c C-o~ by default) on a "link" in a property drawer, [[org-mode:lisp/org.el::(defun org-open-at-point (&optional arg][org-open-at-point]] won't call directly the function [[org-mode:lisp/ol.el::(defun org-link-open (link][org-link-open]] on the ~node-property~ element which is not a link. The function [[org-mode:lisp/org.el::(defun org-open-at-point (&optional arg][org-open-at-point]] falls back by calling interactively the function [[org-mode:lisp/org.el::(defun org-open-at-point-global][org-open-at-point-global]] with the point in the property drawer on top of the "link". It works fine for every links but for link abbreviations when the abbreviation is set locally with ~#+LINK:~ like in this example: #+BEGIN_SRC org ,#+LINK: worg https://orgmode.org/worg/ ,* Heading :PROPERTIES: :MY_URL: [[worg]] :END: #+END_SRC This is normal if you look at implementation of [[org-mode:lisp/org.el::(defun org-open-at-point-global][org-open-at-point-global]]. As we've seen above, the function [[org-mode:lisp/org.el::(defun org-open-at-point-global][org-open-at-point-global]] creates a temporary org-mode buffer to parse the link. So only the variable [[org-mode:lisp/ol.el::(defcustom org-link-abbrev-alist][org-link-abbrev-alist]] which is global is "taken into account", not the local variable [[org-mode:lisp/ol.el::(defvar-local org-link-abbrev-alist-local][org-link-abbrev-alist-local]] which value is ~nil~ in that case. Ignacio Casso and Ihor Radchenko discussed this case in the org-mode mailing list in the thread: https://lists.gnu.org/archive/html/emacs-orgmode/2022-04/msg00367.html And Ignacio Casso has a workaround for that case that uses advices: https://lists.gnu.org/archive/html/emacs-orgmode/2022-04/msg00449.html If you are interested in link abbreviations you can read: - the info node [[info:org#Link Abbreviations]] and - the post [[#/2022-04-04-search-options-link-abbreviations-and-org-open-at-point/][Search options in file links | link abbreviations | ... org-open-at-point]]. WE ARE DONE!!! * [2022-04-29 Fri] Link to a git commit from Org mode using Magit | THIS IS EMACS :PROPERTIES: :CUSTOM_ID: /2022-04-29-link-to-a-git-commit-from-org-mode-using-magit-this-is-emacs/ :REDDIT_POST: https://www.reddit.com/r/emacs/comments/uenjjs/link_to_a_git_commit_from_org_mode_using_magit/ :COMMIT_ORG_MODE: 407104459b86a40e03f5f1f323d28ad935721ef3 :END: Hey Org mode lovers, In the article [[https://en.wikipedia.org/wiki/Hyperlink][Hyperlink (wikipedia)]] we can read that a hyperlink "is a reference to data that the user can follow by clicking". Do you believe me if I tell you that with Org mode the data we refer to in a link can be a buffer in ~magit-revision-mode~ (from [[https://github.com/magit/magit][magit]] package) showing us a specific commit of some git repository? For a non-Emacs user, it's not that you can or can't believe it, it's just that this sentence doesn't make sense. But for me (a regular Emacs user like you), when I read that sentence I think: 1) Really! Emacs, Org, Magit, Buffer, Link! Integration. Non-context switching. Maybe a bit of lisp. Extensible. Yes it makes sense. 2) AFTER ALL THIS IS EMACS! 3) Show me how to do it! The key elements to doing so lie in: 1) Org mode provides the built-in ~elisp~ type link that allows to evaluate any Elisp s-exps or to call any commands when we open this type of link with [[org-mode:lisp/org.el::(defun org-open-at-point (&optional arg][org-open-at-point]] (bound to ~C-c C-o~ by default), 2) and that the command ~magit-show-commit~ used to visit commits in magit buffers when we are on a commit line can also be used non interactively as a regular function where we have to provide it the commit hash and the repository ("module"). First we look at the syntax of ~elisp~ type links. A bracket link is a valid ~elisp~ type link if it starts with the identifier (the type) ~elisp~, then is followed by a colon ~:~ and then followed by either an Elisp form in parentheses or an Elisp command. For instance this link: #+BEGIN_SRC org [[elisp:(+ 1 1)]] #+END_SRC when followed with [[org-mode:lisp/org.el::(defun org-open-at-point (&optional arg][org-open-at-point]] prints the following in the echo area: #+BEGIN_SRC text (+ 1 1) => 2 #+END_SRC And this link: #+BEGIN_SRC org [[elisp:tetris]] #+END_SRC when followed with [[org-mode:lisp/org.el::(defun org-open-at-point (&optional arg][org-open-at-point]] starts the built-in Tetris. By default, the variable [[org-mode:lisp/ol.el::(defcustom org-link-elisp-confirm-function 'yes-or-no-p][org-link-elisp-confirm-function]] is set to the function [[emacs:src/fns.c::DEFUN ("yes-or-no-p"][yes-or-no-p]] which means that when we open an ~elisp~ link we are asked for confirmation before executing the ~elisp~ link. We can set this variable to ~nil~ if we never want to be asked for confirmation. But it might be dangerous (see the docstring of this variable for an example of a dangerous ~elisp~ link that can remove all our user home directory). Besides setting [[org-mode:lisp/ol.el::(defcustom org-link-elisp-confirm-function 'yes-or-no-p][org-link-elisp-confirm-function]] to ~nil~ in order not to be asked confirmation everytime we open an ~elisp~ link we can use the variable [[org-mode:lisp/ol.el::(defcustom org-link-elisp-skip-confirm-regexp][org-link-elisp-skip-confirm-regexp]]. Indeed, any ~elisp~ link that matches the regexp [[org-mode:lisp/ol.el::(defcustom org-link-elisp-skip-confirm-regexp][org-link-elisp-skip-confirm-regexp]] is executed without asking confirmation. For instance, setting up the variable [[org-mode:lisp/ol.el::(defcustom org-link-elisp-skip-confirm-regexp][org-link-elisp-skip-confirm-regexp]] to ~"tetris"~ lets us open the link ~[[elisp:tetris]]~ without asking us confirmation. #+BEGIN_SRC emacs-lisp (setq org-link-elisp-skip-confirm-regexp "tetris") #+END_SRC Now things are getting spicy :) We add the Magit layer to our ~elisp~ link. The scenario is the following. We are "working" on the Org repository that we assume is cloned under the directory ~/tmp/org-mode/~. At some point we've looked at the commit ~baffebbc3~ that fixes a bug in [[org-mode:lisp/org.el::(defun org-get-heading (&optional][org-get-heading]] and we want to keep in our note a link to that commit. To do so we can use the following link: #+BEGIN_SRC org [[elisp:(magit-show-commit "baffebbc3" nil nil "/tmp/org-mode/")]] #+END_SRC Now, if we open that link with [[org-mode:lisp/org.el::(defun org-open-at-point (&optional arg][org-open-at-point]], we jump to the following buffer in ~magit-revision-mode~: #+BEGIN_SRC diff baffebbc33e600ec7abfe5e54b60ea6753d4f272 Author: XXX AuthorDate: Fri Apr 30 14:09:05 2021 +0200 Commit: YYY CommitDate: Mon Apr 25 19:40:05 2022 +0800 Parent: 240a14988 Fix typo: delete-duplicates → delete-dups Contained: main Follows: release_9.5.3 (433) Fix bug in org-get-heading Fixes #26, where fontification could make the matching and extraction of heading components fail. modified lisp/org.el @@ -6167,8 +6167,9 @@ Return nil before first heading." (let ((case-fold-search nil)) (looking-at org-complex-heading-regexp) ;; When using `org-fold-core--optimise-for-huge-buffers', - ;; returned text may be invisible. Clear it up. - (org-fold-core-remove-optimisation (match-beginning 0) (match-end 0)) + ;; returned text will be invisible. Clear it up. + (save-match-data + (org-fold-core-remove-optimisation (match-beginning 0) (match-end 0))) (let ((todo (and (not no-todo) (match-string 2))) (priority (and (not no-priority) (match-string 3))) (headline (pcase (match-string 4) #+END_SRC TAKING NOTE WITH EMACS/ORG-MODE IS F***ING CRAZY. DO YOU AGREE??? Now that we've seen how to use ~elisp~ type links with practical examples, let's see how they are implemented. Except for the reserved link types ~coderef~, ~custom-id~, ~fuzzy~ and ~radio~ we can defined new link types or modify an existing link types using the function [[org-mode:lisp/ol.el::(defun org-link-set-parameters][org-link-set-parameters]]. For instance the link type ~elisp~ is defined like this in the file [[org-mode:lisp/ol.el]] #+BEGIN_SRC emacs-lisp (org-link-set-parameters "elisp" :follow #'org-link--open-elisp) #+END_SRC Fine, but what does it mean to define a link type? It means that we add an entry to the alist [[org-mode:lisp/ol.el::(defcustom org-link-parameters][org-link-parameters]] where we specify for a link type how we want the Org mode features related to links to behave regarding that type. With the default configuration the variable [[org-mode:lisp/ol.el::(defcustom org-link-parameters][org-link-parameters]] looks like this (with some link types skipped): #+BEGIN_SRC emacs-lisp (("eww" :follow org-eww-open :store org-eww-store-link) ... ("info" :follow org-info-open :export org-info-export :store org-info-store-link) ... ("id" :follow org-id-open) ... ("shell" :follow org-link--open-shell) ... ("help" :follow org-link--open-help :store org-link--store-help) ("file" :complete org-link-complete-file) ("elisp" :follow org-link--open-elisp)) #+END_SRC For instance, the variable [[org-mode:lisp/ol.el::(defcustom org-link-parameters][org-link-parameters]] tells org-mode that when we open an ~elisp~ type link with [[org-mode:lisp/org.el::(defun org-open-at-point (&optional arg][org-open-at-point]] (bound to ~C-c C-o~ by default) the function that finally opens the link is [[org-mode:lisp/ol.el::(defun org-link--open-elisp][org-link--open-elisp]] (the one after the keyword ~:follow~ in the variable [[org-mode:lisp/ol.el::(defcustom org-link-parameters][org-link-parameters]]). And for the ~info~ type links (links to info node) the variable [[org-mode:lisp/ol.el::(defcustom org-link-parameters][org-link-parameters]] tells org-mode: 1) to use the function [[org-mode:lisp/ol-info.el::(defun org-info-open (path][org-info-open]] when we open ~info~ type links, 2) to use the function [[org-mode:lisp/ol-info.el::(defun org-info-export (path][org-info-export]] when we export open ~info~ type links, 3) to use the function [[org-mode:lisp/ol-info.el::(defun org-info-store-link][org-info-store-link]] when we store ~info~ type links. If we want to write our own type link we can look at the docstring of [[org-mode:lisp/ol.el::(defcustom org-link-parameters][org-link-parameters]] to know what are the supported keys (~:follow~, ~:export~, ~:store~, ~:activate-func~, ~:complete~, ~:display~, ~:face~, ~:help-echo~, ~:htmlize-link~, ~:keymap~, ~:mouse-face~) and what are their accepted values. Back to ~elisp~ type links. When we call [[org-mode:lisp/org.el::(defun org-open-at-point (&optional arg][org-open-at-point]] (bound to ~C-c C-o~ by default) without universal argument on the following ~elisp~ type link: #+BEGIN_SRC org [[elisp:(+ 1 1)]] #+END_SRC in its body, [[org-mode:lisp/org.el::(defun org-open-at-point (&optional arg][org-open-at-point]] parses the link at point and "delegates" the work to the function [[org-mode:lisp/ol.el::(defun org-link-open (link][org-link-open]] passing it as first argument the parsed link like this: #+BEGIN_SRC emacs-lisp (org-link-open '(link (:type "elisp" :path "(+ 1 1)" :format bracket :raw-link "elisp:(+ 1 1)" ... :parent (paragraph ... :parent (section ... :parent (org-data ...))))) nil ;; called without universal argument ) #+END_SRC Then the function [[org-mode:lisp/ol.el::(defun org-link-open (link][org-link-open]] locally binds the variables ~type~ and ~path~ respectively to ~"elisp"~ and ~"(+ 1 1)"~ getting those values out of the link ~(link (:type "elisp" :path "(+ 1 1)" ...))~ using the function [[org-mode:lisp/org-element.el::(defsubst org-element-property (property][org-element-property]]. Then as ~type~ is not equal to any of the following types ~"file"~, ~"coderef"~, ~"custom-id"~, ~"fuzzy"~ and ~"radio"~ but equal to ~"elisp"~ the function [[org-mode:lisp/ol.el::(defun org-link-open (link][org-link-open]] decides to use the dedicated function [[org-mode:lisp/ol.el::(defun org-link--open-elisp][org-link--open-elisp]] to open ~elisp~ type links. This is done by locally binding ~f~ to [[org-mode:lisp/ol.el::(defun org-link--open-elisp][org-link--open-elisp]] retrieving this value using the function [[org-mode:lisp/ol.el::(defun org-link-get-parameter][org-link-get-parameter]] which is a thin wrapper around the variable [[org-mode:lisp/ol.el::(defcustom org-link-parameters][org-link-parameters]] as we can see in the following code snippet #+BEGIN_SRC emacs-lisp (defun org-link-get-parameter (type key) "..." (plist-get (cdr (assoc type org-link-parameters)) key)) #+END_SRC and as [[org-mode:lisp/ol.el::(defun org-link--open-elisp][org-link--open-elisp]] is a function that accepts two arguments, [[org-mode:lisp/ol.el::(defun org-link-open (link][org-link-open]] finally returns with the following call: #+BEGIN_SRC emacs-lisp (funcall 'org-link--open-elisp "(+ 1 1)" nil) #+END_SRC Here are the parts of [[org-mode:lisp/ol.el::(defun org-link-open (link][org-link-open]] we've just discussed: #+BEGIN_SRC emacs-lisp (defun org-link-open (link &optional arg) "..." (let ((type (org-element-property :type link)) (path (org-element-property :path link))) (pcase type ("file" ...) ((or "coderef" "custom-id" "fuzzy" "radio") ...) (_ ;; Look for a dedicated "follow" function in custom links. (let ((f (org-link-get-parameter type :follow))) (when (functionp f) ;; Function defined in `:follow' parameter may use a single ;; argument, as it was mandatory before Org 9.4. This is ;; deprecated, but support it for now. (condition-case nil (funcall (org-link-get-parameter type :follow) path arg) (wrong-number-of-arguments (funcall (org-link-get-parameter type :follow) path))))))))) #+END_SRC Now we are left with the function [[org-mode:lisp/ol.el::(defun org-link--open-elisp][org-link--open-elisp]]. Assuming we answer ~yes~ to the question raised by calling [[org-mode:lisp/ol.el::(defcustom org-link-elisp-confirm-function 'yes-or-no-p][org-link-elisp-confirm-function]] in the condition of the ~if~ form, the THEN part of the ~if~ is evaluated. As the ~path~ equal to ~"(+ 1 1)"~ starts by a left parentheses ~(~, the string ~"(+ 1 1)"~ is "transformed" into a lisp object by the function ~read~, then is evaluted by the function ~eval~, and its result is substituted in the second placeholder ~%s~ in the string of the ~message~ function. Here are the parts of [[org-mode:lisp/ol.el::(defun org-link--open-elisp][org-link--open-elisp]] we've just discussed: #+BEGIN_SRC emacs-lisp (defun org-link--open-elisp (path _) "..." (if (...) (message "%s => %s" path (if (eq ?\( (string-to-char path)) (eval (read path)) (call-interactively (read path)))) (user-error "Abort"))) #+END_SRC And this is how the message ~(+ 1 1) => 2~ is printed in the echo area when we call [[org-mode:lisp/org.el::(defun org-open-at-point (&optional arg][org-open-at-point]] on top of the ~elisp~ type link ~[[elisp:(+ 1 1)]]~. WE ARE DONE!!! If this is the first time you have seen the ~read~ and ~eval~ functions, you may be interested in these examples: #+BEGIN_SRC emacs-lisp (read "(+ 1 2)") ; (+ 1 2) (type-of (read "(+ 1 2)")) ; cons (functionp (read "(+ 1 2)")) ; nil (type-of (eval (read "(+ 1 2)"))) ; integer (read "(lambda () (+ 1 2))") ; (lambda nil (+ 1 2)) (type-of (read "(lambda () (+ 1 2))")) ; cons (functionp (read "(lambda () (+ 1 2))")) ; t (type-of (eval (read "(lambda () (+ 1 2))"))) ; cons (read "foo") ; foo (type-of (read "foo")) ; symbol (type-of (eval (read "foo"))) ; error: (void-variable foo) (read "\"bar\"") ; "bar" (type-of (read "\"bar\"")) ; string (type-of (eval (read "\"bar\""))) ; string #+END_SRC * [2022-04-27 Wed] FULL example of org-mode links: internal links and search options :PROPERTIES: :CUSTOM_ID: /2022-04-27-full-example-of-org-mode-links-internal-links-and-search-options/ :REDDIT_POST: https://www.reddit.com/r/emacs/comments/ud75wt/full_example_of_orgmode_links_internal_links_and/ :COMMIT_ORG_MODE: 407104459b86a40e03f5f1f323d28ad935721ef3 :END: Hey Emacser, I hope you are doing well :) In [[#/2022-04-04-search-options-link-abbreviations-and-org-open-at-point/][this post (2022-04-04)]] we talked about search options in file links, link abbreviations and the implementation of the command [[org-mode:lisp/org.el::(defun org-open-at-point (&optional arg][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": #+BEGIN_SRC text org-open-at-point │ └> org-link-open │ └> org-link-open-as-file │ └> org-open-file │ └> org-link-search #+END_SRC As the post has become bigger than I expected, I decided not to talk about the last call to the function [[org-mode:lisp/ol.el::(defun org-link-search][org-link-search]], despite my desire to do so. Some time later, I decided to give it a chance. I looked [[org-mode:lisp/ol.el::(defun org-link-search][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 [[#/2022-04-13-cath-throw-pattern-in-emacs-source-code/][A tour of the catch/throw pattern in the Emacs source code (2022-04-13)]] 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-mode:lisp/ol.el::(defun org-link-search][org-link-search]] to make the jump when we call [[org-mode:lisp/org.el::(defun org-open-at-point (&optional arg][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-mode:lisp/ol.el::(defun org-link-search][org-link-search]]. With that done, just between us, let's say we are done with [[org-mode:lisp/ol.el::(defun org-link-search][org-link-search]] function and we've completed the walk inside [[org-mode:lisp/org.el::(defun org-open-at-point (&optional arg][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-mode:lisp/ol.el::(defun org-link-search][org-link-search]] to make the jump when we call [[org-mode:lisp/org.el::(defun org-open-at-point (&optional arg][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) 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-mode:lisp/ol.el::(defun org-link-search][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-mode:lisp/ol.el::(defun org-link-search][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-mode:lisp/ol.el::(defun org-link-search][org-link-search]]. If you finally decide to look at the implementation of [[org-mode:lisp/ol.el::(defun org-link-search][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: - [[info:org#Internal Links]], - [[info:org#Search Options]], - [[info:org#Literal Examples]], - [[info:org#Org Syntax]]. Here is the example: #+BEGIN_SRC org ,* 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)) (ref: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 ~<>~ 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 ~<>~ 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 ~<>~ 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 <>, 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 ~<>~ 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 ~<>~ 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 <>! ,* headline 3 #+END_SRC ** 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 [[emacs:lisp/help-fns.el::(defun describe-symbol][describe-symbol]] bound by default to ~C-h o~. In [[org-mode:lisp/ol.el::(defun org-link-search][org-link-search]], the variable [[emacs:src/buffer.c::DEFVAR_PER_BUFFER ("case-fold-search"][case-fold-search]] is set ~t~ which means that searches and matches should ignore case: #+BEGIN_SRC emacs-lisp (let ((case-fold-search t)) (string-match "FOO" "foo")) ; 0 (let ((case-fold-search nil)) (string-match "FOO" "foo")) ; nil #+END_SRC In [[org-mode:lisp/ol.el::(defun org-link-search][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 [[emacs:lisp/subr.el::(defun replace-regexp-in-string][replace-regexp-in-string]] like this: #+BEGIN_SRC emacs-lisp (let ((s "search \n \t\t option")) (replace-regexp-in-string "\n[ \t]*" " " s)) ;; "search option" #+END_SRC The search option string ~s~ given to [[org-mode:lisp/ol.el::(defun org-link-search][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 [[emacs:src/editfns.c::DEFUN ("string-to-char"][string-to-char]] returns the first character of a string and we can use like this: #+BEGIN_SRC emacs-lisp (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 #+END_SRC In [[org-mode:lisp/ol.el::(defun org-link-search][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 [[emacs:lisp/subr.el::(defun split-string (string &][split-string]] is used to split one of the string into substrings bounded by whitespace like this: #+BEGIN_SRC emacs-lisp (split-string "foo bar baz") ; ("foo" "bar" "baz") #+END_SRC When the given string ~s~ starts with a star ~*~, the star is removed using the function [[emacs:src/fns.c::DEFUN ("substring"][substring]] like this: #+BEGIN_SRC emacs-lisp (substring "*foo" 1) ; "foo" #+END_SRC The searches are done using the function [[emacs:src/search.c::DEFUN ("re-search-forward"][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 [[emacs:src/search.c::DEFUN ("re-search-forward"][re-search-forward]] is a regexp using the function [[emacs:src/search.c::DEFUN ("regexp-quote"][regexp-quote]] that returns a regexp string which matches exactly the string we gave it and this is how [[org-mode:lisp/ol.el::(defun org-link-search][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 [[emacs:src/search.c::DEFUN ("regexp-quote"][regexp-quote]] like this: #+BEGIN_SRC emacs-lisp (regexp-quote "foo.bar") ; "foo\\.bar" (regexp-quote "foo+bar") ; "foo\\+bar" (regexp-quote "foo?bar") ; "foo\\?bar" (regexp-quote "foo*bar") ; "foo\\*bar" #+END_SRC We can go on and on but we won't, I think that's enought 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. Thank you Ihor Radchenko. * [2022-04-22 Fri] Programming with Elisp is magic :PROPERTIES: :CUSTOM_ID: /2022-04-22-programming-with-elisp-is-magic/ :REDDIT_POST: https://www.reddit.com/r/emacs/comments/u9e9r3/programming_with_elisp_is_magic/ :COMMIT_ORG_MODE: bee31004bcf49bbdf353b41d3ee4e6b0c02cc415 :END: BOOM!!! Let's get to the point!!! What's magic when programming Elisp code is that at any time: 1) we can extract a little part of the program, 2) replace some symbols by custom values, 3) send it to the minibuffer with ~M-x eval-expression~ (or ~pp-eval-expression~), press ~RET~ and, 4) automatically get back some value in the echo area (or in the dedicated buffer ~*Pp Eval Output*~). In almost no time, misconceptions about what a program does (or why a program fails) can be spot that way. Those who have already read the post about [[https://www.reddit.com/r/emacs/comments/tw3fpu/search_options_in_file_links_link_abbreviations/][link abbrevations and org-open-at-point]] might be familiar with the above description. Indeed, today's post is literally a section extracted from this previous post. Why am I publishing it again? Because that post didn't received much success (maybe the wrong topic, maybe too long, maybe I don't know) and so does the section about the magic of programming with Elisp. And this is really unfortunate because it describes a super effective strategy do deal with Elisp code. Beside adding "print statements everywhere" (~message~ in Elisp parlance) this is my best tool for working with Elisp code, and I want everyone to know it and use it. I am not a magician who needs to keep his tricks secret, quite the contrary. So, I decided to give that strategy to deal with Elisp code another chance with that post. I hope you find it useful. Here is the context. When we introduced the section "Programming with Elisp is magic" in the post about [[https://www.reddit.com/r/emacs/comments/tw3fpu/search_options_in_file_links_link_abbreviations/][link abbrevations and org-open-at-point]], we were studying some implementation details of the function [[org-mode:lisp/org-element.el::(defun org-element-link-parser (][org-element-link-parser]] that parses an Org link at point, if any. For instance, in the following org buffer (if you have never used "link abbreviations", I encourage you to read the info node [[info:org#Link Abbreviations]]: #+BEGIN_SRC org ,#+LINK: emacs /tmp/emacs/ [[emacs:lisp/simple.el::(defun next-error (&optional]] #+END_SRC with the point at the beginning of the link, calling the function [[org-mode:lisp/org-element.el::(defun org-element-link-parser (][org-element-link-parser]] returns the org object (a list): #+BEGIN_SRC emacs-lisp (link (:type "file" :path "/tmp/emacs/lisp/simple.el" :format bracket :raw-link "/tmp/emacs/lisp/simple.el::(defun next-error (&optional" :application nil :search-option "(defun next-error (&optional" :begin 28 :end 82 :contents-begin nil :contents-end nil :post-blank 0)) #+END_SRC We were particularly interested in the computation of the values of the properties ~:path~ and ~:search-option~. The function [[org-mode:lisp/org-element.el::(defun org-element-link-parser (][org-element-link-parser]] is 128 lines long, uses many regexp to do its jobs, mutates several times the local let binded variable ~path~ that is returned as the value of the property ~:path~ (the one we are interested in). The first time, I looked at its code, I couldn't understand all the subtleties of the implementation just by reading it. This is not a problem, because when reading is not enough, I always use the same strategy: I break the problem down into pieces until I arrive at simple s-expressions that I can understand. And doing it in Emacs/Elisp is super cheap because you can evaluate ANYTHING, ANYWHERE, ANYTIME, for FREE (you just pay the computation). Think about it! Fast feedback, this is the magic of programming with Elisp. So here we are. Let's say we want to be sure that the following snippet in the function [[org-mode:lisp/org-element.el::(defun org-element-link-parser (][org-element-link-parser]] does what it seems to do: #+BEGIN_SRC emacs-lisp (when (string-match "::\\(.*\\)\\'" path) (setq search-option (match-string 1 path)) (setq path (replace-match "" nil nil path))) #+END_SRC In our example, at that point in the function, the local variable ~path~ has the string value ~"/tmp/emacs/lisp/simple.el::(defun next-error (&optional"~. We can test the result of the ~when~ condition by evaluating the following: #+BEGIN_SRC emacs-lisp (string-match "::\\(.*\\)\\'" "/tmp/emacs/lisp/simple.el::(defun next-error (&optional") ;; 25 #+END_SRC By reading the help of ~string-match~, we know that it returns the index of the start of the first match or ~nil~. Ok, there's a match. But, to me the string ~"/tmp/emacs/lisp/simple.el::(defun next-error (&optional"~ is to long with to many repetive characters that don't appear in the regexp ~"::\\(.*\\)\\'"~ to wrap my head around what's going on. So, let's use the good ~foo~ and ~bar~ words to simplify our discoveries and gain confidence about this piece of code. In the regexp, the only part "that seems" of interest is ~::~, so let's try again with the strings ~"/tmp/foo::bar"~, ~"/tmp/foo::"~ and ~"/tmp/foo"~: #+BEGIN_SRC emacs-lisp (string-match "::\\(.*\\)\\'" "/tmp/foo::bar") ;; 8 (string-match "::\\(.*\\)\\'" "/tmp/foo::") ;; 8 (string-match "::\\(.*\\)\\'" "/tmp/foo") ;; nil #+END_SRC It become clearer. We start to get a sense of the match. By reading the documentation ([[info:elisp#Simple Match Data]]), we learn (or recall): 1) that search functions like ~string-match~ or ~looking-at~ set the match data for every successful search, 2) and if the first argument of ~match-string~ is ~0~, we get the entire matching text and if it's ~1~ we get the first parenthetical subexpression of the given regular expression. So, continuing with the string ~"/tmp/foo::bar"~, we have: #+BEGIN_SRC emacs-lisp (let ((path "/tmp/foo::bar")) (when (string-match "::\\(.*\\)\\'" path) (list (match-string 0 path) (match-string 1 path)))) ;; ("::bar" "bar") #+END_SRC Reading the help buffer about ~replace-match~ tells us that this function replaces the text matched by the last search with its first argument. And if we give it an optional fourth argument being a string, the replacement is made on that string. So replacing the entire match with the empty string ~""~ should remove the matched part of the string: #+BEGIN_SRC emacs-lisp (let ((path "/tmp/foo::bar")) (when (string-match "::\\(.*\\)\\'" path) (replace-match "" nil nil path))) ;; "/tmp/foo" #+END_SRC Now putting everything together we can write the following example: #+BEGIN_SRC emacs-lisp (let ((path "/tmp/foo::bar")) (when (string-match "::\\(.*\\)\\'" path) `(:search-option ,(match-string 1 path) :path ,(replace-match "" nil nil path)))) ;; (:search-option "bar" ;; :path "/tmp/foo") #+END_SRC And maybe we've removed some misconceptions about this part of the function [[org-mode:lisp/org-element.el::(defun org-element-link-parser (][org-element-link-parser]]. WE ARE DONE!!! * [2022-04-19 Tue] If you have never used wgrep with rg.el to rename a function in several files, try it | that will blow your mind :PROPERTIES: :CUSTOM_ID: /2022-04-19-wgrep-with-rg-el-to-rename-a-function-in-several-files/ :REDDIT_POST: https://www.reddit.com/r/emacs/comments/u6yibf/if_you_have_never_used_wgrep_with_rgel_to_rename/ :COMMIT_ORG_MODE: 685d78f63cbe210448508b23c0b47d7be70aedfb :END: Hey Emacsers, Have you ever needed to rename a function that appears in several files? Let's see how we can do this with Emacs. In the post [[#/2022-04-15-ripgrep-emacs-rg-el/][the fantastic rg.el]], we've seen that ~rg.el~ is a nice Emacs interface to the cli ~ripgrep~ which lets us do searches for regexp in files interactively with ~rg~ command, get the results in a dedicated buffer ~*rg*~ (by default), browse those matches, modify the searches parameters and modify the matched regexps, all from within the dedicated buffer ~*rg*~. In this post we see how to rename interactively a function that appears in several files using [[https://github.com/dajva/rg.el][rg.el]] and [[https://github.com/mhayashi1120/Emacs-wgrep][wgrep]]! Let's go ;) ** Initial state Let assume that we are working on the ~org-mode~ code base : git clone https://git.savannah.gnu.org/git/emacs/org-mode.git and we want to rename the function ~org-link-expand-abbrev~ (that replaces link abbreviations in a given org link, read its [[#/2022-04-04-search-options-link-abbreviations-and-org-open-at-point/#org-link-expand-abbrev][dedicated section]] in the post [[#/2022-04-04-search-options-link-abbreviations-and-org-open-at-point/][search options and link abbreviations]] for more details) into ~org-link-RENAMED~ like this: : org-link-expand-abbrev -> org-link-RENAMED We use ~git~ (in a terminal) to "monitor" our changes in the code base and to revert back to the initial state at the end of this "demonstration". First, running ~git status~ tells us that we are on the branch ~main~, we have nothing to commit and our working tree is clean: #+BEGIN_SRC bash :dir org-mode :results output git status #+END_SRC prints: #+BEGIN_SRC text On branch main Your branch is up to date with 'origin/main'. nothing to commit, working tree clean #+END_SRC We can obtain the current commit (on which I'm running the example, your ouptuts might differ a little bit if you're checked out at another commit) by running this following command: #+BEGIN_SRC bash :dir org-mode :results output git rev-parse --short HEAD #+END_SRC that prints: #+BEGIN_SRC text 685d78f63 #+END_SRC Now that we are clear about the initial state, we can continue. ** Call ~wgrep-change-to-wgrep-mode~, make changes and abort changes with ~wgrep-abort-changes~ Let's search for the regexp ~org-link-expand-abbrev~ (that exactly matches the string ~org-link-expand-abbrev~) in ~org-mode~ directory using ~rg.el~: 1) ~M-x rg~, 2) write ~org-link-expand-abbrev~, 3) select the directory where ~org-mode~ source code is, 4) choose ~all~ as type file. We get the following buffer named ~*rg*~ (in the mode ~rg-mode~) that shows that we've matched ~org-link-expand-abbrev~ twice, once in the file [[org-mode:lisp/ol.el]] and once in the file [[org-mode:lisp/org-element.el]]: #+BEGIN_SRC text -*- mode: rg; default-directory: "/tmp/org-mode/" -*- rg started at Mon Apr 18 13:03:59 /usr/bin/rg [...] File: [ol.el] lisp/ol.el 1011 (defun org-link-expand-abbrev (link) File: [org-element.el] lisp/org-element.el 3497 (setq raw-link (org-link-expand-abbrev rg finished (2 matches found) at Mon Apr 18 13:03:59 #+END_SRC Now in the buffer ~*rg*~, we press ~e~ (bound to ~wgrep-change-to-wgrep-mode~) and two things happens: 1) the matched lines are now editable in the buffer ~*rg*~ and, 2) the keymap ~wgrep-mode-map~ becomes the local map. Then, in ~*rg*~ buffer, we transform ~org-link-expand-abbrev~ into ~org-link-RENAMED~ the way we prefer (we have all the Emacs power, some of us might use ~query-replace~, other might use [[https://github.com/magnars/multiple-cursors.el][multiple-cursors.el]], other [[https://github.com/victorhge/iedit][iedit]], etc.). And so ~*rg*~ buffer looks like this: #+BEGIN_SRC text -*- mode: rg; default-directory: "/tmp/org-mode/" -*- rg started at Mon Apr 18 13:03:59 /usr/bin/rg [...] File: [ol.el] lisp/ol.el 1011 (defun org-link-RENAMED (link) File: [org-element.el] lisp/org-element.el 3497 (setq raw-link (org-link-RENAMED rg finished (2 matches found) at Mon Apr 18 13:03:59 #+END_SRC Now that we've finished editing the buffer ~*rg*~, we change our mind and finally decide that we no longer want to apply those changes to the corresponding files. No problem, we just have to hit ~C-c C-k~ (bound to ~wgrep-abort-changes~) to abort the changes. We're back to the "normal" ~*rg*~ buffer where nothing is editable and none of our changes have been taken into account: #+BEGIN_SRC text -*- mode: rg; default-directory: "/tmp/org-mode/" -*- rg started at Mon Apr 18 13:03:59 /usr/bin/rg [...] File: [ol.el] lisp/ol.el 1011 (defun org-link-expand-abbrev (link) File: [org-element.el] lisp/org-element.el 3497 (setq raw-link (org-link-expand-abbrev rg finished (2 matches found) at Mon Apr 18 13:03:59 #+END_SRC At that point maybe you should (must) stop me and ask: #+BEGIN_SRC text Are we really 'back to normal'? How can I be sure that my files haven't been compromised? Could you prove it? #+END_SRC As we started with a clean working tree in a ~git~ repository with nothing to commit, we just have to run the command: #+BEGIN_SRC bash :dir org-mode :results output git status #+END_SRC that prints: #+BEGIN_SRC text On branch main Your branch is up to date with 'origin/main'. nothing to commit, working tree clean #+END_SRC This way, we can be sure that none of our files have been modified. Note that when we are editing the buffer ~*rg*~, until we explicitly run a command (like ~wgrep-abort-changes~) of ~wgrep~ package, nothing is reflected in the file system (neither in the buffers that are visiting files that could be modified by ~wgrep~, for instance in our case [[org-mode:lisp/ol.el]] and [[org-mode:lisp/org-element.el]]). ** Changes applied to the file system: ~wgrep-finish-edit~ and ~wgrep-save-all-buffers~ Now, let's modify again the ~*rg*~ buffer, the same way as before (starting by pressing ~e~ (bound to ~wgrep-change-to-wgrep-mode~) to make the buffer editable): #+BEGIN_SRC text -*- mode: rg; default-directory: "/tmp/org-mode/" -*- rg started at Mon Apr 18 13:03:59 /usr/bin/rg [...] File: [ol.el] lisp/ol.el 1011 (defun org-link-RENAMED (link) File: [org-element.el] lisp/org-element.el 3497 (setq raw-link (org-link-RENAMED rg finished (2 matches found) at Mon Apr 18 13:03:59 #+END_SRC This time we want to save those changes in the buffer ~*rg*~ and want to see them reflected in the corresponding files. To do so, we press ~C-x C-s~ (bound to ~wgrep-finish-edit~) and we see in the echo area: : Successfully finished. (2 changed) We might think that those changes have been reflected in the file sytem but this is not the case by default and we can check it as we did before by running the command ~git status~. In the buffer ~*rg*~ that is no longer editable and that took into account those changes, we can do two things: 1) navigate between the matched lines that we've changed pressing ~n~ or ~p~. We see the changes reflected in the buffers ~ol.el~ (visiting the file [[org-mode:lisp/ol.el]]) and ~org-element.el~ (visiting the file [[org-mode:lisp/org-element.el]]). We also observe that those modifications are not saved in the buffers. And if we change our mind again and we no longer want those changes to be applied, in each buffer we can "manually" undo those changes. 2) if we want those changes to be reflected in the file system, we can call the command ~wgrep-save-all-buffers~. We decide to save all the buffers, and so we run: : M-x wgrep-save-all-buffers This time our our changes have been reflected in the file system and we can check it by running the following command: #+BEGIN_SRC bash :dir org-mode :results output git status #+END_SRC that prints: #+BEGIN_SRC text On branch main Your branch is up to date with 'origin/main'. Changes not staged for commit: (use "git add ..." to update what will be committed) (use "git checkout -- ..." to discard changes in working directory) modified: lisp/ol.el modified: lisp/org-element.el no changes added to commit (use "git add" and/or "git commit -a") #+END_SRC So the files [[org-mode:lisp/ol.el]] and [[org-mode:lisp/org-element.el]] have been modified. To be sure that those modifications correspond to our renaming, we can run the following command that prints the ~git~ difference between the last commit and the unstaged modified files: #+BEGIN_SRC bash :dir org-mode :results output git diff #+END_SRC #+BEGIN_SRC diff diff --git a/lisp/ol.el b/lisp/ol.el index 1b2bb9a9a..642dcb5da 100644 --- a/lisp/ol.el +++ b/lisp/ol.el @@ -1008,7 +1008,7 @@ and then used in capture templates." if store-func collect store-func)) -(defun org-link-expand-abbrev (link) +(defun org-link-RENAMED (link) "Replace link abbreviations in LINK string. Abbreviations are defined in `org-link-abbrev-alist'." (if (not (string-match "^\\([^:]*\\)\\(::?\\(.*\\)\\)?$" link)) link diff --git a/lisp/org-element.el b/lisp/org-element.el index 28339c1b8..cbfcfe074 100644 --- a/lisp/org-element.el +++ b/lisp/org-element.el @@ -3494,7 +3494,7 @@ Assume point is at the beginning of the link." ;; (e.g., insert [[shell:ls%20*.org]] instead of ;; [[shell:ls *.org]], which defeats Org's focus on ;; simplicity. - (setq raw-link (org-link-expand-abbrev + (setq raw-link (org-link-RENAMED (org-link-unescape (replace-regexp-in-string "[ \t]*\n[ \t]*" " " #+END_SRC If we were in a refactoring phase in our development where we've decided to rename ~org-link-expand-abbrev~ by ~org-link-RENAMED~, the next step would be to commit those changes. As this is not our case (and also to demonstrate how to revert back ALL the changes not commited that we've made in a git repository) we prefer to revert back to the last commit by running the following command: #+BEGIN_SRC bash :dir org-mode :results output git checkout . #+END_SRC And we can verify that we're back to our original state by running the following commands ~git status~ and ~git rev-parse --short HEAD~ as we did at the beginning of this post. ** Make the changes automatic with ~wgrep-auto-save-buffer~ As written in the documentation of ~wgrep~, if we want to save the buffers automatically when we call ~wgrep-finish-edit~ (and so apply the changes in the file system), we can set the variable ~wgrep-auto-save-buffer~ to ~t~ like this: #+BEGIN_SRC emacs-lisp (setq wgrep-auto-save-buffer t) #+END_SRC ** We could have used ~sed~ to do it non interactively Renaming a function like we did before with ~rg.el~ and ~wgrep~ could also be done using the cli ~sed~ (that can search some regexp in files (not only) and replace matches in-place with another string) combined with eiter ~find~ or ~grep~ to list the files we want to modify which are "passed" to ~sed~ using the utility ~xargs~. Specifically, in ~org-mode~ directory, we can replace the occurences of ~org-link-expand-abbrev~ by ~org-link-RENAMED~, by running the following command line (in a terminal): : find . -type f -print0 | xargs -0 sed -i 's/org-link-expand-abbrev/org-link-RENAMED/g' 1) ~-print0~ tells ~find~ to separate file names with the null character, 2) ~-0~ tells ~xargs~ that arguments are separated by the null character, 3) ~-i~ command line flag tells ~sed~ to do the substitions (command ~sd~ of ~sed~) of ~org-link-expand-abbrev~ by ~org-link-RENAMED~ in-place and, 4) the flag ~g~ (in ~'s/.../.../g'~) tells ~sed~ to apply the replacement to all matches not just the first. Instead of using ~find~, we could have use ~grep~ to list not all the files in ~org-mode~ directory but only those that contains ~org-link-expand-abbrev~. And doing so, we would have made the same replacements. Here is the full command line to run in a terminal that produces the same result: : grep -rlZ 'org-link-expand-abbrev' | xargs -0 sed -i 's/org-link-expand-abbrev/org-link-RENAMED/g' 1) ~r~ flag tells ~grep~ to search recursively in the current directory, 2) ~l~ flag tells ~grep~ to print only file names (not the matches), 3) ~Z~ flag tells grep to print the null characher after each file names, 4) after the pipe ~|~, it's the same as before. WE ARE DONE!!! * [2022-04-15 Fri] Ripgrep is fantastic | Emacs is fantastic | BOOM you get the fantastic rg.el :PROPERTIES: :CUSTOM_ID: /2022-04-15-ripgrep-emacs-rg-el/ :REDDIT_POST: https://www.reddit.com/r/emacs/comments/u4c5rc/ripgrep_is_fantastic_emacs_is_fantastic_boom_you/ :COMMIT_EMACS: 504779f744ccc33c2177dafa34e21d83f6c640a0 :END: Hey Emacsers, Do you know ~rg.el~? [[https://github.com/dajva/rg.el][rg.el]] is an Emacs UI for the cli [[https://github.com/BurntSushi/ripgrep][ripgrep]]. What does that mean? Well... Let's say we want to learn how to write Elisp macros because it seems to fit with the problem we are facing. We've already read the info node about macros (~M-x eval-expression RET (info "(elisp)Macros")~) that contains many examples but we want more examples. We know that macros are defined with the macro ~defmacro~, so why don't we search for calls of ~defmacro~ in Emacs source code to get usage examples of it? In the directory where we've cloned Emacs source code we can run the following command line (in a terminal) that searches directories for the regexp ~\(defmacro~ that are of type ~elisp~, while respecting gitignore using ~ripgrep~ (the binary ~rg~): #+BEGIN_SRC bash rg '\(defmacro' -t elisp #+END_SRC This prints the following in the standard output: #+BEGIN_SRC text admin/cus-test.el 304:(defmacro cus-test-load-1 (&rest body) lisp/align.el 1260:(defmacro align--set-marker (marker-var pos &optional type) lisp/custom.el 231:(defmacro defcustom (symbol standard doc &rest args) 389:(defmacro defface (face spec doc &rest args) 492:(defmacro defgroup (symbol members doc &rest args) 1139:(defmacro deftheme (theme &optional doc) ... MORE HITS HERE #+END_SRC Now, you do whatever you want with those matches in the terminal. You can browse the ouptut looking for some information, chose a match, the open the corresponding file and go to the appropriate line. Everything in the terminal... OR, you can use ~rg.el~ and use its command ~rg~ like this: 1) ~M-x rg~, 2) write the regexp: ~\(defmacro~, 3) select the directory where Emacs source code is, 4) choose ~elisp~ as type file, and you get the following buffer named ~*rg*~ (in the mode ~rg-mode~) with exactly the same matches as before with the command line: #+BEGIN_SRC text -*- mode: rg; default-directory: "/tmp/emacs/" -*- rg started at Fri Apr 15 16:56:04 /usr/bin/rg [...] File: admin/cus-test.el 304 (defmacro cus-test-load-1 (&rest body) File: lisp/align.el 1260 (defmacro align--set-marker (marker-var pos &optional type) File: lisp/custom.el 231 (defmacro defcustom (symbol standard doc &rest args) 389 (defmacro defface (face spec doc &rest args) 492 (defmacro defgroup (symbol members doc &rest args) 1139 (defmacro deftheme (theme &optional doc) ... MORE HITS HERE #+END_SRC The difference is that now you can press (the bindings are defined in ~rg-mode-map~): 1) ~n~ (bound ~next-error-no-select~) to move to the next line with a match, show that file in other buffer and highlight the match, 2) ~p~ (bound ~previous-error-no-select~) move to the previous line with a match, show that file in other buffer and highlight the match. isn't FANTASTIC? Assuming that we've look at some macro definitions, and we've seen that the file ~lisp/subr.el~ defines many of the macros that we have already used in our own code like ~push~, ~pop~ and ~when~. Maybe to reduce the numbers of macro to look at (~1505~ calls to ~defmacro~ - commit ~504779f744ccc33c2177dafa34e21d83f6c640a0~) we can consider only those defined in the file ~lisp/subr.el~. This can be done by modifying a little bit the previous command line like this: #+BEGIN_SRC bash rg '\(defmacro' lisp/subr.el #+END_SRC But, now that we are using ~rg.el~ (INSIDE EMACS) we no longer want to use directly the terminal for that task. So, we go back to the buffer ~*rg*~ and we press ~f~ (bound to ~rg-rerun-change-files~) and we write ~subr.el~ (the last part of the file name), then we press ~RET~ to rerun ~rg~ with the same regexp as before (~\(defmacro~), but this time, only matches in the file ~lisp/subr.el~ are presented in the buffer ~*rg*~: #+BEGIN_SRC text -*- mode: rg; default-directory: "/tmp/emacs/" -*- rg started at Fri Apr 15 17:29:43 /usr/bin/rg [...] File: lisp/subr.el 32 (defmacro declare-function (_fn _file &rest _args) 74 (defmacro noreturn (form) 81 (defmacro 1value (form) 88 (defmacro def-edebug-spec (symbol spec) 110 (defmacro lambda (&rest cdr) 136 (defmacro prog2 (form1 form2 &rest body) 143 (defmacro setq-default (&rest args) 163 (defmacro setq-local (&rest pairs) 193 (defmacro defvar-local (var val &optional docstring) 210 (defmacro push (newelt place) 225 (defmacro pop (place) 243 (defmacro when (cond &rest body) ... MORE HITS HERE #+END_SRC Now we've reduce our study of writing macros to 52 "classic" macros. Still as before, we can use ~n~ and ~p~ to look at those macro definition from ~*rg*~ buffer, the definition poping up in another buffer. For me, this is insane. I love it. But 52 is still an important number. We want to look at some macro definitions to be able to write our own macro. We need it. Let's almost right now. Well... Let's reduce that number. We observe that the file ~lisp/subr.el~ defines also the macro ~with-current-buffer~ and ~with-temp-buffer~ (used everywhere). So, we decide to only look at the macro prefixed by ~with-~ defined in the file ~lisp/subr.el~. We can do it in the terminal running this command: #+BEGIN_SRC bash rg '\(defmacro with' lisp/subr.el #+END_SRC but we prefer doing it with ~rg.el~. So we go back to the buffer ~*rg*~, and now we press ~r~ (bound to ~rg-rerun-change-regexp~). This offers in the minibuffer to modify the current regexp ~\(defmacro~. We modify it to be ~\(defmacro with~, we hit return, and we get the following 20 matches: #+BEGIN_SRC text -*- mode: rg; default-directory: "/tmp/emacs/" -*- rg started at Fri Apr 15 17:49:57 /usr/bin/rg [...] File: lisp/subr.el 2092 (defmacro with-wrapper-hook (hook args &rest body) 3448 (defmacro with-undo-amalgamate (&rest body) 4188 (defmacro with-current-buffer (buffer-or-name &rest body) 4228 (defmacro with-selected-window (window &rest body) 4253 (defmacro with-selected-frame (frame &rest body) 4331 (defmacro with-output-to-temp-buffer (bufname &rest body) 4387 (defmacro with-temp-file (file &rest body) 4407 (defmacro with-temp-message (message &rest body) 4430 (defmacro with-temp-buffer (&rest body) 4445 (defmacro with-silent-modifications (&rest body) 4469 (defmacro with-output-to-string (&rest body) 4481 (defmacro with-local-quit (&rest body) 4549 (defmacro with-demoted-errors (format &rest body) 4712 (defmacro with-case-table (table &rest body) 4726 (defmacro with-file-modes (modes &rest body) 4738 (defmacro with-existing-directory (&rest body) 5297 (defmacro with-eval-after-load (file &rest body) 5417 (defmacro with-syntax-table (table &rest body) 6384 (defmacro with-mutex (mutex &rest body) 6570 (defmacro with-delayed-message (args &rest body) #+END_SRC We can now look at those definition trying to understand how macros are defined and how we can find ideas to solve our problem (either by writing our macro our deciding that a simple function might be enough...) While we are looking at those macro prefixed by ~with-~, we remember that we've seen another macro in another file that was matched in the previous search and so visible in the "previous" contents of ~*rg*~ buffer, and we want to look for it. Do we have to redo everything (~M-x rg~, ....)? Absolutely not! Still in the buffer ~*rg*~ we can just visit backward and forward the previous searches using ~C-c <~ (bound to ~rg-forward-history~) and ~C-c >~ (bound to ~rg-back-history~). Another mega cool feature of ~ripgrep~ is the flag ~--context~ (~-C~ in short) that allows to include a number of lines before and after each matches. For instance, if we want to add 2 lines before and after the matches of the regexp ~\(defmacro with~ in the file ~lisp/subr.el~, in the terminal we can run the following command: #+BEGIN_SRC bash rg --context 2 '\(defmacro with' lisp/subr.el #+END_SRC We can also do this with ~rg.el~. Let's go back to the buffer ~*rg*~ (with the search that matches ~\(defmacro with~ in the file ~lisp/subr.el~). Now we do the following: 1) we press ~m~ (bound to ~rg-menu~) that pops up a menu, 2) we press ~-C~, 3) then in the minibuffer we see ~--context=~, we write ~2~ and press ~RET~, 4) then we press ~g~ (bound to ~rg-recompile~), and this "reruns" the search adding the context around matches like this: #+BEGIN_SRC text File: lisp/subr.el 2090- 2091- 2092 (defmacro with-wrapper-hook (hook args &rest body) 2093- "Run BODY, using wrapper functions from HOOK with additional ARGS. 2094-HOOK is an abnormal hook. Each hook function in HOOK \"wraps\" -- 3446- (cancel-change-group ,handle)))))) 3447- 3448 (defmacro with-undo-amalgamate (&rest body) 3449- "Like `progn' but perform BODY with amalgamated undo barriers. 3450- -- 4186- `(internal--track-mouse (lambda () ,@body))) 4187- 4188 (defmacro with-current-buffer (buffer-or-name &rest body) 4189- "Execute the forms in BODY with BUFFER-OR-NAME temporarily current. 4190-BUFFER-OR-NAME must be a buffer or the name of an existing buffer. -- 4226- (get-buffer-create (generate-new-buffer-name name) inhibit-buffer-hooks)) 4227- 4228 (defmacro with-selected-window (window &rest body) 4229- "Execute the forms in BODY with WINDOW as the selected window. 4230-The value returned is the value of the last form in BODY. -- ... MORE HITS HERE #+END_SRC When I think that life is amazing and then I look at all the work that has already been done everywhere, I think wowwww, this is really amazing. I want to thank you all for all the great programs that lives with us thanks to your imagination and your work. ~ripgrep~ is fantastic. ~Emacs~ is fantastic. BOOM you get the fantastic ~rg.el~. What can we add to this paradise? We can add org-mode to the party. Yes, if you try to open the following org link (in a org-mode buffer), Emacs will ask you to confirm if you want to execute this elisp form, and by answering yes the result of an ~rg~ search will pops up in ~*rg*~ buffer like we did previously (assuming Emacs source code is clone under the directory ~/tmp/emacs/~): #+BEGIN_SRC org [[elisp:(rg-run "\\(defmacro with" "subr.el" "/tmp/emacs/" nil nil '("--context=2"))]] #+END_SRC WE ARE DONE!!! * [2022-04-13 Wed] Don't explain, show me examples! A tour of the catch/throw pattern in the Emacs source code :PROPERTIES: :CUSTOM_ID: /2022-04-13-cath-throw-pattern-in-emacs-source-code/ :REDDIT_POST: https://www.reddit.com/r/emacs/comments/u2u229/dont_explain_show_me_examples_a_tour_of_the/ :COMMIT_EMACS: cca47ae555bfddf87b4871988555738c335f8457 :END: Hey Elispers, Do you want to expand your Elisp toolbox? In this post we look at the ~catch/throw~ pattern offered by Elisp that allows to do nonlocal exits with the function [[emacs:src/eval.c::DEFUN ("throw"][throw]] that can be caught by the [[emacs:src/eval.c::DEFUN ("catch"][catch]] special form. For instance, in the following snippet, in a [[emacs:src/eval.c::DEFUN ("catch"][catch]] block: 1) we define a local list ~l~, 2) then we loop forever (~(while t ...)~), 3) in this loop we generate a random (integer) number between ~0~ and ~9~, 4) then: - if this number is not equal to ~1~, we add it to the list ~l~ and we repeat, - and if it is equal to ~1~, the [[emacs:src/eval.c::DEFUN ("throw"][throw]] statement transfers the control to the enclosing [[emacs:src/eval.c::DEFUN ("catch"][catch]] with the tag ~:one~ (we leave out the ~while~ loop and also the ~let~ block) which returns immediately the builded list ~l~ (last argument of the [[emacs:src/eval.c::DEFUN ("throw"][throw]] statement). #+BEGIN_SRC emacs-lisp (catch :one (let (l) (while t (setq k (random 10)) (if (/= k 1) (push k l) (throw :one l))))) ;; (5 3 8 8 0 3) ;; these are pseudo-random numbers that depend on the seed ;; used by `random' on your running Emacs, so evaluting the ;; preceding form can return something different on your machine. #+END_SRC Handmade examples are effective for discovering new things or remembering the syntax of known things. But when we have to write programs that solve "real" problems, having already been exposed to REAL WORLD examples is a competitive advantage. Indeed, REAL WORLD examples often provide "standard methods" to implement specific actions/tasks in given "environments". In this post, we first present some handmade examples of the ~catch/throw~ pattern and then we look at REAL WORLD examples of the ~catch/throw~ pattern that we find in Emacs source code. Let's go! ** The catch/throw pattern: handmade examples In the info node about [[emacs:src/eval.c::DEFUN ("catch"][catch]] and [[emacs:src/eval.c::DEFUN ("throw"][throw]] ([[info:elisp#Catch and Throw]]), we can read: #+BEGIN_SRC text Most control constructs affect only the flow of control within the construct itself. The function ‘throw’ is the exception to this rule of normal program execution: it performs a nonlocal exit on request. (There are other exceptions, but they are for error handling only.) ‘throw’ is used inside a ‘catch’, and jumps back to that ‘catch’. #+END_SRC So with [[emacs:src/eval.c::DEFUN ("throw"][throw]] inside [[emacs:src/eval.c::DEFUN ("catch"][catch]] we can modify the flow of control. Let's see how with the following examples. We don't provide any explanation hoping that the evaluations in comments show how the flow of control has been modified. Note that if you read this post inside Emacs with org-mode, you can hit ~C-c '~ ([[org-mode:lisp/org.el::(defun org-edit-special (&optional arg][org-edit-special]] by default) in the source block to open a dedicated ~emacs-lisp~ buffer where you can modify and evaluate the examples the way you want as much as you need to be confident about [[emacs:src/eval.c::DEFUN ("catch"][catch]] and [[emacs:src/eval.c::DEFUN ("throw"][throw]]. #+BEGIN_SRC emacs-lisp (catch :foo 'evaluated (throw :foo (+ 2 2)) 'not-evaluated) ; 4 (catch :foo (let ((a-value (+ 3 3))) 'evaluated (throw :foo a-value) 'not-evaluated)) ; 6 (catch 'foo 'evaluated (throw 'foo 'from-throw) 'not-evaluated) ; from-throw (let ((tag-catch 'foo) (tag-throw 'foo)) (catch tag-catch 'evaluated (throw tag-throw 'from-throw) 'not-evaluated)) ; from-throw (catch 'foo 'evaluated-1 (when nil (throw 'foo 'from-throw)) 'evaluated-2) ; evaluated-2 ;; nested `catch' (catch 'foo 'evaluated-1 (catch 'bar 'evaluated-2 (throw 'foo 'from-throw) 'not-evaluated) 'not-evaluated) ; from-throw (catch 'foo 'evaluated-1 (catch 'bar 'evaluated-2 (throw 'bar 'from-throw) 'not-evaluated) 'evaluated-3) ; evaluated-3 ;; `throw' called from another function (let ((f-throw (lambda (x) (throw :foo x)))) (catch :foo (funcall f-throw :bar))) ; :bar ;; raise an error (catch 'foo 'evaluated (throw 'bar t) 'not-evaluated) ; error: (no-catch bar t) #+END_SRC ** The catch/throw pattern: real world examples There are more than 1000 [[emacs:src/eval.c::DEFUN ("catch"][catch]] blocks in Emacs source code. Let's pick some of them that seems to represent in some way the "common" usage of [[emacs:src/eval.c::DEFUN ("catch"][catch]] blocks. Almost all those [[emacs:src/eval.c::DEFUN ("catch"][catch]] blocks follow the same structure: 1) enter in a [[emacs:src/eval.c::DEFUN ("catch"][catch]] block, 2) loop (either by iterating on a structure or by "traversing" a buffer), 3) for each iteration test something and decide if iterate or [[emacs:src/eval.c::DEFUN ("throw"][throw]], 4) if thrown in the loop, leave the [[emacs:src/eval.c::DEFUN ("catch"][catch]] block and return the value passed to the [[emacs:src/eval.c::DEFUN ("throw"][throw]] statement, if ended the loop normally, evaluate the last parts of the [[emacs:src/eval.c::DEFUN ("catch"][catch]] block and return the last one. *** With [[emacs:lisp/subr.el::(defmacro dolist][dolist]] Sometimes, we want to loop over a list and if some "conditions" are verified for an item, we want to return without looping over the rest of the list. This can be done in a [[emacs:src/eval.c::DEFUN ("catch"][catch]] block using [[emacs:lisp/subr.el::(defmacro dolist][dolist]] with a structure similar to this one: #+BEGIN_SRC emacs-lisp (catch :tag (dolist (...) ... (when some-condition-is-true (throw :tag 'some-value))) ...) #+END_SRC We encounter this pattern in the function [[emacs:lisp/emacs-lock.el::(defun emacs-lock--exit-locked-buffer][emacs-lock--exit-locked-buffer]] that returns the first exit-locked buffer found in the list of all live buffers ~(buffer-list)~: #+BEGIN_SRC emacs-lisp (defun emacs-lock--exit-locked-buffer () "Return the first exit-locked buffer found." (save-current-buffer (catch :found (dolist (buffer (buffer-list)) (set-buffer buffer) (unless (or (emacs-lock--can-auto-unlock 'exit) (memq emacs-lock-mode '(nil kill))) (throw :found buffer))) nil))) #+END_SRC We also encounter this pattern in the function [[emacs:lisp/frame.el::(defun handle-delete-frame][handle-delete-frame]] that handles delete-frame events from the X server. This function looks for a "valid frame" (among other stuff being different from the frame where the X event occured) in the list of frames returned by ~(frame-list)~ in order to decide if it is safe to delete the frame where the X event occured with [[emacs:src/frame.c::DEFUN ("delete-frame"][delete-frame]] or if it is better to call the function [[emacs:lisp/files.el::defun save-buffers-kill-emacs][save-buffers-kill-emacs]]: #+BEGIN_SRC emacs-lisp (defun handle-delete-frame (event) "Handle delete-frame events from the X server." (interactive "e") (let* ((frame (posn-window (event-start event)))) (if (catch 'other-frame (dolist (frame-1 (frame-list)) ;; A valid "other" frame is visible, has its `delete-before' ;; parameter unset and is not a child frame. (when (and (not (eq frame-1 frame)) (frame-visible-p frame-1) (not (frame-parent frame-1)) (not (frame-parameter frame-1 'delete-before))) (throw 'other-frame t)))) (delete-frame frame t) ;; xxx says it is ok to ask questions before terminating. (save-buffers-kill-emacs)))) #+END_SRC Note that [[emacs:lisp/frame.el::(defun handle-delete-frame][handle-delete-frame]] is bound to the event ~delete-frame~ in the keymap [[emacs:src/keyboard.c::DEFVAR_LISP ("special-event-map"][special-event-map]] ([[help:special-event-map]]). Now, let's have a look on the function [[emacs:lisp/net/newst-reader.el::(defun newsticker--icon-read][newsticker--icon-read]]. This function is defined in the file [[emacs:lisp/net/newst-reader.el]] part of the package [[emacs:lisp/net/newsticker.el]] which is from its description section: #+BEGIN_SRC text ... an "Atom aggregator", "RSS reader", "RSS aggregator", and "Feed Reader". #+END_SRC We did not choose this function for the service it provides to the package [[emacs:lisp/net/newsticker.el]] but because it is an interesting example dealing with two types of "controlled" nonlocal exits: 1) a nonlocal exit generated by [[emacs:src/eval.c::DEFUN ("throw"][throw]] and handled by [[emacs:src/eval.c::DEFUN ("catch"][catch]] and, 2) a nonlocal exit due to an error that can occur in a function (specifically [[emacs:lisp/image.el::(defun create-image][create-image]]) and handled by [[emacs:src/eval.c::DEFUN ("condition-case"][condition-case]] . This function can be seen as a direct application of the material in the info node ([[info:elisp#Nonlocal Exits]]). The function [[emacs:lisp/net/newst-reader.el::(defun newsticker--icon-read][newsticker--icon-read]] takes an icon name as input, try to create and return an Emacs image for that icon looking for the image from the disk in the user newsticker directory, and if it can't (because it doesn't exist or it fails at creating the corresponding image) it returns a default image provided by Emacs distribution: #+BEGIN_SRC emacs-lisp (defun newsticker--icon-read (feed-name-symbol) "Read the cached icon for FEED-NAME-SYMBOL from disk. Return the image." (catch 'icon (when (file-exists-p (newsticker--icons-dir)) (dolist (file (directory-files (newsticker--icons-dir) t (concat (symbol-name feed-name-symbol) "\\..*"))) (condition-case error-data (throw 'icon (create-image file (and (fboundp 'imagemagick-types) (imagemagick-types) 'imagemagick) nil :ascent 'center :max-width 16 :max-height 16)) (error (message "Error: cannot create icon for %s: %s" feed-name-symbol error-data))))) ;; Fallback: default icon. (find-image '((:type png :file "newsticker/rss-feed.png" :ascent center))))) #+END_SRC Leaving out the details of this function, we can extract a simplified scheme, that says: 1) in a [[emacs:src/eval.c::DEFUN ("catch"][catch]] block, if the directory ~dir~ doesn't exist, return a default image, if it exists loop over all the files in the directory ~dir~, 2) in the loop, for each file try to create an image using that file, if it fails, log the error in the message buffer, if it succeeds, throw to the [[emacs:src/eval.c::DEFUN ("catch"][catch]] for the tag ~icon~ and return the created image from the [[emacs:src/eval.c::DEFUN ("catch"][catch]]: #+BEGIN_SRC emacs-lisp (catch 'icon (when (file-exists-p dir) (dolist (file (directory-files dir)) (condition-case err (throw 'icon (create-image file ...)) (error (message "%s: %s" file err))))) (find-image ...)) #+END_SRC *** With [[emacs:src/search.c::DEFUN ("re-search-forward"][re-search-forward]] In Org related packages, a technique used to find something in the buffer is to: 1) search in the buffer some part that match some regexp (with [[emacs:src/search.c::DEFUN ("re-search-forward"][re-search-forward]]), 2) when we find that part, extract the information available at point (specifically we get it with [[org-mode:lisp/org-element.el::(defun org-element-at-point][org-element-at-point]]), 3) check some conditions on the element we've parsed, 4) depending on the result of the previous check, we continue the search in the buffer or we [[emacs:src/eval.c::DEFUN ("throw"][throw]] and return some result. This technique can be done with some code similar to this snippet: #+BEGIN_SRC emacs-lisp (let ((case-fold-search t) (re ...)) (catch :tag (while (re-search-forward re nil t) (let ((element (org-element-at-point))) (when ... (throw :tag ...)))))) #+END_SRC We encounter this pattern in the following functions [[org-mode:lisp/org.el::(defun org-log-beginning][org-log-beginning]], [[org-mode:lisp/ob-ref.el::(defun org-babel-ref-resolve][org-babel-ref-resolve]] and [[org-mode:lisp/org-colview.el::(defun org-columns-get-format (&optional][org-columns-get-format]]. We reproduce below the source code of [[emacs:lisp/org/ob-core.el::(defun org-babel-find-named-result][org-babel-find-named-result]] which also uses that technique but enclosed in a [[emacs:src/editfns.c::DEFUN ("save-excursion"][save-excursion]] that saves the point and current buffer, executes what's in the body and restores those things: #+BEGIN_SRC emacs-lisp (defun org-babel-find-named-result (name) "Find a named result. Return the location of the result named NAME in the current buffer or nil if no such result exists." (save-excursion (goto-char (point-min)) (let ((case-fold-search t) (re (format "^[ \t]*#\\+%s.*?:[ \t]*%s[ \t]*$" org-babel-results-keyword (regexp-quote name)))) (catch :found (while (re-search-forward re nil t) (let ((element (org-element-at-point))) (when (or (eq (org-element-type element) 'keyword) (< (point) (org-element-property :post-affiliated element))) (throw :found (line-beginning-position))))))))) #+END_SRC The same technique is also used in the function [[emacs:lisp/org/org.el::(defun org-refresh-category-properties][org-refresh-category-properties]] but going backward using the function [[emacs:src/search.c::DEFUN ("re-search-backward"][re-search-backward]] instead of [[emacs:src/search.c::DEFUN ("re-search-forward"][re-search-forward]]. WE ARE DONE!!! * [2022-04-09 Sat] Did you know that Org links in property drawers are not links? :PROPERTIES: :CUSTOM_ID: /2022-04-09-org-links-in-property-drawers-are-not-links/ :REDDIT_POST: https://www.reddit.com/r/emacs/comments/tznia8/did_you_know_that_org_links_in_property_drawers/ :END: Wait a minute! Are you telling me that the URL ~https://orgmode.org/worg/~ used as property value in a property drawer is not a link? Yes! Even if clicking the URL opens it in my browser. Yes! Even if the URL is displayed like any other links in the buffer (using the face ~org-link~). Yes! But, if the URL ~https://orgmode.org/worg/~ is used in a paragraph, it is a link. Yes! WHY? Because, while in both cases, in a property drawer and in a paragraph, the URL ~https://orgmode.org/worg/~ is matched by the regexp [[org-mode:lisp/ol.el::(defvar org-link-any-re nil][org-link-any-re]]: 1) in a property drawer (specifically in a ~node-property~), the URL ~https://orgmode.org/worg/~ is not parsed as a ~link~ object by the Org parser (but only as the ~:value~ of the ~node-property~ object containg it) and, 2) in a paragraph, the URL ~https://orgmode.org/worg/~ is parsed as a ~link~ object by the Org parser. THIS IS THE ORG PARSER THAT DICTATES THE RULES :) END!!! Maybe not. Let's build some examples to get an idea of this difference and what it implies. To falicilate our discussion let's call: 1) R-LINKS the parts of an org-mode buffer that match the regexp [[org-mode:lisp/ol.el::(defvar org-link-any-re nil][org-link-any-re]], 2) P-LINKS the parts of an org-mode buffer that are parsed as ~link~ objects by the Org parser. In an org-mode buffer, the parts that match the regexp [[org-mode:lisp/ol.el::(defvar org-link-any-re nil][org-link-any-re]], the R-LINKS, are all: 1) "activated", meaning they have there text properties set by the function [[org-mode:lisp/org.el::(defun org-activate-links (limit][org-activate-links]] (triggered by ~jit-lock~ mechanism), 2) and depending on the place of the function [[org-mode:lisp/org.el::(defun org-activate-links (limit][org-activate-links]] in the let binded list ~org-font-lock-extra-keywords~ in the function [[org-mode:lisp/org.el::(defun org-set-font-lock-defaults (][org-set-font-lock-defaults]] (used to set font lock defaults for the current buffer), the face of those parts is either the face ~org-link~, another face or ~org-link~'s face merged with another face. For instance, we can look at the text properties of the URL ~https://orgmode.org/worg/~ used in different places (comment, property drawer and paragraph) in the following org-mode buffer: #+BEGIN_SRC org # Worg's URL in a comment: https://orgmode.org/worg/ ,* Heading :PROPERTIES: :MY_URL: https://orgmode.org/worg/ :END: The same URL to Worg in a paragraph: https://orgmode.org/worg/. #+END_SRC by evaluating (with ~pp-eval-expression~) the form : (text-properties-at (point)) with the point on top of each URL. We obtains the 3 following lists: #+BEGIN_SRC emacs-lisp ;; URL in comment (font-lock-multiline t keymap (keymap (follow-link . mouse-face) (mouse-3 . org-find-file-at-mouse) (mouse-2 . org-open-at-mouse)) mouse-face highlight face font-lock-comment-face org-category "links" font-lock-fontified t help-echo "LINK: https://orgmode.org/worg/" fontified t htmlize-link (:uri "https://orgmode.org/worg/")) ;; URL in a property drawer (font-lock-multiline t keymap (keymap (follow-link . mouse-face) (mouse-3 . org-find-file-at-mouse) (mouse-2 . org-open-at-mouse)) mouse-face highlight face org-link org-category "links" help-echo "LINK: https://orgmode.org/worg/" fontified t htmlize-link (:uri "https://orgmode.org/worg/") rear-nonsticky (mouse-face highlight keymap invisible intangible help-echo org-linked-text htmlize-link)) ;; URL in a paragraph (font-lock-multiline t keymap (keymap (follow-link . mouse-face) (mouse-3 . org-find-file-at-mouse) (mouse-2 . org-open-at-mouse)) mouse-face highlight face org-link org-category "links" help-echo "LINK: https://orgmode.org/worg/" fontified t htmlize-link (:uri "https://orgmode.org/worg/") rear-nonsticky (mouse-face highlight keymap invisible intangible help-echo org-linked-text htmlize-link)) #+END_SRC We observe that: 1) those 3 URLs can be open with [[org-mode:lisp/org.el::(defun org-open-at-mouse (ev][org-open-at-mouse]] by clicking (with ~mouse-2~) them (due to the text property ~keymap~), 2) when we over the mouse on them (the 3), we see the help echo showing ~LINK: https://orgmode.org/worg/~, 3) the face (with Emacs default settings) of the URL in the comment is ~font-lock-comment-face~, and the face of the URL in the property drawer and in the paragraph have the same value, the face ~org-link~. Now, if we parse (with the Org parser) the same previous org-mode buffer by evaluating (with ~pp-eval-expression~) the form: : (org-element-parse-buffer) we obtain the following structure (some parts are skipped): #+BEGIN_SRC emacs-lisp (org-data (...) (section (...) (comment (... :value "Worg's URL in a comment: https://orgmode.org/worg/" ...))) (headline (...) (section (...) (property-drawer (...) (node-property (:key "MY_URL" :value "https://orgmode.org/worg/" ...))) (paragraph (...) #("The same URL to Worg in a paragraph: " 0 37 (:parent #3)) (link (:type "https" :path "//orgmode.org/worg/" :format plain :raw-link "https://orgmode.org/worg/" :application nil :search-option nil ...)) #(".\n" 0 2 (:parent #3)))))) #+END_SRC We observe that the only URL that is parsed as a ~link~ object is the URL inside the paragraph. The others are values of the property ~:value~ of a ~comment~ element for the first one and a ~node-property~ element for the second one. So, some R-LINKS are not P-LINKS. Now, if we look at the function [[org-mode:lisp/org-element.el::(defun org-element-link-parser (][org-element-link-parser]] #+BEGIN_SRC emacs-lisp (defun org-element-link-parser () "..." (catch 'no-object (let (...) (cond ((and org-target-link-regexp (save-excursion (or (bolp) (backward-char)) (looking-at org-target-link-regexp))) ;; ... ) ((looking-at org-link-bracket-re) ;; ... ) ((looking-at org-link-plain-re) ;; ... ) ((looking-at org-link-angle-re) ;; ... ) (t (throw 'no-object nil))) (list 'link (list ...))))) #+END_SRC which is responsible to parse ~link~ objects, and we look at the function [[org-mode:lisp/ol.el::(defun org-link-make-regexps (][org-link-make-regexps]] which is responsible to set the variable [[org-mode:lisp/ol.el::(defvar org-link-any-re nil][org-link-any-re]] (among other link related variables): #+BEGIN_SRC emacs-lisp (defun org-link-make-regexps () "..." (let (...) (setq ;; ... org-link-any-re (concat "\\(" org-link-bracket-re "\\)\\|\\(" org-link-angle-re "\\)\\|\\(" org-link-plain-re "\\)")))) #+END_SRC we see that, except for radio target links (~<<...>>~), P-LINKS are also R-LINKS. So someone who implements a command that operates on "links" must decide: 1) whether the command is aimed at P-LINKS only (which is the case of the command [[org-mode:lisp/ol.el::(defun org-next-link (&optional search-backward][org-next-link]] bound by default to ~C-c C-x C-n~), 2) or at all R-LINKS more broadly (which is the case of [[org-mode:lisp/org.el::(defun org-open-at-point (&optional arg][org-open-at-point]] bound by default to ~C-c C-o~). We can check this by calling once the command [[org-mode:lisp/ol.el::(defun org-next-link (&optional search-backward][org-next-link]] with the point at the beginning of the previous org-mode buffer. We see that the point moves to the third URL in the buffer, the only one that is a P-LINK. And if we call the command [[org-mode:lisp/org.el::(defun org-open-at-point (&optional arg][org-open-at-point]] with the point on each URL, we see that the URL ~https://orgmode.org/worg/~ is open 3 times in our browser. This is because the command [[org-mode:lisp/org.el::(defun org-open-at-point (&optional arg][org-open-at-point]] provides support for R-LINKS that are not P-LINKS. We can see this by looking at the source: #+BEGIN_SRC emacs-lisp (defun org-open-at-point (&optional arg) "..." (interactive "P") ;; ... (unless (run-hook-with-args-until-success 'org-open-at-point-functions) (let* ((context (org-element-lineage (org-element-context) '(citation citation-reference clock comment comment-block footnote-definition footnote-reference headline inline-src-block inlinetask keyword link node-property planning src-block timestamp) t)) (type (org-element-type context)) ...) (cond ((not type) (user-error "No link found")) ;; No valid link at point. For convenience, look if something ;; looks like a link under point in some specific places. ((memq type '(comment comment-block node-property keyword)) (call-interactively #'org-open-at-point-global)) ;; ... ((eq type 'link) (org-link-open context arg)) ;; ... (t (user-error "No link found"))))) (run-hook-with-args 'org-follow-link-hook)) #+END_SRC Specifically, the command [[org-mode:lisp/org.el::(defun org-open-at-point (&optional arg][org-open-at-point]], for the R-LINKS that are part of one of the following org elements ~comment~, ~comment-block~, ~node-property~, ~keyword~, delegate the action to the command [[org-mode:lisp/org.el::defun org-open-at-point-global (][org-open-at-point-global]]. If you want to know more about the command [[org-mode:lisp/org.el::(defun org-open-at-point (&optional arg][org-open-at-point]] you can read this post: [[#/2022-04-04-search-options-link-abbreviations-and-org-open-at-point/][Search options in file links | link abbreviations | COME WITH ME on this JOURNEY into the heart of the command org-open-at-point]] WE ARE DONE!!! * [2022-04-04 Mon] Search options in file links | link abbreviations | COME WITH ME on this JOURNEY into the heart of the command org-open-at-point :PROPERTIES: :CUSTOM_ID: /2022-04-04-search-options-link-abbreviations-and-org-open-at-point/ :REDDIT_POST: https://www.reddit.com/r/emacs/comments/tw3fpu/search_options_in_file_links_link_abbreviations/ :END: Hey you, unconditional Emacser, fanatic Org user, fearless Elisp programmer, This post is for you :) We all have different needs when it comes to taking notes. Fortunately, with org-mode many workflows are possible. In my case, my notes consist of: 1) some pieces of text (almost no markups), 2) many code snippets (inside source blocks) and, 3) many, MANY links to specific places (function, variables, ...) in repositories that I cloned on my machine. Today I want to talk about links. Let's say we are working on the function ~next-error~ which is defined in the file ~/tmp/emacs/lisp/simple.el~ (assuming we have Emacs repository cloned under the directory ~/tmp/~) and we want to add a link in an org-mode file to that function. How do we do it? How does it work? In this post, we answer to those questions. Let's go ;) To clone Emacs repository under the directory ~/tmp/~, you can run the following command (in a terminal): : cd /tmp/ && git clone git://git.sv.gnu.org/emacs.git ** Search options in file links To link to the file ~/tmp/emacs/lisp/simple.el~ we can use the following "external" org link ("external" means a link outside the current buffer/file, see [[info:org#External Links]]) starting with the identifier ~file~ following by a colon ~:~ and the path of the file: #+BEGIN_SRC org [[file:/tmp/emacs/lisp/simple.el]] #+END_SRC As the file name we are providing is "complete" (starting by ~/~, it also works if it starts by ~~~, ~./~ and ~../~), we can omit the ~file~ identifier. So, this following link will also works: #+BEGIN_SRC org [[/tmp/emacs/lisp/simple.el]] #+END_SRC Those links (as any links) can have descriptions like this: #+BEGIN_SRC org [[file:/tmp/emacs/lisp/simple.el][A DESCRIPTION]] [[/tmp/emacs/lisp/simple.el][A DESCRIPTION]] #+END_SRC but in this post we don't consider link's descriptions in the examples. If we call [[org-mode:lisp/org.el::(defun org-open-at-point (&optional][org-open-at-point]] (bound to ~C-c C-o~ by default) on top of one of the previous links, we'll visit the file ~/tmp/emacs/lisp/simple.el~ in another window (due to the default value of [[org-mode:lisp/ol.el::(defcustom org-link-frame-setup][org-link-frame-setup]]). If we just wanted to link the file ~simple.el~, we can stop here. But, we want something more specific, we want to link to the definition of the function ~next-error~ in the file ~simple.el~. Well, Org provides a way to indicate in the link the search we want to perform in the file we've indicated. This can be done (see [[info:org#Search Options]]): 1) by adding two colons ~::~ after the "complete" file name and, 2) adding the "search option" to perform after the two colons ~::~. *** Search by line number In our case, after visiting the file ~simple.el~, we want the result of the search to place the point at the beginning of the function definition ~next-error~. This definition starts at the line ~320~ (with Emacs checked out at commit ~0e7314f6f1~). In Org link, do do a "search" by line number, we just have to add the line number after the two colons ~::~. So, the following link links to the definition of the function ~next-error~ in the file ~/tmp/emacs/lisp/simple.el~ (with Emacs checked out at commit ~0e7314f6f1~): #+BEGIN_SRC org [[/tmp/emacs/lisp/simple.el::320]] #+END_SRC While, this works well, this is not my preferred method to link to the definion of ~next-error~, because any time the file ~simple.el~ changes, the link to the definition might be broken. Any changes that happened before the line ~320~ of the function definition ~next-error~ that adds or removes lines in some way modifies the starting lines of the function definition ~next-error~. (For instance before the commit ~2ebd950239~ (2021-03-16) the starting point of the definition of ~next-error~ was at line ~329~). *** Text search Let's see another type of search, the "text search" type, provided by Org link mechanism, that can link to the function ~next-error~ and is perhaps less dependent on the changes that occurs in the file ~simple.el~. Specifiying the text to search in an Org link is done by adding the text to search after the two colons ~::~ added after the file path. Recall that the function ~next-error~ is defined like this: #+BEGIN_SRC emacs-lisp (defun next-error (&optional arg reset) ;; ... ) #+END_SRC So, to link to the definition of ~next-error~ in the file ~/tmp/emacs/lisp/simple.el~, we can use the (text) search option ~(defun next-error (&optional~ as done in the following link: #+BEGIN_SRC org [[/tmp/emacs/lisp/simple.el::(defun next-error (&optional]] #+END_SRC Calling [[org-mode:lisp/org.el::(defun org-open-at-point (&optional][org-open-at-point]] on top of that link will effectively visit the file ~/tmp/emacs/lisp/simple.el~ and put the point at the beginning of the function definition ~next-error~. If you didn't know this was possible, isn't that SUPER COOL? With those kind of links you can take your notes to ANOTHER LEVEL. OK... But why didn't we choose another text to search like: 1) ~(defun next-error (&optional arg reset))~ (the whole line) or, 2) ~(defun next-error~ (just the beginning, up to the name of the function). In the first case, using ~(defun next-error (&optional arg reset))~ as text search option raises an error because the text starts by a left parenthesis ~(~ and finish by a right parenthesis ~)~. And so, after visiting the file ~simple.el~ (in some way) the function [[org-mode:lisp/org.el::(defun org-open-at-point (&optional][org-open-at-point]] searches a string that looks like ~(FOO)~ with [[org-mode:lisp/ol.el::(defun org-link-search (s][org-link-search]] which will do a search for a code reference (not for the string ~(FOO)~) and will fail. In the second case, using ~(defun next-error~ as text search option puts the point at the beginning of the function ~next-error-buffer-on-selected-frame~. This happens because the search done in the file ~simple.el~ starts a the beginning of the buffer and stop at the first match which turns to be at the function ~next-error-buffer-on-selected-frame~ which is defined before the function ~next-error~. Anyway, if you really want to know why the "search option" you've used doesn't work "the way it should works" (note that what you think or I think doesn't matter, the code tell the truth), you can take a look at the function [[org-mode:lisp/ol.el::(defun org-link-search (s][org-link-search]]. This is the function that does the search once the file has been visited, where its argument ~s~ is the "search option" after the two colon ~::~ in our links. ** Link abbreviations Let's assume that in our org-mode file, we've used the previous described method to link to dozens of functions and variables in the Emacs source code. What if we move Emacs source code from ~/tmp/emacs/~ to ~/another-path-to/emacs/~? All our links are now dead. You might tell me: "what's the problem? You just have to search all the occurences of ~[[/tmp/emacs/~ and replace them by ~[[/another-path-to/emacs/~. There are many way to do this (with the utility ~sed~, or from within Emacs with ~query-replace~ for instance)." And yes this is possible, but org-mode is SO GOOD that it provides a mechanism that mitigates a lot this case of scenario that is call: link abbreviations (see [[info:org#Link Abbreviations]]). Link abbreviations allow us to declare mappings between abbreviations (that are a word, starting with a letter, followed by letters, numbers, hyphens ~-~ and underscores ~_~) and links. And instead of using the links in the bracket links we use the abbreviations. This can be done: 1) localy (that means per file/buffer) using the org keyword ~LINK~ or, 2) globally (valid for all org files) defining the mapping in the variable [[org-mode:lisp/ol.el::(defcustom org-link-abbrev-alist][org-link-abbrev-alist]]. Let's see how to use it with an example. *** An example using ~#+LINK:~ statments By evaluating the following s-exp in the minibuffer (~M-x eval-expression~): #+BEGIN_SRC emacs-lisp (with-current-buffer (get-buffer-create "*link abbrev*") (org-mode)) (switch-to-buffer "*link abbrev*") #+END_SRC we create the org-mode buffer ~*link abbrev*~ and we display it in the selected window. In this buffer, we add the following abbreviated link that map the abbreviation ~emacs~ to the link ~/tmp/emacs/~: #+BEGIN_SRC org ,#+LINK: emacs /tmp/emacs/ #+END_SRC Then we add the following link ~[[emacs]]~ in the buffer ~*link abbrev*~, that should looks like this: #+BEGIN_SRC org ,#+LINK: emacs /tmp/emacs/ 1) link to the directory ~/tmp/emacs/~ - [[emacs]] #+END_SRC With the point (the cursor) on top of that link, let's type ~C-c C-o~ (bound to [[org-mode:lisp/org.el::(defun org-open-at-point (&optional][org-open-at-point]] by default). What happened? Our cursor moved to the beginning of the word ~emacs~ after the keyword ~LINK~. What? Maybe you were expecting something different, like to visit a dired buffer listing the directory ~/tmp/emacs/~. But, [[org-mode:lisp/org.el::(defun org-open-at-point (&optional][org-open-at-point]] did a text search in the buffer ~*link abbrev*~ from its beginning and stopped at the first match of the word ~emacs~. This is the normal behavior. What we forget is to "active" (to set) the abbreviated link in the buffer. To do so, we can type ~C-c C-c~ with point on the line starting by ~#+LINK:~. This restarts ~org-mode~ and as consequence, due to the declaration of the link abbreviation set the local variable [[org-mode:lisp/ol.el::(defvar-local org-link-abbrev-alist-local][org-link-abbrev-alist-local]] to: #+BEGIN_SRC emacs-lisp (("emacs" . "/tmp/emacs/")) #+END_SRC as we can see by running the following with the buffer ~*link abbrev*~ being the current buffer: : M-x eval-expression RET org-link-abbrev-alist-local Now, in the buffer ~*link abbrev*~, with the point on top of the link ~[[emacs]],~ by pressing ~C-c C-o~ we visit a dired buffer listing the directory ~/tmp/emacs/~. If we want to link to the file ~lisp/simple.el~ in the directory ~/tmp/emacs/~ using the abbreviation ~emacs~, we add a colon ~:~ after the abbreviation and the rest of the file name after this colon like this: #+BEGIN_SRC org ,#+LINK: emacs /tmp/emacs/ 1) link to the directory ~/tmp/emacs/~ - [[emacs]] 2) link to the file ~/tmp/emacs/lisp/simple.el~ - [[emacs:lisp/simple.el]] #+END_SRC Now, in the buffer ~*link abbrev*~, with the point on top of the link ~[[emacs:lisp/simple.el]],~ by pressing ~C-c C-o~ we visit the file ~/tmp/emacs/lisp/simple.el~. If we want to link to the function ~next-error~ in the file ~simple.el~ as we did in the previous section but this time using the abbreviation ~emacs~, we use the same syntax. Specifically, after the abbreviated link ~emacs:lisp/simple.el~, we add two colons ~::~ and the search option ~(defun next-error (&optional~ like this: #+BEGIN_SRC org ,#+LINK: emacs /tmp/emacs/ 1) link to the directory ~/tmp/emacs/~ - [[emacs]] 2) link to the file ~/tmp/emacs/lisp/simple.el~ - [[emacs:lisp/simple.el]] 3) link to the function ~next-error~ in the file ~/tmp/emacs/lisp/simple.el~ - [[emacs:lisp/simple.el::(defun next-error (&optional]] #+END_SRC Now, in the buffer ~*link abbrev*~, with the point on top of the link ~[[emacs:lisp/simple.el::(defun next-error (&optional]],~ by pressing ~C-c C-o~ we jump to the beginning of the function ~next-error~ in the file ~/tmp/emacs/lisp/simple.el~. *** The global variable [[org-mode:lisp/ol.el::(defcustom org-link-abbrev-alist][org-link-abbrev-alist]] Link abbreviations can be defined globally, by setting the variable [[org-mode:lisp/ol.el::(defcustom org-link-abbrev-alist][org-link-abbrev-alist]]. For instance, to define the abbreviation ~emacs~ that maps to the link (here file path) ~/tmp/emacs/~, we define [[org-mode:lisp/ol.el::(defcustom org-link-abbrev-alist][org-link-abbrev-alist]] like this: #+BEGIN_SRC emacs-lisp (setq org-link-abbrev-alist '(("emacs" . "/tmp/emacs/"))) #+END_SRC Assuming we also want to define the abbreviation ~org-mode~ (along with ~emacs~ abbreviation) that maps to the link ~/tmp/org-mode/~, we can defined [[org-mode:lisp/ol.el::(defcustom org-link-abbrev-alist][org-link-abbrev-alist]] like this: #+BEGIN_SRC emacs-lisp (setq org-link-abbrev-alist '(("emacs" . "/tmp/emacs/") ("org-mode" . "/tmp/org-mode/"))) #+END_SRC Note, that per buffer link abbreviations (defined with ~#+LINK:~) take precedence over global abbreviation defined in [[org-mode:lisp/ol.el::(defcustom org-link-abbrev-alist][org-link-abbrev-alist]]. ** How does org-open-at-point work? *** Using the macro [[org-mode:testing/org-test.el::(defmacro org-test-with-temp-text][org-test-with-temp-text]] to build our examples As we can read in the docstring of [[org-mode:lisp/org.el::(defun org-open-at-point (&optional][org-open-at-point]], this command can "open" the link, the timestamp, the footnote or the tags at point. This commands is versatile and does a lot. In this post, we won't discuss all the possibilities offered by [[org-mode:lisp/org.el::(defun org-open-at-point (&optional][org-open-at-point]] depending on the "context" of the org object at point. We narrow our "study" to the abbreviated link discussed in the previous section: : [[emacs:lisp/simple.el::(defun next-error (&optional]] in a buffer where the local value of [[org-mode:lisp/ol.el::(defvar-local org-link-abbrev-alist-local][org-link-abbrev-alist-local]] is set to: #+BEGIN_SRC emacs-lisp (("emacs" . "/tmp/emacs/")) #+END_SRC We could use an org-mode buffer containing the following content #+BEGIN_SRC org ,#+LINK: emacs /tmp/emacs/ [[emacs:lisp/simple.el::(defun next-error (&optional]] #+END_SRC to do our "study", but we prefer to take another approach and build the examples with the macro [[org-mode:testing/org-test.el::(defmacro org-test-with-temp-text][org-test-with-temp-text]] that we discussed in the post [[#/2022-03-11-org-mode-source-code-5000-examples/][Did you know that org-mode's source code contains more than 5000 examples?]]. This macro allows to evaluate the forms after the first argument being a string that is inserted in an org-mode buffer made current, with the point at the beginning of the buffer if there is no substring ~~ in the first argument. For instance, the action of calling the command [[org-mode:lisp/org.el::(defun org-open-at-point (&optional][org-open-at-point]] with the point before the first bracket in the previous org-mode buffer (assuming the link abbreviation has been set), could be reproduced by evaluating the following form that uses [[org-mode:testing/org-test.el::(defmacro org-test-with-temp-text][org-test-with-temp-text]]: #+BEGIN_SRC emacs-lisp (org-test-with-temp-text "#+LINK: emacs /tmp/emacs/ [[emacs:lisp/simple.el::(defun next-error (&optional]]" (org-mode-restart) (org-open-at-point)) #+END_SRC In this previous form, the call to [[org-mode:lisp/org.el::(defun org-mode-restart][org-mode-restart]] is used to set the (local) abbreviated link. In other term, to set the local variable [[org-mode:lisp/ol.el::(defvar-local org-link-abbrev-alist-local][org-link-abbrev-alist-local]] to ~(("emacs" . "/tmp/emacs/"))~. And to make everything "transparent", in the preceding form, we can replace the ~#+LINK:~ statment and the call to [[org-mode:lisp/org.el::(defun org-mode-restart][org-mode-restart]] by a ~let~ binding of the variable [[org-mode:lisp/ol.el::(defvar-local org-link-abbrev-alist-local][org-link-abbrev-alist-local]] in which we call [[org-mode:lisp/org.el::(defun org-open-at-point (&optional][org-open-at-point]] with the point still before the first bracket: #+BEGIN_SRC emacs-lisp (org-test-with-temp-text "[[emacs:lisp/simple.el::(defun next-error (&optional]]" (let ((org-link-abbrev-alist-local '(("emacs" . "/tmp/emacs/")))) (org-open-at-point))) #+END_SRC As we've set our working environment, we can continue our tour :) *** TLDR Before going into the details, we present an overview of the "call stack" implied by the call of the function [[org-mode:lisp/org.el::(defun org-open-at-point (&optional][org-open-at-point]] in the following form: #+BEGIN_SRC emacs-lisp (org-test-with-temp-text "[[emacs:lisp/simple.el::(defun next-error (&optional]]" (let ((org-link-abbrev-alist-local '(("emacs" . "/tmp/emacs/")))) (org-open-at-point))) #+END_SRC The "call stack" can be represented like this: #+BEGIN_SRC text org-open-at-point │ └> org-link-open │ └> org-link-open-as-file │ └> org-open-file │ └> org-link-search #+END_SRC This "call stack" brings some information but not as much as if we had provided the arguments passed to each function for each call. Here are the function calls with their arguments as they appear when [[org-mode:lisp/org.el::(defun org-open-at-point (&optional][org-open-at-point]] is called. First, we have: #+BEGIN_SRC emacs-lisp (org-test-with-temp-text "[[emacs:lisp/simple.el::(defun next-error (&optional]]" (let ((org-link-abbrev-alist-local '(("emacs" . "/tmp/emacs/")))) (org-open-at-point))) #+END_SRC That leads to this function call: #+BEGIN_SRC emacs-lisp (org-link-open '(link (:type "file" :path "/tmp/emacs/lisp/simple.el" :format bracket :raw-link "/tmp/emacs/lisp/simple.el::(defun next-error (&optional" :application nil :search-option "(defun next-error (&optional" :begin 1 :end 55 :contents-begin nil :contents-end nil :post-blank 0 :parent (paragraph (... :parent (section (... :parent (org-data (...)))))))) nil) #+END_SRC Which leads to this function call: #+BEGIN_SRC emacs-lisp (org-link-open-as-file "/tmp/emacs/lisp/simple.el::(defun next-error (&optional" nil) #+END_SRC Which leads to this function call: #+BEGIN_SRC emacs-lisp (org-open-file "/tmp/emacs/lisp/simple.el" nil nil "(defun next-error") #+END_SRC Which after visiting the file ~/tmp/emacs/lisp/simple.el~ leads to this last function call: #+BEGIN_SRC emacs-lisp (org-link-search "(defun next-error (&optional") #+END_SRC If you are interesting about the details here we go! *** [[org-mode:lisp/org.el::(defun org-open-at-point (&optional][org-open-at-point]] Besides a check for some org modules, recording the window configuration and removing the occur highlights from the buffer, [[org-mode:lisp/org.el::(defun org-open-at-point (&optional][org-open-at-point]] does the following: 1) check if the user has defined some functions in the hook [[org-mode:lisp/org.el::(defvar org-open-at-point-functions][org-open-at-point-functions]] (~nil~ by default) that can "open" the link at point: 1) if this the case, "open" the link with that function, 2) if this is not the case do other stuff that we discuss below, 2) after the link has been followed, no matter how, run the hook ~org-follow-link-hook~. Here are the parts of [[org-mode:lisp/org.el::(defun org-open-at-point (&optional][org-open-at-point]] we've just discussed: #+BEGIN_SRC emacs-lisp (defun org-open-at-point (&optional arg) "..." (interactive "P") (org-load-modules-maybe) (setq org-window-config-before-follow-link (current-window-configuration)) (org-remove-occur-highlights nil nil t) (unless (run-hook-with-args-until-success 'org-open-at-point-functions) ;; ... ) (run-hook-with-args 'org-follow-link-hook)) #+END_SRC In our case (as the hook [[org-mode:lisp/org.el::(defvar org-open-at-point-functions][org-open-at-point-functions]] is ~nil~), [[org-mode:lisp/org.el::(defun org-open-at-point (&optional][org-open-at-point]] enters in the ~unless~ block. In, the ~unless~ block, [[org-mode:lisp/org.el::(defun org-open-at-point (&optional][org-open-at-point]]: 1) locally sets the variable ~context~ to be an appropriate org object or org element (as understood by [[org-mode:lisp/org-element.el]]), 2) locally sets the variable ~type~ to be the type of ~context~, in our case, ~type~ is equal to the symbol ~link~, 3) calls an appropriate function depending on the value of ~link~, in our case, the call is the following where ~arg~ is the prefix argument: #+BEGIN_SRC emacs-lisp (org-link-open context arg) #+END_SRC Here are the parts of [[org-mode:lisp/org.el::(defun org-open-at-point (&optional][org-open-at-point]] we've just discussed: #+BEGIN_SRC emacs-lisp (defun org-open-at-point (&optional arg) "..." ;; ... (unless (run-hook-with-args-until-success 'org-open-at-point-functions) (let* ((context (org-element-lineage (org-element-context) '(citation citation-reference clock comment comment-block footnote-definition footnote-reference headline inline-src-block inlinetask keyword link node-property planning src-block timestamp) t)) (type (org-element-type context)) ;; ... ) (cond ;; ... ((eq type 'link) (org-link-open context arg)) ;; ... ))) ;; ... ) #+END_SRC We can compute the value assigned to the variable ~context~ by evaluating this form: #+BEGIN_SRC emacs-lisp (org-test-with-temp-text "[[emacs:lisp/simple.el::(defun next-error (&optional]]" (let ((org-link-abbrev-alist-local '(("emacs" . "/tmp/emacs/")))) (org-element-lineage (org-element-context) '(citation citation-reference clock comment comment-block footnote-definition footnote-reference headline inline-src-block inlinetask keyword link node-property planning src-block timestamp) t))) #+END_SRC which gives us: #+BEGIN_SRC emacs-lisp (link (:type "file" :path "/tmp/emacs/lisp/simple.el" :format bracket :raw-link "/tmp/emacs/lisp/simple.el::(defun next-error (&optional" :application nil :search-option "(defun next-error (&optional" :begin 1 :end 55 :contents-begin nil :contents-end nil :post-blank 0 :parent (paragraph (... :parent (section (... :parent (org-data (...)))))))) #+END_SRC So in the function [[org-mode:lisp/org.el::(defun org-open-at-point (&optional][org-open-at-point]], our bracket link is parsed into a list that is then passed as first argument to the function [[org-mode:lisp/ol.el::(defun org-link-open (link][org-link-open]] that way: #+BEGIN_SRC emacs-lisp (org-link-open '(link (:type "file" :path "/tmp/emacs/lisp/simple.el" :format bracket :raw-link "/tmp/emacs/lisp/simple.el::(defun next-error (&optional" :application nil :search-option "(defun next-error (&optional" :begin 1 :end 55 :contents-begin nil :contents-end nil :post-blank 0 :parent (paragraph (... :parent (section (... :parent (org-data (...)))))))) nil) #+END_SRC We'll look at this function call in a moment, but for now let's get closer to the parsing step. *** Parsing step **** [[org-mode:lisp/org-element.el::(defun org-element-context][org-element-context]] There are many things we can look at regarding the parsing of this link, but here we restrict our study to the path (value of ~:path~ keyword in the plist) and the search option (value of ~:search-option~ keyword in the plist). The way [[org-mode:lisp/org-element.el::(defun org-element-lineage][org-element-lineage]] works and the arguments we gave it implies that the link object we got is the same object returned by the function [[org-mode:lisp/org-element.el::(defun org-element-context][org-element-context]] that can be computed as follow: #+BEGIN_SRC emacs-lisp (org-test-with-temp-text "[[emacs:lisp/simple.el::(defun next-error (&optional]]" (let ((org-link-abbrev-alist-local '(("emacs" . "/tmp/emacs/")))) (org-element-context))) #+END_SRC [[org-mode:lisp/org-element.el::(defun org-element-context][org-element-context]] returns the smallest element or object at point. This happens by: 1) getting the element at point using [[org-mode:lisp/org-element.el::(defun org-element-at-point][org-element-at-point]], 2) as this element is of type ~paragraph~, narrow the buffer according to the limits of that element (nothing changed here because the limits of the ~paragraph~ are the limits of the whole buffer), 3) search for a "valid" object (i.e. that belongs to the list returned by ~(org-element-restriction 'paragraph)~) in the narrowed region containing point, iterating over all the objects in the narrowed region using the function [[org-mode:lisp/org-element.el::(defun org-element--object-lex][org-element--object-lex]] that returns, starting from point, the next object respecting a given restriction which turned out to be the restriction of the container element. 4) when the object is found, return it (with its ~:parent~ property set "correctly" using [[org-mode:lisp/org-element.el::(defsubst org-element-put-property][org-element-put-property]]), if none, return the element container. Here are the parts of [[org-mode:lisp/org-element.el::(defun org-element-context][org-element-context]] we've just discussed: #+BEGIN_SRC emacs-lisp (defun org-element-context (&optional element) "..." (catch 'objects-forbidden (org-with-wide-buffer (let* ((pos (point)) (element (or element (org-element-at-point))) (type (org-element-type element)) ;; ... ) (cond ;; ... ;; At a paragraph, a table-row or a verse block, objects are ;; located within their contents. ((memq type '(paragraph table-row verse-block)) (let ((cbeg (org-element-property :contents-begin element)) (cend (org-element-property :contents-end element))) (if (and cbeg cend (>= pos cbeg) (or (< pos cend) (and (= pos cend) (eobp)))) (narrow-to-region cbeg cend) (throw 'objects-forbidden element)))) ;; ... ) (goto-char (point-min)) (let ((restriction (org-element-restriction type)) (parent element) last) (catch 'exit (while t (let ((next (org-element--object-lex restriction))) (when next (org-element-put-property next :parent parent)) (if (or (not next) (> (org-element-property :begin next) pos)) (throw 'exit (or last parent)) (let ((end (org-element-property :end next)) (cbeg (org-element-property :contents-begin next)) (cend (org-element-property :contents-end next))) (cond ( ;; Skip objects ending before point. ... ;; move point (goto-char end) (when (and (= end pos) (not (memq (char-before) '(?\s ?\t)))) (setq last next))) ( ;; If POS is within a container object, move into that object. ;; move point (goto-char cbeg) (narrow-to-region (point) cend) (setq parent next) (setq restriction (org-element-restriction next))) (t (throw 'exit next))))))))))))) #+END_SRC So, the object that [[org-mode:lisp/org-element.el::(defun org-element-context][org-element-context]] returned in our specific case is exactly the same as the evaluation of the following s-exp returns: #+BEGIN_SRC emacs-lisp (org-test-with-temp-text "[[emacs:lisp/simple.el::(defun next-error (&optional]]" (let* ((org-link-abbrev-alist-local '(("emacs" . "/tmp/emacs/"))) (parent (org-element-at-point)) (restriction (org-element-restriction 'paragraph)) (object (org-element--object-lex restriction))) (org-element-put-property object :parent parent))) #+END_SRC which is: #+BEGIN_SRC emacs-lisp (link (:type "file" :path "/tmp/emacs/lisp/simple.el" :format bracket :raw-link "/tmp/emacs/lisp/simple.el::(defun next-error (&optional" :application nil :search-option "(defun next-error (&optional" :begin 1 :end 55 :contents-begin nil :contents-end nil :post-blank 0 :parent (paragraph (... :parent (section (... :parent (org-data (...)))))))) #+END_SRC **** [[org-mode:lisp/org-element.el::(defun org-element--object-lex][org-element--object-lex]] In [[org-mode:lisp/org-element.el::(defun org-element-context][org-element-context]], the function [[org-mode:lisp/org-element.el::(defun org-element--object-lex][org-element--object-lex]], starting at the beginning of the narrowed region, does the following: 1) searches for the beginning of a valid object matching the regular expression [[org-mode:lisp/org-element.el::(defvar org-element--object-regexp][org-element--object-regexp]], 2) moves point to the beginning of the match, 3) locally sets the variable ~result~ to be the previous match, 4) finds that the character after point matches a left bracket ~[~ (written ~?\[~ in elisp), 5) then finds that: a) the second element of ~result~ (~(aref result 1)~) matches another left bracket and b) ~link~ is part of the valid object to parse (~restriction~), 6) due to the checks done at step 5), calls the function [[org-mode:lisp/org-element.el::(defun org-element-link-parser][org-element-link-parser]] to parse the link at point, 7) then sets the local variable ~found~ to be that link, 8) and finally returned ~found~ (the link). Here are the parts of [[org-mode:lisp/org-element.el::(defun org-element--object-lex][org-element--object-lex]] we've just discussed: #+BEGIN_SRC emacs-lisp (defun org-element--object-lex (restriction) "..." (cond ;; ... (t (let* ((start (point)) (limit ;; ... ) found) (save-excursion (while (and (not found) (re-search-forward org-element--object-regexp limit 'move)) (goto-char (match-beginning 0)) (let ((result (match-string 0))) (setq found (cond ;; .. (t (pcase (char-after) ;; ... (?\[ (pcase (aref result 1) ((and ?\[ (guard (memq 'link restriction))) (org-element-link-parser)) ;; ... )) ;; ... )))) ;; ... )) (cond (found) ;; ... )))))) #+END_SRC **** [[org-mode:lisp/org-element.el::(defun org-element-link-parser][org-element-link-parser]] So, leaving aside the parent of the link object that [[org-mode:lisp/org-element.el::(defun org-element-context][org-element-context]] returns, in our specific case the propreties of the link object we are interested in are computed by the function [[org-mode:lisp/org-element.el::(defun org-element-link-parser][org-element-link-parser]], and we can see that by evaluating the following s-exp: #+BEGIN_SRC emacs-lisp (org-test-with-temp-text "[[emacs:lisp/simple.el::(defun next-error (&optional]]" (let* ((org-link-abbrev-alist-local '(("emacs" . "/tmp/emacs/")))) (org-element-link-parser))) #+END_SRC which gives us the following link object: #+BEGIN_SRC emacs-lisp (link (:type "file" :path "/tmp/emacs/lisp/simple.el" :format bracket :raw-link "/tmp/emacs/lisp/simple.el::(defun next-error (&optional" :application nil :search-option "(defun next-error (&optional" :begin 1 :end 55 :contents-begin nil :contents-end nil :post-blank 0)) #+END_SRC Let's break down what the function [[org-mode:lisp/org-element.el::(defun org-element-link-parser][org-element-link-parser]] does when we evaluted the previous s-exp: 1) the link at point is recognized as a bracket link via the condition ~(looking-at org-link-bracket-re)~ in the second clause of the main ~cond~ special form, 2) then the expressions in the body of this clause are evaluated, 3) one of them sets the local variable ~raw-link~ to be the link matched by the first subexpression in ~org-link-bracket-re~ where some string manipulation are realized before expanding the abbreviation part (its first part, which is ~emacs~) using the function [[org-mode:lisp/ol.el::(defun org-link-expand-abbrev][org-link-expand-abbrev]] and replaced it by ~/tmp/emacs/~, 4) then another expression in that same clause checks that ~raw-link~ looks like a file, sets the local variable ~type~ to be the string ~"file"~ and set the local variable ~path~ to be equal to ~raw-link~, 5) then out of the main ~cond~ special form, given that the link is of type ~file~, the local variable ~search-option~ is set to be right part (part after the substring ~::~) of the variable ~path~ (still being the string ~"/tmp/emacs/lisp/simple.el::(defun next-error (&optional"~), and then set the variable ~path~ to be the left part (part before the substring ~::~) of itself. 6) finally, it returns the link object being a list where its car is the symbol ~link~ and the cdr is a property list where for instance, the keyword ~:search-option~ is associated with the value ~search-option~ previously computed. Here are the parts of [[org-mode:lisp/org-element.el::(defun org-element-link-parser][org-element-link-parser]] we've just discussed: #+BEGIN_SRC emacs-lisp (defun org-element-link-parser () "..." (catch 'no-object (let ((begin (point)) ;; ... type path raw-link search-option) (cond ;; ... ((looking-at org-link-bracket-re) (setq raw-link (org-link-expand-abbrev (org-link-unescape (replace-regexp-in-string "[ \t]*\n[ \t]*" " " (match-string-no-properties 1))))) (cond ((or (file-name-absolute-p raw-link) (string-match "\\`\\.\\.?/" raw-link)) (setq type "file") (setq path raw-link)) ;; ... )) ;; ... (t (throw 'no-object nil))) ;; ... (when (string-match "\\`file\\(?:\\+\\(.+\\)\\)?\\'" type) (setq application (match-string 1 type)) (setq type "file") (when (string-match "::\\(.*\\)\\'" path) (setq search-option (match-string 1 path)) (setq path (replace-match "" nil nil path))) ;; ... ) ;; ... (list 'link (list :type type :path path :raw-link (or raw-link path) :search-option search-option ;; ... ))))) #+END_SRC ***** Programming with Elisp is magic What's magic when programming Elisp code is that at any time we can extract a little part of the program, replace some symbols by custom values, send it to the minibuffer with ~M-x eval-expression~ (or ~pp-eval-expression~), press ~RET~, and automatically get back some value in the echo area (or in the dedicated buffer ~*Pp Eval Output*~). In almost no time, misconceptions about what a program does (or why a program fails) can be spot that way. Let's say we want to be sure that the following snippet in the function [[org-mode:lisp/org-element.el::(defun org-element-link-parser][org-element-link-parser]] does what it seems to do: #+BEGIN_SRC emacs-lisp (when (string-match "::\\(.*\\)\\'" path) (setq search-option (match-string 1 path)) (setq path (replace-match "" nil nil path))) #+END_SRC In our example, at that point in the function, the local variable ~path~ has the string value ~"/tmp/emacs/lisp/simple.el::(defun next-error (&optional"~. We can test the result of the ~when~ condition by evaluating the following: #+BEGIN_SRC emacs-lisp (string-match "::\\(.*\\)\\'" "/tmp/emacs/lisp/simple.el::(defun next-error (&optional") ;; 25 #+END_SRC By reading the help of ~string-match~, we know that it returns the index of the start of the first match or nil. Ok, there's a match. But, to me the string ~"/tmp/emacs/lisp/simple.el::(defun next-error (&optional"~ is to long with to many repetive characters that don't appear in the regexp ~"::\\(.*\\)\\'"~ to wrap my head around what's going on. So, let's use the good ~foo~ and ~bar~ words to simplify our discoveries and gain confidence about this piece of code. In the regexp, the only part "that seems" of interest is ~::~, so let's try again with the strings ~"/tmp/foo::bar"~, ~"/tmp/foo::"~ and ~"/tmp/foo"~: #+BEGIN_SRC emacs-lisp (string-match "::\\(.*\\)\\'" "/tmp/foo::bar") ;; 8 (string-match "::\\(.*\\)\\'" "/tmp/foo::") ;; 8 (string-match "::\\(.*\\)\\'" "/tmp/foo") ;; nil #+END_SRC It become clearer. We start to get a sense of the match. By reading the documentation ([[info:elisp#Simple Match Data]]), we learn (or recall): 1) that search functions like ~string-match~ or ~looking-at~ set the match data for every successful search, 2) and if the first argument of ~match-string~ is ~0~, we get the entire matching text and if it's ~1~ we get the first parenthetical subexpression of the given regular expression. So, continuing with the string ~"/tmp/foo::bar"~, we have: #+BEGIN_SRC emacs-lisp (let ((path "/tmp/foo::bar")) (when (string-match "::\\(.*\\)\\'" path) (list (match-string 0 path) (match-string 1 path)))) ;; ("::bar" "bar") #+END_SRC Reading the help buffer about ~replace-match~ tells us that this function replaces the text matched by the last search with its first argument. And if we give it an optional fourth argument being a string, the replacement is made on that string. So replacing the entire match with the empty string ~""~ should remove the matched part of the string: #+BEGIN_SRC emacs-lisp (let ((path "/tmp/foo::bar")) (when (string-match "::\\(.*\\)\\'" path) (replace-match "" nil nil path))) ;; "/tmp/foo" #+END_SRC Now putting everything together we can write the following example: #+BEGIN_SRC emacs-lisp (let ((path "/tmp/foo::bar")) (when (string-match "::\\(.*\\)\\'" path) `(:search-option ,(match-string 1 path) :path ,(replace-match "" nil nil path)))) ;; (:search-option "bar" ;; :path "/tmp/foo") #+END_SRC And maybe we've removed some misconceptions about this part of the function [[org-mode:lisp/org-element.el::(defun org-element-link-parser][org-element-link-parser]]. **** [[org-mode:lisp/ol.el::(defun org-link-expand-abbrev][org-link-expand-abbrev]] :PROPERTIES: :CUSTOM_ID: /2022-04-04-search-options-link-abbreviations-and-org-open-at-point/#org-link-expand-abbrev :END: Regarding the parsing step of the link, we still have one function to cover: [[org-mode:lisp/ol.el::(defun org-link-expand-abbrev][org-link-expand-abbrev]]. This function replaces the link abbreviation in the link string looking up at the variables [[org-mode:lisp/ol.el::(defcustom org-link-abbrev-alist][org-link-abbrev-alist]] and [[org-mode:lisp/ol.el::(defvar-local org-link-abbrev-alist-local][org-link-abbrev-alist-local]]. In our case we expect it to transform the link (as a string) ~"emacs:lisp/simple.el::(defun next-error (&optional"~ into the link (as a string) ~"/tmp/emacs/lisp/simple.el::(defun next-error (&optional"~ given that the local variable [[org-mode:lisp/ol.el::(defvar-local org-link-abbrev-alist-local][org-link-abbrev-alist-local]] is set to ~'(("emacs" . "/tmp/emacs/"))~ when we call it. A bunch of examples are often better to describe function calls than to stare at the source. So, let's do 4 evaluations that shows how [[org-mode:lisp/ol.el::(defun org-link-expand-abbrev][org-link-expand-abbrev]] behaves (given our specific link) and its relation (dependency) with the variables [[org-mode:lisp/ol.el::(defcustom org-link-abbrev-alist][org-link-abbrev-alist]] and [[org-mode:lisp/ol.el::(defvar-local org-link-abbrev-alist-local][org-link-abbrev-alist-local]]. To make those example more readable (as done previously), we use as input the "fake" link ~"emacs:foo::bar"~: #+BEGIN_SRC emacs-lisp (org-link-expand-abbrev "emacs:foo::bar") ;; "emacs:foo::bar" (let ((org-link-abbrev-alist-local '(("XXX" . "/tmp/emacs/")))) (org-link-expand-abbrev "emacs:foo::bar")) ;; "emacs:foo::bar" (let ((org-link-abbrev-alist-local '(("emacs" . "/tmp/emacs/")))) (org-link-expand-abbrev "emacs:foo::bar")) ;; "/tmp/emacs/foo::bar" (let ((org-link-abbrev-alist '(("emacs" . "/TMP/EMACS/")))) (org-link-expand-abbrev "emacs:foo::bar")) ;; "/TMP/EMACS/foo::bar" (let ((org-link-abbrev-alist '(("emacs" . "/TMP/EMACS/"))) (org-link-abbrev-alist-local '(("emacs" . "/tmp/emacs/")))) (org-link-expand-abbrev "emacs:foo::bar")) ;; "/tmp/emacs/foo::bar" #+END_SRC So, what did we learnt from running those examples: 1) if none of the variables [[org-mode:lisp/ol.el::(defcustom org-link-abbrev-alist][org-link-abbrev-alist]] and [[org-mode:lisp/ol.el::(defvar-local org-link-abbrev-alist-local][org-link-abbrev-alist-local]] are defined the link is not expanded, 2) If one of those variables is set when we call the function and if the abbreviation is defined in one of them, the link is expanded. 3) Finally, if both variables are set and defined the same abbreviation, the buffer local wins over the global. Now by taking a look at its source, we can tell that the function [[org-mode:lisp/ol.el::(defun org-link-expand-abbrev][org-link-expand-abbrev]] works like this: 1) do a string matching on the link to get the part before the first colon (which might be an abbreviation), 2) do a lookup for this abbreviation in the variables [[org-mode:lisp/ol.el::(defvar-local org-link-abbrev-alist-local][org-link-abbrev-alist-local]] and [[org-mode:lisp/ol.el::(defcustom org-link-abbrev-alist][org-link-abbrev-alist]], prioritizing the local variable, 3) if the abbreviation is found, replace it in the link by its replacement text. Here are the parts of [[org-mode:lisp/ol.el::(defun org-link-expand-abbrev][org-link-expand-abbrev]] we've just discussed: #+BEGIN_SRC emacs-lisp (defun org-link-expand-abbrev (link) "Replace link abbreviations in LINK string. Abbreviations are defined in `org-link-abbrev-alist'." (if (not (string-match "^\\([^:]*\\)\\(::?\\(.*\\)\\)?$" link)) link (let* ((key (match-string 1 link)) (as (or (assoc key org-link-abbrev-alist-local) (assoc key org-link-abbrev-alist))) (tag (and (match-end 2) (match-string 3 link))) rpl) (if (not as) link (setq rpl (cdr as)) (cond ;; ... (t (concat rpl tag))))))) #+END_SRC In those examples showing how the function [[org-mode:lisp/ol.el::(defun org-link-expand-abbrev][org-link-expand-abbrev]] works, we've left aside other super cool features of abbreviated links that we can read in the info node (see [[info:org#Link Abbreviations]]). We've finished our tour of the parsing step that happened in [[org-mode:lisp/org.el::(defun org-open-at-point (&optional][org-open-at-point]] when we try to "open" the link : [[emacs:lisp/simple.el::(defun next-error (&optional]] in an org-mode buffer where the local variable [[org-mode:lisp/ol.el::(defvar-local org-link-abbrev-alist-local][org-link-abbrev-alist-local]] is set to ~'(("emacs" . "/tmp/emacs/"))~. *** [[org-mode:lisp/ol.el::(defun org-link-open (link][org-link-open]] Now that we've looked at the parsing step that happened in [[org-mode:lisp/org.el::(defun org-open-at-point (&optional][org-open-at-point]], what we want to understand is the following function call (where ~nil~ is due to the way we called [[org-mode:lisp/org.el::(defun org-open-at-point (&optional][org-open-at-point]], without any prefix argument): #+BEGIN_SRC emacs-lisp (org-link-open '(link (:type "file" :path "/tmp/emacs/lisp/simple.el" :format bracket :raw-link "/tmp/emacs/lisp/simple.el::(defun next-error (&optional" :application nil :search-option "(defun next-error (&optional" :begin 1 :end 55 :contents-begin nil :contents-end nil :post-blank 0 :parent (paragraph (... :parent (section (... :parent (org-data (...)))))))) nil) #+END_SRC The function [[org-mode:lisp/ol.el::(defun org-link-open (link][org-link-open]] decides what to do next according to the type of the link: 1) As our link is of type ~file~, [[org-mode:lisp/ol.el::(defun org-link-open (link][org-link-open]] will delegate the work to the function [[org-mode:lisp/ol.el::(defun org-link-open-as-file][org-link-open-as-file]]. 2) To do so, it builds up a path concatenating the corresponding value of the property ~:path~ and ~:search-option~ of the link separating them by two colons ~::~. This path is locally stored in the variable ~path~. This variable will be the first argument passed to the function [[org-mode:lisp/ol.el::(defun org-link-open-as-file][org-link-open-as-file]]. 2) Then, it checks if we passed a prefix argument to [[org-mode:lisp/ol.el::(defun org-link-open (link][org-link-open]] or if the application (~:application~) specified in the link is ~"emacs"~ or ~"sys"~. In our case, none are true, so the last argument passed to the function [[org-mode:lisp/ol.el::(defun org-link-open-as-file][org-link-open-as-file]] will be ~nil~. So now, fate is in the hands of the function [[org-mode:lisp/ol.el::(defun org-link-open-as-file][org-link-open-as-file]], and more specifically, the following call: #+BEGIN_SRC emacs-lisp (org-link-open-as-file "/tmp/emacs/lisp/simple.el::(defun next-error (&optional" nil) #+END_SRC Here are the parts of [[org-mode:lisp/ol.el::(defun org-link-open (link][org-link-open]] we've just discussed: #+BEGIN_SRC emacs-lisp (defun org-link-open (link &optional arg) "..." (let ((type (org-element-property :type link)) (path (org-element-property :path link))) (pcase type ("file" (let* ((option (org-element-property :search-option link)) (path (if option (concat path "::" option) path))) (org-link-open-as-file path (pcase (org-element-property :application link) ((guard arg) arg) ("emacs" 'emacs) ("sys" 'system))))) ;; ... ))) #+END_SRC *** [[org-mode:lisp/ol.el::(defun org-link-open-as-file][org-link-open-as-file]] The function [[org-mode:lisp/ol.el::(defun org-link-open-as-file][org-link-open-as-file]] does the following: 1) split the given path ~"/tmp/emacs/lisp/simple.el::(defun next-error (&optional"~ into two parts a) ~option~ (which is ~"(defun next-error (&optional"~) and b) ~file-name~ (which is ~"/tmp/emacs/lisp/simple.el"~). 2) as ~file-name~ doesn't follow a pattern expected to be open with dired, check if the string ~option~ represents a number, and so the search in the file would happened jumping to the line with that number, if not this will be a text search which is our case. This is done to determinate the last arguments passed to the function [[org-mode:lisp/org.el::(defun org-open-file (path][org-open-file]] which return. So, now the "control" is passed to the function [[org-mode:lisp/org.el::(defun org-open-file (path][org-open-file]] as follow: #+BEGIN_SRC emacs-lisp (org-open-file "/tmp/emacs/lisp/simple.el" nil nil "(defun next-error") #+END_SRC As the function [[org-mode:lisp/ol.el::(defun org-link-open-as-file][org-link-open-as-file]] is short enough, we reproduce it totally: #+BEGIN_SRC emacs-lisp (defun org-link-open-as-file (path arg) "..." (let* ((option (and (string-match "::\\(.*\\)\\'" path) (match-string 1 path))) (file-name (if (not option) path (substring path 0 (match-beginning 0))))) (if (string-match "[*?{]" (file-name-nondirectory file-name)) (dired file-name) (apply #'org-open-file file-name arg (cond ((not option) nil) ((string-match-p "\\`[0-9]+\\'" option) (list (string-to-number option))) (t (list nil option))))))) #+END_SRC We get close to the end of this post ;) We just need to look at the function [[org-mode:lisp/org.el::(defun org-open-file (path][org-open-file]]. *** [[org-mode:lisp/org.el::(defun org-open-file (path][org-open-file]] The main part of [[org-mode:lisp/org.el::(defun org-open-file (path][org-open-file]] is to determine which application to use to open the file taking into account its arguments and user options (specifically [[org-mode:lisp/org.el::(defcustom org-file-apps][org-file-apps]] and [[org-mode:lisp/ol.el::(defcustom org-link-frame-setup][org-link-frame-setup]]). In our case, as the first association in [[org-mode:lisp/org.el::(defcustom org-file-apps][org-file-apps]] is ~(auto-mode . emacs)~ (by default) and the extension of the file ~/tmp/emacs/lisp/simple.el~ (which is ~.el~) matches one of the car of the associations in [[emacs:lisp/files.el::(defvar auto-mode-alist][auto-mode-alist]], the function [[org-mode:lisp/org.el::(defun org-open-file (path][org-open-file]] locally sets the variable ~cmd~ to the symbol ~emacs~. As consequence, in the main ~cond~ special form of [[org-mode:lisp/org.el::(defun org-open-file (path][org-open-file]], the expressions in the body of the clause with the condition ~(or (stringp cmd) (eq cmd 'emacs))~ (which evaluate to ~t~) are evaluated. Specifically, 1) a lookup in the alist [[org-mode:lisp/ol.el::(defcustom org-link-frame-setup][org-link-frame-setup]] returns the function to use to visite files, which is ~find-file-other-window~ by default, 2) this function is used to visit the file ~/tmp/emacs/lisp/simple.el~, 3) once the file is opened, removed the narrowing restriction from the current buffer (using ~widen~), 4) and finally do the search for the text ~"(defun next-error (&optional"~ using the function [[org-mode:lisp/ol.el::(defun org-link-search (s][org-link-search]]. Thus, SKIPING all the details (that makes [[org-mode:lisp/org.el::(defun org-open-file (path][org-open-file]] functional in real life), specifically the lookups in the alist [[org-mode:lisp/org.el::(defcustom org-file-apps][org-file-apps]], the function [[org-mode:lisp/org.el::(defun org-open-file (path][org-open-file]] in our case can be reduced to the following form: #+BEGIN_SRC emacs-lisp (let ((file "/tmp/emacs/lisp/simple.el") ; first argument of `org-open-file' (search "(defun next-error (&optional") ; last urgument of `org-open-file' (f (cdr (assq 'file org-link-frame-setup)))) (funcall f file) (widen) (org-link-search search)) #+END_SRC Here are the parts of [[org-mode:lisp/org.el::(defun org-open-file (path][org-open-file]] we've just discussed: #+BEGIN_SRC emacs-lisp (defun org-open-file (path &optional in-emacs line search) "..." (let* ((file (if (equal path "") buffer-file-name (substitute-in-file-name (expand-file-name path)))) (file-apps (append org-file-apps (org--file-default-apps))) (apps (cl-remove-if #'org--file-apps-entry-dlink-p file-apps)) ;; ... (a-m-a-p (assq 'auto-mode apps)) (dfile (downcase file)) ;; ... (save-position-maybe ;; ... ) cmd link-match-data) (cond ;; ... (t (setq cmd (or ;; ... (assoc-default dfile (org--file-apps-regexp-alist apps a-m-a-p) 'string-match) ;; ... )))) ;; ... (cond ;; ... ((or (stringp cmd) (eq cmd 'emacs)) (funcall (cdr (assq 'file org-link-frame-setup)) file) (widen) (cond (line (org-goto-line line) (when (derived-mode-p 'org-mode) (org-reveal))) (search (condition-case err (org-link-search search) ;; Save position before error-ing out so user ;; can easily move back to the original buffer. (error (funcall save-position-maybe) (error (nth 1 err))))))) ;; ... ) ;; ... )) #+END_SRC WE ARE DONE!!! ** [[#/questions-and-answers/#2022-04-04-search-options-link-abbreviations-and-org-open-at-point][Q&A]] * [2022-03-22 Tue] Org Speed Keys! BOOM! Great org-mode's feature! And a good OPPORTUNITY to talk about self-insert-command :PROPERTIES: :CUSTOM_ID: /2022-03-22-org-speed-keys-and-self-insert-command/ :REDDIT_POST: https://www.reddit.com/r/emacs/comments/tk8qou/org_speed_keys_boom_great_orgmodes_feature_and_a/ :END: Hey Emacsers, How are you doing? This post is "pretty" special to me because I can still remember and feel: 1) the first time I discovered Org Speed Keys and, 2) the first time I took a look at its implementation. It was two AHA moments, one in my Emacs journey and the other in my Elisp journey. I hope you'll enjoy it :) ** Org Speed Keys Org speed keys is not a minor mode but an org-mode feature that can be turned on by setting the variable [[org-mode:lisp/org-keys.el::(defcustom org-use-speed-commands][org-use-speed-commands]] to ~t~. When the cursor is at a the beginning of a heading it allows us to call a command by pressing a single "printing" key (keys that would normally insert a character in the buffer). For instance, if we are at the beginning of a heading, and we press ~n~, the cursor moves to the next visible heading (or stays at the current heading if it's the last one in the buffer) if we use the default bindings provided by org-mode in the variable [[org-mode:lisp/org-keys.el::(defcustom org-speed-commands][org-speed-commands]]. It is "almost" the same as pressing ~C-c C-n~ but SHORTER. Isn't it super cool??? If you've never tried it, go for it. Just run: : M-x eval-expression RET (setq org-use-speed-commands t) Open one of your org documents then with the cursor at at the very beginning of a heading press ~n~ or ~p~ repeatedly. We can see org speed command bindings, by typing ~?~ (which call [[org-mode:lisp/org-keys.el::(defun org-speed-command-help][org-speed-command-help]]) with the cursor at the very beginning of a heading. This pops up the following help buffer: #+BEGIN_SRC text Speed commands ============== Outline Navigation ------------------ n (org-speed-move-safe 'org-next-visible-heading) p (org-speed-move-safe 'org-previous-visible-heading) f (org-speed-move-safe 'org-forward-heading-same-level) b (org-speed-move-safe 'org-backward-heading-same-level) F org-next-block B org-previous-block u (org-speed-move-safe 'outline-up-heading) j org-goto g (org-refile '(4)) Outline Visibility ------------------ c org-cycle C org-shifttab org-display-outline-path s org-toggle-narrow-to-subtree k org-cut-subtree = org-columns ... THERE ARE MORE ... ? org-speed-command-help #+END_SRC If we want to use our own bindings, we can modify: - the variable [[org-mode:lisp/org-keys.el::(defcustom org-speed-commands][org-speed-commands]] since org-mode 9.5, - the variable ~org-speed-commands-user~ for previous versions. Note: the variable ~org-speed-commands-user~ has been made obsolete since org-mode 9.5 but it is still supported and should be removed from org-mode 9.6 as we can read in the source code [[org-mode:lisp/org-keys.el]]: #+BEGIN_SRC text ;; FIXME: don't check `org-speed-commands-user' past 9.6 #+END_SRC ** How does org-mode implement Org Speed keys? *** Remapping [[emacs:src/cmds.c::DEFUN ("self-insert-command",][self-insert-command]] When org-mode is turned on, among other initialization tasks, it remaps the command [[emacs:src/cmds.c::DEFUN ("self-insert-command",][self-insert-command]] to the command [[org-mode:lisp/org.el::(defun org-self-insert-command][org-self-insert-command]] in the keymap [[org-mode:lisp/org-keys.el::(defvar org-mode-map][org-mode-map]], as we can see in the file [[org-mode:lisp/org-keys.el]]: #+BEGIN_SRC emacs-lisp (org-remap org-mode-map 'self-insert-command 'org-self-insert-command 'delete-char 'org-delete-char 'delete-backward-char 'org-delete-backward-char 'kill-line 'org-kill-line 'open-line 'org-open-line 'yank 'org-yank 'comment-dwim 'org-comment-dwim 'move-beginning-of-line 'org-beginning-of-line 'move-end-of-line 'org-end-of-line 'forward-paragraph 'org-forward-paragraph 'backward-paragraph 'org-backward-paragraph 'backward-sentence 'org-backward-sentence 'forward-sentence 'org-forward-sentence 'fill-paragraph 'org-fill-paragraph 'delete-indentation 'org-delete-indentation 'transpose-words 'org-transpose-words) #+END_SRC In the case of [[emacs:src/cmds.c::DEFUN ("self-insert-command",][self-insert-command]], the preceding remapping can be written like this (this is what [[org-mode:lisp/org-keys.el::(defun org-remap][org-remap]] does): #+BEGIN_SRC emacs-lisp (define-key org-mode-map [remap self-insert-command] 'org-self-insert-command) #+END_SRC As a consequence, in org-mode, when we press a printing key (that would by default insert a character in the buffer), let say we press ~n~, the "command loop editor" does several things: 1) it performs a key lookup for ~n~ in the current active maps and finds that ~n~ is bound to [[emacs:src/cmds.c::DEFUN ("self-insert-command",][self-insert-command]] (due to the default binding in the keymap [[emacs:lisp/subr.el::(defvar global-map][global-map]] which is the global map by default), then, 2) it checks if the command [[emacs:src/cmds.c::DEFUN ("self-insert-command",][self-insert-command]] has a remapping in the current active maps (with the function [[emacs:src/keymap.c::DEFUN ("command-remapping"][command-remapping]]), 3) it finds that [[emacs:src/cmds.c::DEFUN ("self-insert-command",][self-insert-command]] is remapped to [[org-mode:lisp/org.el::(defun org-self-insert-command][org-self-insert-command]] (due to the remapping in the keymap [[org-mode:lisp/org-keys.el::(defvar org-mode-map][org-mode-map]]) and instead of calling [[emacs:src/cmds.c::DEFUN ("self-insert-command",][self-insert-command]], it calls [[org-mode:lisp/org.el::(defun org-self-insert-command][org-self-insert-command]]. Now, the command [[org-mode:lisp/org.el::(defun org-self-insert-command][org-self-insert-command]] has complete power over the actions to perform. Nothing forces it to insert the character ~n~ in the buffer. *** If it is magic, this is [[org-mode:lisp/org.el::(defun org-self-insert-command][org-self-insert-command]] Before talking about how [[org-mode:lisp/org.el::(defun org-self-insert-command][org-self-insert-command]] performs speed keys, let me ask you some questions: - Have you ever noticed that when you modify the title of a headline the tags are automatically re-aligned? Guess what? This is [[org-mode:lisp/org.el::(defun org-self-insert-command][org-self-insert-command]] in action (calling [[org-mode:lisp/org.el::(defun org-fix-tags-on-the-fly][org-fix-tags-on-the-fly]] just after inserting a character). - Have you ever noticed that inserting less characters in a table field than its width doesn't move the right bar of the field? This is [[org-mode:lisp/org.el::(defun org-self-insert-command][org-self-insert-command]] in action. - Did you know that in a table, if you type printing keys just after moving with ~TAB~, ~S-TAB~, ~RET~, the table field is made blank before inserting the characters (with [[org-mode:lisp/org-table.el::(defcustom org-table-auto-blank-field][org-table-auto-blank-field]] set to ~t~)? This is also [[org-mode:lisp/org.el::(defun org-self-insert-command][org-self-insert-command]] (see [[https://www.youtube.com/watch?v=w4wxGOijyZs][Inside Emacs #6 (part 5) Why is it so fast to edit tables with org-table?]]). *** [[org-mode:lisp/org.el::(defun org-self-insert-command][org-self-insert-command]] implementation Let's get back to our example. We've pressed the "printing" key ~n~ and "the command loop editor" has called [[org-mode:lisp/org.el::(defun org-self-insert-command][org-self-insert-command]]. The command [[org-mode:lisp/org.el::(defun org-self-insert-command][org-self-insert-command]] does the following (assuming we have set [[org-mode:lisp/org-keys.el::(defcustom org-use-speed-commands][org-use-speed-commands]] to ~t~): 1) locally set the variable ~kv~ to the vector ~[?n]~ corresponding to the key sequence that invoked the command [[org-mode:lisp/org.el::(defun org-self-insert-command][org-self-insert-command]] (the "printing" key ~n~ we've pressed), 2) run the hook [[org-mode:lisp/org-keys.el::(defcustom org-speed-command-hook][org-speed-command-hook]] "until success" with the argument ~"n"~ (a string, corresponding to the evaluation of ~(make-string 1 (aref [?n] 0))~ knowing that ~kv~ is ~[?n]~). Considering that the hook [[org-mode:lisp/org-keys.el::(defcustom org-speed-command-hook][org-speed-command-hook]] is equal to the list ~(org-speed-command-activate org-babel-speed-command-activate)~ by default, the function [[emacs:src/eval.c::DEFUN ("run-hook-with-args-until-success"][run-hook-with-args-until-success]] evaluate successively the forms: 1) ~(org-speed-command-activate "n")~, 2) ~(org-babel-speed-command-activate "n")~, stopping at the first one that returns non-nil, and return that value, or return ~nil~ if both evaluate to ~nil~. 3) Then, this returned value becomes the value of the variable ~org-speed-command~, 4) if ~org-speed-command~ is either a command, a function or a non empty list, ~org-speed-command~ is called or evaluated (and [[org-mode:lisp/org.el::(defun org-self-insert-command][org-self-insert-command]] returns "maybe" without inserting ~n~ in the buffer). If ~org-speed-command~ is neither a command, a function nor a non empty list, the command [[org-mode:lisp/org.el::(defun org-self-insert-command][org-self-insert-command]] is called with the variable [[org-mode:lisp/org-keys.el::(defcustom org-use-speed-commands][org-use-speed-commands]] locally set to ~nil~ which leads to skip the first clause of the special form ~cond~ in the body of [[org-mode:lisp/org.el::(defun org-self-insert-command][org-self-insert-command]], and [[org-mode:lisp/org.el::(defun org-self-insert-command][org-self-insert-command]] among other "actions" will insert the character ~n~ in the buffer. Below you can see the parts of the command [[org-mode:lisp/org.el::(defun org-self-insert-command][org-self-insert-command]] related to the speed keys feature that we have just discussed: #+BEGIN_SRC emacs-lisp (defun org-self-insert-command (N) ;; ... (interactive "p") (cond ((and org-use-speed-commands (let ((kv (this-command-keys-vector))) (setq org-speed-command (run-hook-with-args-until-success 'org-speed-command-hook (make-string 1 (aref kv (1- (length kv)))))))) (cond ((commandp org-speed-command) (setq this-command org-speed-command) (call-interactively org-speed-command)) ((functionp org-speed-command) (funcall org-speed-command)) ((and org-speed-command (listp org-speed-command)) (eval org-speed-command)) (t (let (org-use-speed-commands) (call-interactively 'org-self-insert-command))))) ((and ;; ... (org-at-table-p) ;; .... ) (self-insert-command N) ;; ... ) (t ;; ... (self-insert-command N) (org-fix-tags-on-the-fly) ;; ... ))) #+END_SRC *** [[org-mode:lisp/org-keys.el::(defun org-speed-command-activate][org-speed-command-activate]] We still have one function to cover: [[org-mode:lisp/org-keys.el::(defun org-speed-command-activate][org-speed-command-activate]] (knowing that [[org-mode:lisp/org-keys.el::(defun org-babel-speed-command-activate][org-babel-speed-command-activate]] does "almost the same thing" but for the cursor at the beginning of source blocks). Indeed, continuing with our example, if evaluating ~(org-speed-command-activate "n")~ returns a command (a function or a non empty list) this command will be called instead of inserting the character ~"n"~. In the simple case, when [[org-mode:lisp/org-keys.el::(defcustom org-use-speed-commands][org-use-speed-commands]] is set to ~t~ (it could also be a function), [[org-mode:lisp/org-keys.el::(defun org-speed-command-activate][org-speed-command-activate]] checks: 1) if the point (the cursor) is at the beginning of line ~(bolp)~ and, 2) if this line is a heading ~(looking-at org-outline-regexp)~. If true, [[org-mode:lisp/org-keys.el::(defun org-speed-command-activate][org-speed-command-activate]] looks for the key ~"n"~ in the alist [[org-mode:lisp/org-keys.el::(defcustom org-speed-commands][org-speed-commands]] defined by default like this: #+BEGIN_SRC emacs-lisp (defcustom org-speed-commands '(("Outline Navigation") ("n" . (org-speed-move-safe 'org-next-visible-heading)) ("p" . (org-speed-move-safe 'org-previous-visible-heading)) ("f" . (org-speed-move-safe 'org-forward-heading-same-level)) ("b" . (org-speed-move-safe 'org-backward-heading-same-level)) ("F" . org-next-block) ("B" . org-previous-block) ;; ... )) #+END_SRC and returns the s-exp associated with the key ~"n"~ which is by default: : (org-speed-move-safe 'org-next-visible-heading) As we left aside the case where [[org-mode:lisp/org-keys.el::(defcustom org-use-speed-commands][org-use-speed-commands]] is a function and we anticipate the org-mode version 9.6, we can write a simplified version of [[org-mode:lisp/org-keys.el::(defun org-speed-command-activate][org-speed-command-activate]], that we call ~org-speed-command-activate-SIMPLE~, like this: #+BEGIN_SRC emacs-lisp (defun org-speed-command-activate-SIMPLE (keys) (when (and (bolp) (looking-at org-outline-regexp)) (cdr (assoc keys org-speed-commands)))) #+END_SRC So, when we pressed the key ~n~ (in org-mode): org-babel-next-src-block 1) if we are at the beginning of a heading, the cursor is moved "safely" to the next visible heading, 2) if we are at the beginning of a source block (due to [[org-mode:lisp/org-keys.el::(defun org-babel-speed-command-activate][org-babel-speed-command-activate]] in the hook [[org-mode:lisp/org-keys.el::(defcustom org-speed-command-hook][org-speed-command-hook]]), the cursor is moved to the next source block, 3) and if we were anywhere else, the character ~n~ is inserted in the buffer. We are done with the mechanism of Org Speed Keys. Let's talk a bit about the command [[emacs:src/cmds.c::DEFUN ("self-insert-command",][self-insert-command]]. ** [[emacs:src/cmds.c::DEFUN ("self-insert-command",][self-insert-command]] *** Bindings of printing characters to [[emacs:src/cmds.c::DEFUN ("self-insert-command",][self-insert-command]] in the keymap [[emacs:lisp/subr.el::(defvar global-map][global-map]] In this section, our goal is to see what printing characters are bound to the command [[emacs:src/cmds.c::DEFUN ("self-insert-command",][self-insert-command]]. In the info node [[info:elisp#Controlling Active Maps]], we can read the following: #+BEGIN_SRC text -- Variable: global-map This variable contains the default global keymap that maps Emacs keyboard input to commands. The global keymap is normally this keymap. The default global keymap is a full keymap that binds ‘self-insert-command’ to all of the printing characters. #+END_SRC Indeed, the variable [[emacs:lisp/subr.el::(defvar global-map][global-map]] is defined in the file [[emacs:lisp/subr.el]] and is set to be the global keymap (using [[emacs:src/keymap.c::DEFUN ("use-global-map"][use-global-map]]) as we can see: #+BEGIN_SRC emacs-lisp (defvar global-map (let ((map (make-keymap))) ;; ... (define-key map "\C-i" #'self-insert-command) (let* ((vec1 (make-vector 1 nil)) (f (lambda (from to) (while (< from to) (aset vec1 0 from) (define-key map vec1 #'self-insert-command) (setq from (1+ from)))))) (funcall f #o040 #o0177) (when (eq system-type 'ms-dos) ;FIXME: Why? (funcall f #o0200 #o0240)) (funcall f #o0240 #o0400)) (define-key map "\C-a" #'beginning-of-line) (define-key map "\C-b" #'backward-char) (define-key map "\C-e" #'end-of-line) (define-key map "\C-f" #'forward-char) ;; ... map) "..." ) (use-global-map global-map) #+END_SRC Many bindings in the keymap [[emacs:lisp/subr.el::(defvar global-map][global-map]] are added in other files. For instance the key sequences ~C-g~, ~C-u~, ~C-k~, ~C-w~ and ~C-y~ are added to [[emacs:lisp/subr.el::(defvar global-map][global-map]] in the file [[emacs:lisp/bindings.el]] like this: #+BEGIN_SRC emacs-lisp (define-key global-map "\C-g" 'keyboard-quit) (define-key global-map "\C-u" 'universal-argument) (define-key global-map "\C-k" 'kill-line) (define-key global-map "\C-w" 'kill-region) (define-key global-map "\C-y" 'yank) #+END_SRC Now, let's focus in the ~let*~ binding in the definition of the keymap [[emacs:lisp/subr.el::(defvar global-map][global-map]]. In this ~let*~ binding, we define a function ~f~ that binds in the local keymap ~map~ all the characters (represented as integer) between ~from~ to ~to~ (excluded), and we apply it to the following limits (excluding the ms-dos case): 1) from ~#o040~ to ~#o0177~, 2) from ~#o0240~ to ~#o0400~. If it is not clear which printing characters are bound to [[emacs:src/cmds.c::DEFUN ("self-insert-command",][self-insert-command]] in the keymap [[emacs:lisp/subr.el::(defvar global-map][global-map]] then we can "list" them like this: #+BEGIN_SRC emacs-lisp (with-temp-buffer (dotimes (i (- #o0177 #o040)) (insert (+ #o040 i))) (buffer-string)) ;; " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" (with-temp-buffer (dotimes (i (- #o0400 #o0240)) (insert (+ #o0240 i))) (buffer-string)) ;; " ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ" #+END_SRC *** Remapping [[emacs:src/cmds.c::DEFUN ("self-insert-command",][self-insert-command]] to [[emacs:lisp/subr.el::(defun undefined][undefined]] in [[emacs:lisp/info.el::(define-derived-mode Info-mode][Info-mode]], [[emacs:lisp/help-mode.el::(define-derived-mode help-mode][help-mode]] and [[emacs:lisp/simple.el::(define-derived-mode special-mode][special-mode]] In some cases (like in [[emacs:lisp/info.el::(define-derived-mode Info-mode][Info-mode]] and [[emacs:lisp/help-mode.el::(define-derived-mode help-mode][help-mode]]) we want to override all printing keys to be undefined before remapping some of them to specific commands. This is done by remapping the command [[emacs:src/cmds.c::DEFUN ("self-insert-command",][self-insert-command]] in the local keymap to the function [[emacs:lisp/subr.el::(defun undefined][undefined]]. Note that remapping [[emacs:src/cmds.c::DEFUN ("self-insert-command",][self-insert-command]] to ~nil~ in the local keymaps won't override [[emacs:src/cmds.c::DEFUN ("self-insert-command",][self-insert-command]] in the global map as we can read in the info node [[info:elisp#Key Lookup]]: #+BEGIN_SRC text The symbol ‘undefined’ is worth special mention: it means to treat the key as undefined. Strictly speaking, the key is defined, and its binding is the command ‘undefined’; but that command does the same thing that is done automatically for an undefined key: it rings the bell (by calling ‘ding’) but does not signal an error. ‘undefined’ is used in local keymaps to override a global key binding and make the key undefined locally. A local binding of ‘nil’ would fail to do this because it would not override the global binding. #+END_SRC In the case of [[emacs:lisp/info.el::(define-derived-mode Info-mode][Info-mode]] this is done by calling the function [[emacs:lisp/subr.el::(defun suppress-keymap][suppress-keymap]] (which overrides all printing keys to be undefined, maps ~-~ to the command [[emacs:lisp/simple.el::(defun negative-argument][negative-argument]] and the numbers 0,1,..., 9 to the command [[emacs:lisp/simple.el::(defun digit-argument][digit-argument]]), when defining the keymap [[emacs:lisp/info.el::(defvar Info-mode-map][Info-mode-map]] as follow: #+BEGIN_SRC emacs-lisp (defvar Info-mode-map (let ((map (make-keymap))) (suppress-keymap map) ;; ... (define-key map "1" 'Info-nth-menu-item) (define-key map "2" 'Info-nth-menu-item) ;;... (define-key map "n" 'Info-next) (define-key map "p" 'Info-prev) (define-key map "q" 'quit-window) ;;... map) "...") #+END_SRC In the case of [[emacs:lisp/help-mode.el::(define-derived-mode help-mode][help-mode]] this is done by setting the parent keymap of [[emacs:lisp/help-mode.el::(defvar help-mode-map][help-mode-map]] to be a composed keymap of [[emacs:lisp/button.el::(defvar button-buffer-map][button-buffer-map]] and the parent map [[emacs:lisp/simple.el::(defvar special-mode-map][special-mode-map]] (which overrides all printing keys to be undefined, maps ~-~ to the command [[emacs:lisp/simple.el::(defun negative-argument][negative-argument]] and the numbers 0,1,..., 9 to the command [[emacs:lisp/simple.el::(defun digit-argument][digit-argument]]), as we can see below: #+BEGIN_SRC emacs-lisp (defvar help-mode-map (let ((map (make-sparse-keymap))) (set-keymap-parent map (make-composed-keymap button-buffer-map special-mode-map)) (define-key map "n" 'help-goto-next-page) (define-key map "p" 'help-goto-previous-page) ;; ... map) "..." ) #+END_SRC *** Best function name ever: [[emacs:src/dispnew.c::bitch_at_user (void)][bitch_at_user]] [[emacs:src/dispnew.c::bitch_at_user (void)][bitch_at_user]]!!! What a beautiful name for a function! This is a ~C~ function we can find in Emacs source code. If I'm not wrong it is called in the body of 3 functions. Mainly, this function when called will ring the bell. This function is defined in the file [[emacs:src/dispnew.c]] as follow: #+BEGIN_SRC c void bitch_at_user (void) { if (noninteractive) putchar (07); else if (!INTERACTIVE) /* Stop executing a keyboard macro. */ { const char *msg = "Keyboard macro terminated by a command ringing the bell"; Fsignal (Quser_error, list1 (build_string (msg))); } else ring_bell (XFRAME (selected_frame)); } #+END_SRC Why am I talking about this function? Because its name is AMAZING and because I encountered it when I was reading the source code of [[emacs:src/cmds.c::DEFUN ("self-insert-command",][self-insert-command]]. Indeed, the function [[emacs:src/dispnew.c::bitch_at_user (void)][bitch_at_user]] is called in the body of [[emacs:src/cmds.c::DEFUN ("self-insert-command",][self-insert-command]] when the command [[emacs:src/cmds.c::DEFUN ("self-insert-command",][self-insert-command]] is invoked by a key sequence that is not a printing key. For instance, we can trigger it by binding for the key sequence ~C-~ (pick any key sequence you are not using) to [[emacs:src/cmds.c::DEFUN ("self-insert-command",][self-insert-command]] like this: #+BEGIN_SRC emacs-lisp (global-set-key (kbd "C-") 'self-insert-command) #+END_SRC And now, if we press ~C-~, we trigger the function [[emacs:src/dispnew.c::bitch_at_user (void)][bitch_at_user]] and: 1) we see ~Wrong type argument: characterp, C-f1~ in the echo area and, 2) depending on the "setting of the bell" (see [[info:elisp#Beeping]]) a) nothing more happens, b) we hear the bell ringing, or c) we see the screen flashing. AS NO SENTENCE CAN BEAT THE SOURCE CODE, here is the source code of [[emacs:src/cmds.c::DEFUN ("self-insert-command",][self-insert-command]] defined in the file [[emacs:src/cmds.c]]: #+BEGIN_SRC c DEFUN ("self-insert-command", Fself_insert_command, Sself_insert_command, 1, 2, "(list (prefix-numeric-value current-prefix-arg) last-command-event)", doc: /* Insert the character you type. Whichever character C you type to run this command is inserted. The numeric prefix argument N says how many times to repeat the insertion. Before insertion, `expand-abbrev' is executed if the inserted character does not have word syntax and the previous character in the buffer does. After insertion, `internal-auto-fill' is called if `auto-fill-function' is non-nil and if the `auto-fill-chars' table has a non-nil value for the inserted character. At the end, it runs `post-self-insert-hook'. */) (Lisp_Object n, Lisp_Object c) { CHECK_FIXNUM (n); /* Backward compatibility. */ if (NILP (c)) c = last_command_event; if (XFIXNUM (n) < 0) error ("Negative repetition argument %"pI"d", XFIXNUM (n)); if (XFIXNAT (n) < 2) call0 (Qundo_auto_amalgamate); /* Barf if the key that invoked this was not a character. */ if (!CHARACTERP (c)) bitch_at_user (); else { int character = translate_char (Vtranslation_table_for_input, XFIXNUM (c)); int val = internal_self_insert (character, XFIXNAT (n)); if (val == 2) Fset (Qundo_auto__this_command_amalgamating, Qnil); frame_make_pointer_invisible (SELECTED_FRAME ()); } return Qnil; } #+END_SRC WE ARE DONE!!! ** [[#/questions-and-answers/#2022-03-22-org-speed-keys-and-self-insert-command][Q&A]] * [2022-03-11 Fri] Did you know that org-mode's source code contains more than 5000 examples? :PROPERTIES: :CUSTOM_ID: /2022-03-11-org-mode-source-code-5000-examples/ :REDDIT_POST: https://www.reddit.com/r/emacs/comments/tblodh/did_you_know_that_orgmodes_source_code_contains/ :END: Hey org-mode lovers, Have you ever been in the following situation? You have tried to understand a specific aspect of org-mode (say a command) and you have done the following: 1) you have played with the command (writing your own examples), 2) you have read all the parts in the manual dealing with this command, 3) you have read the docstring of the command, 4) you have looked for explanations on the web, 5) you even have read the source code of the command, but you still haven't figured it out after all your efforts. I've been in this situation and I thought it would be easier if there were more examples. BUT THERE ARE MORE EXAMPLES. We just have to change our lenses to see them. What if we looked at org-mode tests as examples of org-mode? BOOM. We have our 5000 examples. Before going any further, let's make it clear that we don't need to understand or know how to use [[emacs:lisp/emacs-lisp/ert.el][ert]] (the built-in package used for testing) to benefit from org-mode testing. And this post is not about [[emacs:lisp/emacs-lisp/ert.el][ert]] testing but about the information that we can get from the org-mode test suite. In this post, we use the example of the command [[org-mode:lisp/org.el::(defun org-insert-heading][org-insert-heading]] (bound by default to ~M-~, that allows to insert a new heading or item with the same depth at point) to "demonstrate" that the tests are indeed examples. Let's assume we are already familiar with [[org-mode:lisp/org.el::(defun org-insert-heading][org-insert-heading]], we use it every day, but sometimes we don't understand why it behaves the way it does. What can we do to find out the truth about [[org-mode:lisp/org.el::(defun org-insert-heading][org-insert-heading]]? ** Org manual First of all, we can take a look in the manual of org. In the info node [[info:org#Plain Lists]] we can read: #+BEGIN_SRC text ‘M-’ (‘org-insert-heading’) Insert new item at current level. With a prefix argument, force a new heading (see Structure Editing). If this command is used in the middle of an item, that item is _split_ in two, and the second part becomes the new item(5). If this command is executed _before item’s body_, the new item is created _before_ the current one. ... (5) If you do not want the item to be split, customize the variable ‘org-M-RET-may-split-line’. #+END_SRC That might be enough, but let's say it isn't, and we continue our investigation to find the truth. ** Help buffer Secondly, we can find more information about [[org-mode:lisp/org.el::(defun org-insert-heading][org-insert-heading]] by reading its docstring by running: : C-h f org-insert-heading RET which pops up this help buffer: #+BEGIN_SRC text org-insert-heading is an interactive compiled Lisp function. (org-insert-heading &optional ARG INVISIBLE-OK TOP) Insert a new heading or an item with the same depth at point. If point is at the beginning of a heading, insert a new heading or a new headline above the current one. When at the beginning of a regular line of text, turn it into a heading. If point is in the middle of a line, split it and create a new headline with the text in the current line after point (see ‘org-M-RET-may-split-line’ on how to modify this behavior). As a special case, on a headline, splitting can only happen on the title itself. E.g., this excludes breaking stars or tags. With a ‘C-u’ prefix, set ‘org-insert-heading-respect-content’ to a non-nil value for the duration of the command. This forces the insertion of a heading after the current subtree, independently on the location of point. With a ‘C-u C-u’ prefix, insert the heading at the end of the tree above the current heading. For example, if point is within a 2nd-level heading, then it will insert a 2nd-level heading at the end of the 1st-level parent subtree. When INVISIBLE-OK is set, stop at invisible headlines when going back. This is important for non-interactive uses of the command. When optional argument TOP is non-nil, insert a level 1 heading, unconditionally. #+END_SRC Now we not only know what the command does but also how to call it. There is also some information about its non-interactive use. Reading the Org manual and its docstring may have given us the information we wanted, but let's say we want to know more and continue our investigation to find the truth. ** The source code The truth resides in the source code! Isn't that right? Ok, let's take a look at the command [[org-mode:lisp/org.el::(defun org-insert-heading][org-insert-heading]] defined in the file [[org-mode:lisp/org.el]] as follow: #+BEGIN_SRC emacs-lisp (defun org-insert-heading (&optional arg invisible-ok top) ;; HERE WAS THE DOCSTRING (interactive "P") (let* ((blank? (org--blank-before-heading-p (equal arg '(16)))) (level (org-current-level)) (stars (make-string (if (and level (not top)) level 1) ?*))) (cond ((or org-insert-heading-respect-content (member arg '((4) (16))) (and (not invisible-ok) (invisible-p (max (1- (point)) (point-min))))) ;; Position point at the location of insertion. Make sure we ;; end up on a visible headline if INVISIBLE-OK is nil. (org-with-limited-levels (if (not level) (outline-next-heading) ;before first headline (org-back-to-heading invisible-ok) (when (equal arg '(16)) (org-up-heading-safe)) (org-end-of-subtree))) (unless (bolp) (insert "\n")) (when (and blank? (save-excursion (backward-char) (org-before-first-heading-p))) (insert "\n") (backward-char)) (when (and (not level) (not (eobp)) (not (bobp))) (when (org-at-heading-p) (insert "\n")) (backward-char)) (unless (and blank? (org-previous-line-empty-p)) (org-N-empty-lines-before-current (if blank? 1 0))) (insert stars " ") ;; When INVISIBLE-OK is non-nil, ensure newly created headline ;; is visible. (unless invisible-ok (pcase (get-char-property-and-overlay (point) 'invisible) (`(outline . ,o) (move-overlay o (overlay-start o) (line-end-position 0))) (_ nil)))) ;; At a headline... ((org-at-heading-p) (cond ((bolp) (when blank? (save-excursion (insert "\n"))) (save-excursion (insert stars " \n")) (unless (and blank? (org-previous-line-empty-p)) (org-N-empty-lines-before-current (if blank? 1 0))) (end-of-line)) ((and (org-get-alist-option org-M-RET-may-split-line 'headline) (org-match-line org-complex-heading-regexp) (org-pos-in-match-range (point) 4)) ;; Grab the text that should moved to the new headline. ;; Preserve tags. (let ((split (delete-and-extract-region (point) (match-end 4)))) (if (looking-at "[ \t]*$") (replace-match "") (org-align-tags)) (end-of-line) (when blank? (insert "\n")) (insert "\n" stars " ") (when (org-string-nw-p split) (insert split)))) (t (end-of-line) (when blank? (insert "\n")) (insert "\n" stars " ")))) ;; On regular text, turn line into a headline or split, if ;; appropriate. ((bolp) (insert stars " ") (unless (and blank? (org-previous-line-empty-p)) (org-N-empty-lines-before-current (if blank? 1 0)))) (t (unless (org-get-alist-option org-M-RET-may-split-line 'headline) (end-of-line)) (insert "\n" stars " ") (unless (and blank? (org-previous-line-empty-p)) (org-N-empty-lines-before-current (if blank? 1 0)))))) (run-hooks 'org-insert-heading-hook)) #+END_SRC You can get org-mode's source code by running the following command: : git clone https://git.savannah.gnu.org/git/emacs/org-mode.git Ok... The source code helps, but now we need to know a lot more about the implementations of ~org-mode~ and ~emacs/elisp~ than we want to spend time on. Indeed, if we want to understand the implementation of [[org-mode:lisp/org.el::(defun org-insert-heading][org-insert-heading]] we have to understand: 1) (org API) ~org--blank-before-heading-p~, ~org-current-level~, ~org-with-limited-levels~, ~outline-next-heading~, ~org-back-to-heading~, ~org-up-heading-safe~, ~org-end-of-subtree~, ~org-before-first-heading-p~, ~org-N-empty-lines-before-current~, etc., 2) (emacs/elisp API) ~make-string~, ~member~, ~invisible-p~, ~bolp~, ~save-excursion~, ~eobp~, ~bobp~, ~pcase~, ~get-char-property-and-overlay~, etc. So, what can we do? Maybe we can look at the test of the command [[org-mode:lisp/org.el::(defun org-insert-heading][org-insert-heading]]. ** The Tests (aka. "The Examples") *** [[org-mode:testing/lisp/test-org.el::(ert-deftest test-org/insert-heading][test-org/insert-heading]] In the directory [[org-mode:testing]], doing a search for [[org-mode:testing/lisp/test-org.el::(ert-deftest test-org/insert-heading][test-org/insert-heading]] [[org-mode:lisp/org.el::(defun org-insert-heading][org-insert-heading]] using ~grep~ (or ~ripgrep~) shows that the command [[org-mode:lisp/org.el::(defun org-insert-heading][org-insert-heading]] is tested in the [[emacs:lisp/emacs-lisp/ert.el][ert]] test [[org-mode:testing/lisp/test-org.el::(ert-deftest test-org/insert-heading][test-org/insert-heading]] defined in the file [[org-mode:testing/lisp/test-org.el]] as follows (we reproduce only the first 4 [[emacs:lisp/emacs-lisp/ert.el::(cl-defmacro should][should]] forms - it contains 28 [[emacs:lisp/emacs-lisp/ert.el::(cl-defmacro should][should]] forms and 1 [[emacs:lisp/emacs-lisp/ert.el::(cl-defmacro should-not][should-not]] form): #+BEGIN_SRC emacs-lisp (ert-deftest test-org/insert-heading () "Test `org-insert-heading' specifications." ;; In an empty buffer, insert a new headline. (should (equal "* " (org-test-with-temp-text "" (org-insert-heading) (buffer-string)))) ;; At the beginning of a line, turn it into a headline. (should (equal "* P" (org-test-with-temp-text "P" (org-insert-heading) (buffer-string)))) ;; In the middle of a line, split the line if allowed, otherwise, ;; insert the headline at its end. (should (equal "Para\n* graph" (org-test-with-temp-text "Paragraph" (let ((org-M-RET-may-split-line '((default . t)))) (org-insert-heading)) (buffer-string)))) (should (equal "Paragraph\n* " (org-test-with-temp-text "Paragraph" (let ((org-M-RET-may-split-line '((default . nil)))) (org-insert-heading)) (buffer-string)))) ;; ... ) #+END_SRC What can we observe from those 4 [[emacs:lisp/emacs-lisp/ert.el::(cl-defmacro should][should]] forms? 1) they have the same "shape", so we can deduce that if we understand how the first one works, we will understand the others, 2) they use only a few symbols (much less than in the source code of [[org-mode:lisp/org.el::(defun org-insert-heading][org-insert-heading]]): - [[emacs:lisp/emacs-lisp/ert.el::(cl-defmacro should][should]] from [[emacs:lisp/emacs-lisp/ert.el][ert]] package, - ~equal~, ~let~ and ~buffer-string~ from Emacs/Elisp, - [[org-mode:testing/org-test.el::(defmacro org-test-with-temp-text][org-test-with-temp-text]] specific to Org test suite and - [[org-mode:lisp/org.el::(defun org-insert-heading][org-insert-heading]] and [[org-mode:lisp/org.el::(defcustom org-M-RET-may-split-line][org-M-RET-may-split-line]], the command and the variable we are testing. Now let's describe these [[emacs:lisp/emacs-lisp/ert.el::(cl-defmacro should][should]] forms in words as best we can. After doing this, perhaps we will think of these [[emacs:lisp/emacs-lisp/ert.el::(cl-defmacro should][should]] forms as small examples, each describing a behavior of the command [[org-mode:lisp/org.el::(defun org-insert-heading][org-insert-heading]]. *** Example 1 The first [[emacs:lisp/emacs-lisp/ert.el::(cl-defmacro should][should]] form: #+BEGIN_SRC emacs-lisp (should (equal "* " (org-test-with-temp-text "" (org-insert-heading) (buffer-string)))) #+END_SRC could be translated like this: 1) in a temporary buffer in org-mode ([[org-mode:testing/org-test.el::(defmacro org-test-with-temp-text][org-test-with-temp-text]]), 2) keep the buffer empty (~""~), 3) keep the point at the beginning, 4) call [[org-mode:lisp/org.el::(defun org-insert-heading][org-insert-heading]], 5) return the string from the temporary buffer (~buffer-string~), 6) compare this string to the string ~"* "~ (~equal~), 7) if they are equal, return ~t~, if not, return the error ~ert-test-failed~ ([[emacs:lisp/emacs-lisp/ert.el::(cl-defmacro should][should]]). *** [[emacs:lisp/emacs-lisp/ert.el][ert]] and [[org-mode:testing/org-test.el::(defmacro org-test-with-temp-text][org-test-with-temp-text]] To evaluate the preceding form we need to have [[emacs:lisp/emacs-lisp/ert.el][ert]] loaded (for the macro [[emacs:lisp/emacs-lisp/ert.el::(cl-defmacro should][should]]) which can be achieve by evaluating the following form: #+BEGIN_SRC emacs-lisp (require 'ert) #+END_SRC and we need the macro [[org-mode:testing/org-test.el::(defmacro org-test-with-temp-text][org-test-with-temp-text]] (defined in the file [[org-mode:testing/org-test.el]]) to be defined which can be done by evaluating its definition: #+BEGIN_SRC emacs-lisp (defmacro org-test-with-temp-text (text &rest body) (declare (indent 1)) `(let ((inside-text (if (stringp ,text) ,text (eval ,text))) (org-mode-hook nil)) (with-temp-buffer (org-mode) (let ((point (string-match "" inside-text))) (if point (progn (insert (replace-match "" nil nil inside-text)) (goto-char (1+ (match-beginning 0)))) (insert inside-text) (goto-char (point-min)))) (font-lock-ensure (point-min) (point-max)) ,@body))) #+END_SRC *** [[emacs:lisp/emacs-lisp/ert.el::(cl-defmacro should][should]], [[emacs:lisp/emacs-lisp/ert.el::(cl-defmacro should-not][should-not]], [[emacs:lisp/emacs-lisp/ert.el::(cl-defmacro should-error][should-error]] Our examples only use the macro [[emacs:lisp/emacs-lisp/ert.el::(cl-defmacro should][should]] but if we want to read more tests it is useful to know the macros [[emacs:lisp/emacs-lisp/ert.el::(cl-defmacro should-not][should-not]] and [[emacs:lisp/emacs-lisp/ert.el::(cl-defmacro should-error][should-error]] that [[emacs:lisp/emacs-lisp/ert.el][ert]] package provides. In the following Elisp snippet, we give some examples of the use of [[emacs:lisp/emacs-lisp/ert.el::(cl-defmacro should][should]], [[emacs:lisp/emacs-lisp/ert.el::(cl-defmacro should-not][should-not]] and [[emacs:lisp/emacs-lisp/ert.el::(cl-defmacro should-error][should-error]]: #+BEGIN_SRC emacs-lisp (should 3) ; 3 (should t) ; t (should nil) ;; Debugger entered--Lisp error: (ert-test-failed ((should nil) :form nil :value nil)) (should-not nil) ; nil (should-not t) ;; Debugger entered--Lisp error: (ert-test-failed ((should-not t) :form t :value t)) (should-error (= "1" 1)) ; (wrong-type-argument number-or-marker-p "1") (should-error t) ;; Debugger entered--Lisp error: (ert-test-failed ((should-error t) :form t :value t :fail-reason "did not signal an error")) #+END_SRC *** Example 2 The second [[emacs:lisp/emacs-lisp/ert.el::(cl-defmacro should][should]] form: #+BEGIN_SRC emacs-lisp (should (equal "* P" (org-test-with-temp-text "P" (org-insert-heading) (buffer-string)))) #+END_SRC could be translated like this: 1) in a temporary buffer in org-mode ([[org-mode:testing/org-test.el::(defmacro org-test-with-temp-text][org-test-with-temp-text]]), 2) add the string ~P~ and place the point before ~P~ (~"P"~), 3) call [[org-mode:lisp/org.el::(defun org-insert-heading][org-insert-heading]], 4) return the string from the temporary buffer (~buffer-string~), 5) compare this string to the string ~"* P"~ (~equal~), 6) if they are equal, return ~t~, if not, return the error ~ert-test-failed~ ([[emacs:lisp/emacs-lisp/ert.el::(cl-defmacro should][should]]). *** Example 3 The third [[emacs:lisp/emacs-lisp/ert.el::(cl-defmacro should][should]] form: #+BEGIN_SRC emacs-lisp (should (equal "Para\n* graph" (org-test-with-temp-text "Paragraph" (let ((org-M-RET-may-split-line '((default . t)))) (org-insert-heading)) (buffer-string)))) #+END_SRC could be translated like this: 1) in a temporary buffer in org-mode ([[org-mode:testing/org-test.el::(defmacro org-test-with-temp-text][org-test-with-temp-text]]), 2) add the string ~Paragraph~ and place the point between the character ~a~ and ~g~ (~"Paragraph"~), 3) locally set the variable [[org-mode:lisp/org.el::(defcustom org-M-RET-may-split-line][org-M-RET-may-split-line]] to the alist ~'((default . t))~ and call [[org-mode:lisp/org.el::(defun org-insert-heading][org-insert-heading]] with this binding (~(let ...)~), 5) return the string from the temporary buffer (~buffer-string~), 6) compare this string to the string ~"Para\n* graph"~ (~equal~ and note that ~\n~ is the newline), 7) if they are equal, return ~t~, if not, return the error ~ert-test-failed~ ([[emacs:lisp/emacs-lisp/ert.el::(cl-defmacro should][should]]). *** Example 4 The fourth [[emacs:lisp/emacs-lisp/ert.el::(cl-defmacro should][should]] form: #+BEGIN_SRC emacs-lisp (should (equal "Paragraph\n* " (org-test-with-temp-text "Paragraph" (let ((org-M-RET-may-split-line '((default . nil)))) (org-insert-heading)) (buffer-string)))) #+END_SRC could be translated like this: 1) in a temporary buffer in org-mode ([[org-mode:testing/org-test.el::(defmacro org-test-with-temp-text][org-test-with-temp-text]]), 2) add the string ~Paragraph~ and place the point between the character ~a~ and ~g~ (~"Paragraph"~), 3) locally set the variable [[org-mode:lisp/org.el::(defcustom org-M-RET-may-split-line][org-M-RET-may-split-line]] to the alist ~'((default . nil))~ and call [[org-mode:lisp/org.el::(defun org-insert-heading][org-insert-heading]] with this binding (~(let ...)~), 5) return the string from the temporary buffer (~buffer-string~), 6) compare this string to the string ~"Paragraph\n* "~ (~equal~ and note that ~\n~ is the newline), 7) if they are equal, return ~t~, if not, return the error ~ert-test-failed~ ([[emacs:lisp/emacs-lisp/ert.el::(cl-defmacro should][should]]). ** Where the truth lives By now you should be convinced (that's what I hope) that the org-mode test suite is a gold mine for us (org-mode users) and that is where part of THE ORG-MODE TRUTH lives. ... More than 5000 examples ... WE ARE DONE! ** [[#/questions-and-answers/#2022-03-11-org-mode-source-code-5000-examples][Q&A]] * [2022-03-05 Sat] I bet you use hl-line-mode... Do you know how it works? Overlays, post-command-hook and only 5 functions!!! :PROPERTIES: :CUSTOM_ID: /2022-03-05-i-bet-you-use-hl-line-mode/ :REDDIT_POST: https://www.reddit.com/r/emacs/comments/t7doal/i_bet_you_use_hllinemode_do_you_know_how_it_works/ :END: Hey Emacsers, How are you doing? I'm excited about this post because when I understood how [[emacs:lisp/hl-line.el::(define-minor-mode hl-line-mode][hl-line-mode]] works, it opened new horizons for me in the world of Elisp. I hope you will feel the same way after: 1) reading this post or, 2) by directly reading the source code of [[emacs:lisp/hl-line.el::(define-minor-mode hl-line-mode][hl-line-mode]] that can be found in the file [[emacs:lisp/hl-line.el]] (only 205 LOC skipping comments and empty lines). You can get emacs's source code by running the following command: : git clone git://git.sv.gnu.org/emacs.git ** ~hl-line-mode~ *** What is ~hl-line-mode~? [[emacs:lisp/hl-line.el::(define-minor-mode hl-line-mode][hl-line-mode]] is a minor mode that highlights the current line. If there are multiple windows in your frame using [[emacs:lisp/hl-line.el::(define-minor-mode hl-line-mode][hl-line-mode]] you can control whether all windows have the line highlighted or only the ~selected-window~ with the option [[emacs:lisp/hl-line.el::(defcustom hl-line-sticky-flag][hl-line-sticky-flag]]. If you prefer not to highlight the whole line but only a range around the point this is also possible with [[emacs:lisp/hl-line.el::(defvar hl-line-range-function][hl-line-range-function]]. In this post, we are not interested in these options, but only in the mechanism and the default behavior that highlights the entire line in the current buffer. *** How does it work? [[emacs:lisp/hl-line.el::(define-minor-mode hl-line-mode][hl-line-mode]] moves an overlay responsible for highlighting the current line after each command call. This is done by adding a specific function to the hook [[emacs:src/keyboard.c:: DEFVAR_LISP ("post-command-hook"][post-command-hook]] when the mode [[emacs:lisp/hl-line.el::(define-minor-mode hl-line-mode][hl-line-mode]] is turned on. If you are already familiar with [[emacs:src/keyboard.c:: DEFVAR_LISP ("post-command-hook"][post-command-hook]] and Emacs overlays, you're good. But, if not, let's break things down together. One things to remember about Emacs ([[info:elisp#Command Loop][info:elisp#Command Loop]]) is: #+BEGIN_SRC text When you run Emacs, it enters the “editor command loop” almost immediately. This loop reads key sequences, executes their definitions, and displays the results. #+END_SRC Specifically, each time we call a command (inserting a character also calls a command, [[emacs:src/cmds.c::DEFUN ("self-insert-command",][self-insert-command]] by default), the "editor command loop": 1) runs the hook [[emacs:src/keyboard.c:: DEFVAR_LISP ("pre-command-hook"][pre-command-hook]] before executing the command, 2) runs the hook [[emacs:src/keyboard.c:: DEFVAR_LISP ("post-command-hook"][post-command-hook]] after executing the command. (run the hook ~X~ means: call all the functions in the list ~X~). So we can trigger actions after each command call by adding functions in the list [[emacs:src/keyboard.c:: DEFVAR_LISP ("post-command-hook"][post-command-hook]]. This is what [[emacs:lisp/hl-line.el::(define-minor-mode hl-line-mode][hl-line-mode]] does. When turned on, the mode adds the function [[emacs:lisp/hl-line.el::(defun hl-line-highlight][hl-line-highlight]] to the list [[emacs:src/keyboard.c:: DEFVAR_LISP ("post-command-hook"][post-command-hook]] as follow: #+BEGIN_SRC emacs-lisp (add-hook 'post-command-hook #'hl-line-highlight nil t) #+END_SRC Note the ~t~ at the end of the previous s-exp that makes the hook buffer-local. So, in a buffer that has [[emacs:lisp/hl-line.el::(define-minor-mode hl-line-mode][hl-line-mode]] turned on, each time we call a command (basically, "each time we do something"): 1) the command is executed and, 2) [[emacs:lisp/hl-line.el::(defun hl-line-highlight][hl-line-highlight]] is called. What exactly does [[emacs:lisp/hl-line.el::(defun hl-line-highlight][hl-line-highlight]] do? [[emacs:lisp/hl-line.el::(defun hl-line-highlight][hl-line-highlight]]: 1) creates an overlay at point with the ~face~ property equal to [[emacs:lisp/hl-line.el::(defcustom hl-line-face][hl-line-face]] (calling the function [[emacs:lisp/hl-line.el::(defun hl-line-make-overlay][hl-line-make-overlay]]) if it doesn't exist yet, 2) assigns this overlay to the variable [[emacs:lisp/hl-line.el::(defvar-local hl-line-overlay][hl-line-overlay]] and, 3) moves (places) this overlay on the current line (calling the function [[emacs:lisp/hl-line.el::(defun hl-line-move][hl-line-move]]). Here's the a snippet of [[emacs:lisp/hl-line.el::(defun hl-line-highlight][hl-line-highlight]] (I have removed some details): #+BEGIN_SRC emacs-lisp (defun hl-line-highlight () (if hl-line-mode (progn (unless (overlayp hl-line-overlay) (setq hl-line-overlay (hl-line-make-overlay))) ;; ... (hl-line-move hl-line-overlay) ;; ... ) (hl-line-unhighlight))) #+END_SRC The function [[emacs:lisp/hl-line.el::(defun hl-line-make-overlay][hl-line-make-overlay]] uses the function ~make-overlay~ to make the overlay and uses the function [[emacs:src/buffer.c::DEFUN ("overlay-put"][overlay-put]] to set the ~priority~ and ~face~ property of the newly created overlay: #+BEGIN_SRC emacs-lisp (defun hl-line-make-overlay () (let ((ol (make-overlay (point) (point)))) (overlay-put ol 'priority hl-line-overlay-priority) (overlay-put ol 'face hl-line-face) ol)) #+END_SRC As we left aside the range function [[emacs:lisp/hl-line.el::(defvar hl-line-range-function][hl-line-range-function]] (which is set to ~nil~ by default), we can see below a simplified implementation of [[emacs:lisp/hl-line.el::(defun hl-line-move][hl-line-move]], that we call ~hl-line-move-NO-RANGE-FUNCTION~ that uses the function [[emacs:src/buffer.c::DEFUN ("move-overlay"][move-overlay]] to move the limits of the overlay and set them to be the beginning of the current line and beginning of the next line: #+BEGIN_SRC emacs-lisp (defun hl-line-move-NO-RANGE-FUNCTION (overlay) (let ((beg (line-beginning-position)) (end (line-beginning-position 2))) (move-overlay overlay beg end))) #+END_SRC We have left out some details (the functions [[emacs:lisp/hl-line.el::(defun hl-line-unhighlight][hl-line-unhighlight]] [[emacs:lisp/hl-line.el::(defun hl-line-maybe-unhighlight][hl-line-maybe-unhighlight]] and the use of the hook ~change-major-mode-hook~), because our goal was to focus on the mechanism and not all the options and implementation details. I hope this was useful. *** ~global-hl-line-mode~ [[emacs:lisp/hl-line.el::(define-minor-mode global-hl-line-mode][global-hl-line-mode]] is a global minor mode that offers line highlighting in all buffers. The mechanism is "almost" the same as [[emacs:lisp/hl-line.el::(define-minor-mode hl-line-mode][hl-line-mode]] and both share the functions [[emacs:lisp/hl-line.el::(defun hl-line-make-overlay][hl-line-make-overlay]] and [[emacs:lisp/hl-line.el::(defun hl-line-move][hl-line-move]], the variables [[emacs:lisp/hl-line.el::(defvar hl-line-overlay-priority][hl-line-overlay-priority]], [[emacs:lisp/hl-line.el::(defvar hl-line-range-function][hl-line-range-function]] and they use the same "face" [[emacs:lisp/hl-line.el::(defcustom hl-line-face][hl-line-face]]. So, if you understand how [[emacs:lisp/hl-line.el::(define-minor-mode hl-line-mode][hl-line-mode]] works, you already almost understand how [[emacs:lisp/hl-line.el::(define-minor-mode global-hl-line-mode][global-hl-line-mode]] works. In the next parts of this post, we build examples using [[emacs:src/keyboard.c:: DEFVAR_LISP ("post-command-hook"][post-command-hook]] and overlays separately to try to get a good overview of their use. ** Playing with ~pre-command-hook~ and ~post-command-hook~ In this section everything happens in the buffer ~*test hooks*~. Let's switch to the new buffer ~*test hooks*~ in ~emacs-lisp-mode~ by evaluating the following s-exp in the minibuffer (~M-x eval-expression~): #+BEGIN_SRC emacs-lisp (progn (with-current-buffer (get-buffer-create "*test hooks*") (emacs-lisp-mode)) (switch-to-buffer "*test hooks*")) #+END_SRC We've already seen that by adding functions to the hooks (lists) [[emacs:src/keyboard.c:: DEFVAR_LISP ("pre-command-hook"][pre-command-hook]] and [[emacs:src/keyboard.c:: DEFVAR_LISP ("post-command-hook"][post-command-hook]] we can trigger actions before or after any command call. The first things we can do is to inspect the variable [[emacs:src/keyboard.c:: DEFVAR_LISP ("post-command-hook"][post-command-hook]] by running: : M-x describe-variable RET post-command-hook RET This will pops up an help buffer that looks like this (the value depends on the packages you are using): #+BEGIN_SRC text post-command-hook is a variable defined in ‘src/keyboard.c’. Its value is (jit-lock--antiblink-post-command yas--post-command-handler eldoc-schedule-timer company-post-command t) Local in buffer *test hooks*; global value is (global-font-lock-mode-check-buffers global-eldoc-mode-check-buffers smartparens-global-mode-check-buffers show-smartparens-global-mode-check-buffers yas-global-mode-check-buffers magit-auto-revert-mode-check-buffers global-hl-line-highlight insight-check-cursor-color sp--post-command-hook-handler winner-save-old-configurations) This variable may be risky if used as a file-local variable. Probably introduced at or before Emacs version 19.20. Normal hook run after each command is executed. If an unhandled error happens in running this hook, the function in which the error occurred is unconditionally removed, since otherwise the error might happen repeatedly and make Emacs nonfunctional. It is a bad idea to use this hook for expensive processing. If unavoidable, wrap your code in ‘(while-no-input (redisplay) CODE)’ to avoid making Emacs unresponsive while the user types. See also ‘pre-command-hook’. #+END_SRC In this help buffer, we see that the local value in the buffer ~*test hooks*~ is the list: : (jit-lock--antiblink-post-command yas--post-command-handler eldoc-schedule-timer company-post-command t) We also see its global value and the last part of the help buffer is the docstring of this variable where we can read: #+BEGIN_SRC text If an unhandled error happens in running this hook, the function in which the error occurred is unconditionally removed, since otherwise the error might happen repeatedly and make Emacs nonfunctional. #+END_SRC This tells us that it is safe to play with [[emacs:src/keyboard.c:: DEFVAR_LISP ("post-command-hook"][post-command-hook]] because if we add a function to it that raises an error the function will be unconditionally removed. So let's add to [[emacs:src/keyboard.c:: DEFVAR_LISP ("post-command-hook"][post-command-hook]] a symbol that has no function definition (ie. raises the error ~void-function~ when called as a function). By evaluating the following s-exp (~eval-last-sexp~ bound to ~C-x C-e~): #+BEGIN_SRC emacs-lisp (add-hook 'post-command-hook 'test-void-function) #+END_SRC we see in the echo area: #+BEGIN_SRC text Error in post-command-hook (test-void-function): (void-function test-void-function) #+END_SRC And if we inspect the variable [[emacs:src/keyboard.c:: DEFVAR_LISP ("post-command-hook"][post-command-hook]] (as we did previously), we see that ~test-void-function~ symbol isn't in the hook. What happened? 1) we called ~eval-last-sexp~, 2) then the "editor command loop" ran the hook [[emacs:src/keyboard.c:: DEFVAR_LISP ("pre-command-hook"][pre-command-hook]], 3) then the expression ~(add-hook 'post-command-hook 'test-void-function)~ has been evaluated, which added ~test-void-function~ symbol to [[emacs:src/keyboard.c:: DEFVAR_LISP ("post-command-hook"][post-command-hook]], 4) then the "editor command loop" ran the hook [[emacs:src/keyboard.c:: DEFVAR_LISP ("post-command-hook"][post-command-hook]], and when it try to call the function ~test-void-function~, it raised the error ~void-function~ and remove ~test-void-function~ from the hook. Now that we are confident that playing with the hook [[emacs:src/keyboard.c:: DEFVAR_LISP ("post-command-hook"][post-command-hook]] won't break our running Emacs, let's build the main example of this section. We write 2 functions ~test-hook-pre~ and ~test-hook-post~ that print out respectively the name of the command that is about to run and the name of the command that just ran. To do that we use the emacs variable [[emacs:src/keyboard.c:: DEFVAR_LISP ("this-command"][this-command]] (that holds the command now being executed) and adds ~test-hook-pre~ to [[emacs:src/keyboard.c:: DEFVAR_LISP ("pre-command-hook"][pre-command-hook]] and ~test-hook-post~ to [[emacs:src/keyboard.c:: DEFVAR_LISP ("post-command-hook"][post-command-hook]]. Note that the info nodes related to this examples are: - [[info:elisp#Command Overview]] - [[info:elisp#Command Loop Info]] Then we call some commands. And finally we observe what has been printed out in the buffer ~*Messages*~. In the buffer ~*test hooks*~, we remove everything and add the following expressions: #+BEGIN_SRC emacs-lisp (defun test-hook-pre () (message " BEFORE | %s" this-command)) (defun test-hook-post () (message " AFTER | %s" this-command)) (add-hook 'pre-command-hook 'test-hook-pre) (add-hook 'post-command-hook 'test-hook-post) (message ":::::::: print me ::::::::") #+END_SRC Then, with the point after the last s-exp (last parenthesis), we do in order (without doing anything else, this is important for the messages we want to see printed): 1) ~M-x eval-buffer~ (this evaluate all this expressions), 2) ~C-a~ (move to the beginning), 3) ~C-e~ (move to the end of line), 4) ~C-x C-e~ (eval the last expression). Then, we should see in the buffer (almost at the end) ~*Messages*~ the following: #+BEGIN_SRC text :::::::: print me :::::::: AFTER | eval-buffer BEFORE | move-beginning-of-line AFTER | move-beginning-of-line BEFORE | move-end-of-line AFTER | move-end-of-line BEFORE | eval-last-sexp :::::::: print me :::::::: ":::::::: print me ::::::::" AFTER | eval-last-sexp #+END_SRC This gives us an overview of the behavior of the "editor command loop". Are you annoyed by the noise you have in your echo area? Me too. Let's remove the functions ~test-hook-pre~ and ~test-hook-post~ respectively from the hooks [[emacs:src/keyboard.c:: DEFVAR_LISP ("pre-command-hook"][pre-command-hook]] and [[emacs:src/keyboard.c:: DEFVAR_LISP ("post-command-hook"][post-command-hook]] by evaluating the following s-exps. This should "clean up" our echo area. #+BEGIN_SRC emacs-lisp (remove-hook 'pre-command-hook 'test-hook-pre) (remove-hook 'post-command-hook 'test-hook-post) #+END_SRC We are done with the hooks [[emacs:src/keyboard.c:: DEFVAR_LISP ("pre-command-hook"][pre-command-hook]] and [[emacs:src/keyboard.c:: DEFVAR_LISP ("post-command-hook"][post-command-hook]]. Let's play with overlays. ** Moving overlays and priorities In the post [[#/2022-02-26-org-mode-visibility-of-headings/][Have you ever wondered how org-mode toggles the visibility of headings?]], we already played with overlays specifically the ~invisible~ property of overlay. We also know that overlays take priority over text properties. Below we will see: 1) how to move overlays ([[emacs:src/buffer.c::DEFUN ("move-overlay"][move-overlay]]) and, 2) which overlay wins when they overlap (~priority~ property). Note that all the evaluations of the s-expressions are done in the minibuffer with ~M-x eval-expression~ and the point in the buffer we operate on, that we call ~*overlays*~. Let's switch to the new buffer ~*overlays*~ in ~fundamental-mode~ by evaluating the following s-exp: #+BEGIN_SRC emacs-lisp (switch-to-buffer (get-buffer-create "*overlays*")) #+END_SRC Let's insert the characters ~FOO---BAR---BAZ~ such that the buffer ~*overlays*~ look likes this: #+BEGIN_SRC text FOO---BAR---BAZ #+END_SRC We make two overlays: 1) ~ov-x~ on top of ~FOO~ with a background green (~#00ff00~), 2) ~ov-y~ on top of ~BAZ~ with a background red (~#ff0000~). To do so, we evaluate the following form: #+BEGIN_SRC emacs-lisp (progn (setq ov-x (make-overlay 1 4)) (overlay-put ov-x 'face '(:background "#00ff00")) (setq ov-y (make-overlay 13 16)) (overlay-put ov-y 'face '(:background "#ff0000"))) #+END_SRC Now we have ~FOO~ with a background green and ~BAZ~ with a background red. Let's move the overlay ~ov-x~ (the green) on top of the characters ~BAR~ the same way [[emacs:lisp/hl-line.el::(define-minor-mode hl-line-mode][hl-line-mode]] does. To do so we use the function [[emacs:src/buffer.c::DEFUN ("move-overlay"][move-overlay]] as follow: #+BEGIN_SRC emacs-lisp (move-overlay ov-x 7 10) #+END_SRC When more than one overlay overlaps, Emacs decides for each property which overlay "wins" (see [[info:elisp#Overlay Properties][info:elisp#Overlay Properties]]) over the others by looking up at the overlay property ~priority~ which should be a positive integer or ~nil~. Let's see this in our example. First, we set the overlay ~ov-x~ to have a ~priority~ equal to ~10~ and the overlay ~ov-y~ to have a priority equal to ~20~ by evaluating the following form: #+BEGIN_SRC emacs-lisp (progn (overlay-put ov-x 'priority 10) (overlay-put ov-y 'priority 20)) #+END_SRC Now we move the overlay ~ov-y~ to be on top of the characters ~BAR~ (and so overlap with the overlay ~ov-x~) by evaluating this s-exp: #+BEGIN_SRC emacs-lisp (move-overlay ov-y 7 10) #+END_SRC The buffer ~*overlays*~ shows the characters ~BAR~ with a background red that corresponds to the overlay ~ov-y~ which have a priority ~20~ superior to the priority ~10~ of the overlay ~ov-x~. Now let's make ~ov-x~ win by raising its priority to ~30~: #+BEGIN_SRC emacs-lisp (overlay-put ov-x 'priority 30) #+END_SRC ISN'T IT SUPER COOL!!! Something interesting we can do now is to ~M-x describe-char~, with the point between ~A~ and ~R~ in the word ~BAR~, which pops up the following help buffer: #+BEGIN_SRC text position: 9 of 15 (53%), column: 8 character: R (displayed as R) (codepoint 82, #o122, #x52) charset: ascii (ASCII (ISO646 IRV)) code point in charset: 0x52 script: latin syntax: w which means: word category: .:Base, L:Left-to-right (strong), a:ASCII, l:Latin, r:Roman to input: type "C-x 8 RET 52" or "C-x 8 RET LATIN CAPITAL LETTER R" buffer code: #x52 file code: #x52 (encoded by coding system utf-8-unix) display: by this font (glyph code) ftcrhb:-PfEd-DejaVu Sans Mono-normal-normal-normal-*-15-*-*-*-m-0-iso10646-1 (#x35) Character code properties: customize what to show name: LATIN CAPITAL LETTER R general-category: Lu (Letter, Uppercase) decomposition: (82) ('R') There are 2 overlays here: From 7 to 10 face (:background "#00ff00") priority 30 From 7 to 10 face (:background "#ff0000") priority 20 #+END_SRC Note that it might differs in your running emacs (different fonts, maybe information about overlays if you are using ~hl-line-mode~, ...). There are 2 overlays!!! To finish this post, we remove the overlays like this: #+BEGIN_SRC emacs-lisp (remove-overlays) #+END_SRC WE ARE DONE!!! * [2022-02-26 Sat] Have you ever wondered how org-mode toggles the visibility of headings? :PROPERTIES: :CUSTOM_ID: /2022-02-26-org-mode-visibility-of-headings/ :REDDIT_POST: https://www.reddit.com/r/emacs/comments/t1r2wq/have_you_ever_wondered_how_orgmode_toggles_the/ :END: Have you ever wondered how org-mode toggles the visibility of headings? YES!!! Me too! Let's get into it ;) ** org-mode visibility of headings ~org-mode~ is built on top of ~outline-mode~ which is responsible for the visibility changes of the headings. How does it work? ~outline-mode~ uses overlays, specifically the overlay property ~invisible~ to toggle the visibility of the headings: 1) To hide the body of a heading, ~outline-mode~ makes an overlay from the end of the line of the heading to the end of the body of the heading, and sets the property ~invisible~ of the overlay to be the symbol ~outline~. Hence, the part of the buffer with this overlay is "replaced" (visually, not the content of the buffer) by ellipsis. Why is this? Because, when ~outline-mode~ is turned on, it adds the cons ~(outline . t)~ to the variable ~buffer-invisibility-spec~ which becomes buffer local and is responsible for the invisibility of each buffer. 2) To make the body of a heading visible, ~outline-mode~ removes any overlays in the body of the heading that have its property ~invisible~ set to the symbol ~outline~. To see exactly how this is achieved you can refer to the functions [[emacs:lisp/outline.el::(defun outline-flag-region][outline-flag-region]], [[emacs:lisp/outline.el::(defun outline-hide-entry][outline-hide-entry]] and [[emacs:lisp/outline.el::(defun outline-show-entry][outline-show-entry]] defined in the file [[emacs:lisp/outline.el]] and also the definition of the mode [[emacs:lisp/outline.el::(define-derived-mode outline-mode][outline-mode]] in the same file. You can get emacs's source code by running the following command: : git clone git://git.sv.gnu.org/emacs.git OK! The mechanism of ~outline-mode~ uses overlays, [[emacs:src/buffer.c::DEFVAR_PER_BUFFER ("buffer-invisibility-spec"][buffer-invisibility-spec]] and ellipsis. But how do those "emacs/elisp features" play together? In the next parts of this post, we build examples using them to try to get a good feel for their use. ** text properties In a buffer, each character point can have text properties attached to it that can be used to do many things (like controlling the appearance of the character). For instance, in an ~emacs-lisp-mode~ buffer, with the following s-exp, and the cursor (the point) after the first parenthesis: #+BEGIN_SRC emacs-lisp (setq my-var nil) #+END_SRC if we run: : M-x eval-expression RET (text-properties-at (point)) we get: #+BEGIN_SRC emacs-lisp (face font-lock-keyword-face fontified t) #+END_SRC The character point "s" (point 2, i.e. the "s" at the second position in the buffer) has: 1) the text property ~face~ equal to the face [[emacs:lisp/font-lock.el::(defface font-lock-keyword-face][font-lock-keyword-face]] which is why it is displayed with a different foreground color (depending on your theme) than the text ~my-var~ for instance, 2) the text property ~fontified~ equal to ~t~ which we don't describe here. We can read more about the special text properties in the manual ([[info:elisp#Special Properties]]). If we want more information (not only the text properties) about the character point "s" (point 2), we can run (still with with the cursor after the first parenthesis): : M-x describe-char which pops up the following help buffer: #+BEGIN_SRC text position: 2 of 18 (6%), column: 1 character: s (displayed as s) (codepoint 115, #o163, #x73) charset: ascii (ASCII (ISO646 IRV)) code point in charset: 0x73 script: latin syntax: w which means: word category: .:Base, L:Left-to-right (strong), a:ASCII, l:Latin, r:Roman to input: type "C-x 8 RET 73" or "C-x 8 RET LATIN SMALL LETTER S" buffer code: #x73 file code: #x73 (encoded by coding system prefer-utf-8-unix) display: by this font (glyph code) ftcrhb:-PfEd-DejaVu Sans Mono-normal-normal-normal-*-15-*-*-*-m-0-iso10646-1 (#x56) Character code properties: customize what to show name: LATIN SMALL LETTER S general-category: Ll (Letter, Lowercase) decomposition: (115) ('s') There are text properties here: face font-lock-keyword-face fontified t #+END_SRC Note that your result may differ in your running emacs (different fonts, maybe information about overlays if you are using ~hl-line-mode~, ...). ** Why are we talking about text properties if the mechanism of ~outline-mode~ uses overlays? Because: 1) Both text properties and overlays can "alter/control" the appearance of the buffer's text on the screen and so we have to know something important that is (from the manual [[info:elisp#Overlay Properties]]): #+BEGIN_SRC text all overlays take priority over text properties. #+END_SRC 2) buffer invisibility can also be achieve with text properties (for instance, this is what ~org-mode~ does to hide the brackets and the link part of links like this ~[[link][description]]~), and it is important to notice it. ** overlays, invisible overlay property, buffer-invisibility-spec We can make a part of a buffer invisible using: 1) the ~invisible~ text property (of that part), 2) the ~invisible~ overlay property ("on top of that part"). The "admitted" values of the ~invisible~ overlay property (or text property) and the invisibility effect expected depend on the value of the variable [[emacs:src/buffer.c::DEFVAR_PER_BUFFER ("buffer-invisibility-spec"][buffer-invisibility-spec]]. In this section: 1) we define overlays, 2) we set the variable ~buffer-invisibility-spec~, 3) we give different values to the ~invisible~ property of the overlays, 4) we observe the appearance of the buffer, 5) we repeat step 2) to 4) several times. 6) we hope we get a good feeling of invisibility in Emacs. Also note that all the evaluations of the s-expressions are done in the minibuffer with ~M-x eval-expression~ and the point in the buffer we operate on, that we call ~*invisible*~. Let's switch to the new "fresh" buffer ~*invisible*~ in ~fundamental-mode~ by evaluating the following s-exp: #+BEGIN_SRC emacs-lisp (switch-to-buffer (get-buffer-create "*invisible*")) #+END_SRC Let's insert the characters ~XXXXXX~ at the beginning of the buffer ~*invisible*~: #+BEGIN_SRC text XXXXXX #+END_SRC *** ~buffer-invisibility-spec~ equal to ~t~ Now if we evaluate the variable ~buffer-invisibility-spec~, we should get ~t~ (the default) in the echo area. If not, we set this variable to ~t~ like this: #+BEGIN_SRC emacs-lisp (setq buffer-invisibility-spec t) #+END_SRC Now, we make an overlay "on top" of ~XXXXXX~ (from point 1 to point 7 in the buffer) that we assign to the variable ~ov-x~ using [[emacs:src/buffer.c::DEFUN ("make-overlay"][make-overlay]]: #+BEGIN_SRC emacs-lisp (setq ov-x (make-overlay 1 7)) #+END_SRC and we see the following in the echo area: #+BEGIN_SRC text # #+END_SRC Now, by setting the property ~invisible~ of the overlay ~ov-x~ to ~t~ using the function [[emacs:src/buffer.c::DEFUN ("overlay-put"][overlay-put]] like this #+BEGIN_SRC emacs-lisp (overlay-put ov-x 'invisible t) #+END_SRC we make the characters ~XXXXXX~ disappear. This is due to the value of ~buffer-invisibility-spec~ equal to ~t~ (the default) which means that text is invisible if it has a non-nil ~invisible~ (text or overlay) property. Now, evaluating the following s-exp sets ~invisible~ property of the overlay ~ov-x~ to ~nil~ #+BEGIN_SRC emacs-lisp (overlay-put ov-x 'invisible nil) #+END_SRC makes the characters ~XXXXXX~ to reappear in the buffer ~*invisible*~. We also could have removed the overlay ~ov-x~ to make the characters ~XXXXXX~ to reappear. Let's see how. First, as previously, we set the ~invisible~ property of the overlay ~ov-x~ to ~t~ to make the characters ~XXXXXX~ to disappear: #+BEGIN_SRC emacs-lisp (overlay-put ov-x 'invisible t) #+END_SRC Then, instead of setting back the ~invisible~ overlay property to ~nil~ of ~ov-x~ we remove it. To do so, we use the function [[emacs:lisp/subr.el::(defun remove-overlays][remove-overlays]] that let you remove all the overlays in a range of the buffer that have a specific property set to some value (in our case the property ~invisible~ set to ~t~ in the range 1 to 7 of the buffer). So evaluating the following s-exp: #+BEGIN_SRC emacs-lisp (remove-overlays 1 7 'invisible t) #+END_SRC removes the overlay ~ov-x~ in the buffer ~*invisible*~ and make the characters ~XXXXXX~ to reappear. *** ~buffer-invisibility-spec~ equal to ~nil~ As we removed the overlay ~ov-x~, we redefined it as previously by evaluating the following s-exp: #+BEGIN_SRC emacs-lisp (setq ov-x (make-overlay 1 7)) #+END_SRC Let's set ~buffer-invisibility-spec~ to ~nil~: #+BEGIN_SRC emacs-lisp (setq buffer-invisibility-spec nil) #+END_SRC Then, by evaluating the following s-exp, we expect the characters ~XXXXXX~ to disappear: #+BEGIN_SRC emacs-lisp (overlay-put ov-x 'invisible t) #+END_SRC BUT they don't. This is normal, as we've just set ~buffer-invisibility-spec~ to ~nil~, we've "disabled" the invisibility feature in the buffer ~*invisible*~. Now, we restore the ~invisible~ property of the overlay ~ov-x~ so as not to interfere with the next example by evaluating: #+BEGIN_SRC emacs-lisp (overlay-put ov-x 'invisible nil) #+END_SRC *** ~buffer-invisibility-spec~ equal to ~((foo) t)~ Let's add the characters ~YYYYYY~ after the characters ~XXXXXX~ with 3 dashes ~---~ in between such that the buffer ~*invisible*~ is now: #+BEGIN_SRC text XXXXXX---YYYYYY #+END_SRC Now, we make an overlay "on top" of ~YYYYYY~ (from point 10 to point 16 in the buffer) that we assign to the variable ~ov-y~ using ~make-overlay~: #+BEGIN_SRC emacs-lisp (setq ov-y (make-overlay 10 16)) #+END_SRC We set back ~buffer-invisibility-spec~ to ~t~ (the default): #+BEGIN_SRC emacs-lisp (setq buffer-invisibility-spec t) #+END_SRC Then we add the list ~(foo)~ to the variable ~buffer-invisibility-spec~ using the function [[emacs:lisp/subr.el::(defun add-to-invisibility-spec][add-to-invisibility-spec]] as follow: #+BEGIN_SRC emacs-lisp (add-to-invisibility-spec '(foo)) #+END_SRC Now, the value of ~buffer-invisibility-spec~ is ~((foo) t)~. This implies that, now to make a part of the buffer invisible, the ~invisible~ property must be ~foo~ or ~t~. Before, it could have been any value that is non-nil. This way we can toggle the visibility of some parts of the buffer while other parts remain invisible (see [[org-mode:lisp/ol.el::(defun org-toggle-link-display][org-toggle-link-display]] for instance). Let's make ~XXXXXX~ disappear "permanently" by setting the ~invisible~ property of ~ov-x~ to ~t~: #+BEGIN_SRC emacs-lisp (overlay-put ov-x 'invisible t) #+END_SRC The characters ~XXXXXX~ disappear and the buffer ~*invisible*~ is now: #+BEGIN_SRC text ---YYYYYY #+END_SRC Now, we set the ~invisible~ property of ~ov-y~ to be equal to ~foo~: #+BEGIN_SRC emacs-lisp (overlay-put ov-y 'invisible 'foo) #+END_SRC The characters ~YYYYYY~ disappear and the buffer ~*invisible*~ is now: #+BEGIN_SRC text --- #+END_SRC Now, what we can do is to make ~YYYYYY~ appears again by removing ~(foo)~ from the invisibility spec ~buffer-invisibility-spec~ while the characters ~XXXXXX~ stay invisible: #+BEGIN_SRC emacs-lisp (remove-from-invisibility-spec '(foo)) #+END_SRC Now, the buffer ~*invisible*~ is: #+BEGIN_SRC text ---YYYYYY #+END_SRC Note that: 1) the overlay ~ov-x~ still has its property ~invisible~ equal to ~t~ and, 2) the overlay ~ov-y~ still has its property ~invisible~ equal to ~foo~. You can verify it by evaluating the following s-exp: #+BEGIN_SRC emacs-lisp (overlay-get ov-x 'invisible) ; t (overlay-get ov-y 'invisible) ; foo #+END_SRC ** ellipsis and ~buffer-invisibility-spec~ equal to ~((foo . t) t)~ *** default ellipsis ~...~ If the variable ~buffer-invisibility-spec~ as a list contains a cons ~(foo . t)~, every continuous part of the buffer with the ~invisible~ property set to ~foo~ is replaced by ellipsis which are by default ~...~. The buffer ~*invisible*~ still contains the characters ~XXXXXX---YYYYYY~, but maybe not all the characters are visible. So let's put our buffer in an appropriate state for this section. We removes all the overlays in the buffer (which makes all the content of the buffer visible again). We redifined the ~ov-x~ and ~ov-y~ as previously (same part of the buffer (1 to 7) and (10 to 16)). And we set ~buffer-invisibility-spec~ to be ~((foo . t) t)~. We can do this by evaluating the following expression (in the minibuffer with point in the buffer ~*invisible*~): #+BEGIN_SRC emacs-lisp (progn (remove-overlays) (setq ov-x (make-overlay 1 7)) (setq ov-y (make-overlay 10 16)) (setq buffer-invisibility-spec t) (add-to-invisibility-spec '(foo . t))) #+END_SRC The buffer ~*invisible*~ is now: #+BEGIN_SRC text XXXXXX---YYYYYY #+END_SRC By evaluating the following s-exp, we set the ~invisible~ property of the overlay ~ov-y~ to ~foo~ #+BEGIN_SRC emacs-lisp (overlay-put ov-y 'invisible 'foo) #+END_SRC and this replaces (visually not the content of the buffer) the characters ~YYYYYY~ by the default ellipsis ~...~ and the buffer ~*invisible*~ looks like this: #+BEGIN_SRC text XXXXXX---... #+END_SRC *** custom ellipsis modifying the display table We assume with the buffer ~*invisible*~ is in the same state as in the previous section. Our goal in this section is to modify the default ellipsis ~...~. To do so we: 1) create a display table with the function [[emacs:lisp/disp-table.el::(defun make-display-table][make-display-table]], 2) we set its special slot 4 (responsible of the display of the ellipsis) which must be a vector of glyph using the function [[emacs:lisp/disp-table.el::(defun set-display-table-slot][set-display-table-slot]], 3) we set the variable [[emacs:src/buffer.c:: DEFVAR_PER_BUFFER ("buffer-display-table"][buffer-display-table]] of the buffer ~*invisible*~ to be this new display table, 4) we observe the appearance of the buffer ~*invisible*~. So by evaluating the following s-exp: #+BEGIN_SRC emacs-lisp (let ((tbl (make-display-table)) (glyph-vector (vector (make-glyph-code ?\ 'font-lock-warning-face) (make-glyph-code ?\; 'font-lock-warning-face) (make-glyph-code ?- 'font-lock-warning-face) (make-glyph-code ?\) 'font-lock-warning-face)))) (set-display-table-slot tbl 4 glyph-vector) (setq buffer-display-table tbl)) #+END_SRC the buffer ~*invisible*~ should looks like this (if the ~invisible~ property of the overlay ~ov-y~ is still equal to ~foo~): #+BEGIN_SRC text XXXXXX--- ;-) #+END_SRC You can read more about character display and display table in the manual ([[info:elisp#Character Display]]). WE ARE DONE :-) * [2022-02-20 Sun] You want to write a custom org backend? Let's write onlybold backend together to get you started :PROPERTIES: :CUSTOM_ID: /2022-02-20-onlybold-org-backend/ :REDDIT_POST: https://www.reddit.com/r/emacs/comments/swvbmm/you_want_to_write_a_custom_org_backend_lets_write/ :END: Hi Emacsers, Recently I've been playing with ~org-element~ and ~org-export~. Specifically, I was interested in the mechanism of the org exporter system and its flexibility. The goal of this post is to get you started with the creation of org backends. To do so, we build an org backend that: 1) keeps only ~bold~ elements, 2) surrounds ~bold~ elements with ~***~ before and after, 3) surrounds ~paragraph~ elements with ~::~ before and after, 4) surrounds ~section~ elements with ~<--~ before and ~-->~ after (removing the last newline). We call it ~onlybold~. Before we start, if you are interested, I recommend you to read in org-mode's source code the following files: - [[org-mode:lisp/ox.el]], - [[org-mode:lisp/ox-html.el]], - [[org-mode:lisp/ox-md.el]], - [[org-mode:lisp/ox-org.el]], - [[org-mode:testing/lisp/test-ox.el]] (AMAZING). You can get org-mode's source code by running the following command: : git clone https://git.savannah.gnu.org/git/emacs/org-mode.git Let's get started. ** what we want to achieve We want to export this org buffer: #+BEGIN_SRC org I like *bold-1* and *bold-2* and you? I don't. I prefer *bold-3*. I've loved *bold-4* since I was a child. I'm /italic/. #+END_SRC into another buffer like this: #+BEGIN_SRC text <--::***bold-1*** ***bold-2*** ***bold-3***:: ::***bold-4*** ::--> #+END_SRC ** org export mechanism When org exports an org buffer, basically it does two things: 1) parse the org buffer producing a tree (a nested elisp list) representing the org buffer and, 2) recursively build a string by traversing the tree and choosing for each node what to do with it by looking up its associated transcode function defined by the org backend. This means that *org* does the *hard work* for us "parsing" and "traversing". To build our ~onlybold~ org backend and any other org backends, in the simplest case, we just have to provide the *transcode functions* (or simply *transcoders*). ** transcoders, org-export-define-backend and org-export-to-buffer The function [[org-mode:lisp/ox.el::(defun org-export-define-backend][org-export-define-backend]] takes as arguments: 1) the backend's name we want to define and 2) an alist of transcoders. A *transcoder* (or a transcode function), is a function that handles an org element when it is being exported. For instance, our backend ~onlybold~ must define a transcoder for ~bold~ elements that surrounds bold texts with 3 stars ~***~ like this: : bold text -> ***bold text*** Most transcoders take three arguments: 1) the element as it appears in the parsed tree, 2) a content strings corresponding to the children of the element already "transcoded", 3) the communication channel that contains all the information the export system needs to correctly export the document (the obvious ones are the title, date and author of the document that can be defined inside the document using lines starting by ~#+TITLE:~, ~#+DATE:~ or ~#+AUTHOR~). Let's define ~onlybold-bold~, the transcoder of ~bold~ elements: #+BEGIN_SRC emacs-lisp (defun onlybold-bold (bold contents info) (concat "***" contents "***")) #+END_SRC Now, we can define the first version of ~onlybold~ backend, which transcodes only ~bold~ elements: #+BEGIN_SRC emacs-lisp (org-export-define-backend 'onlybold '((bold . onlybold-bold))) #+END_SRC Then we defined the command ~onlybold-export~ that pops up the buffer ~*onlybold*~ which contains the exported content (using ~onlybold~ backend) of the current buffer: #+BEGIN_SRC emacs-lisp (defun onlybold-export () (interactive) (org-export-to-buffer 'onlybold "*onlybold*")) #+END_SRC Now, if we call the command ~onlybold-export~ inside our org buffer, the buffer ~*onlybold*~ pops up with *nothing* in it. We might be disappointed, but we aren't. This is totally normal. In a specific backend, when an element doesn't have a transcoder to handle it, the element is skipped. (In the same vein, if a transcoder return ~nil~ for an element, the element is also skipped). ** parsed tree, section elements and paragraph elements In our org buffer, the bold elements belong to paragraphs that belong to a section. We can see this by looking at the parsed tree in the buffer ~*Pp Eval Output*~ after running the following command (being in the org buffer): : M-x pp-eval-expression RET (org-element-parse-buffer) We get the following tree ( ~...~ represents information that are not related to the shape of the tree): #+BEGIN_SRC emacs-lisp (org-data nil (section (...) (paragraph (...) #("I like " ...) (bold (...) #("bold-1" ...)) #("and " ...) (bold (...) #("bold-2" ...)) #("and you?\nI don't. I prefer " ...) (bold (...) #("bold-3" ...)) #(".\n" ...)) (paragraph (...) #("I've loved " ...) (bold (...) #("bold-4" ...)) #("since I was a child.\n" ...)) (paragraph (...) #("I'm " ...) (italic (...) #("italic" ...)) #("." ...)))) #+END_SRC Indeed, ~bold~ elements belong to ~paragraph~ elements that belong to a ~section~ element. And as we have just seen, if a backend doesn't provide a transcoder for an element, this element will be ignored in the exported result. So let's write ~onlybold-section~, the transcoder of ~section~ elements which surrounds their content with ~<--~ and ~-->~: #+BEGIN_SRC emacs-lisp (defun onlybold-section (section contents info) (concat "<--" contents "-->")) #+END_SRC and ~onlybold-paragraph~, the transcoder of ~paragraph~ elements which surrounds their content with ~::~: #+BEGIN_SRC emacs-lisp (defun onlybold-paragraph (paragraph contents info) (concat "::" contents "::")) #+END_SRC Then, we modify ~onlybold~ backend like this: #+BEGIN_SRC emacs-lisp (org-export-define-backend 'onlybold '((bold . onlybold-bold) (section . onlybold-section) (paragraph . onlybold-paragraph))) #+END_SRC Now, if we call the command ~onlybold-export~ inside our org buffer, the buffer ~*onlybold*~ pops up with this content: #+BEGIN_SRC text <--::I like ***bold-1*** and ***bold-2*** and you? I don't. I prefer ***bold-3***. :: ::I've loved ***bold-4*** since I was a child. :: ::I'm . :: --> #+END_SRC This is better: 1) The ~bold~ elements has been transcoded as we expected, 2) The "normal" text remains the same as in our org buffer and, 3) note that the ~italic~ element has been ignored (which was expected because we didn't provide a transcoder for ~italic~ elements). ** only keep bold elements ~plain-text~ elements are the leaves of the parsed tree and they are strings. This is the right level to operate in order to keep only ~bold~ elements. So now, let's handle the ~plain-text~ elements and keep only ~bold~ elements. There is at least two ways to do it: 1) using the filter system provided by the org export system (and so provide a filter that applies to ~plain-text~ elements) or, 2) providing a specific transcoder for ~plain-text~ elements. We implement the latter. Let's write the transcoder ~onlybold-plain-text~ which checks if the *parent* of the ~plain-text~ element (the string) is a ~bold~ element. If this is the case, we return the string and if not we return ~nil~: #+BEGIN_SRC emacs-lisp (defun onlybold-plain-text (text info) (when (eq 'bold (org-element-type (org-element-property :parent text))) text)) #+END_SRC Note that the arity (number of arguments) of ~onlybold-plain-text~ is different from the transcoders that we've seen so far. Then we add it to ~onlybold~ backend: #+BEGIN_SRC emacs-lisp (org-export-define-backend 'onlybold '((bold . onlybold-bold) (section . onlybold-section) (paragraph . onlybold-paragraph) (plain-text . onlybold-plain-text))) #+END_SRC Now, if we call the command ~onlybold-export~ inside our org buffer, the buffer ~*onlybold*~ pops up with this content: #+BEGIN_SRC text <--::***bold-1*** ***bold-2*** ***bold-3***:: ::***bold-4*** :: :::: --> #+END_SRC We have filtered the text to keep only ~bold~ elements. ** remove empty paragraphs and the last newline of the section Let's go further and remove the last empty paragraph. To do so, we can "ask" the transcoder ~onlybold-paragraph~ to return ~nil~ when its contents is "empty", specifically when its content is the empty strings ~""~ or a newline ~"\n"~. Here is the new implementation: #+BEGIN_SRC emacs-lisp (defun onlybold-paragraph (paragraph contents info) (if (member contents '("" "\n")) nil (concat "::" contents "::"))) #+END_SRC Now, if we call the command ~onlybold-export~ inside our org buffer, the buffer ~*onlybold*~ pops up with this content: #+BEGIN_SRC text <--::***bold-1*** ***bold-2*** ***bold-3***:: ::***bold-4*** :: --> #+END_SRC We are almost happy :) Only one thing remains... The end of the section ~-->~ alone in the last line is "quite ugly". Let's put it just after ~::~ that close the last paragraph. We can do this by modifying ~onlybold-section~ and "asking" it to remove the last newline of its content which is matched by the regexp ~"\n\\'"~: #+BEGIN_SRC emacs-lisp (defun onlybold-section (section contents info) (let ((cts (replace-regexp-in-string "\n\\'" "" contents))) (concat "<--" cts "-->"))) #+END_SRC Now, if we call the command ~onlybold-export~ inside our org buffer, the buffer ~*onlybold*~ pops up with this content: #+BEGIN_SRC text <--::***bold-1*** ***bold-2*** ***bold-3***:: ::***bold-4*** ::--> #+END_SRC We are done ;) I hope that this toy example helps you get started with the creation of org backends ** acknowledgments I want to take the opportunity of this post to thank: 1) Nicolas Goaziou who is the author and maintainer of [[org-mode:lisp/ox.el]], and [[org-mode:lisp/org-element.el]]. 2) All the people who work and contribute to org-mode (built-in and external packages), 3) All the people who work and contribute to Emacs (built-in and external packages). And I want to tell you that: Each time a piece of your code is heavy, I know that: 1) this piece of code fixes a bug or, 2) this piece of code handles an edge case or, 3) this piece of code provides flexibility (via options) to the end user. Each time your code is simple, I know that you worked hard to make it simple. And the more important, each time I read a piece of your code I feel closer to you. Emacs is pure joy and it is thanks to you. * [2022-02-11 Fri] Some examples on closures and lexical-binding :PROPERTIES: :CUSTOM_ID: /2022-02-11-closures-and-lexical-binding/ :REDDIT_POST: https://www.reddit.com/r/emacs/comments/sq1esz/some_examples_on_closures_and_lexicalbinding/ :END: Hi Emacsers, I've spent some time learning Elisp closures and lexical binding. Although the documentation is clear with simple examples, I needed to to play with other examples to get my bearings. As you might be interested, I will share them with you :) The first section contains the info nodes and help corresponding to the examples. The second section shows 10 "basic" examples using [[emacs:src/lread.c:: DEFVAR_LISP ("lexical-binding"][lexical-binding]], [[emacs:lisp/subr.el::(defmacro lambda][lambda]], [[emacs:lisp/emacs-lisp/byte-run.el::(defmacro defun][defun]], [[emacs:src/eval.c::DEFUN ("let"][let]], [[emacs:lisp/obsolete/cl.el::(defmacro lexical-let][lexical-let]], [[emacs:src/eval.c::DEFUN ("setq"][setq]] and [[emacs:src/eval.c::DEFUN ("defvar"][defvar]]. And the third section shows a more advanced example using [[emacs:lisp/subr.el::(defmacro lambda][lambda]], [[emacs:src/eval.c::DEFUN ("let"][let]], [[emacs:lisp/subr.el::(defmacro dolist][dolist]] and [[emacs:lisp/emacs-lisp/byte-run.el::(defmacro defun][defun]]. Have a nice day, ps: In the documentation we see that "Lexical binding is also more compatible with concurrency, which was added to Emacs in version 26.1.". Does anyone know why this is? If so, can you indicate to me the files in the emacs source code where I can see these benefits? I'm really curious... thank you. ** info and help Info nodes: - ~M-x eval-expression RET (info "(elisp)Variable Scoping")~ - ~M-x eval-expression RET (info "(elisp)Closures")~ help: - ~C-h v lexical-binding~ - ~C-h f defvar~ - ~C-h f lambda~ - ~C-h f lexical-let~ ** 10 examples with lexical-binding, lambda, defun, let, lexical-let, setq, defvar - example 1 #+BEGIN_SRC emacs-lisp (setq lexical-binding nil) (lambda (x) (* x x)) ; (lambda (x) (* x x)) #+END_SRC - example 2 #+BEGIN_SRC emacs-lisp (setq lexical-binding t) (lambda (x) (* x x)) ; (closure (t) (x) (* x x)) #+END_SRC - example 3 #+BEGIN_SRC emacs-lisp (setq lexical-binding nil) (let ((x 1)) (lambda (y) (+ x y))) ;; (lambda (y) (+ x y)) #+END_SRC - example 4 #+BEGIN_SRC emacs-lisp (setq lexical-binding t) (let ((x 1)) (lambda (y) (+ x y))) ;; (closure ((x . 1) t) (y) (+ x y)) #+END_SRC - example 5 #+BEGIN_SRC emacs-lisp (setq lexical-binding t) (let ((n 1)) (defun n+ (y) (+ n y))) (n+ 1) ; 2 (let ((n -10)) (n+ 1)) ; 2 #+END_SRC - example 6 #+BEGIN_SRC emacs-lisp (setq lexical-binding nil) (let ((n 1)) (defun n+ (y) (+ n y))) (should-error (n+ 1) :type 'void-variable) ; (void-variable n) (let ((n -10)) (n+ 1)) ; -9 #+END_SRC - example 7 #+BEGIN_SRC emacs-lisp (setq lexical-binding nil) ;; Note that `lexical-let' is defined in `lisp/obsolete/cl.el' (lexical-let ((n 1)) (defun n+ (y) (+ n y))) (n+ 1) ; 2 (let ((n -10)) (n+ 1)) ; 2 #+END_SRC - example 8 #+BEGIN_SRC emacs-lisp (setq lexical-binding t) (setq xx 10) (let ((xx 1)) (defun xx+ (y) (+ xx y))) (xx+ 1) ; 2 (let ((x -10)) (xx+ 1)) ; 2 #+END_SRC - example 9 #+BEGIN_SRC emacs-lisp (setq lexical-binding t) (defvar xxx 10) (let ((xxx 1)) (defun xxx+ (y) (+ xxx y))) (xxx+ 1) ; 11 (let ((xxx -10)) (xxx+ 1)) ; -9 #+END_SRC - example 10 #+BEGIN_SRC emacs-lisp (setq lexical-binding t) (let ((xxxx 1)) (defun xxxx+ (y) (+ y xxxx))) (defvar xxxx 10) (xxxx+ 1) ; 11 (let ((xxxx -10)) (xxxx+ 1)) ; -9 #+END_SRC ** advanced example using lambda, let, dolist, defun Evaluating the following forms with lexical binding: #+BEGIN_SRC emacs-lisp (setq lexical-binding t) (defun call-f (f x) `(:x-in-call-f ,x :result-of-f ,(funcall f x) :type-of-f ,(car f) :env-of-f ,(and (eq (car f) 'closure) (cadr f)))) (dolist (x '(1 2 3)) (let ((f (lambda (y) `(:x-in-f ,x :y ,y)))) (message "%S" (append `(:x-in-dolist ,x) (call-f f -1))))) #+END_SRC prints out the following into the message buffer: #+BEGIN_SRC emacs-lisp (:x-in-dolist 1 :x-in-call-f -1 :result-of-f (:x-in-f 1 :y -1) :type-of-f closure :env-of-f ((--dolist-tail-- 1 2 3) t)) (:x-in-dolist 2 :x-in-call-f -1 :result-of-f (:x-in-f 2 :y -1) :type-of-f closure :env-of-f ((--dolist-tail-- 2 3) t)) (:x-in-dolist 3 :x-in-call-f -1 :result-of-f (:x-in-f 3 :y -1) :type-of-f closure :env-of-f ((--dolist-tail-- 3) t)) #+END_SRC Evaluating the following forms with dynamic binding: #+BEGIN_SRC emacs-lisp (setq lexical-binding nil) (defun call-f (f x) `(:x-in-call-f ,x :result-of-f ,(funcall f x) :type-of-f ,(car f) :env-of-f ,(and (eq (car f) 'closure) (cadr f)))) (dolist (x '(1 2 3)) (let ((f (lambda (y) `(:x-in-f ,x :y ,y)))) (message "%S" (append `(:x-in-dolist ,x) (call-f f -1))))) #+END_SRC prints out the following into the message buffer: #+BEGIN_SRC emacs-lisp (:x-in-dolist 1 :x-in-call-f -1 :result-of-f (:x-in-f -1 :y -1) :type-of-f lambda :env-of-f nil) (:x-in-dolist 2 :x-in-call-f -1 :result-of-f (:x-in-f -1 :y -1) :type-of-f lambda :env-of-f nil) (:x-in-dolist 3 :x-in-call-f -1 :result-of-f (:x-in-f -1 :y -1) :type-of-f lambda :env-of-f nil) #+END_SRC * Questions and Answers :PROPERTIES: :CUSTOM_ID: /questions-and-answers/ :END: ** [[#/2022-04-04-search-options-link-abbreviations-and-org-open-at-point/][Search options in file links | link abbreviations | COME WITH ME on this JOURNEY into the heart of the command org-open-at-point]] :PROPERTIES: :CUSTOM_ID: /questions-and-answers/#2022-04-04-search-options-link-abbreviations-and-org-open-at-point :END: *** Is there a way to make clickable noweb references ~<>~ in source blocks in order to jump to its block definition? What we want to accomplish here is to jump to the definition of a noweb block, let's say ~my-noweb~, by calling a command (maybe [[org-mode:lisp/org.el::(defun org-open-at-point (&optional arg][org-open-at-point]]) when the point is on a reference ~<>~ of that block, for instance in an org buffer with the following content: #+BEGIN_SRC org ,#+NAME: my-noweb ,#+BEGIN_SRC emacs-lisp (setq sentence '(foo bar baz)) ,#+END_SRC ,#+BEGIN_SRC emacs-lisp :noweb yes :results value verbatim <> (reverse sentence) ,#+END_SRC ,#+RESULTS: : (baz bar foo) #+END_SRC This can be done in at least two ways. **** Using the built-in command ~org-babel-goto-named-src-block~ First, we can use the built-in command ~org-babel-goto-named-src-block~ (bound to ~C-c C-v g~ by default). After calling ~org-babel-goto-named-src-block~ with the point on top of the reference ~<>~: 1) we're asked in the minibuffer to choose a name, 2) we pick the noweb ref ~my-noweb~, press ~RET~, 3) we jump to the source block named ~my-noweb~. **** Using the hook ~org-open-at-point-functions~ Second possibility, we can define a command that jump to a noweb block definition when we call it with the point on top of a noweb reference without prompting anything in the minibuffer. Then we can call it directly or better (if it's the behavior we want) we can add this command to the variable ~org-open-at-point-functions~. And, now in a source block with the point on top of a noweb reference, we can call [[org-mode:lisp/org.el::(defun org-open-at-point (&optional arg][org-open-at-point]] (~C-c C-o~ by default) which will call this new command and jump to the noweb block definition at point (instead of running ~org-babel-open-src-block-result~). Here an implementation of such a command that we call ~org-goto-noweb~. #+BEGIN_SRC emacs-lisp (require 'org) (defun org-noweb-ref-p () "Return the noweb reference at point if any. If not return `nil'." (interactive) (let* ((context (org-element-context)) (type (org-element-type context)) (noweb-ref (and (memq type '(inline-src-block src-block)) (org-in-regexp (org-babel-noweb-wrap))))) (when noweb-ref (buffer-substring (+ (car noweb-ref) (length org-babel-noweb-wrap-start)) (- (cdr noweb-ref) (length org-babel-noweb-wrap-end)))))) (defun org-goto-noweb () "Go to the noweb ref at point." (interactive) (when-let ((ref (org-noweb-ref-p))) (let ((point (org-babel-find-named-block ref))) (if point ;; Taken from `org-open-at-point'. (progn (org-mark-ring-push) (goto-char point) (org-show-context) ;; return non-nil, in order to use it in ;; the variable `org-open-at-point-functions' 'noweb-found) (message "source-code block `%s' not found in this buffer" ref))))) (add-to-list 'org-open-at-point-functions #'org-goto-noweb) #+END_SRC And here the ~ert~ test for the command ~org-goto-noweb~: #+BEGIN_SRC emacs-lisp ;; from org-mode: testing/org-test.el (defmacro org-test-with-temp-text (text &rest body) "Run body in a temporary buffer with Org mode as the active mode holding TEXT. If the string \"\" appears in TEXT then remove it and place the point there before running BODY, otherwise place the point at the beginning of the inserted text." (declare (indent 1)) `(let ((inside-text (if (stringp ,text) ,text (eval ,text))) (org-mode-hook nil)) (with-temp-buffer (org-mode) (let ((point (string-match "" inside-text))) (if point (progn (insert (replace-match "" nil nil inside-text)) (goto-char (1+ (match-beginning 0)))) (insert inside-text) (goto-char (point-min)))) (font-lock-ensure (point-min) (point-max)) ,@body))) (ert-deftest org-goto-noweb-test () (should (org-test-with-temp-text "#+BEGIN_SRC emacs-lisp :noweb yes <> (reverse sentence) ,#+END_SRC" (org-noweb-ref-p))) (should-not (org-test-with-temp-text "#+BEGIN_SRC emacs-lisp :noweb yes <> (reverse sentence) ,#+END_SRC" (org-noweb-ref-p))) ;; source blocks (should (org-test-with-temp-text "#+NAME: my-noweb ,#+BEGIN_SRC emacs-lisp (setq sentence '(foo bar baz)) ,#+END_SRC ,#+BEGIN_SRC emacs-lisp :noweb yes <> (reverse sentence) ,#+END_SRC" (org-goto-noweb) (forward-line) (looking-at "\(setq"))) (should-not (org-test-with-temp-text "#+NAME: my-noweb ,#+BEGIN_SRC emacs-lisp (setq sentence '(foo bar baz)) ,#+END_SRC ,#+BEGIN_SRC emacs-lisp :noweb yes <> (reverse sentence) ,#+END_SRC" (org-goto-noweb))) ;; inline source blocks (should (org-test-with-temp-text "#+NAME: my-noweb ,#+BEGIN_SRC emacs-lisp (setq sentence '(foo bar baz)) ,#+END_SRC src_emacs-lisp{<>}" (org-goto-noweb) (forward-line) (looking-at "\(setq")))) #+END_SRC ** [[#/2022-03-22-org-speed-keys-and-self-insert-command/][Org Speed Keys! BOOM! Great org-mode's feature! And a good OPPORTUNITY to talk about self-insert-command]] :PROPERTIES: :CUSTOM_ID: /questions-and-answers/#2022-03-22-org-speed-keys-and-self-insert-command :END: *** How are speed keys different from/better than ~evil-mode~? Does speed keys can be use along with ~evil-mode~? **** Quick answer 1) If we are in evil normal state ~~, Org speed keys won't work (because most of the printing keys are used for something else, specifically they are bound in ~evil-normal-state-map~), 2) if we are in evil insert state ~~ or in evil emacs state ~~, Org speed keys works (because the printing keys are not bound by ~evil-mode~ in ~evil-insert-state-map~ nor in ~evil-emacs-state-map~). **** Answer with more details Org speed keys are not better than ~evil-mode~ nor the other way, they are really differents. ***** Org speed keys Org speed keys are a kind of a "hack" that hijack the "inserting emacs process". It can be seen as: when we try to insert a character, do not use the standard command for inserting, use another one that checks for the position of the cursor in the buffer, if it is at a specific location, performs a lookup for the command to call, if we find one, call it, if none, insert the character. It is all about one command ~self-insert-command~ and one remapping ~self-insert-command~ to ~org-self-insert-command~. ***** ~evil-mode~ ~evil-mode~ manipulates the hierarchy of all the keymaps that are active, making the current evil "state" map to win over the others. We can check this by inspecting the list of the current active keymaps with the function ~current-active-maps~. If the same key sequence is bound several times to different commands in the list returned by ~current-active-maps~, the first binding in the list wins over the others. For instance, when we are in evil normal state ~~, the bindings in the keymap ~evil-normal-state-map~ takes precedence (over almost all) the other binding in the current active maps, and will appear at the beginning of the list returned by ~current-active-maps~. Assuming we are in evil normal state ~~, to check this previous assertion, we can run: : M-x pp-eval-expression RET (current-active-maps) Now, if we switch to the evil insert state ~~, the bindings in the keymap ~evil-insert-state-map~ takes precedence (over almost all) the other binding in the current active maps, and will appear at the beginning of the list returned by ~current-active-maps~. Assuming we are in evil normal state ~~, to check this previous assertion, we can run: : M-x pp-eval-expression RET (current-active-maps) Now, what's interesting, is that evil insert state ~~ doesn't bind the printing keys nor remap the command ~self-insert-command~. So, when we are in evil insert state ~~, and we press the key ~n~ for instance, the "command loop editor", when perfoming the key lookup in the current active maps, won't find the binding coming from the keymaps ~evil-insert-state-map~ but the binding coming form the current global map which resolves (by default) to the command ~self-insert-command~. And, if we've remapped ~self-insert-command~ to ~org-self-insert-command~, (which is the case in org-mode with ~org-use-speed-commands~ set to ~t~), the "command loop editor" will call ~org-self-insert-command~. *** Is there a good reason for the entry point to be a variable switch, and for the bindings to be managed by a list, instead of having a minor mode layering its keymap onto standard org-mode bindings? I think that the benefit of this approach (remapping ~self-insert-command~ to ~org-self-insert-command~) over having a minor mode layering its keymap onto standard ~org-mode~ is that we don't have to switch between keymaps or minor modes to get the feature. Let's say we define a minor mode ~X-mode~ with the keymap ~X-mode-map~ that binds the key ~n~ to ~org-next-visible-heading~. Now, when ~X-mode~ is turned on, ~X-mode-map~ "wins" over ~org-mode-map~ and typing ~n~ will get us (from anywhere in the buffer) to the next heading. Now, what should we do to insert the character ~n~ in the buffer? We should turn off ~X-mode~ to remove the binding from ~X-mode-map~. With the remapping method we don't have to switch between minor modes before moving to the next heading by typing ~n~. The only restriction is to be at the beginning of a heading. There is always a trade off. I don't know if it is a good reason but this the only one that I see. ** [[#/2022-03-11-org-mode-source-code-5000-examples/][Did you know that org-mode's source code contains more than 5000 examples?]] :PROPERTIES: :CUSTOM_ID: /questions-and-answers/#2022-03-11-org-mode-source-code-5000-examples :END: *** How do you count 5000 examples? Any ~should~, ~should-not~ and ~should-error~ used in a ~ert~ test in the repository [[org-mode:testing]] is counted as an example. So now, to count all the examples, we have to count all those form and this can be done by searching recursively in the [[org-mode:testing]] for the string ~(should~. This can be done using ~grep~ to match recursively the string ~(should~ in the directory [[org-mode:testing]] and counting the number of line with ~wc~ utility: #+BEGIN_SRC bash # org-mode at commit cbe3f2d69 cd org-mode/testing/ grep -r '(should' | wc -l #+END_SRC #+RESULTS: : 5235