Org-mode mailing list
 help / color / mirror / Atom feed
From: Jacopo De Simoi <wilderjds@protonmail.com>
To: emacs-orgmode@gnu.org
Subject: [PATCH] Allow tangling to a list of files
Date: Mon, 05 Jul 2021 18:54:14 +0000
Message-ID: <DZYHT29KLdzymzGwYjsbCYOEOLBOPDZUtIJaKOv0jWoNwzkr1kFcKzX59GJxm214zdfmBVGJQfXIwueTgJw9FbdOBUeqNL0bqUfTifEZ_88=@protonmail.com> (raw)


[-- Attachment #1.1: Type: text/plain, Size: 675 bytes --]

Dear All,

Please find attached a patch (against master) that adds a feature to the tangle framework. Essentially, the following block would now tangle to two files

#+begin_src sh '("filename1" "filename2")
#my script
#+end_src

Usecases

- literate config (e.g. .zshrc) of several machines at once (e.g. tangling via tramp).
- literate similar versions of the same script which differ only in small chunks (I use it for a slightly different latexmkrc for my projects on Dropbox)

The patch also streamlines the tangling routines.

Tests have been checked.

I am of course open to discussions and comments. Looking forward to hear from you.
Thanks for your time

Best
Jacopo

[-- Attachment #1.2: Type: text/html, Size: 758 bytes --]

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-ob-tangle-Accept-lists-of-files-as-tangling-target.patch --]
[-- Type: text/x-diff; name=0001-ob-tangle-Accept-lists-of-files-as-tangling-target.patch, Size: 8030 bytes --]

From 3a04df5e636c11bfcd8e2183e4a3e336daeb46a9 Mon Sep 17 00:00:00 2001
From: Jacopo De Simoi <wilderjds@protonmail.com>
Date: Fri, 2 Jul 2021 18:31:41 -0400
Subject: [PATCH 1/2] ob-tangle: Accept lists of files as tangling target

* lisp/ob-tangle.el (org-babel-tangle): Drop the now
superfluous parameter `only-this-block'.

(org-babel-effective-tangled-filenames): Return a list
of filenames to use as targets for tangling.

(org-babel-tangle-collect-blocks): Adapt to changes to
`org-babel-tangle-single-block', which now returns an
alist.

(org-babel-tangle-single-block): Return an alist to be
used from `org-babel-tangle'.  A single block can now
be tangled to several files at once.

This commit correctly parses list of filenames as
arguments of the :tangle parameter.  In doing so it
streamlines both `org-babel-tangle-single-block' and
`org-babel-tangle-collect-blocks'.  This was inspired
by the solution to the question
[https://emacs.stackexchange.com/questions/39032/tangle-the-same-src-block-to-different-files]
which I asked few years ago.

The suggested solution does not work with recent org-mode,
so I came up with a working rewrite.
---
 lisp/ob-tangle.el | 88 ++++++++++++++++++++++++-----------------------
 1 file changed, 45 insertions(+), 43 deletions(-)

diff --git a/lisp/ob-tangle.el b/lisp/ob-tangle.el
index 2f60ef9a4..3799da03f 100644
--- a/lisp/ob-tangle.el
+++ b/lisp/ob-tangle.el
@@ -275,7 +275,7 @@ matching a regular expression."
 		   (mapc (lambda (mode) (set-file-modes file-name mode)) modes)
                    (push file-name path-collector))))))
 	 (if (equal arg '(4))
-	     (org-babel-tangle-single-block 1 t)
+	     (org-babel-tangle-single-block 1)
 	   (org-babel-tangle-collect-blocks lang-re tangle-file)))
 	(message "Tangled %d code block%s from %s" block-counter
 		 (if (= block-counter 1) "" "s")
@@ -350,22 +350,24 @@ that the appropriate major-mode is set.  SPEC has the form:
 	       (org-fill-template
 		org-babel-tangle-comment-format-end link-data)))))
 
-(defun org-babel-effective-tangled-filename (buffer-fn src-lang src-tfile)
-  "Return effective tangled filename of a source-code block.
+(defun org-babel-effective-tangled-filenames (buffer-fn src-lang src-tfile)
+  "Return a list of effective tangled filename of a source-code block.
 BUFFER-FN is the name of the buffer, SRC-LANG the language of the
 block and SRC-TFILE is the value of the :tangle header argument,
 as computed by `org-babel-tangle-single-block'."
-  (let ((base-name (cond
+  (if (consp src-tfile)
+      src-tfile
+    (list (let ((base-name (cond
                     ((string= "yes" src-tfile)
                      ;; Use the buffer name
                      (file-name-sans-extension buffer-fn))
                     ((string= "no" src-tfile) nil)
                     ((> (length src-tfile) 0) src-tfile)))
         (ext (or (cdr (assoc src-lang org-babel-tangle-lang-exts)) src-lang)))
-    (when base-name
+        (when base-name
       ;; decide if we want to add ext to base-name
       (if (and ext (string= "yes" src-tfile))
-          (concat base-name "." ext) base-name))))
+          (concat base-name "." ext) base-name))))))
 
 (defun org-babel-tangle-collect-blocks (&optional lang-re tangle-file)
   "Collect source blocks in the current Org file.
@@ -388,27 +390,26 @@ be used to limit the collected code blocks by target file."
 	(let* ((info (org-babel-get-src-block-info 'light))
 	       (src-lang (nth 0 info))
 	       (src-tfile (cdr (assq :tangle (nth 2 info)))))
-	  (unless (or (string= src-tfile "no")
+	  (unless (or (and (not (consp src-tfile)) (string= src-tfile "no"))
 		      (and tangle-file (not (equal tangle-file src-tfile)))
 		      (and lang-re (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
-                               (nth 1 block) 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)))))))
+	    (let ((new-blocks (org-babel-tangle-single-block counter)))
+              (dolist (bl new-blocks)
+		(let* ((block (cdr bl))
+                       (file-name (car bl))
+                       (by-fn (assoc file-name blocks)))
+	          (if by-fn (setcdr by-fn (cons (car block) (cdr by-fn)))
+		    (push (cons file-name block) blocks)))))))))
     ;; Ensure blocks are in the correct order.
     (mapcar (lambda (b) (cons (car b) (nreverse (cdr b))))
 	    (nreverse blocks))))
 
