Thanks for your suggestion. Or, return bare only if :tangle has multiple targets in `org-babel-tangle--unbracketed-link', then we can leave everything else unchanged. please find the updated patch attached. Let me know your thoughts.
llcc On Fri, Mar 21, 2025 at 2:48 AM Ihor Radchenko <yanta...@posteo.net> wrote: > > Lei Zhe <lzhe...@gmail.com> writes: > > > Then, for the returned value `result' of org-babel-tangle-single-block: > > The original function returns (file-name1 ... link1 ...). > > The proposed function returns ((file-name1 ... link1 ...)). > > > > If we change the function behavior, the difference must be a breaking > > change. > > It looks like we have to change the return value one way or another. > Either how I suggested or using your suggestion with multiple > links/file-names specified in the return value. > (of course, if you have better, less breaking, ideas, please share them) > > We can minimize breakage with the return value I am suggesting as the > following: > > 1. Introduce a new optional parameter ALLOW-MULTIPLE for > `org-babel-tangle-single-block' > 2. When ALLOW-MULTIPLE is non-nil, return as I suggested > 3. When ALLOW-MULTIPLE is nil and :tangle specifies single target, use the > previous return value convention > 4. When ALLOW-MULTIPLE is nil and :tangle specifies multiple targets, > still use previous return value convention by returning the first > target only and displaying a warning > > Then, all the users of `org-babel-tangle-single-block' will pass non-nil > ALLOW-MULTIPLE argument. External users will naturally pass nil, keeping > the old convention and not breaking anything. External users used on new > Org files that make use of the multiple tangle targets feature will get > a runtime warning. > > Does it make sense? > > -- > Ihor Radchenko // yantar92, > Org mode maintainer, > Learn more about Org mode at <https://orgmode.org/>. > Support Org development at <https://liberapay.com/org-mode>, > or support my work at <https://liberapay.com/yantar92>
From 8fb558fb080586a2c14be226999f1d6fa606f140 Mon Sep 17 00:00:00 2001 From: llcc <lzhes43@gmail.com> Date: Sun, 9 Mar 2025 15:05:35 +0800 Subject: [PATCH] New feature: tangle org source blocks to multiple targets 1. add `:tangle-directory' to specify tangle directory. 2. `:tangle' now accepts symbols that return a path string, or a list of file path, or a single string --- lisp/ob-tangle.el | 80 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 55 insertions(+), 25 deletions(-) diff --git a/lisp/ob-tangle.el b/lisp/ob-tangle.el index 38cad78ab..f0cb546e6 100644 --- a/lisp/ob-tangle.el +++ b/lisp/ob-tangle.el @@ -500,34 +500,30 @@ code blocks by target file." (org-element-at-point) 'headline t)) 1))) - (if (eq last-heading-pos current-heading-pos) (cl-incf counter) - (setq counter 1) - (setq last-heading-pos current-heading-pos))) + (if (eq last-heading-pos current-heading-pos) (cl-incf counter) + (setq counter 1) + (setq last-heading-pos current-heading-pos))) (unless (or (org-in-commented-heading-p) (org-in-archived-heading-p)) - (let* ((info (org-babel-get-src-block-info 'no-eval)) - (src-lang (nth 0 info)) - (src-tfile (cdr (assq :tangle (nth 2 info))))) - (unless (or (string= src-tfile "no") + (let* ((block (org-babel-tangle-single-block counter t)) + (src-file (car block)) + (src-lang (caar block))) + (unless (or (not src-file) ;; src block without lang - (and (not src-lang) (string= src-tfile "yes")) - (and tangle-file (not (equal tangle-file src-tfile))) + (and (not src-lang) src-file) + (and tangle-file (not (equal tangle-file src-file))) ;; lang-re but either no lang or lang doesn't match - (and lang-re + (and lang-re (or (not src-lang) (not (string-match-p lang-re src-lang))))) - ;; Add the spec for this block to blocks under its tangled - ;; file name. - (let* ((block (org-babel-tangle-single-block counter)) - (src-tfile (cdr (assq :tangle (nth 4 block)))) - (file-name (org-babel-effective-tangled-filename - buffer-fn src-lang src-tfile)) - (by-fn (assoc file-name blocks))) - (if by-fn (setcdr by-fn (cons (cons src-lang block) (cdr by-fn))) - (push (cons file-name (list (cons src-lang block))) blocks))))))) + (setq blocks + (mapcar (lambda (group) + (cons (car group) + (apply #'append (mapcar #'cdr (cdr group))))) + (seq-group-by #'car (append block blocks)))))))) ;; Ensure blocks are in the correct order. (mapcar (lambda (b) (cons (car b) (nreverse (cdr b)))) - (nreverse blocks)))) + (nreverse blocks)))) (defun org-babel-tangle--unbracketed-link (params) "Get a raw link to the src block at point, without brackets. @@ -541,6 +537,7 @@ The PARAMS are the 3rd element of the info for the same src block." (match-string 1 l)))) (when bare (if (and org-babel-tangle-use-relative-file-links + (length= (cdr (assq :tangle params)) 1) (string-match org-link-types-re bare) (string= (match-string 1 bare) "file")) (concat "file:" @@ -550,6 +547,39 @@ The PARAMS are the 3rd element of the info for the same src block." bare)))))) (defvar org-outline-regexp) ; defined in lisp/org.el + +(defun org-babel-tangle--concat-targets (buffer-fn info) + "Return a list of tangled files based on the `:tangle' +and `:tangle-directory' in PARAMS." + (let* ((params (nth 2 info)) + (src-lang (nth 0 info)) + (src-tdirectories (cdr (assq :tangle-directory params))) + (src-tfiles (cdr (assq :tangle params))) + (src-tfiles (pcase (type-of src-tfiles) + ('cons src-tfiles) + ('symbol (eval src-tfiles)) + (_ (eval src-tfiles))))) + (unless (or (not src-tdirectories) + (consp src-tdirectories)) + (setq src-tdirectories (list src-tdirectories))) + (unless (consp src-tfiles) + (setq src-tfiles + (list (cond ((string= src-tfiles "yes") + (file-name-nondirectory + (org-babel-effective-tangled-filename buffer-fn src-lang src-tfiles))) + ((string= src-tfiles "no") nil) + (t src-tfiles))))) + (when (and src-tdirectories + (not (equal src-tfiles '(nil)))) + (setq src-tfiles + (apply 'append + (mapcar (lambda (src-tdirectory) + (mapcar (lambda (src-tfile) + (expand-file-name src-tfile src-tdirectory)) + src-tfiles)) + src-tdirectories)))) + src-tfiles)) + (defun org-babel-tangle-single-block (block-counter &optional only-this-block) "Collect the tangled source for current block. Return the list of block attributes needed by @@ -580,7 +610,7 @@ non-nil, return the full association list to be used by ;; Run the tangle-body-hook. (let ((body (if (org-babel-noweb-p params :tangle) (if (string= "strip-tangle" (cdr (assq :noweb (nth 2 info)))) - (replace-regexp-in-string (org-babel-noweb-wrap) "" (nth 1 info)) + (replace-regexp-in-string (org-babel-noweb-wrap) "" (nth 1 info)) (org-babel-expand-noweb-references info)) (nth 1 info)))) (with-temp-buffer @@ -616,7 +646,6 @@ non-nil, return the full association list to be used by (match-end 0) (point-min)))) (point))))) - (src-tfile (cdr (assq :tangle params))) (result (list start-line (if org-babel-tangle-use-relative-file-links @@ -629,9 +658,10 @@ non-nil, return the full association list to be used by (org-trim (org-remove-indentation body))) comment))) (if only-this-block - (let* ((file-name (org-babel-effective-tangled-filename - file src-lang src-tfile))) - (list (cons file-name (list (cons src-lang result))))) + (let* ((file-names (org-babel-tangle--concat-targets file info))) + (mapcar (lambda (file-name) + (cons file-name (list (cons src-lang result)))) + file-names)) result))) (defun org-babel-tangle-comment-links (&optional info) -- 2.37.2.windows.2