2

Suppose I have two code blocks in my org file

#+BEGIN_SRC python :tangle ~/dummy.py
class A:
    pass
#+END_SRC

#+BEGIN_SRC python :tangle ~/dummy.py
class B:
   pass
#+END_SRC

After org-babel-tangle I get a file dummy.py containing two classes separated by one empty line. However, according to pep8 there should be two empty lines between class definitions so as a result I receive a warning message in my editor.

How can I ensure more than one empty line between (selected) code blocks?

PS. I know the :padline argument. I wish there was an option :padline 2 meaning: precede a block using two empty lines.

1 Answer 1

3

In the original org-babel-tangle function (C-h k org-babel-tangle), found in ob-tangle.el, a space is inserted between two lines unless the argument to :padline is no:

            ;; Handle :padlines unless first line in file
            (unless (or (string= "no" (cdr (assq :padline (nth 4 spec))))
                (= (point) (point-min)))
              (insert "\n"))

A quick and dirty solution to your problem is to hack org-babel-tangle so that it does what you want. Changing those lines to the following, for example, should do the trick:

        ;; Handle :padlines unless first line in file
        (let ((padlines (format "%s" (cdr (assq :padline (nth 4 spec))))))
        (cond
         ((and (string= "nil" padlines) (not (= (point) (point-min))))
          (insert "\n"))
         ((string= "no" padlines)
          nil)
         ((numberp (string-to-int padlines))
          (dotimes (i (string-to-int padlines)) (insert "\n")))
         (t
          (insert "\n"))))

FOR COMPLETION: Ideally you won't want to change org-babel-tangle from ob-tangle.el, but rather shadow it. Just evaluate the following in your scratch buffer, or load it in your init.el:

