blog

1 DONE Showing what data went into a code block on export

Sometimes I define variables in the header of a code block and then use the code to analyze the data. In org-mode this is super, and you can read the file and easily see what is going on.

When you export the file, however, the information is lost, and in the exported result you cannot see what data went into a code block, or figure out where it is from.

Today we examine how to get that information into exported code. First, we setup a simple example that will do what need.


Table name: tbl-data
x y
1 1
2 4
3 9

Now a code block that has a defined variable in the header that uses data from the table defined above.

Language = python
Parameters = :var data=tbl-data :results value

Results: print-table
1 1
2 4
3 9

During export, org-mode does some interesting things to the document, including removing the headers from the code blocks, which makes it impossible to access them inside the export. The headers are apparently removed during org-babel-exp-process-buffer. It does not appear possible to advise this function because it processes the whole buffer at once, and we need to save data for each code block.

So, we will have to preprocess the buffer to get the parameters on each block, and then put the parameters in the export afterwards. For this, we can use a filter. We will preprocess the buffer to get names of tables, and parameters of src-blocks. (I suppose we could put this preprocessing in the advice function, but I tend to avoid advice when possible).

Here is how we can get a list of the table-names indicating their name or that they are results (results are enclosed in ()).

(org-element-map (org-element-parse-buffer) 'table
  (lambda (element)     
    (or (org-element-property :name element) (org-element-property :results element))))

Results:
tbl-data (print-table) () ()

Similarly, here is the list of parameters for each block.

(org-element-map (org-element-parse-buffer) 'src-block
  (lambda (element)     
    (org-element-property :parameters element)))

Results:
:var data=tbl-data :results value

Now, we combine them with filters to modify the output. First, we preprocess to get each list, and then in the filter, we will pop off each value and insert the data. We will also get the language for each code block, and add that in the export. We use a filter because we are not modified the transcoded text, simply adding some new text in front of it.

(defun ox-mrkup-filter-table (text back-end info)
  (let ((tblname (pop tblnames)))
    (message "tblname is \"%s\"" tblname)
    ; pop does not remove nil from the list, so we do it here.
    (when (null tblname) (setq tblnames (cdr tblnames)))
    (cond
     ((listp tblname)  ; from results
      (concat (format "<br>Results: %s" (car tblname)) text))
     ((null tblname)   ; no name
      text)
     (t ; everything else
      (concat (format "<br>Table name: %s" tblname) text)))))

(defun ox-mrkup-filter-src-block (text back-end info)
  (let ((params (pop src-params))
        (lang (pop src-langs)))
    (when (null params) (setq src-params (cdr src-params)))
    (if params  
        (concat (format "<pre>Language = %s\nParameters = %s</pre>" lang params) text)
      text)))

;; preprocess to get table names, src parameters and languages.
(let ((tblnames (org-element-map (org-element-parse-buffer) 'table
                  (lambda (element)     
                    (or (org-element-property :name element)                    
                        (org-element-property :results element)))))

      (src-params (org-element-map (org-element-parse-buffer) 'src-block
                    (lambda (element)     
                      (org-element-property :parameters element))))

      (src-langs (org-element-map (org-element-parse-buffer) 'src-block
                    (lambda (element)     
                      (org-element-property :language element))))

      ;; register the filters
      (org-export-filter-table-functions '(ox-mrkup-filter-table))
      (org-export-filter-src-block-functions '(ox-mrkup-filter-src-block)))

  ;; and export the result
  (browse-url (org-export-to-file 'html "custom-src-table-export-3.html")))
#<process open custom-src-table-export-3.html>

Here is the resulting html file: ./custom-src-table-export-3.html which shows the new export behavior. It might not be too difficult to make links between the parameters and the tables, but it would require parsing the :parameters string. For now, this makes it easy enough to read in HTML where the data is coming from (assuming fluency in org-mode header arguments!).

Special thanks to Aaron Ecay, and Charles Berry on the org-mode mailing list for pointing me towards a solution.

Created: 2014-09-22 Mon 12:33

Emacs 24.4.50.1 (Org mode 8.2.7c)

Validate