-(defun org-babel-tangle-single-block (block-counter &optional only-this-block)
+(defun org-babel-tangle-single-block (block-counter)
   "Collect the tangled source for current block.
-Return the list of block attributes needed by
-`org-babel-tangle-collect-blocks'.  When ONLY-THIS-BLOCK is
-non-nil, return the full association list to be used by
+Return the association list of blocks needed by
+`org-babel-tangle-collect-blocks' or
 `org-babel-tangle' directly."
   (let* ((info (org-babel-get-src-block-info))
 	 (start-line
@@ -470,31 +471,32 @@ non-nil, return the full association list to be used by
 			 (match-end 0)
 		       (point-min))))
 	      (point)))))
-	 (result
-	  (list start-line
-		(if org-babel-tangle-use-relative-file-links
-		    (file-relative-name file)
-		  file)
-		(if (and org-babel-tangle-use-relative-file-links
-			 (string-match org-link-types-re link)
-			 (string= (match-string 1 link) "file"))
-		    (concat "file:"
-			    (file-relative-name (substring link (match-end 0))
-						(file-name-directory
-						 (cdr (assq :tangle params)))))
-		  link)
-		source-name
-		params
-		(if org-src-preserve-indentation
-		    (org-trim body t)
-		  (org-trim (org-remove-indentation body)))
-		comment)))
-    (if only-this-block
-        (let* ((src-tfile (cdr (assq :tangle (nth 4 result))))
-               (file-name (org-babel-effective-tangled-filename
-                           (nth 1 result) src-lang src-tfile)))
-          (list (cons file-name (list (cons src-lang result)))))
-      result)))
+         (src-tfile (cdr (assq :tangle params)))
+         (file-names (org-babel-effective-tangled-filenames
+                      (if org-babel-tangle-use-relative-file-links
+		          (file-relative-name file)
+		        file) src-lang src-tfile)))
+    (mapcar (lambda (file-name)
+              (cons file-name (list (cons src-lang
+                                          (list start-line
+		                                (if org-babel-tangle-use-relative-file-links
+		                                    (file-relative-name file)
+		                                  file)
+		                                (if (and org-babel-tangle-use-relative-file-links
+			                                 (string-match org-link-types-re link)
+			                                 (string= (match-string 1 link) "file"))
+		                                    (concat "file:"
+			                                    (file-relative-name (substring link (match-end 0))
+						                                (file-name-directory
+                                                                                 file-name)))
+		                                            link)
+		                                source-name
+		                                params
+		                                (if org-src-preserve-indentation
+		                                    (org-trim body t)
+		                                  (org-trim (org-remove-indentation body)))
+		                                comment)))))
+            file-names)))
 
 (defun org-babel-tangle-comment-links (&optional info)
   "Return a list of begin and end link comments for the code block at point.
-- 
2.31.1


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #3: 0002-org-manual-Update-docs.patch --]
[-- Type: text/x-diff; name=0002-org-manual-Update-docs.patch, Size: 1186 bytes --]

From b7d1e15f5f79bdbec72a36125c700e06d1b33b39 Mon Sep 17 00:00:00 2001
From: Jacopo De Simoi <wilderjds@protonmail.com>
Date: Fri, 2 Jul 2021 18:53:53 -0400
Subject: [PATCH 2/2] org-manual: Update docs

* doc/org-manual.org (Header arguments): document that
lists can be passed as arguments to :tangle
---
 doc/org-manual.org | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/doc/org-manual.org b/doc/org-manual.org
index 977ef80b9..eaee9b248 100644
--- a/doc/org-manual.org
+++ b/doc/org-manual.org
@@ -17961,6 +17961,14 @@ to source file(s).
   file name as being relative to the directory of the Org file's
   location.  Example: =:tangle FILENAME=.
 
+- {{{var(FILENAME-LIST)}}} ::
+
+  Export the code block to possibly several source files whose file name is derived from
+  a list of strings passed to the =tangle= header argument.  Org derives the
+  file names as being relative to the directory of the Org file's
+  location.  Example: =:tangle ’("FILENAME1" "FILENAME2")=.
+
+
 #+cindex: @samp{mkdirp}, header argument
 The =mkdirp= header argument creates parent directories for tangled
 files if the directory does not exist.  A =yes= value enables
-- 
2.31.1


             reply	other threads:[~2021-07-05 18:55 UTC|newest]

Thread overview: 21+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-07-05 18:54 Jacopo De Simoi [this message]
2021-07-06  2:57 ` Vladimir Lomov
2021-07-06  4:43 ` Greg Minshall
2021-07-06  5:09   ` Jacopo De Simoi via General discussions about Org-mode.
2021-07-06  6:11     ` Vladimir Lomov
2021-07-06 15:24       ` Jacopo De Simoi via General discussions about Org-mode.
2021-07-07  3:27         ` Vladimir Lomov
2021-07-07  4:09           ` Tim Cross
2021-07-07  5:01           ` Jacopo De Simoi
2021-07-07  6:56           ` Greg Minshall
2021-07-07 11:05             ` Jacopo De Simoi
2021-07-09 12:26             ` Vladimir Lomov
2021-07-09 13:39               ` Jacopo De Simoi
2021-07-09 22:47               ` Tim Cross
2021-07-06  7:30     ` Tim Cross
2021-07-07 23:06       ` Jacopo De Simoi via General discussions about Org-mode.
2021-07-07 23:28         ` Tim Cross
2021-07-08  0:01           ` Jacopo De Simoi
2021-07-08  0:41             ` Tom Gillespie
2021-07-08 16:41               ` Trust me I am a Doctor
2021-07-08 17:42                 ` Jacopo De Simoi
     [not found] <-0ZoEP_lzUvrnWSq9TwiYHNJ0Spa94xjiTOF0TU8np0pYgHEPx-62_dr5xBMd3VUu7frSRXxiAFje99v2jeaJg==@protonmail.internalid>

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://orgmode.org

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to='DZYHT29KLdzymzGwYjsbCYOEOLBOPDZUtIJaKOv0jWoNwzkr1kFcKzX59GJxm214zdfmBVGJQfXIwueTgJw9FbdOBUeqNL0bqUfTifEZ_88=@protonmail.com' \
    --to=wilderjds@protonmail.com \
    --cc=emacs-orgmode@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

Org-mode mailing list

This inbox may be cloned and mirrored by anyone:

	git clone --mirror https://orgmode.org/list/0 list/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 list list/ https://orgmode.org/list \
		emacs-orgmode@gnu.org
	public-inbox-index list

Example config snippet for mirrors.
Newsgroups are available over NNTP:
	nntp://news.yhetil.org/yhetil.emacs.orgmode
	nntp://news.gmane.io/gmane.emacs.orgmode


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git