11

By default, Org-mode will override current babel src block content on file original content (when the file already has content).

For example, I have an org-mode file content as the bellowing block:

#+BEGIN_SRC org
,* config file

,** config 1

,#+BEGIN_SRC clojure :tangle "data/code/tangle-append-test.clj"
(println "hello")
,#+END_SRC

,** config 2

,#+BEGIN_SRC clojure :tangle "data/code/tangle-append-test.clj"
(println "world")
,#+END_SRC

#+END_SRC

If you tangle the whole buffer file, then the result will looks like this:

The tangled file result content:

#+NAME: data/code/tangle-append-test.clj
#+BEGIN_SRC clojure
(println "hello")

(println "world")

#+END_SRC

This is fine. But here is my problem:

But if you tangle only current src block with [C-u C-c C-v t]. Then the current tangled src block will overwrite the whole destination file, like this:

 $ cat data/code/tangle-append-test.clj

(println "world")

UPDATED

My question: The single babel block override original file content, I hope to only append current tangled block without overwriting.

stardiviner
  • 1,888
  • 26
  • 45
  • I was wondering if you came up with a decent solution since you posted this question. I'd be interested in hearing it out – Daniel Mar 13 '17 at 19:51
  • @dangom Maybe use an advice to detect tangle file exist or not, or empty then decide whether overwrite or append. But I don't know how to write this advice. – stardiviner Mar 14 '17 at 01:41
  • @stardiviner, Why would you want to only tangle the current block? I always use **M-x org-babel-tangle** (`C-c C-v t`) and tangle all blocks in the file. My tangled files are in the .gitignore anyway because I treat them only as an artifact to run the application. They are essentially throwaway code, I regenerate them whenever I need to. The real code is in the source blocks. – Martín Feb 16 '18 at 23:25
  • I'm not want to only tangle the current block. I want to append block to file instead of override. And because I use it in Literate DevOps style with Org-mode. – stardiviner Feb 17 '18 at 02:05
  • What I mean is, in Literate Programming the idea is that the source code is in the document, and you process the document to generate the source files or scripts needed to run your application. What harm does it do to regenerate all tangled files from scratch? Why do you need to only tangle the current block and append it to the file? What benefit does that have that is better than regenerating all the files? – Martín Feb 18 '18 at 16:16
  • I just re-read your question and I think I can see where are you coming from. Let us say that you have a literate devops document where you need to perform a sequence of 17 steps to do something. Each has a source code block and you have to execute them in order, with each step building on the work done by the previous step. If you are in step 13 and you have to close the document to continue it later, you might need to redo all the previous steps when you resume the work [...] – Martín Feb 18 '18 at 16:29
  • [...] But if in each step you append the code into a tangled file, you can then execute the step 14 later, continuing from where you left off last time. I just don't like that approach. I prefer my code blocks to be independent from each other and I want to be able to run any code block I want in any order I want anywhere on the document. I structure my docs to be able to run block 14, then block 3, then block 8, and it has to work. – Martín Feb 18 '18 at 16:29
  • The example in my question is just an demo, I do "Literate DevOps" more for administrator works like modify an option value of Nginx, or un-comment a line of config file, or add a new content to config file. So it just use Org-mode Bbael just like "Literate Programming", But not heavily depend on "tangling". That's why I need to append. Because I will not tangle the default config file. and will not delete it. I just modify, update, append to the config. – stardiviner Feb 19 '18 at 06:12

3 Answers3

11

I recommend using the builtin org-mode :noweb-ref header to concatenate the code blocks because doing so will resolve the block tangling issue and will allow more flexibility in the future.

  • Updated Code

      * config file
    
      #+BEGIN_SRC clojure :noweb yes :exports none :mkdirp yes :tangle data/code/tangle-append-test.clj 
        <<configs>>
      #+END_SRC
    
      ** config 1
    
      #+BEGIN_SRC clojure :noweb-ref configs
        (println "hello")
      #+END_SRC
    
      ** config 2
    
      #+BEGIN_SRC clojure :noweb-ref configs
        (println "world")
      #+END_SRC
    
  • Tangling the code with C-c C-v C-t produces:

      (println "hello")
      (println "world")
    

