Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create hyperlinks for noweb references in org-mode

I'm wondering if there is a way to add hyperlinks to noweb references, i.e., in the following org-mode snippet:

#+name: list-all
#+begin_src sh
ls -a
#+end_src

and we come here

#+begin_src sh :noweb no-export :tangle myscript.sh
echo "Hello world"

<<list-all>>
#+end_src

When exporting to html or latex, I would like to have the <<list-all>> to be a link to the code block being referenced by it. I have noticed that this is the case in some noweb implementations other than org-modes' one. I've checked the documentation and I don't seem to find anything about this. This would be an invaluable feature to have.

like image 535
margolari Avatar asked Sep 17 '25 12:09

margolari


1 Answers

I'm not able to put the links within the src block (for that you probably need to add a filter to org-export-filter-src-block-functions: the filter, however, needs to work with the exported format).

However, I am happy with this solution I implemented after having read this reddit post.

Example

Let's say you have this org file:

#+name: first-fragment
#+begin_src python
  x=1
#+end_src

Some text.

#+name: second-fragment
#+begin_src python
  y=2
#+end_src

Some text.

#+name: to-be-tangled
#+begin_src python :noweb no-export :tangle program.py
  <<first-fragment>>
  <<second-fragment>>
  print(f'{x} and {y}')
#+end_src

Then I can export it to html with this elisp code:

(require 'ox-html)
(load (concat default-directory "htmlize.el"))
(require 'htmlize)

(setq make-backup-files nil
      org-html-doctype "html5"
      org-html-html5-fancy t
      org-html-style-default ""
      org-html-htmlize-output-type 'css
      org-html-validation-link nil
      org-html-postamble t
      org-html-postamble-format '(("en" "")))

(defun first-group-match (regex string)
  "Return the substring matching the first group in REGEX,
   together with the ending point in STRING.

   The result is a cons cell (MATCH . POSITION).
   "
  (save-match-data
    (if (string-match regex string)
        (cons (substring string (match-beginning 1) (match-end 1)) (match-end 1))
      nil)
    )

  )

(defun all-string-matches (regex string)
  "Return a list of all group matches."
  (if (< (length string) 5)
      nil
    (let* ((match (first-group-match regex string))
           (name (car match))
           (end-name (if name (cdr match) -1))
           (next (all-string-matches regex
                                     (substring string end-name))))
      (if name
          (cons name next)
        next))))

(defun get-noweb-links (block)
  "Get all the <<>> references as a list."
  (cl-loop for name in
           (all-string-matches "<<\\([^<>\n]+\\)>>" (org-element-property :value block))
           collect
           (format "[[%s][%s]]" name name)))

(defun append-refs (block refs)
  "Append to BLOCK a line with REFS."
  (let ((begin (org-element-property :begin block))
        (end (org-element-property :end block))
        (string (format "\n/This code refers to: %s/\n\n" refs)))
    (goto-char (+ added-characters (org-element-property :end block)))
    (insert string)
    (setq added-characters (+ added-characters (length string)))))


(defun add-references (_)
  "Add a line with referenced blocks to all src blocks that contains noweb links."
  (let ((src-blocks (org-element-map (org-element-parse-buffer) 'src-block
                      (lambda (b) b))))
    (cl-loop for b in src-blocks do
             (when (string-match "<<[^<>\n]+>>" (org-element-property :value b))
               (append-refs b (string-join (get-noweb-links b) ", "))))))


(setq added-characters 0)
(add-hook 'org-export-before-parsing-functions 'add-references)

(dolist (file command-line-args-left)
  (with-current-buffer
      (find-file-literally file)
    (org-html-export-to-html)))

Resulting in this:

<!DOCTYPE html>
<html lang="en">
<head>
<!-- 2022-12-10 sab 10:32 -->
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>&lrm;</title>
<meta name="author" content="eMMe" />
<meta name="generator" content="Org Mode" />
</head>
<body>
<div id="content" class="content">
<div class="org-src-container">
<pre class="src src-python" id="org121b865"><span class="org-variable-name">x</span><span class="org-operator">=</span>1
</pre>
</div>

<p>
Some text.
</p>

<div class="org-src-container">
<pre class="src src-python" id="orga7322c4"><span class="org-variable-name">y</span><span class="org-operator">=</span>2
</pre>
</div>

<p>
Some text.
</p>

<div class="org-src-container">
<pre class="src src-python" id="orgcde0461"><span class="org-operator">&lt;&lt;</span>first<span class="org-operator">-</span>fragment<span class="org-operator">&gt;&gt;</span>
<span class="org-operator">&lt;&lt;</span>second<span class="org-operator">-</span>fragment<span class="org-operator">&gt;&gt;</span>
<span class="org-builtin">print</span>(f<span class="org-string">'</span>{x}<span class="org-string"> and </span>{y}<span class="org-string">'</span>)
</pre>
</div>

<p>
<i>This code refers to: <a href="#org121b865">first-fragment</a>, <a href="#orga7322c4">second-fragment</a></i>
</p>
</div>
</body>
</html>
like image 150
eMMe Avatar answered Sep 19 '25 08:09

eMMe