@@ -2844,6 +2844,10 @@ CONTEXT may be one of :tangle, :export or :eval."
28442844 (cl-some (lambda (v ) (member v allowed-values))
28452845 (split-string (or (cdr (assq :noweb params)) " " )))))
28462846
2847+ (defvar org-babel-expand-noweb-references--cache nil
2848+ " Noweb reference cache used during expansion." )
2849+ (defvar org-babel-expand-noweb-references--cache-buffer nil
2850+ " Cons of (buffer . modified-tick) cached by `org-babel-expand-noweb-references--cache' ." )
28472851(defun org-babel-expand-noweb-references (&optional info parent-buffer )
28482852 " Expand Noweb references in the body of the current source code block.
28492853
@@ -2885,106 +2889,131 @@ block but are passed literally to the \"example-block\"."
28852889 (not (equal (cdr v) " no" ))))))
28862890 (noweb-re (format " \\ (.*?\\ )\\ (%s \\ )"
28872891 (with-current-buffer parent-buffer
2888- (org-babel-noweb-wrap ))))
2889- (cache nil )
2890- (c-wrap
2891- (lambda (s )
2892- ; ; Comment string S, according to LANG mode. Return new
2893- ; ; string.
2894- (unless org-babel-tangle-uncomment-comments
2895- (with-temp-buffer
2896- (funcall (org-src-get-lang-mode lang))
2897- (comment-region (point )
2898- (progn (insert s) (point )))
2899- (org-trim (buffer-string ))))))
2900- (expand-body
2901- (lambda (i )
2902- ; ; Expand body of code represented by block info I.
2903- (let ((b (if (org-babel-noweb-p (nth 2 i) :eval )
2904- (org-babel-expand-noweb-references i)
2905- (nth 1 i))))
2906- (if (not comment) b
2907- (let ((cs (org-babel-tangle-comment-links i)))
2908- (concat (funcall c-wrap (car cs)) " \n "
2909- b " \n "
2910- (funcall c-wrap (cadr cs))))))))
2911- (expand-references
2912- (lambda (ref cache )
2913- (pcase (gethash ref cache)
2914- (`(, last . , previous )
2915- ; ; Ignore separator for last block.
2916- (let ((strings (list (funcall expand-body last ))))
2917- (dolist (i previous)
2918- (let ((parameters (nth 2 i)))
2919- ; ; Since we're operating in reverse order, first
2920- ; ; push separator, then body.
2921- (push (or (cdr (assq :noweb-sep parameters)) " \n " )
2922- strings)
2923- (push (funcall expand-body i) strings)))
2924- (mapconcat #'identity strings " " )))
2925- ; ; Raise an error about missing reference, or return the
2926- ; ; empty string.
2927- ((guard (or org-babel-noweb-error-all-langs
2928- (member lang org-babel-noweb-error-langs)))
2929- (error " Cannot resolve %s (see `org-babel-noweb-error-langs' ) "
2930- (org-babel-noweb-wrap ref)))
2931- (_ " " )))))
2932- (replace-regexp-in-string
2933- noweb-re
2934- (lambda (m )
2935- (with-current-buffer parent-buffer
2936- (save-match-data
2937- (let* ((prefix (match-string 1 m))
2938- (id (match-string 3 m))
2939- (evaluate (string-match-p " (.*)" id))
2940- (expansion
2941- (cond
2942- (evaluate
2943- ; ; Evaluation can potentially modify the buffer
2944- ; ; and invalidate the cache: reset it.
2945- (setq cache nil )
2946- (let ((raw (org-babel-ref-resolve id)))
2947- (if (stringp raw) raw (format " %S " raw))))
2948- ; ; Return the contents of headlines literally.
2949- ((org-babel-ref-goto-headline-id id)
2950- (org-babel-ref-headline-body ))
2951- ; ; Look for a source block named SOURCE-NAME. If
2952- ; ; found, assume it is unique; do not look after
2953- ; ; `:noweb-ref' header argument.
2954- ((org-with-point-at 1
2955- (let ((r (org-babel-named-src-block-regexp-for-name id)))
2956- (and (re-search-forward r nil t )
2957- (not (org-in-commented-heading-p))
2958- (funcall expand-body
2959- (org-babel-get-src-block-info t ))))))
2960- ; ; Retrieve from the Library of Babel.
2961- ((nth 2 (assoc-string id org-babel-library-of-babel)))
2962- ; ; All Noweb references were cached in a previous
2963- ; ; run. Extract the information from the cache.
2964- ((hash-table-p cache)
2965- (funcall expand-references id cache))
2966- ; ; Though luck. We go into the long process of
2967- ; ; checking each source block and expand those
2968- ; ; with a matching Noweb reference. Since we're
2969- ; ; going to visit all source blocks in the
2970- ; ; document, cache information about them as well.
2971- (t
2972- (setq cache (make-hash-table :test #'equal ))
2973- (org-with-wide-buffer
2974- (org-babel-map-src-blocks nil
2975- (if (org-in-commented-heading-p)
2976- (org-forward-heading-same-level nil t )
2977- (let* ((info (org-babel-get-src-block-info t ))
2978- (ref (cdr (assq :noweb-ref (nth 2 info)))))
2979- (push info (gethash ref cache))))))
2980- (funcall expand-references id cache)))))
2981- ; ; Interpose PREFIX between every line.
2982- (if noweb-prefix
2983- (mapconcat #'identity
2984- (split-string expansion " [\n\r ]" )
2985- (concat " \n " prefix))
2986- expansion)))))
2987- body t t 2 )))
2892+ (org-babel-noweb-wrap )))))
2893+ (unless (equal (cons parent-buffer
2894+ (with-current-buffer parent-buffer
2895+ (buffer-chars-modified-tick )))
2896+ org-babel-expand-noweb-references--cache-buffer)
2897+ (setq org-babel-expand-noweb-references--cache nil
2898+ org-babel-expand-noweb-references--cache-buffer
2899+ (cons parent-buffer
2900+ (with-current-buffer parent-buffer
2901+ (buffer-chars-modified-tick )))))
2902+ (cl-macrolet ((c-wrap
2903+ (s)
2904+ ; ; Comment string S, according to LANG mode. Return new
2905+ ; ; string.
2906+ `(unless org-babel-tangle-uncomment-comments
2907+ (with-temp-buffer
2908+ (funcall (org-src-get-lang-mode lang))
2909+ (comment-region (point )
2910+ (progn (insert , s ) (point )))
2911+ (org-trim (buffer-string )))))
2912+ (expand-body
2913+ (i)
2914+ ; ; Expand body of code represented by block info I.
2915+ `(let ((b (if (org-babel-noweb-p (nth 2 , i ) :eval )
2916+ (org-babel-expand-noweb-references , i )
2917+ (nth 1 , i ))))
2918+ (if (not comment) b
2919+ (let ((cs (org-babel-tangle-comment-links , i )))
2920+ (concat (c-wrap (car cs)) " \n "
2921+ b " \n "
2922+ (c-wrap (cadr cs)))))))
2923+ (expand-references
2924+ (ref)
2925+ `(pcase (gethash , ref org-babel-expand-noweb-references--cache)
2926+ (`(, last . , previous )
2927+ ; ; Ignore separator for last block.
2928+ (let ((strings (list (expand-body last ))))
2929+ (dolist (i previous)
2930+ (let ((parameters (nth 2 i)))
2931+ ; ; Since we're operating in reverse order, first
2932+ ; ; push separator, then body.
2933+ (push (or (cdr (assq :noweb-sep parameters)) " \n " )
2934+ strings)
2935+ (push (expand-body i) strings)))
2936+ (mapconcat #'identity strings " " )))
2937+ ; ; Raise an error about missing reference, or return the
2938+ ; ; empty string.
2939+ ((guard (or org-babel-noweb-error-all-langs
2940+ (member lang org-babel-noweb-error-langs)))
2941+ (error " Cannot resolve %s (see `org-babel-noweb-error-langs' ) "
2942+ (org-babel-noweb-wrap , ref )))
2943+ (_ " " ))))
2944+ (replace-regexp-in-string
2945+ noweb-re
2946+ (lambda (m )
2947+ (with-current-buffer parent-buffer
2948+ (save-match-data
2949+ (let* ((prefix (match-string 1 m))
2950+ (id (match-string 3 m))
2951+ (evaluate (string-match-p " (.*)" id))
2952+ (expansion
2953+ (cond
2954+ (evaluate
2955+ (prog1
2956+ (let ((raw (org-babel-ref-resolve id)))
2957+ (if (stringp raw) raw (format " %S " raw)))
2958+ ; ; Evaluation can potentially modify the buffer
2959+ ; ; and invalidate the cache: reset it.
2960+ (unless (equal org-babel-expand-noweb-references--cache-buffer
2961+ (cons parent-buffer
2962+ (buffer-chars-modified-tick )))
2963+ (setq org-babel-expand-noweb-references--cache nil
2964+ org-babel-expand-noweb-references--cache-buffer
2965+ (cons parent-buffer
2966+ (with-current-buffer parent-buffer
2967+ (buffer-chars-modified-tick )))))))
2968+ ; ; Already cached.
2969+ ((and (hash-table-p org-babel-expand-noweb-references--cache)
2970+ (gethash id org-babel-expand-noweb-references--cache))
2971+ (expand-references id))
2972+ ; ; Return the contents of headlines literally.
2973+ ((org-babel-ref-goto-headline-id id)
2974+ (org-babel-ref-headline-body ))
2975+ ; ; Look for a source block named SOURCE-NAME. If
2976+ ; ; found, assume it is unique; do not look after
2977+ ; ; `:noweb-ref' header argument.
2978+ ((org-with-point-at 1
2979+ (let ((r (org-babel-named-src-block-regexp-for-name id)))
2980+ (and (re-search-forward r nil t )
2981+ (not (org-in-commented-heading-p))
2982+ (let ((info (org-babel-get-src-block-info t )))
2983+ (unless (hash-table-p org-babel-expand-noweb-references--cache)
2984+ (setq org-babel-expand-noweb-references--cache (make-hash-table :test #'equal )))
2985+ (push info (gethash id org-babel-expand-noweb-references--cache))
2986+ (expand-body info))))))
2987+ ; ; Retrieve from the Library of Babel.
2988+ ((nth 2 (assoc-string id org-babel-library-of-babel)))
2989+ ; ; All Noweb references were cached in a previous
2990+ ; ; run. Yet, ID is not in cache (see the above
2991+ ; ; condition). Process missing reference in
2992+ ; ; `expand-references' .
2993+ ((hash-table-p org-babel-expand-noweb-references--cache)
2994+ (expand-references id))
2995+ ; ; Though luck. We go into the long process of
2996+ ; ; checking each source block and expand those
2997+ ; ; with a matching Noweb reference. Since we're
2998+ ; ; going to visit all source blocks in the
2999+ ; ; document, cache information about them as well.
3000+ (t
3001+ (setq org-babel-expand-noweb-references--cache (make-hash-table :test #'equal ))
3002+ (org-with-wide-buffer
3003+ (org-babel-map-src-blocks nil
3004+ (if (org-in-commented-heading-p)
3005+ (org-forward-heading-same-level nil t )
3006+ (let* ((info (org-babel-get-src-block-info t ))
3007+ (ref (cdr (assq :noweb-ref (nth 2 info)))))
3008+ (push info (gethash ref org-babel-expand-noweb-references--cache))))))
3009+ (expand-references id)))))
3010+ ; ; Interpose PREFIX between every line.
3011+ (if noweb-prefix
3012+ (mapconcat #'identity
3013+ (split-string expansion " [\n\r ]" )
3014+ (concat " \n " prefix))
3015+ expansion)))))
3016+ body t t 2 ))))
29883017
29893018(defun org-babel--script-escape-inner (str )
29903019 (let (in-single in-double backslash out)
0 commit comments