0
0
Fork 1
mirror of https://git.savannah.gnu.org/git/emacs/org-mode.git synced 2024-09-30 00:27:52 +00:00

ob-tangle.el: Improve tangling

* lisp/ob-tangle.el (org-babel-tangle-collect-blocks): Group
collected blocks by tangled file name.
(org-babel-tangle): Avoid quadratic behavior in number of blocks and
set modes before writing to file.
* testing/lisp/test-ob-tangle.el (ob-tangle/block-order): Update test.
This commit is contained in:
Sébastien Miquel 2021-05-01 21:18:44 +02:00 committed by Bastien
parent f84033b088
commit a2cb9b853d
2 changed files with 74 additions and 79 deletions

View file

@ -225,67 +225,55 @@ matching a regular expression."
(or (cdr (assq :tangle (nth 2 (org-babel-get-src-block-info 'light)))) (or (cdr (assq :tangle (nth 2 (org-babel-get-src-block-info 'light))))
(user-error "Point is not in a source code block")))) (user-error "Point is not in a source code block"))))
path-collector) path-collector)
(mapc ;; map over all languages (mapc ;; map over file-names
(lambda (by-lang) (lambda (by-fn)
(let* ((lang (car by-lang)) (let ((file-name (car by-fn)))
(specs (cdr by-lang)) (when file-name
(ext (or (cdr (assoc lang org-babel-tangle-lang-exts)) lang)) (let ((lspecs (cdr by-fn))
(lang-f (org-src-get-lang-mode lang)) (fnd (file-name-directory file-name))
she-banged) modes make-dir she-banged lang)
(mapc ;; drop source-blocks to file
(lambda (spec) ;; We avoid append-to-file as it does not work with tramp.
(let ((get-spec (lambda (name) (cdr (assoc name (nth 4 spec)))))) (with-temp-buffer
(let* ((tangle (funcall get-spec :tangle)) (mapc
(she-bang (let ((sheb (funcall get-spec :shebang))) (lambda (lspec)
(when (> (length sheb) 0) sheb))) (let* ((block-lang (car lspec))
(tangle-mode (funcall get-spec :tangle-mode)) (spec (cdr lspec))
(base-name (cond (get-spec (lambda (name) (cdr (assq name (nth 4 spec)))))
((string= "yes" tangle) (she-bang (let ((sheb (funcall get-spec :shebang)))
(file-name-sans-extension (when (> (length sheb) 0) sheb)))
(nth 1 spec))) (tangle-mode (funcall get-spec :tangle-mode)))
((string= "no" tangle) nil) (unless (string-equal block-lang lang)
((> (length tangle) 0) tangle))) (setq lang block-lang)
(file-name (when base-name (let ((lang-f (org-src-get-lang-mode lang)))
;; decide if we want to add ext to base-name (when (fboundp lang-f) (ignore-errors (funcall lang-f)))))
(if (and ext (string= "yes" tangle)) ;; if file contains she-bangs, then make it executable
(concat base-name "." ext) base-name)))) (when she-bang
(when file-name (unless tangle-mode (setq tangle-mode #o755)))
;; Possibly create the parent directories for file. (when tangle-mode
(let ((m (funcall get-spec :mkdirp)) (add-to-list 'modes tangle-mode))
(fnd (file-name-directory file-name))) ;; Possibly create the parent directories for file.
(and m fnd (not (string= m "no")) (let ((m (funcall get-spec :mkdirp)))
(make-directory fnd 'parents))) (and m fnd (not (string= m "no"))
;; delete any old versions of file (setq make-dir t)))
(and (file-exists-p file-name) ;; Handle :padlines unless first line in file
(not (member file-name (mapcar #'car path-collector))) (unless (or (string= "no" (funcall get-spec :padline))
(delete-file file-name)) (= (point) (point-min)))
;; drop source-block to file (insert "\n"))
(with-temp-buffer (when (and she-bang (not she-banged))
(when (fboundp lang-f) (ignore-errors (funcall lang-f)))
(when (and she-bang (not (member file-name she-banged)))
(insert (concat she-bang "\n")) (insert (concat she-bang "\n"))
(setq she-banged (cons file-name she-banged))) (setq she-banged t))
(org-babel-spec-to-string spec) (org-babel-spec-to-string spec)
;; We avoid append-to-file as it does not work with tramp. (setq block-counter (+ 1 block-counter))))
(let ((content (buffer-string))) lspecs)
(with-temp-buffer (when make-dir
(when (file-exists-p file-name) (make-directory fnd 'parents))
(insert-file-contents file-name)) ;; erase previous file and set permissions on empty
(goto-char (point-max)) ;; file before writing
;; Handle :padlines unless first line in file (write-region "" nil file-name nil 0)
(unless (or (string= "no" (cdr (assq :padline (nth 4 spec)))) (mapc (lambda (mode) (set-file-modes file-name mode)) modes)
(= (point) (point-min))) (write-region nil nil file-name)
(insert "\n")) (push file-name path-collector))))))
(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)) (if (equal arg '(4))
(org-babel-tangle-single-block 1 t) (org-babel-tangle-single-block 1 t)
(org-babel-tangle-collect-blocks lang-re tangle-file))) (org-babel-tangle-collect-blocks lang-re tangle-file)))
@ -300,12 +288,8 @@ matching a regular expression."
(lambda (file) (lambda (file)
(org-babel-with-temp-filebuffer file (org-babel-with-temp-filebuffer file
(run-hooks 'org-babel-post-tangle-hook))) (run-hooks 'org-babel-post-tangle-hook)))
(mapcar #'car path-collector))) path-collector))
;; set permissions on tangled files path-collector))))
(mapc (lambda (pair)
(when (cdr pair) (set-file-modes (car pair) (cdr pair))))
path-collector)
(mapcar #'car path-collector)))))
(defun org-babel-tangle-clean () (defun org-babel-tangle-clean ()
"Remove comments inserted by `org-babel-tangle'. "Remove comments inserted by `org-babel-tangle'.
@ -368,12 +352,12 @@ that the appropriate major-mode is set. SPEC has the form:
(defun org-babel-tangle-collect-blocks (&optional lang-re tangle-file) (defun org-babel-tangle-collect-blocks (&optional lang-re tangle-file)
"Collect source blocks in the current Org file. "Collect source blocks in the current Org file.
Return an association list of source-code block specifications of Return an association list of language and source-code block
the form used by `org-babel-spec-to-string' grouped by language. specifications of the form used by `org-babel-spec-to-string'
Optional argument LANG-RE can be used to limit the collected grouped by tangled file name. Optional argument LANG-RE can be
source code blocks by languages matching a regular expression. used to limit the collected source code blocks by languages
Optional argument TANGLE-FILE can be used to limit the collected matching a regular expression. Optional argument TANGLE-FILE can
code blocks by target file." be used to limit the collected code blocks by target file."
(let ((counter 0) last-heading-pos blocks) (let ((counter 0) last-heading-pos blocks)
(org-babel-map-src-blocks (buffer-file-name) (org-babel-map-src-blocks (buffer-file-name)
(let ((current-heading-pos (let ((current-heading-pos
@ -390,12 +374,23 @@ code blocks by target file."
(unless (or (string= src-tfile "no") (unless (or (string= src-tfile "no")
(and tangle-file (not (equal tangle-file src-tfile))) (and tangle-file (not (equal tangle-file src-tfile)))
(and lang-re (not (string-match-p lang-re src-lang)))) (and lang-re (not (string-match-p lang-re src-lang))))
;; Add the spec for this block to blocks under its ;; Add the spec for this block to blocks under its tangled
;; language. ;; file name.
(let ((by-lang (assoc src-lang blocks)) (let* ((block (org-babel-tangle-single-block counter))
(block (org-babel-tangle-single-block counter))) (base-name (cond
(if by-lang (setcdr by-lang (cons block (cdr by-lang))) ((string= "yes" src-tfile)
(push (cons src-lang (list block)) blocks))))))) ;; buffer name
(file-name-sans-extension
(nth 1 block)))
((> (length src-tfile) 0) src-tfile)))
(ext (or (cdr (assoc src-lang org-babel-tangle-lang-exts)) src-lang))
(file-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)))
(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)))))))
;; Ensure blocks are in the correct order. ;; Ensure blocks are in the correct order.
(mapcar (lambda (b) (cons (car b) (nreverse (cdr b)))) (mapcar (lambda (b) (cons (car b) (nreverse (cdr b))))
(nreverse blocks)))) (nreverse blocks))))

View file

@ -308,7 +308,7 @@ another block
(delete-file file))))) (delete-file file)))))
;; Preserve order with mixed languages. ;; Preserve order with mixed languages.
(should (should
(equal '("1" "3" "2" "4") (equal '("1" "2" "3" "4")
(let ((file (make-temp-file "org-tangle-"))) (let ((file (make-temp-file "org-tangle-")))
(unwind-protect (unwind-protect
(progn (progn