X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/bc81e2c4e885787603da3e0314d6ea45a43f7862..ef62b23df5a7007c3d8c74dbca87ba83e9da682e:/lisp/org/ob.el diff --git a/lisp/org/ob.el b/lisp/org/ob.el index 538158469c..0512248758 100644 --- a/lisp/org/ob.el +++ b/lisp/org/ob.el @@ -1,11 +1,11 @@ ;;; ob.el --- working with code blocks in org-mode -;; Copyright (C) 2009, 2010 Free Software Foundation, Inc. +;; Copyright (C) 2009-2012 Free Software Foundation, Inc. -;; Author: Eric Schulte, Dan Davison +;; Authors: Eric Schulte +;; Dan Davison ;; Keywords: literate programming, reproducible research ;; Homepage: http://orgmode.org -;; Version: 7.7 ;; This file is part of GNU Emacs. @@ -32,6 +32,7 @@ (defvar org-src-lang-modes) (defvar org-babel-library-of-babel) (declare-function show-all "outline" ()) +(declare-function org-reduce "org" (CL-FUNC CL-SEQ &rest CL-KEYS)) (declare-function tramp-compat-make-temp-file "tramp-compat" (filename &optional dir-flag)) (declare-function tramp-dissect-file-name "tramp" (name &optional nodefault)) @@ -78,6 +79,7 @@ (declare-function org-list-struct "org-list" ()) (declare-function org-list-prevs-alist "org-list" (struct)) (declare-function org-list-get-list-end "org-list" (item struct prevs)) +(declare-function org-strip-protective-commas "org" (beg end)) (defgroup org-babel nil "Code block evaluation and management in `org-mode' documents." @@ -103,6 +105,7 @@ against accidental code block evaluation. The `org-babel-no-eval-on-ctrl-c-ctrl-c' variable can be used to remove code block execution from the C-c C-c keybinding." :group 'org-babel + :version "24.1" :type '(choice boolean function)) ;; don't allow this variable to be changed through file settings (put 'org-confirm-babel-evaluate 'safe-local-variable (lambda (x) (eq x t))) @@ -110,10 +113,18 @@ remove code block execution from the C-c C-c keybinding." (defcustom org-babel-no-eval-on-ctrl-c-ctrl-c nil "Remove code block evaluation from the C-c C-c key binding." :group 'org-babel + :version "24.1" :type 'boolean) +(defcustom org-babel-results-keyword "RESULTS" + "Keyword used to name results generated by code blocks. +Should be either RESULTS or NAME however any capitalization may +be used." + :group 'org-babel + :type 'string) + (defvar org-babel-src-name-regexp - "^[ \t]*#\\+\\(srcname\\|source\\|function\\):[ \t]*" + "^[ \t]*#\\+name:[ \t]*" "Regular expression used to match a source name line.") (defvar org-babel-multi-line-header-regexp @@ -143,7 +154,7 @@ remove code block execution from the C-c C-c keybinding." (defvar org-babel-inline-src-block-regexp (concat ;; (1) replacement target (2) lang - "[^-[:alnum:]]\\(src_\\([^ \f\t\n\r\v]+\\)" + "\\(?:^\\|[^-[:alnum:]]\\)\\(src_\\([^ \f\t\n\r\v]+\\)" ;; (3,4) (unused, headers) "\\(\\|\\[\\(.*?\\)\\]\\)" ;; (5) body @@ -159,6 +170,39 @@ not match KEY should be returned." (lambda (p) (when (funcall (if others #'not #'identity) (eq (car p) key)) p)) params))) +(defun org-babel-get-inline-src-block-matches() + "Set match data if within body of an inline source block. +Returns non-nil if match-data set" + (let ((src-at-0-p (save-excursion + (beginning-of-line 1) + (string= "src" (thing-at-point 'word)))) + (first-line-p (= 1 (line-number-at-pos))) + (orig (point))) + (let ((search-for (cond ((and src-at-0-p first-line-p "src_")) + (first-line-p "[[:punct:] \t]src_") + (t "[[:punct:] \f\t\n\r\v]src_"))) + (lower-limit (if first-line-p + nil + (- (point-at-bol) 1)))) + (save-excursion + (when (or (and src-at-0-p (bobp)) + (and (re-search-forward "}" (point-at-eol) t) + (re-search-backward search-for lower-limit t) + (> orig (point)))) + (when (looking-at org-babel-inline-src-block-regexp) + t )))))) + +(defvar org-babel-inline-lob-one-liner-regexp) +(defun org-babel-get-lob-one-liner-matches() + "Set match data if on line of an lob one liner. +Returns non-nil if match-data set" + (save-excursion + (unless (= (point) (point-at-bol)) ;; move before inline block + (re-search-backward "[ \f\t\n\r\v]" nil t)) + (if (looking-at org-babel-inline-lob-one-liner-regexp) + t + nil))) + (defun org-babel-get-src-block-info (&optional light) "Get information on the current source block. @@ -183,22 +227,30 @@ Returns a list (nth 2 info) (org-babel-parse-header-arguments (match-string 1))))) (when (looking-at org-babel-src-name-w-name-regexp) - (setq name (org-babel-clean-text-properties (match-string 4))) - (when (match-string 6) + (setq name (org-babel-clean-text-properties (match-string 3))) + (when (and (match-string 5) (> (length (match-string 5)) 0)) (setf (nth 2 info) ;; merge functional-syntax vars and header-args (org-babel-merge-params - (mapcar (lambda (ref) (cons :var ref)) - (org-babel-ref-split-args (match-string 6))) + (mapcar + (lambda (ref) (cons :var ref)) + (mapcar + (lambda (var) ;; check that each variable is initialized + (if (string-match ".+=.+" var) + var + (error + "variable \"%s\"%s must be assigned a default value" + var (if name (format " in block \"%s\"" name) "")))) + (org-babel-ref-split-args (match-string 5)))) (nth 2 info)))))) ;; inline source block - (when (save-excursion (re-search-backward "[ \f\t\n\r\v]" nil t) - (looking-at org-babel-inline-src-block-regexp)) + (when (org-babel-get-inline-src-block-matches) (setq info (org-babel-parse-inline-src-block-match)))) ;; resolve variable references and add summary parameters (when (and info (not light)) (setf (nth 2 info) (org-babel-process-params (nth 2 info)))) (when info (append info (list name indent))))) +(defvar org-current-export-file) ; dynamically bound (defun org-babel-confirm-evaluate (info) "Confirm evaluation of the code block INFO. This behavior can be suppressed by setting the value of @@ -211,11 +263,15 @@ of potentially harmful code." (let* ((eval (or (cdr (assoc :eval (nth 2 info))) (when (assoc :noeval (nth 2 info)) "no"))) (query (cond ((equal eval "query") t) + ((and org-current-export-file + (equal eval "query-export")) t) ((functionp org-confirm-babel-evaluate) (funcall org-confirm-babel-evaluate (nth 0 info) (nth 1 info))) (t org-confirm-babel-evaluate)))) (if (or (equal eval "never") (equal eval "no") + (and org-current-export-file (or (equal eval "no-export") + (equal eval "never-export"))) (and query (not (yes-or-no-p (format "Evaluate this%scode block%son your system? " @@ -223,7 +279,9 @@ of potentially harmful code." (if (nth 4 info) (format " (%s) " (nth 4 info)) " ")))))) (prog1 nil (message "Evaluation %s" - (if (or (equal eval "never") (equal eval "no")) + (if (or (equal eval "never") (equal eval "no") + (equal eval "no-export") + (equal eval "never-export")) "Disabled" "Aborted"))) t))) @@ -313,10 +371,36 @@ then run `org-babel-pop-to-session'." (add-hook 'org-metadown-hook 'org-babel-pop-to-session-maybe) +(defconst org-babel-common-header-args-w-values + '((cache . ((no yes))) + (cmdline . :any) + (colnames . ((nil no yes))) + (comments . ((no link yes org both noweb))) + (dir . :any) + (eval . ((never query))) + (exports . ((code results both none))) + (file . :any) + (hlines . ((no yes))) + (mkdirp . ((yes no))) + (no-expand) + (noeval) + (noweb . ((yes no tangle))) + (noweb-ref . :any) + (noweb-sep . :any) + (padline . ((yes no))) + (results . ((file list vector table scalar verbatim) + (raw org html latex code pp wrap) + (replace silent append prepend) + (output value))) + (rownames . ((no yes))) + (sep . :any) + (session . :any) + (shebang . :any) + (tangle . ((tangle yes no :any))) + (var . :any))) + (defconst org-babel-header-arg-names - '(cache cmdline colnames dir exports file noweb results - session tangle var eval noeval comments no-expand shebang - padline noweb-ref) + (mapcar #'car org-babel-common-header-args-w-values) "Common header arguments used by org-babel. Note that individual languages may define their own language specific header arguments as well.") @@ -331,7 +415,7 @@ specific header arguments as well.") '((:session . "none") (:results . "replace") (:exports . "results")) "Default arguments to use when evaluating an inline source block.") -(defvar org-babel-data-names '("TBLNAME" "RESNAME" "RESULTS" "DATA")) +(defvar org-babel-data-names '("TBLNAME" "RESULTS" "NAME")) (defvar org-babel-result-regexp (concat "^[ \t]*#\\+" @@ -364,11 +448,17 @@ can not be resolved.") (defvar org-babel-after-execute-hook nil "Hook for functions to be called after `org-babel-execute-src-block'") + (defun org-babel-named-src-block-regexp-for-name (name) "This generates a regexp used to match a src block named NAME." - (concat org-babel-src-name-regexp (regexp-quote name) "[ \t\n]*" + (concat org-babel-src-name-regexp (regexp-quote name) + "\\([ \t]\\|$\\|(\\)" ".*[\r\n]" (substring org-babel-src-block-regexp 1))) +(defun org-babel-named-data-regexp-for-name (name) + "This generates a regexp used to match data named NAME." + (concat org-babel-result-regexp (regexp-quote name) "\\([ \t]\\|$\\)")) + ;;; functions (defvar call-process-region) ;;;###autoload @@ -379,9 +469,8 @@ Insert the results of execution into the buffer. Source code execution and the collection and formatting of results can be controlled through a variety of header arguments. -With prefix argument ARG, force re-execution even if a an -existing result cached in the buffer would otherwise have been -returned. +With prefix argument ARG, force re-execution even if an existing +result cached in the buffer would otherwise have been returned. Optionally supply a value for INFO in the form returned by `org-babel-get-src-block-info'. @@ -391,7 +480,10 @@ the header arguments specified at the front of the source code block." (interactive) (let ((info (or info (org-babel-get-src-block-info)))) - (when (org-babel-confirm-evaluate info) + (when (org-babel-confirm-evaluate + (let ((i info)) + (setf (nth 2 i) (org-babel-merge-params (nth 2 info) params)) + i)) (let* ((lang (nth 0 info)) (params (if params (org-babel-process-params @@ -463,7 +555,7 @@ block." (defun org-babel-expand-body:generic (body params &optional var-lines) "Expand BODY with PARAMS. -Expand a block of code with org-babel according to it's header +Expand a block of code with org-babel according to its header arguments. This generic implementation of body expansion is called for languages which have not defined their own specific org-babel-expand-body:lang function." @@ -518,19 +610,57 @@ arguments and pop open the results in a preview buffer." (interactive) ;; TODO: report malformed code block ;; TODO: report incompatible combinations of header arguments - (let ((too-close 2)) ;; <- control closeness to report potential match + ;; TODO: report uninitialized variables + (let ((too-close 2) ;; <- control closeness to report potential match + (names (mapcar #'symbol-name org-babel-header-arg-names))) (dolist (header (mapcar (lambda (arg) (substring (symbol-name (car arg)) 1)) (and (org-babel-where-is-src-block-head) (org-babel-parse-header-arguments (org-babel-clean-text-properties (match-string 4)))))) - (dolist (name (mapcar #'symbol-name org-babel-header-arg-names)) + (dolist (name names) (when (and (not (string= header name)) - (<= (org-babel-edit-distance header name) too-close)) + (<= (org-babel-edit-distance header name) too-close) + (not (member header names))) (error "supplied header \"%S\" is suspiciously close to \"%S\"" header name)))) (message "No suspicious header arguments found."))) +;;;###autoload +(defun org-babel-insert-header-arg () + "Insert a header argument selecting from lists of common args and values." + (interactive) + (let* ((lang (car (org-babel-get-src-block-info 'light))) + (lang-headers (intern (concat "org-babel-header-arg-names:" lang))) + (headers (append (if (boundp lang-headers) + (mapcar (lambda (h) (cons h :any)) + (eval lang-headers)) + nil) + org-babel-common-header-args-w-values)) + (arg (org-icompleting-read + "Header Arg: " + (mapcar + (lambda (header-spec) (symbol-name (car header-spec))) + headers)))) + (insert ":" arg) + (let ((vals (cdr (assoc (intern arg) headers)))) + (when vals + (insert + " " + (cond + ((eq vals :any) + (read-from-minibuffer "value: ")) + ((listp vals) + (mapconcat + (lambda (group) + (let ((arg (org-icompleting-read + "value: " + (cons "default" (mapcar #'symbol-name group))))) + (if (and arg (not (string= "default" arg))) + (concat arg " ") + ""))) + vals "")))))))) + ;;;###autoload (defun org-babel-load-in-session (&optional arg info) "Load the body of the current source-code block. @@ -624,6 +754,7 @@ Return t if a code block was found at point, nil otherwise." (if (org-bound-and-true-p org-edit-src-from-org-mode) (org-edit-src-exit))) t))) +(def-edebug-spec org-babel-do-in-edit-buffer (body)) (defun org-babel-do-key-sequence-in-edit-buffer (key) "Read key sequence and execute the command in edit buffer. @@ -720,6 +851,7 @@ end-body --------- point at the end of the body" (goto-char end-block)))) (unless visited-p (kill-buffer to-be-removed)) (goto-char point)))) +(def-edebug-spec org-babel-map-src-blocks (form body)) ;;;###autoload (defmacro org-babel-map-inline-src-blocks (file &rest body) @@ -742,6 +874,56 @@ buffer." (goto-char (match-end 0)))) (unless visited-p (kill-buffer to-be-removed)) (goto-char point)))) +(def-edebug-spec org-babel-map-inline-src-blocks (form body)) + +(defvar org-babel-lob-one-liner-regexp) +;;;###autoload +(defmacro org-babel-map-call-lines (file &rest body) + "Evaluate BODY forms on each call line in FILE. +If FILE is nil evaluate BODY forms on source blocks in current +buffer." + (declare (indent 1)) + (let ((tempvar (make-symbol "file"))) + `(let* ((,tempvar ,file) + (visited-p (or (null ,tempvar) + (get-file-buffer (expand-file-name ,tempvar)))) + (point (point)) to-be-removed) + (save-window-excursion + (when ,tempvar (find-file ,tempvar)) + (setq to-be-removed (current-buffer)) + (goto-char (point-min)) + (while (re-search-forward org-babel-lob-one-liner-regexp nil t) + (goto-char (match-beginning 1)) + (save-match-data ,@body) + (goto-char (match-end 0)))) + (unless visited-p (kill-buffer to-be-removed)) + (goto-char point)))) +(def-edebug-spec org-babel-map-call-lines (form body)) + +;;;###autoload +(defmacro org-babel-map-executables (file &rest body) + (declare (indent 1)) + (let ((tempvar (make-symbol "file")) + (rx (make-symbol "rx"))) + `(let* ((,tempvar ,file) + (,rx (concat "\\(" org-babel-src-block-regexp + "\\|" org-babel-inline-src-block-regexp + "\\|" org-babel-lob-one-liner-regexp "\\)")) + (visited-p (or (null ,tempvar) + (get-file-buffer (expand-file-name ,tempvar)))) + (point (point)) to-be-removed) + (save-window-excursion + (when ,tempvar (find-file ,tempvar)) + (setq to-be-removed (current-buffer)) + (goto-char (point-min)) + (while (re-search-forward ,rx nil t) + (goto-char (match-beginning 1)) + (when (looking-at org-babel-inline-src-block-regexp)(forward-char 1)) + (save-match-data ,@body) + (goto-char (match-end 0)))) + (unless visited-p (kill-buffer to-be-removed)) + (goto-char point)))) +(def-edebug-spec org-babel-map-executables (form body)) ;;;###autoload (defun org-babel-execute-buffer (&optional arg) @@ -751,10 +933,10 @@ the current buffer." (interactive "P") (org-babel-eval-wipe-error-buffer) (org-save-outline-visibility t - (org-babel-map-src-blocks nil - (org-babel-execute-src-block arg)) - (org-babel-map-inline-src-blocks nil - (org-babel-execute-src-block arg)))) + (org-babel-map-executables nil + (if (looking-at org-babel-lob-one-liner-regexp) + (org-babel-lob-execute-maybe) + (org-babel-execute-src-block arg))))) ;;;###autoload (defun org-babel-execute-subtree (&optional arg) @@ -783,7 +965,7 @@ the current subtree." lst) (norm (arg) (let ((v (if (and (listp (cdr arg)) (null (cddr arg))) - (copy-seq (cdr arg)) + (copy-sequence (cdr arg)) (cdr arg)))) (when (and v (not (and (sequencep v) (not (consp v)) @@ -899,8 +1081,11 @@ portions of results lines." (beginning-of-line) (if (re-search-forward org-babel-result-regexp nil t) (let ((start (progn (beginning-of-line 2) (- (point) 1))) - (end (progn (goto-char (- (org-babel-result-end) 1)) (point))) - ov) + (end (progn + (while (looking-at org-babel-multi-line-header-regexp) + (forward-line 1)) + (goto-char (- (org-babel-result-end) 1)) (point))) + ov) (if (memq t (mapcar (lambda (overlay) (eq (overlay-get overlay 'invisible) 'org-babel-hide-result)) @@ -943,40 +1128,21 @@ Return an association list of any source block params which may be specified in the properties of the current outline entry." (save-match-data (let (val sym) - (delq nil - (mapcar - (lambda (header-arg) - (and (setq val - (or (org-entry-get (point) header-arg t) - (org-entry-get (point) (concat ":" header-arg) t))) - (cons (intern (concat ":" header-arg)) - (org-babel-read val)))) + (org-babel-parse-multiple-vars + (delq nil (mapcar - 'symbol-name - (append - org-babel-header-arg-names - (progn - (setq sym (intern (concat "org-babel-header-arg-names:" lang))) - (and (boundp sym) (eval sym)))))))))) - -(defun org-babel-params-from-buffer () - "Retrieve per-buffer parameters. - Return an association list of any source block params which -may be specified in the current buffer." - (let (local-properties) - (save-match-data - (save-excursion - (save-restriction - (widen) - (goto-char (point-min)) - (while (re-search-forward - (org-make-options-regexp (list "BABEL" "PROPERTIES")) nil t) - (setq local-properties - (org-babel-merge-params - local-properties - (org-babel-parse-header-arguments - (org-match-string-no-properties 2))))) - local-properties))))) + (lambda (header-arg) + (and (setq val (org-entry-get (point) header-arg t)) + (cons (intern (concat ":" header-arg)) + (org-babel-read val)))) + (mapcar + 'symbol-name + (append + org-babel-header-arg-names + (progn + (setq sym (intern (concat "org-babel-header-arg-names:" + lang))) + (and (boundp sym) (eval sym))))))))))) (defvar org-src-preserve-indentation) (defun org-babel-parse-src-block-match () @@ -988,21 +1154,22 @@ may be specified in the current buffer." (body (org-babel-clean-text-properties (let* ((body (match-string 5)) (sub-length (- (length body) 1))) - (if (string= "\n" (substring body sub-length)) + (if (and (> sub-length 0) + (string= "\n" (substring body sub-length))) (substring body 0 sub-length) - body)))) + (or body ""))))) (preserve-indentation (or org-src-preserve-indentation - (string-match "-i\\>" switches)))) + (save-match-data + (string-match "-i\\>" switches))))) (list lang ;; get block body less properties, protective commas, and indentation (with-temp-buffer (save-match-data - (insert (org-babel-strip-protective-commas body)) + (insert (org-babel-strip-protective-commas body lang)) (unless preserve-indentation (org-do-remove-indentation)) (buffer-string))) (org-babel-merge-params org-babel-default-header-args - (org-babel-params-from-buffer) (org-babel-params-from-properties lang) (if (boundp lang-headers) (eval lang-headers) nil) (org-babel-parse-header-arguments @@ -1016,53 +1183,107 @@ may be specified in the current buffer." (lang-headers (intern (concat "org-babel-default-header-args:" lang)))) (list lang (org-babel-strip-protective-commas - (org-babel-clean-text-properties (match-string 5))) + (org-babel-clean-text-properties (match-string 5)) lang) (org-babel-merge-params org-babel-default-inline-header-args - (org-babel-params-from-buffer) (org-babel-params-from-properties lang) (if (boundp lang-headers) (eval lang-headers) nil) (org-babel-parse-header-arguments (org-babel-clean-text-properties (or (match-string 4) ""))))))) +(defun org-babel-balanced-split (string alts) + "Split STRING on instances of ALTS. +ALTS is a cons of two character options where each option may be +either the numeric code of a single character or a list of +character alternatives. For example to split on balanced +instances of \"[ \t]:\" set ALTS to '((32 9) . 58)." + (flet ((matches (ch spec) (if (listp spec) (member ch spec) (equal spec ch))) + (matched (ch last) + (if (consp alts) + (and (matches ch (cdr alts)) + (matches last (car alts))) + (matches ch alts)))) + (let ((balance 0) (quote nil) (partial nil) (lst nil) (last 0)) + (mapc (lambda (ch) ; split on [], (), "" balanced instances of [ \t]: + (setq balance (+ balance + (cond ((or (equal 91 ch) (equal 40 ch)) 1) + ((or (equal 93 ch) (equal 41 ch)) -1) + (t 0)))) + (when (and (equal 34 ch) (not (equal 92 last))) + (setq quote (not quote))) + (setq partial (cons ch partial)) + (when (and (= balance 0) (not quote) (matched ch last)) + (setq lst (cons (apply #'string (nreverse + (if (consp alts) + (cddr partial) + (cdr partial)))) + lst)) + (setq partial nil)) + (setq last ch)) + (string-to-list string)) + (nreverse (cons (apply #'string (nreverse partial)) lst))))) + +(defun org-babel-join-splits-near-ch (ch list) + "Join splits where \"=\" is on either end of the split." + (flet ((last= (str) (= ch (aref str (1- (length str))))) + (first= (str) (= ch (aref str 0)))) + (reverse + (org-reduce (lambda (acc el) + (let ((head (car acc))) + (if (and head (or (last= head) (first= el))) + (cons (concat head el) (cdr acc)) + (cons el acc)))) + list :initial-value nil)))) + (defun org-babel-parse-header-arguments (arg-string) "Parse a string of header arguments returning an alist." (when (> (length arg-string) 0) - (delq nil - (mapcar - (lambda (arg) - (if (string-match - "\\([^ \f\t\n\r\v]+\\)[ \f\t\n\r\v]+\\([^ \f\t\n\r\v]+.*\\)" - arg) - (cons (intern (match-string 1 arg)) - (org-babel-read (org-babel-chomp (match-string 2 arg)))) - (cons (intern (org-babel-chomp arg)) nil))) - (let ((balance 0) (partial nil) (lst nil) (last 0)) - (mapc (lambda (ch) ; split on [] balanced instances of [ \t]: - (setq balance (+ balance - (cond ((equal 91 ch) 1) - ((equal 93 ch) -1) - (t 0)))) - (setq partial (cons ch partial)) - (when (and (= ch 58) (= balance 0) - (or (= last 32) (= last 9))) - (setq lst (cons (apply #'string (nreverse (cddr partial))) - lst)) - (setq partial (list ch))) - (setq last ch)) - (string-to-list arg-string)) - (nreverse (cons (apply #'string (nreverse partial)) lst))))))) + (org-babel-parse-multiple-vars + (delq nil + (mapcar + (lambda (arg) + (if (string-match + "\\([^ \f\t\n\r\v]+\\)[ \f\t\n\r\v]+\\([^ \f\t\n\r\v]+.*\\)" + arg) + (cons (intern (match-string 1 arg)) + (org-babel-read (org-babel-chomp (match-string 2 arg)))) + (cons (intern (org-babel-chomp arg)) nil))) + ((lambda (raw) + (cons (car raw) (mapcar (lambda (r) (concat ":" r)) (cdr raw)))) + (org-babel-balanced-split arg-string '((32 9) . 58)))))))) + +(defun org-babel-parse-multiple-vars (header-arguments) + "Expand multiple variable assignments behind a single :var keyword. + +This allows expression of multiple variables with one :var as +shown below. + +#+PROPERTY: var foo=1, bar=2" + (let (results) + (mapc (lambda (pair) + (if (eq (car pair) :var) + (mapcar (lambda (v) (push (cons :var (org-babel-trim v)) results)) + (org-babel-join-splits-near-ch + 61 (org-babel-balanced-split (cdr pair) 32))) + (push pair results))) + header-arguments) + (nreverse results))) (defun org-babel-process-params (params) "Expand variables in PARAMS and add summary parameters." - (let* ((vars-and-names (org-babel-disassemble-tables - (mapcar (lambda (el) - (if (consp (cdr el)) - (cdr el) (org-babel-ref-parse (cdr el)))) - (org-babel-get-header params :var)) - (cdr (assoc :hlines params)) - (cdr (assoc :colnames params)) - (cdr (assoc :rownames params)))) + (let* ((processed-vars (mapcar (lambda (el) + (if (consp (cdr el)) + (cdr el) + (org-babel-ref-parse (cdr el)))) + (org-babel-get-header params :var))) + (vars-and-names (if (and (assoc :colname-names params) + (assoc :rowname-names params)) + (list processed-vars) + (org-babel-disassemble-tables + processed-vars + (cdr (assoc :hlines params)) + (cdr (assoc :colnames params)) + (cdr (assoc :rownames params))))) (raw-result (or (cdr (assoc :results params)) "")) (result-params (append (split-string (if (stringp raw-result) @@ -1169,7 +1390,7 @@ of the vars, cnames and rnames." (setq var (cons (car var) (org-babel-del-hlines (cdr var)))))) var) vars) - cnames rnames))) + (reverse cnames) (reverse rnames)))) (defun org-babel-reassemble-table (table colnames rownames) "Add column and row names to a table. @@ -1244,7 +1465,7 @@ org-babel-named-src-block-regexp." (regexp (org-babel-named-src-block-regexp-for-name name)) msg) (goto-char (point-min)) (when (or (re-search-forward regexp nil t) - (re-search-backward regexp nil t)) + (re-search-backward regexp nil t)) (match-beginning 0))))) (defun org-babel-src-block-names (&optional file) @@ -1253,7 +1474,7 @@ org-babel-named-src-block-regexp." (when file (find-file file)) (goto-char (point-min)) (let (names) (while (re-search-forward org-babel-src-name-w-name-regexp nil t) - (setq names (cons (match-string 4) names))) + (setq names (cons (match-string 3) names))) names))) ;;;###autoload @@ -1269,16 +1490,22 @@ org-babel-named-src-block-regexp." (progn (goto-char point) (org-show-context)) (message "result '%s' not found in this buffer" name)))) -(defun org-babel-find-named-result (name) +(defun org-babel-find-named-result (name &optional point) "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)) - (when (re-search-forward - (concat org-babel-result-regexp - "[ \t]" (regexp-quote name) "[ \t\n\f\v\r]") nil t) - (beginning-of-line 0) (point)))) + (goto-char (or point (point-min))) + (catch 'is-a-code-block + (when (re-search-forward + (concat org-babel-result-regexp + "[ \t]" (regexp-quote name) "[ \t]*[\n\f\v\r]") nil t) + (when (and (string= "name" (downcase (match-string 1))) + (or (beginning-of-line 1) + (looking-at org-babel-src-block-regexp) + (looking-at org-babel-multi-line-header-regexp))) + (throw 'is-a-code-block (org-babel-find-named-result name (point)))) + (beginning-of-line 0) (point))))) (defun org-babel-result-names (&optional file) "Returns the names of results in FILE or the current buffer." @@ -1333,6 +1560,8 @@ is created. In both cases if the region is demarcated and if the region is not active then the point is demarcated." (interactive "P") (let ((info (org-babel-get-src-block-info 'light)) + (headers (progn (org-babel-where-is-src-block-head) + (match-string 4))) (stars (concat (make-string (or (org-current-level) 1) ?*) " "))) (if info (mapc @@ -1345,11 +1574,16 @@ region is not active then the point is demarcated." (buffer-substring (point-at-bol) (point-at-eol))) (delete-region (point-at-bol) (point-at-eol))) - (insert (concat (if (looking-at "^") "" "\n") - indent "#+end_src\n" - (if arg stars indent) "\n" - indent "#+begin_src " lang - (if (looking-at "[\n\r]") "" "\n"))))) + (insert (concat + (if (looking-at "^") "" "\n") + indent "#+end_src\n" + (if arg stars indent) "\n" + indent "#+begin_src " lang + (if (> (length headers) 1) + (concat " " headers) headers) + (if (looking-at "[\n\r]") + "" + (concat "\n" (make-string (current-column) ? ))))))) (move-end-of-line 2)) (sort (if (region-active-p) (list (mark) (point)) (list (point))) #'>)) (let ((start (point)) @@ -1368,7 +1602,6 @@ region is not active then the point is demarcated." (goto-char start) (move-end-of-line 1))))) (defvar org-babel-lob-one-liner-regexp) -(defvar org-babel-inline-lob-one-liner-regexp) (defun org-babel-where-is-src-block-result (&optional insert info hash indent) "Find where the current source block results begin. Return the point at the beginning of the result of the current @@ -1379,13 +1612,11 @@ following the source block." (let* ((on-lob-line (save-excursion (beginning-of-line 1) (looking-at org-babel-lob-one-liner-regexp))) - (inlinep (save-excursion - (re-search-backward "[ \f\t\n\r\v]" nil t) - (when (looking-at org-babel-inline-src-block-regexp) - (match-end 0)))) + (inlinep (when (org-babel-get-inline-src-block-matches) + (match-end 0))) (name (if on-lob-line - (nth 0 (org-babel-lob-get-info)) - (nth 4 (or info (org-babel-get-src-block-info))))) + (mapconcat #'identity (butlast (org-babel-lob-get-info)) "") + (nth 4 (or info (org-babel-get-src-block-info 'light))))) (head (unless on-lob-line (org-babel-where-is-src-block-head))) found beg end) (when head (goto-char head)) @@ -1437,7 +1668,7 @@ following the source block." (lambda (el) " ") (org-number-sequence 1 indent) "") "") - "#+results" + "#+" org-babel-results-keyword (when hash (concat "["hash"]")) ":" (when name (concat " " name)) "\n")) @@ -1537,6 +1768,10 @@ raw ----- results are added directly to the Org-mode file. This is a good option if you code block will output org-mode formatted text. +wrap ---- results are added directly to the Org-mode file as with + \"raw\", but are wrapped in a RESULTS drawer, allowing + them to later be replaced or removed automatically. + org ----- similar in effect to raw, only the results are wrapped in an org code block. Similar to the raw option, on export the results will be interpreted as org-formatted @@ -1570,10 +1805,8 @@ code ---- the results are extracted in the syntax of the source (save-excursion (let* ((inlinep (save-excursion - (or (= (point) (point-at-bol)) - (re-search-backward "[ \f\t\n\r\v]" nil t)) - (when (or (looking-at org-babel-inline-src-block-regexp) - (looking-at org-babel-inline-lob-one-liner-regexp)) + (when (or (org-babel-get-inline-src-block-matches) + (org-babel-get-lob-one-liner-matches)) (goto-char (match-end 0)) (insert (if (listp result) "\n" " ")) (point)))) @@ -1605,41 +1838,46 @@ code ---- the results are extracted in the syntax of the source ((member "prepend" result-params)))) ; already there (setq results-switches (if results-switches (concat " " results-switches) "")) - ;; insert results based on type - (cond - ;; do nothing for an empty result - ((= (length result) 0)) - ;; insert a list if preferred - ((member "list" result-params) - (insert - (org-babel-trim - (org-list-to-generic - (cons 'unordered - (mapcar - (lambda (el) (list nil (if (stringp el) el (format "%S" el)))) - (if (listp result) result (list result)))) - '(:splicep nil :istart "- " :iend "\n"))) - "\n")) - ;; assume the result is a table if it's not a string - ((not (stringp result)) - (goto-char beg) - (insert (concat (orgtbl-to-orgtbl - (if (or (eq 'hline (car result)) - (and (listp (car result)) - (listp (cdr (car result))))) - result (list result)) - '(:fmt (lambda (cell) (format "%s" cell)))) "\n")) - (goto-char beg) (when (org-at-table-p) (org-table-align))) - ((member "file" result-params) - (insert result)) - (t (goto-char beg) (insert result))) - (when (listp result) (goto-char (org-table-end))) - (setq end (point-marker)) - ;; possibly wrap result (flet ((wrap (start finish) - (goto-char beg) (insert (concat start "\n")) (goto-char end) (insert (concat finish "\n")) - (setq end (point-marker)))) + (goto-char beg) (insert (concat start "\n")) + (goto-char end) (goto-char (point-at-eol)) + (setq end (point-marker))) + (proper-list-p (it) (and (listp it) (null (cdr (last it)))))) + ;; insert results based on type + (cond + ;; do nothing for an empty result + ((null result)) + ;; insert a list if preferred + ((member "list" result-params) + (insert + (org-babel-trim + (org-list-to-generic + (cons 'unordered + (mapcar + (lambda (el) (list nil (if (stringp el) el (format "%S" el)))) + (if (listp result) result (list result)))) + '(:splicep nil :istart "- " :iend "\n"))) + "\n")) + ;; assume the result is a table if it's not a string + ((proper-list-p result) + (goto-char beg) + (insert (concat (orgtbl-to-orgtbl + (if (or (eq 'hline (car result)) + (and (listp (car result)) + (listp (cdr (car result))))) + result (list result)) + '(:fmt (lambda (cell) (format "%s" cell)))) "\n")) + (goto-char beg) (when (org-at-table-p) (org-table-align))) + ((and (listp result) (not (proper-list-p result))) + (insert (format "%s\n" result))) + ((member "file" result-params) + (when inlinep (goto-char inlinep)) + (insert result)) + (t (goto-char beg) (insert result))) + (when (proper-list-p result) (goto-char (org-table-end))) + (setq end (point-marker)) + ;; possibly wrap result (cond ((member "html" result-params) (wrap "#+BEGIN_HTML" "#+END_HTML")) @@ -1653,10 +1891,9 @@ code ---- the results are extracted in the syntax of the source ((member "raw" result-params) (goto-char beg) (if (org-at-table-p) (org-cycle))) ((member "wrap" result-params) - (when (and (stringp result) (not (member "file" result-params))) - (org-babel-examplize-region beg end results-switches)) - (wrap "#+BEGIN_RESULT" "#+END_RESULT")) - ((and (stringp result) (not (member "file" result-params))) + (wrap ":RESULTS:" ":END:")) + ((and (not (proper-list-p result)) + (not (member "file" result-params))) (org-babel-examplize-region beg end results-switches) (setq end (point))))) ;; possibly indent the results to match the #+results line @@ -1665,7 +1902,7 @@ code ---- the results are extracted in the syntax of the source (not (and (listp result) (member "append" result-params)))) (indent-rigidly beg end indent)))) - (if (= (length result) 0) + (if (null result) (if (member "value" result-params) (message "Code block returned no value.") (message "Code block produced no output.")) @@ -1676,8 +1913,9 @@ code ---- the results are extracted in the syntax of the source (interactive) (let ((location (org-babel-where-is-src-block-result nil info)) start) (when location + (setq start (- location 1)) (save-excursion - (goto-char location) (setq start (point)) (forward-line 1) + (goto-char location) (forward-line 1) (delete-region start (org-babel-result-end)))))) (defun org-babel-result-end () @@ -1688,6 +1926,9 @@ code ---- the results are extracted in the syntax of the source ((org-at-item-p) (let* ((struct (org-list-struct)) (prvs (org-list-prevs-alist struct))) (org-list-get-list-end (point-at-bol) struct prvs))) + ((looking-at "^\\([ \t]*\\):RESULTS:") + (progn (re-search-forward (concat "^" (match-string 1) ":END:")) + (forward-char 1) (point))) (t (let ((case-fold-search t) (blocks-re (regexp-opt @@ -1717,10 +1958,16 @@ file's directory then expand relative links." (stringp (car result)) (stringp (cadr result))) (format "[[file:%s][%s]]" (car result) (cadr result)))))) +(defvar org-babel-capitalize-examplize-region-markers nil + "Make true to capitalize begin/end example markers inserted by code blocks.") + (defun org-babel-examplize-region (beg end &optional results-switches) "Comment out region using the inline '==' or ': ' org example quote." (interactive "*r") - (flet ((chars-between (b e) (string-match "[\\S]" (buffer-substring b e)))) + (flet ((chars-between (b e) + (not (string-match "^[\\s]*$" (buffer-substring b e)))) + (maybe-cap (str) (if org-babel-capitalize-examplize-region-markers + (upcase str) str))) (if (or (chars-between (save-excursion (goto-char beg) (point-at-bol)) beg) (chars-between end (save-excursion (goto-char end) (point-at-eol)))) (save-excursion @@ -1737,10 +1984,12 @@ file's directory then expand relative links." (t (goto-char beg) (insert (if results-switches - (format "#+begin_example%s\n" results-switches) - "#+begin_example\n")) + (format "%s%s\n" + (maybe-cap "#+begin_example") + results-switches) + (maybe-cap "#+begin_example\n"))) (if (markerp end) (goto-char end) (forward-char (- end beg))) - (insert "#+end_example\n")))))))) + (insert (maybe-cap "#+end_example\n"))))))))) (defun org-babel-update-block-body (new-body) "Update the body of the current code block to NEW-BODY." @@ -1756,12 +2005,11 @@ Later elements of PLISTS override the values of previous elements. This takes into account some special considerations for certain parameters when merging lists." (let ((results-exclusive-groups - '(("file" "list" "vector" "table" "scalar" "verbatim" "raw" "org" - "html" "latex" "code" "pp" "wrap") - ("replace" "silent" "append" "prepend") - ("output" "value"))) + (mapcar (lambda (group) (mapcar #'symbol-name group)) + (cdr (assoc 'results org-babel-common-header-args-w-values)))) (exports-exclusive-groups - '(("code" "results" "both" "none"))) + (mapcar (lambda (group) (mapcar #'symbol-name group)) + (cdr (assoc 'exports org-babel-common-header-args-w-values)))) (variable-index 0) params results exports tangle noweb cache vars shebang comments padline) (flet ((e-merge (exclusive-groups &rest result-params) @@ -1805,12 +2053,16 @@ parameters when merging lists." vars)) vars) (list (cons name pair)))) - ;; if no name is given, then assign to variables in order - (prog1 (setf (cddr (nth variable-index vars)) - (concat (symbol-name - (car (nth variable-index vars))) - "=" (cdr pair))) - (incf variable-index))))) + ;; if no name is given and we already have named variables + ;; then assign to named variables in order + (if (and vars (nth variable-index vars)) + (prog1 (setf (cddr (nth variable-index vars)) + (concat (symbol-name + (car (nth variable-index vars))) + "=" (cdr pair))) + (incf variable-index)) + (error "variable \"%s\" must be assigned a default value" + (cdr pair)))))) (:results (setq results (e-merge results-exclusive-groups results @@ -1860,6 +2112,12 @@ parameters when merging lists." '(results exports tangle noweb padline cache shebang comments)) params)) +(defvar *org-babel-use-quick-and-dirty-noweb-expansion* nil + "Set to true to use regular expressions to expand noweb references. +This results in much faster noweb reference expansion but does +not properly allow code blocks to inherit the \":noweb-ref\" +header argument from buffer or subtree wide properties.") + (defun org-babel-expand-noweb-references (&optional info parent-buffer) "Expand Noweb references in the body of the current source code block. @@ -1895,25 +2153,20 @@ block but are passed literally to the \"example-block\"." (lang (nth 0 info)) (body (nth 1 info)) (comment (string= "noweb" (cdr (assoc :comments (nth 2 info))))) + (rx-prefix (concat "\\(" org-babel-src-name-regexp "\\|" + ":noweb-ref[ \t]+" "\\)")) (new-body "") index source-name evaluate prefix blocks-in-buffer) (flet ((nb-add (text) (setq new-body (concat new-body text))) (c-wrap (text) (with-temp-buffer (funcall (intern (concat lang "-mode"))) (comment-region (point) (progn (insert text) (point))) - (org-babel-trim (buffer-string)))) - (blocks () ;; return the info lists of all blocks in this buffer - (let (infos) - (save-restriction - (widen) - (org-babel-map-src-blocks nil - (setq infos (cons (org-babel-get-src-block-info 'light) - infos)))) - (reverse infos)))) + (org-babel-trim (buffer-string))))) (with-temp-buffer (insert body) (goto-char (point-min)) (setq index (point)) - (while (and (re-search-forward "<<\\(.+?\\)>>" nil t)) + (while (and (re-search-forward "<<\\([^ \t\n].+?[^ \t\n]\\|[^ \t\n]\\)>>" + nil t)) (save-match-data (setf source-name (match-string 1))) (save-match-data (setq evaluate (string-match "\(.*\)" source-name))) (save-match-data @@ -1928,6 +2181,8 @@ block but are passed literally to the \"example-block\"." (setq index (point)) (nb-add (with-current-buffer parent-buffer + (save-restriction + (widen) (mapconcat ;; interpose PREFIX between every line #'identity (split-string @@ -1943,21 +2198,43 @@ block but are passed literally to the \"example-block\"." (when (org-babel-ref-goto-headline-id source-name) (org-babel-ref-headline-body))) ;; find the expansion of reference in this buffer - (mapconcat - (lambda (i) - (when (string= source-name - (or (cdr (assoc :noweb-ref (nth 2 i))) - (nth 4 i))) - (let ((body (org-babel-expand-noweb-references i))) - (if comment - ((lambda (cs) - (concat (c-wrap (car cs)) "\n" - body "\n" (c-wrap (cadr cs)))) - (org-babel-tangle-comment-links i)) - body)))) - (or blocks-in-buffer - (setq blocks-in-buffer (blocks))) - "") + (let ((rx (concat rx-prefix source-name "[ \t\n]")) + expansion) + (save-excursion + (goto-char (point-min)) + (if *org-babel-use-quick-and-dirty-noweb-expansion* + (while (re-search-forward rx nil t) + (let* ((i (org-babel-get-src-block-info 'light)) + (body (org-babel-expand-noweb-references i)) + (sep (or (cdr (assoc :noweb-sep (nth 2 i))) + "\n")) + (full (if comment + ((lambda (cs) + (concat (c-wrap (car cs)) "\n" + body "\n" + (c-wrap (cadr cs)))) + (org-babel-tangle-comment-links i)) + body))) + (setq expansion (cons sep (cons full expansion))))) + (org-babel-map-src-blocks nil + (let ((i (org-babel-get-src-block-info 'light))) + (when (equal (or (cdr (assoc :noweb-ref (nth 2 i))) + (nth 4 i)) + source-name) + (let* ((body (org-babel-expand-noweb-references i)) + (sep (or (cdr (assoc :noweb-sep (nth 2 i))) + "\n")) + (full (if comment + ((lambda (cs) + (concat (c-wrap (car cs)) "\n" + body "\n" + (c-wrap (cadr cs)))) + (org-babel-tangle-comment-links i)) + body))) + (setq expansion + (cons sep (cons full expansion))))))))) + (and expansion + (mapconcat #'identity (nreverse (cdr expansion)) ""))) ;; possibly raise an error if named block doesn't exist (if (member lang org-babel-noweb-error-langs) (error "%s" (concat @@ -1965,7 +2242,7 @@ block but are passed literally to the \"example-block\"." "could not be resolved (see " "`org-babel-noweb-error-langs')")) ""))) - "[\n\r]") (concat "\n" prefix))))) + "[\n\r]") (concat "\n" prefix)))))) (nb-add (buffer-substring index (point-max))))) new-body)) @@ -1974,10 +2251,16 @@ block but are passed literally to the \"example-block\"." (when text (set-text-properties 0 (length text) nil text) text)) -(defun org-babel-strip-protective-commas (body) +(defun org-babel-strip-protective-commas (body &optional lang) "Strip protective commas from bodies of source blocks." - (when body - (replace-regexp-in-string "^,#" "#" body))) + (with-temp-buffer + (insert body) + (if (and lang (string= lang "org")) + (progn (goto-char (point-min)) + (while (re-search-forward "^[ \t]*\\(,\\)" nil t) + (replace-match "" nil nil nil 1))) + (org-strip-protective-commas (point-min) (point-max))) + (buffer-string))) (defun org-babel-script-escape (str &optional force) "Safely convert tables into elisp lists." @@ -2044,7 +2327,7 @@ appropriate." cell)) (defun org-babel-number-p (string) - "If STRING represents a number return it's value." + "If STRING represents a number return its value." (if (and (string-match "^-?[0-9]*\\.?[0-9]*$" string) (= (length (substring string (match-beginning 0) (match-end 0)))