(defun org-babel-tangle (&optional arg target-file lang)
  "Write code blocks to source-specific files.
Extract the bodies of all source code blocks from the current
file into their own source-specific files.
With one universal prefix argument, only tangle the block at point.
When two universal prefix arguments, only tangle blocks for the
tangle file of the block at point.
Optional argument TARGET-FILE can be used to specify a default
export file for all source blocks.  Optional argument LANG can be
used to limit the exported source code blocks by language."
  (interactive "P")
  (run-hooks 'org-babel-pre-tangle-hook)
  ;; Possibly Restrict the buffer to the current code block
  (save-restriction
    (save-excursion
      (when (equal arg '(4))
    (let ((head (org-babel-where-is-src-block-head)))
      (if head
          (goto-char head)
        (user-error "Point is not in a source code block"))))
      (let ((block-counter 0)
        (org-babel-default-header-args
         (if target-file
         (org-babel-merge-params org-babel-default-header-args
                     (list (cons :tangle target-file)))
           org-babel-default-header-args))
        (tangle-file
         (when (equal arg '(16))
           (or (cdr (assq :tangle (nth 2 (org-babel-get-src-block-info 'light))))
           (user-error "Point is not in a source code block"))))
        path-collector)
    (mapc ;; map over all languages
     (lambda (by-lang)
       (let* ((lang (car by-lang))
          (specs (cdr by-lang))
          (ext (or (cdr (assoc lang org-babel-tangle-lang-exts)) lang))
          (lang-f (intern
               (concat
                (or (and (cdr (assoc lang org-src-lang-modes))
                     (symbol-name
                      (cdr (assoc lang org-src-lang-modes))))
                lang)
                "-mode")))
          she-banged)
         (mapc
          (lambda (spec)
        (let ((get-spec (lambda (name) (cdr (assoc name (nth 4 spec))))))
          (let* ((tangle (funcall get-spec :tangle))
             (she-bang (let ((sheb (funcall get-spec :shebang)))
                                     (when (> (length sheb) 0) sheb)))
             (tangle-mode (funcall get-spec :tangle-mode))
             (base-name (cond
                     ((string= "yes" tangle)
                      (file-name-sans-extension
                       (nth 1 spec)))
                     ((string= "no" tangle) nil)
                     ((> (length tangle) 0) tangle)))
             (file-name (when base-name
                      ;; decide if we want to add ext to base-name
                      (if (and ext (string= "yes" tangle))
                      (concat base-name "." ext) base-name))))
            (when file-name
              ;; Possibly create the parent directories for file.
              (let ((m (funcall get-spec :mkdirp))
                (fnd (file-name-directory file-name)))
            (and m fnd (not (string= m "no"))
                 (make-directory fnd 'parents)))
              ;; delete any old versions of file
              (and (file-exists-p file-name)
               (not (member file-name (mapcar #'car path-collector)))
               (delete-file file-name))
              ;; drop source-block to file
              (with-temp-buffer
            (when (fboundp lang-f) (ignore-errors (funcall lang-f)))
            (when (and she-bang (not (member file-name she-banged)))
              (insert (concat she-bang "\n"))
              (setq she-banged (cons file-name she-banged)))
            (org-babel-spec-to-string spec)
            ;; We avoid append-to-file as it does not work with tramp.
            (let ((content (buffer-string)))
              (with-temp-buffer
                (when (file-exists-p file-name)
                  (insert-file-contents file-name))
                (goto-char (point-max))
        ;; Handle :padlines unless first line in file
        (let ((padlines (format "%s" (cdr (assq :padline (nth 4 spec))))))
        (cond
         ((and (string= "nil" padlines) (not (= (point) (point-min))))
          (insert "\n"))
         ((string= "no" padlines)
          nil)
         ((numberp (string-to-int padlines))
          (dotimes (i (string-to-int padlines)) (insert "\n")))
         (t
          (insert "\n"))))
                (insert content)
                (write-region nil nil file-name))))
              ;; if files contain she-bangs, then make the executable
              (when she-bang
            (unless tangle-mode (setq tangle-mode #o755)))
              ;; update counter
              (setq block-counter (+ 1 block-counter))
              (unless (assoc file-name path-collector)
            (push (cons file-name tangle-mode) path-collector))))))
          specs)))
     (if (equal arg '(4))
         (org-babel-tangle-single-block 1 t)
       (org-babel-tangle-collect-blocks lang tangle-file)))
    (message "Tangled %d code block%s from %s" block-counter
         (if (= block-counter 1) "" "s")
         (file-name-nondirectory
          (buffer-file-name
           (or (buffer-base-buffer) (current-buffer)))))
    ;; run `org-babel-post-tangle-hook' in all tangled files
    (when org-babel-post-tangle-hook
      (mapc
       (lambda (file)
         (org-babel-with-temp-filebuffer file
           (run-hooks 'org-babel-post-tangle-hook)))
       (mapcar #'car path-collector)))
    ;; set permissions on tangled files
    (mapc (lambda (pair)
        (when (cdr pair) (set-file-modes (car pair) (cdr pair))))
          path-collector)
    (mapcar #'car path-collector)))))
7
  • I'm sure there are tons of implementations of a simple stringify function (e.g here) which you can use like this: (let (( padlines (stringify (cdr (...)..). That will let you use strings or numbers as arguments.
    – NickD
    Mar 28, 2017 at 18:35
  • @Nick, that's not the issue at all. The issue is that org-mode complains when :padline is something else than a string. I don't think that the typecheck is related to ob-tangle.
    – Daniel
    Mar 28, 2017 at 18:38
  • It complains when it gets to (string= "no" padlines), so if you have made sure that by that time, it is already a string, it won't complain any more.
    – NickD
    Mar 28, 2017 at 18:43
  • You were right. Thanks for catching the detail. There is still one problem with my solution, though. It defaults to no-pad, whereas the original defaults to 1. Still have to fix that later.
    – Daniel
    Mar 28, 2017 at 18:51
  • 1
    You might want to submit a patch for this to the org-mode mailing list as an enhancement.
    – NickD
    Mar 28, 2017 at 21:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.