ob-ref.el (9507B)
1 ;;; ob-ref.el --- Babel Functions for Referencing External Data -*- lexical-binding: t; -*- 2 3 ;; Copyright (C) 2009-2023 Free Software Foundation, Inc. 4 5 ;; Authors: Eric Schulte 6 ;; Dan Davison 7 ;; Keywords: literate programming, reproducible research 8 ;; URL: https://orgmode.org 9 10 ;; This file is part of GNU Emacs. 11 12 ;; GNU Emacs is free software: you can redistribute it and/or modify 13 ;; it under the terms of the GNU General Public License as published by 14 ;; the Free Software Foundation, either version 3 of the License, or 15 ;; (at your option) any later version. 16 17 ;; GNU Emacs is distributed in the hope that it will be useful, 18 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 ;; GNU General Public License for more details. 21 22 ;; You should have received a copy of the GNU General Public License 23 ;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. 24 25 ;;; Commentary: 26 27 ;; Functions for referencing data from the header arguments of a 28 ;; org-babel block. The syntax of such a reference should be 29 30 ;; #+VAR: variable-name=file:resource-id 31 32 ;; - variable-name :: the name of the variable to which the value 33 ;; will be assigned 34 35 ;; - file :: path to the file containing the resource, or omitted if 36 ;; resource is in the current file 37 38 ;; - resource-id :: the id or name of the resource 39 40 ;; So an example of a simple source block referencing table data in 41 ;; the same file would be 42 43 ;; #+NAME: sandbox 44 ;; | 1 | 2 | 3 | 45 ;; | 4 | org-babel | 6 | 46 ;; 47 ;; #+begin_src emacs-lisp :var table=sandbox 48 ;; (message table) 49 ;; #+end_src 50 51 ;;; Code: 52 53 (require 'org-macs) 54 (org-assert-version) 55 56 (require 'ob-core) 57 (require 'org-macs) 58 (require 'cl-lib) 59 60 (declare-function org-babel-lob-get-info "ob-lob" (&optional datum no-eval)) 61 (declare-function org-element-at-point "org-element" (&optional pom cached-only)) 62 (declare-function org-element-property "org-element" (property element)) 63 (declare-function org-element-type "org-element" (element)) 64 (declare-function org-end-of-meta-data "org" (&optional full)) 65 (declare-function org-find-property "org" (property &optional value)) 66 (declare-function org-id-find-id-file "org-id" (id)) 67 (declare-function org-id-find-id-in-file "org-id" (id file &optional markerp)) 68 (declare-function org-in-commented-heading-p "org" (&optional no-inheritance)) 69 (declare-function org-narrow-to-subtree "org" (&optional element)) 70 (declare-function org-fold-show-context "org-fold" (&optional key)) 71 72 (defvar org-babel-update-intermediate nil 73 "Update the in-buffer results of code blocks executed to resolve references.") 74 75 (defun org-babel-ref-parse (assignment) 76 "Parse a variable ASSIGNMENT in a header argument. 77 78 If the right hand side of the assignment has a literal value 79 return that value, otherwise interpret it as a reference to an 80 external resource and find its value using `org-babel-ref-resolve'. 81 82 Return a list with two elements: the name of the variable, and an 83 Emacs Lisp representation of the value of the variable." 84 (when (string-match "\\(.+?\\)=" assignment) 85 (let ((var (org-trim (match-string 1 assignment))) 86 (ref (org-trim (substring assignment (match-end 0))))) 87 (cons (intern var) 88 (let ((out (save-excursion 89 (when org-babel-current-src-block-location 90 (goto-char (if (markerp org-babel-current-src-block-location) 91 (marker-position org-babel-current-src-block-location) 92 org-babel-current-src-block-location))) 93 (org-babel-read ref)))) 94 (if (equal out ref) 95 (if (and (string-prefix-p "\"" ref) 96 (string-suffix-p "\"" ref)) 97 (read ref) 98 (org-babel-ref-resolve ref)) 99 out)))))) 100 101 (defun org-babel-ref-goto-headline-id (id) 102 (or (let ((h (org-find-property "CUSTOM_ID" id))) 103 (when h (goto-char h))) 104 (let* ((file (org-id-find-id-file id)) 105 (m (when file (org-id-find-id-in-file id file 'marker)))) 106 (when (and file m) 107 (message "file:%S" file) 108 (pop-to-buffer-same-window (marker-buffer m)) 109 (goto-char m) 110 (move-marker m nil) 111 (org-fold-show-context) 112 t)))) 113 114 (defun org-babel-ref-headline-body () 115 (save-restriction 116 (org-narrow-to-subtree) 117 (buffer-substring 118 (save-excursion (goto-char (point-min)) 119 (org-end-of-meta-data) 120 (point)) 121 (point-max)))) 122 123 (defvar org-babel-library-of-babel) 124 (defun org-babel-ref-resolve (ref) 125 "Resolve the reference REF and return its value." 126 (save-window-excursion 127 (with-current-buffer (or org-babel-exp-reference-buffer (current-buffer)) 128 (save-excursion 129 (let ((case-fold-search t) 130 args new-refere new-header-args new-referent split-file split-ref 131 index contents) 132 ;; if ref is indexed grab the indices -- beware nested indices 133 (when (and (string-match "\\[\\([^\\[]*\\)\\]$" ref) 134 (let ((str (substring ref 0 (match-beginning 0)))) 135 (= (cl-count ?\( str) (cl-count ?\) str)))) 136 (if (> (length (match-string 1 ref)) 0) 137 (setq index (match-string 1 ref)) 138 (setq contents t)) 139 (setq ref (substring ref 0 (match-beginning 0)))) 140 ;; assign any arguments to pass to source block 141 (when (string-match 142 "^\\(.+?\\)\\(\\[\\(.*\\)\\]\\|\\(\\)\\)(\\(.*\\))$" ref) 143 (setq new-refere (match-string 1 ref)) 144 (setq new-header-args (match-string 3 ref)) 145 (setq new-referent (match-string 5 ref)) 146 (when (> (length new-refere) 0) 147 (when (> (length new-referent) 0) 148 (setq args (mapcar (lambda (ref) (cons :var ref)) 149 (org-babel-ref-split-args new-referent)))) 150 (when (> (length new-header-args) 0) 151 (setq args (append (org-babel-parse-header-arguments 152 new-header-args) 153 args))) 154 (setq ref new-refere))) 155 (when (string-match "^\\(.+\\):\\(.+\\)$" ref) 156 (setq split-file (match-string 1 ref)) 157 (setq split-ref (match-string 2 ref)) 158 (find-file split-file) 159 (setq ref split-ref)) 160 (org-with-wide-buffer 161 (goto-char (point-min)) 162 (let* ((params (append args '((:results . "none")))) 163 (regexp (org-babel-named-data-regexp-for-name ref)) 164 (result 165 (catch :found 166 ;; Check for code blocks or named data. 167 (while (re-search-forward regexp nil t) 168 ;; Ignore COMMENTed headings and orphaned 169 ;; affiliated keywords. 170 (unless (org-in-commented-heading-p) 171 (let ((e (org-element-at-point))) 172 (when (equal (org-element-property :name e) ref) 173 (goto-char 174 (org-element-property :post-affiliated e)) 175 (pcase (org-element-type e) 176 (`babel-call 177 (throw :found 178 (org-babel-execute-src-block 179 nil (org-babel-lob-get-info e) params))) 180 ((and `src-block (guard (not contents))) 181 (throw :found 182 (org-babel-execute-src-block 183 nil nil 184 (and 185 (not org-babel-update-intermediate) 186 params)))) 187 ((and (let v (org-babel-read-element e)) 188 (guard v)) 189 (throw :found v)) 190 (_ (error "Reference not found"))))))) 191 ;; Check for local or global headlines by ID. 192 (when (org-babel-ref-goto-headline-id ref) 193 (throw :found (org-babel-ref-headline-body))) 194 ;; Check the Library of Babel. 195 (let ((info (cdr (assq (intern ref) 196 org-babel-library-of-babel)))) 197 (when info 198 (throw :found 199 (org-babel-execute-src-block nil info params)))) 200 (error "Reference `%s' not found in this buffer" ref)))) 201 (cond 202 ((and result (symbolp result)) (format "%S" result)) 203 ((and index (listp result)) 204 (org-babel-ref-index-list index result)) 205 (t result))))))))) 206 207 (defun org-babel-ref-index-list (index lis) 208 "Return the subset of LIS indexed by INDEX. 209 210 Indices are 0 based and negative indices count from the end of 211 LIS, so 0 references the first element of LIS and -1 references 212 the last. If INDEX is separated by \",\"s then each \"portion\" 213 is assumed to index into the next deepest nesting or dimension. 214 215 A valid \"portion\" can consist of either an integer index, two 216 integers separated by a \":\" in which case the entire range is 217 returned, or an empty string or \"*\" both of which are 218 interpreted to mean the entire range and as such are equivalent 219 to \"0:-1\"." 220 (if (and (> (length index) 0) (string-match "^\\([^,]*\\),?" index)) 221 (let* ((ind-re "\\(\\([-[:digit:]]+\\):\\([-[:digit:]]+\\)\\|\\*\\)") 222 (lgth (length lis)) 223 (portion (match-string 1 index)) 224 (remainder (substring index (match-end 0))) 225 (wrap (lambda (num) (if (< num 0) (+ lgth num) num))) 226 (open (lambda (ls) (if (and (listp ls) (= (length ls) 1)) (car ls) ls)))) 227 (funcall 228 open 229 (mapcar 230 (lambda (sub-lis) 231 (if (listp sub-lis) 232 (org-babel-ref-index-list remainder sub-lis) 233 sub-lis)) 234 (if (or (= 0 (length portion)) (string-match ind-re portion)) 235 (mapcar 236 (lambda (n) (nth n lis)) 237 (apply 'org-number-sequence 238 (if (and (> (length portion) 0) (match-string 2 portion)) 239 (list 240 (funcall wrap (string-to-number (match-string 2 portion))) 241 (funcall wrap (string-to-number (match-string 3 portion)))) 242 (list (funcall wrap 0) (funcall wrap -1))))) 243 (list (nth (funcall wrap (string-to-number portion)) lis)))))) 244 lis)) 245 246 (defun org-babel-ref-split-args (arg-string) 247 "Split ARG-STRING into top-level arguments of balanced parenthesis." 248 (mapcar #'org-trim (org-babel-balanced-split arg-string 44))) 249 250 (provide 'ob-ref) 251 252 ;;; ob-ref.el ends here