As new sections are added, deleted, moved, or updated, tangling the code should always produce the code in the order expected:

  • Future Code

      * config file
    
      #+BEGIN_SRC clojure :noweb yes :exports none :mkdirp yes :tangle data/code/tangle-append-test.clj 
        <<configs>>
      #+END_SRC
    
      ** config 1 - Added
    
      #+BEGIN_SRC clojure :noweb-ref configs
        (println "Howdy")
      #+END_SRC
    
      ** config 2 - Updated
    
      #+BEGIN_SRC clojure :noweb-ref configs
        (println "World")
      #+END_SRC
    
      ** config 1 - Moved
    
      #+BEGIN_SRC clojure :noweb-ref configs
        (println "hello")
      #+END_SRC
    
      ** config 4 - Added 
    
      #+BEGIN_SRC clojure :noweb-ref configs
        (println "earth")
      #+END_SRC
    
  • Future Tangle

      (println "Howdy")
      (println "World")
      (println "hello")
      (println "earth")
    

Hope that helped.


This code was tested using:
GNU Emacs 25.2.1 (x86_64-unknown-cygwin, GTK+ Version 3.22.10)
Org mode version 9.1.2

vfclists
  • 1,347
  • 1
  • 11
  • 28
Melioratus
  • 4,504
  • 1
  • 25
  • 43
7

The following elisp code defines a new function org-babel-tangle-append appending the current source block to the tangle file. In org-mode 9.1.6 there is no user option to configure org-babel-tangle in that way.

If you copy-paste the full elisp code into your init file behind package-initialize the new function org-babel-tangle-append is bound to the key sequence C-c C-v + in org-mode after the next restart of emacs.

The code is tested with

  • emacs-version 25.1.50.2
  • org-version 9.1.6

In org-babel-tangle of that org version the old tangle file is deleted with delete-file before the new block(s) are collected there. The new function org-babel-tangle-append works by calling org-babel-tangle with delete-file temporarily overridden by ignore.

(defun org-babel-tangle-append ()
  "Append source code block at point to its tangle file.
The command works like `org-babel-tangle' with prefix arg
but `delete-file' is ignored."
  (interactive)
  (cl-letf (((symbol-function 'delete-file) #'ignore))
    (org-babel-tangle '(4))))

(defun org-babel-tangle-append-setup ()
  "Add key-binding C-c C-v C-t for `org-babel-tangle-append'."
  (org-defkey org-mode-map (kbd "C-c C-v +") 'org-babel-tangle-append))

(add-hook 'org-mode-hook #'org-babel-tangle-append-setup)
Tobias
  • 32,569
  • 1
  • 34
  • 75
  • Is it possible to an advice with `advice-add`? – stardiviner Feb 19 '18 at 06:07
  • @stardiviner It is clearly possible to advice `org-babel-tangle`. But why would you do that? Appending the source blocks is added functionality and not something like a bugfix. So a new command is better than modifying the old one IMHO. – Tobias Feb 19 '18 at 07:27
  • This is not answer in my case... I need to update file, not rewrite it, because of old bug in node fs watcher, that broke livereloading in many node applications – 11111000000 Apr 08 '18 at 13:36
  • @Tobias could you please explain why you use `4` in `(org-babel-tangle '(4))`? – reFORtEM Mar 21 '20 at 01:52
  • 1
    @William The use of `4` in `(org-babel-tangle '(4))` is how `C-u` is effected in code. See [How is "C-u" expressed in function call? : emacs](https://www.reddit.com/r/emacs/comments/f6fd5g/how_is_cu_expressed_in_function_call/) – vfclists Aug 25 '20 at 21:56
  • @Tobias Does `(cl-letf (((symbol-function 'delete-file) #'ignore))` disable the delete-file function only with the scope of the `org-babel-tangle-append` command? What does `cl-letf` do, how does it work? – vfclists Dec 27 '20 at 03:18
  • 1
    @vfclists `cl-letf` is well-documented at https://www.gnu.org/software/emacs/manual/html_node/cl/Modify-Macros.html. In that context `(symbol-function SYMBOL)` is one of the posible *places*. that specifies the function cell of `SYMBOL`. Yes, the binding of `delete-file` to `#'ignore` is local to the body of the `cl-letf`. – Tobias Dec 27 '20 at 05:23
3

What I do is I specify a single block per output file, and use noweb references to collect what goes into it:

Here are the blocks with the source you want:

#+name: hello-world
#+begin_src clojure
(println "hello")
#+end_src

#+name: hello-world
#+begin_src clojure
(println "world")
#+end_src

#+name: goodbye
#+begin_src clojure
(println "goodbye")
#+end_src

Here's the block that collects it all and tangles it:

#+begin_src clojure :noweb yes :tangle data/code/tangle-append-test.clj
<<hello-world>>
<<goodbye>>
#+end_src
Andrea Reina
  • 216
  • 1
  • 4