Hi all,
I am following up on a question I posted here a while ago to show my progress in case it can help others and to ask for a few clarifications. I would appreciate any feedback. My goal is to add a label to source blocks on HTML export to indicate the name of the file to which the block is tangled. The issues originally raised were: 1) How to display the label in HTML export? 2) How to get the tangle file name, which may come from 1) a file property (e.g. #+PROPERTY: header-args :tangle file-main.py in the file header), 2) a section property (e.g. :header-args: :tangle file-prop.py in a property drawer), 3) the source block header line (e.g. #+BEGIN_SRC python :tangle no)? It appears that the best way to achieve this is to define a derived backend with special handling for src-block objects as in http://orgmode.org/manual/Advanced-configuration.html. * Example file Here is the example file I am working with. #+BEGIN_SRC org ,#+TITLE: Test ,#+PROPERTY: header-args :tangle file-main.py ,#+NAME: src-no-1 ,#+BEGIN_SRC python :tangle no # SHOULD NOT BE TANGLED (1) ,#+END_SRC ,#+NAME: src-header ,#+BEGIN_SRC python :tangle file-header.py # Tangle to file-header.py ,#+END_SRC ,#+NAME: src-main ,#+BEGIN_SRC python # Tangle to file-main.py ,#+END_SRC ,* section :PROPERTIES: :header-args: :tangle file-prop.py :END: ,#+NAME: src-prop ,#+BEGIN_SRC python :tangle file-prop.py # Tangle to file-prop.py ,#+END_SRC ,#+NAME: src-no-2 ,#+BEGIN_SRC python :tangle no # SHOULD NOT BE TANGLED (2) ,#+END_SRC #+END_SRC * Formatting the label I think I have a satisfactory (at least for me) solution for this: in my src-block exporter I first pass the src-block by the regular HTML exporter: #+BEGIN_SRC emacs-lisp (let ((export-out (org-export-with-backend 'html src-block contents info))) ...) #+END_SRC Later, I add a HTML "<div>" block under the "<pre>" block for the source. I do this using a regexp: #+BEGIN_SRC emacs-lisp (let ((src-start-pat "\\(<pre class=\"src src-[^>]+>\\)")) (setq export-out (replace-regexp-in-string src-start-pat (concat "\\1<div style=\"position: absolute; bottom: 0;" " right: 0; text-align:right;\" class=\"src-label\">" tang "</div>") export-out))) #+END_SRC This adds the label on the bottom right corner of the "<pre>" block and uses the "src-label" class, which can be customized via CSS. * Getting the tangle filename I am working with the assumption that this will be done from my src-block handling function in the derived backend: #+BEGIN_SRC emacs-lisp (defun my-html-src-block (src-block contents info) ...) (org-export-define-derived-backend 'my-html 'html :translate-alist '((src-block . my-html-src-block))) #+END_SRC I run the following code to perform the export using my new backend: #+BEGIN_SRC emacs-lisp (org-export-to-file 'my-html (buffer-file-name)) #+END_SRC *Question 1*: Is the `org-element-src-block-parser' function supposed to resolve the tangle filename (including inheritance, i.e. to handle the 3 :tangle sources listed above)? >From the docstring, it would appear that it shouldn't return a :tangle keyword and that the tangling functions are responsible for choosing the output tangle file for each block. Is this correct? Hoping that my understanding of *Question 1* is correct, I tried to use the `org-babel-tangle-single-block' and `org-babel-tangle-collect-blocks' functions, which are used during the actual tangling, to get the tangle filename. ** Working version Here is what I was able to get to work on my simple example: #+BEGIN_SRC emacs-lisp (defun my-html-src-block (src-block contents info) "Transcode a SRC-BLOCK element from Org to HTML. CONTENTS is nil. INFO is a plist used as a communication channel." (let* ((src-block-name (org-element-property :name src-block)) (src-blocks (cdr (car (org-babel-tangle-collect-blocks)))) tang (export-out (org-export-with-backend 'html src-block contents info))) (dolist (src-coll src-blocks) (when (string= (nth 3 src-coll) src-block-name) (setq tang (cdr (assoc :tangle (nth 4 src-coll)))))) (if (and tang (> (length tang) 0) (not (string= tang "no"))) (let ((src-start-pat "\\(<pre class=\"src src-[^>]+>\\)")) (setq export-out (replace-regexp-in-string src-start-pat (concat "\\1<div style=\"position: absolute; bottom: 0;" " right: 0; text-align:right;\" class=\"src-label\">" tang "</div>") export-out)))) export-out)) #+END_SRC To summarize: - Collect all the tangled blocks - Among these, find the one currently passed as input (match by source block name) - Get the :tangle property from the matching collected source-block The main problem I see which this approach is that it collects all the source blocks for each src-block entry to process. This makes the exporting process slow. Another issue is that this requires named blocks, but this constraint is acceptable for me. ** Improvements (which I cannot get to work) - Add collected blocks to `info' in order to perform the collection only once and re-use it for subsequent blocks: I couldn't get it to work, function arguments seem to be passed by value (?) - Use `org-babel-tangle-single-block' to process only one block at a time, this sounds like the right method. I believe I must first move point to the src-block being processed, which I try to do with (org-babel-goto-named-src-block src-block-name): this gives me the correct tangle filename when run from a simple code snippet in the scratch buffer (using (org-element-map (org-element-parse-buffer) ...)), but results in the wrong tangle file when I do that within the `my-html-src-block' function (the :tangle options passed on the #+BEGIN_SRC line as in case 3. are ignored and are always preempted by the inherited tangle value coming either from the ,#+PROPERTY entry or the PROPERTY drawer value). *Question 2*: How can I use `org-babel-tangle-single-block' within the custom src-block handling function? I notice the (point) value is different after the (org-babel-goto-named-src-block src-block-name) call when used from the `my-html-src-block' function (possibly due to some pre-processing performed on the buffer, I am not sure). Sorry for the lengthy post, I hope it is not out of place on this list. Thanks in advance for any help. thibault * Original message > Hi list, > > I have an org file that I am tangling into multiple files, and exporting > to html. What I would like to do is to label each source block in the > exported html with the filename used for tangling this specific block. > I don't have a strong opinion about the actual appearance of the label > (adding a comment at the top of the source block, hover in the code > textarea, other?). > > I couldn't find any option to do that, so I initially though of using > `org-export-filter-src-block-functions' (following > http://orgmode.org/manual/Advanced-configuration.html). I was not able > to get the org-element object for the current block from the parameter > received by the filter. It looks like the third parameter (`info') may > have more information but I currently don't see how to get (1) the > current source block, and (2) the tangling filename. > > I then tried to define a derived backend with custom handling of source > blocks as described in the documentation. > > I currently have the following, where I try to get the tangle filename > using `org-babel-get-src-block-info', and later insert it into the html > content using an ugly regexp. > > (defun my-html-src-block (src-block contents info) > "Transcode a SRC-BLOCK element from Org to HTML. > CONTENTS is nil. INFO is a plist used as a communication > channel." > (let* ((lang (org-element-property :language src-block)) > (src-info (org-babel-get-src-block-info t src-block)) > (tang (cdr (assq :tangle (nth 2 src-info)))) > (export-out (org-export-with-backend 'html src-block contents info)) > ) > (if (and (string= lang "lua") (and tang (> (length tang) 0))) > (progn > (let ((src-start-pat "\\(<pre class=\"src src-lua\" > id=\"[^\"]+*\">\\)")) > (replace-regexp-in-string src-start-pat > (concat "\\1" > "\n-- [" > tang > "]\n\n") export-out) > ) > ) > export-out > ) > ) > ) > > (org-export-define-derived-backend 'my-html 'html > :translate-alist '((src-block . my-html-src-block))) > > Besides feeling wrong, this always gets the tangle name from the top > level org option ("#+PROPERTY: tangle main-file" at the top of the file) > instead of the one overridden by individual blocks "#+BEGIN_SRC :tangle > other-file". Moreover the added comment is not syntax highlighted and > this feels really wrong. > > Does anybody have any idea on how to achieve this? > > Thanks in advance. > > thibault