diff --git a/lisp/org-src.el b/lisp/org-src.el index b0f952fae..850525b8d 100644 --- a/lisp/org-src.el +++ b/lisp/org-src.el @@ -240,6 +240,12 @@ issued in the language major mode buffer." (defvar org-src--preserve-indentation nil) (defvar org-src--remote nil) (defvar org-src--saved-temp-window-config nil) +(defvar org-src--source-type nil + "Type of element being edited, as a symbol.") +(defvar org-src--tab-width nil + "Contains `tab-width' value from Org source buffer. +However, if `indent-tabs-mode' is nil in that buffer, its value +is 0.") (defun org-src--construct-edit-buffer-name (org-buffer-name lang) "Construct the buffer name for a source editing buffer." @@ -387,20 +393,34 @@ spaces after it as being outside." (defun org-src--contents-for-write-back () "Return buffer contents in a format appropriate for write back. Assume point is in the corresponding edit buffer." - (let ((indentation (or org-src--block-indentation 0)) - (preserve-indentation org-src--preserve-indentation) + (let ((indentation-offset + (if org-src--preserve-indentation 0 + (+ (or org-src--block-indentation 0) + (if (memq org-src--source-type '(example-block src-block)) + org-edit-src-content-indentation + 0)))) + (use-tabs? (and (> org-src--tab-width 0) t)) + (source-tab-width org-src--tab-width) (contents (org-with-wide-buffer (buffer-string))) (write-back org-src--allow-write-back)) (with-temp-buffer + ;; Reproduce indentation parameters from source buffer. + (setq-local indent-tabs-mode use-tabs?) + (when (> source-tab-width 0) (setq-local tab-width source-tab-width)) + ;; Apply WRITE-BACK function on edit buffer contents. (insert (org-no-properties contents)) (goto-char (point-min)) - (when (functionp write-back) (funcall write-back)) - (unless (or preserve-indentation (= indentation 0)) - (let ((ind (make-string indentation ?\s))) - (goto-char (point-min)) - (while (not (eobp)) - (when (looking-at-p "[ \t]*\\S-") (insert ind)) - (forward-line)))) + (when (functionp write-back) (save-excursion (funcall write-back))) + ;; Add INDENTATION-OFFSET to every non-empty line in buffer, + ;; unless indentation is meant to be preserved. + (when (> indentation-offset 0) + (while (not (eobp)) + (skip-chars-forward " \t") + (unless (eolp) ;ignore blank lines + (let ((i (current-column))) + (delete-region (line-beginning-position) (point)) + (indent-to (+ i indentation-offset)))) + (forward-line))) (buffer-string)))) (defun org-src--edit-element @@ -438,6 +458,7 @@ Leave point in edit buffer." (with-current-buffer old-edit-buffer (org-src--remove-overlay)) (kill-buffer old-edit-buffer)) (let* ((org-mode-p (derived-mode-p 'org-mode)) + (source-tab-width (if indent-tabs-mode tab-width 0)) (type (org-element-type datum)) (ind (org-with-wide-buffer (goto-char (org-element-property :begin datum)) @@ -477,10 +498,12 @@ Leave point in edit buffer." ;; Transmit buffer-local variables for exit function. It must ;; be done after initializing major mode, as this operation ;; may reset them otherwise. + (setq-local org-src--tab-width source-tab-width) (setq-local org-src--from-org-mode org-mode-p) (setq-local org-src--beg-marker beg) (setq-local org-src--end-marker end) (setq-local org-src--remote remote) + (setq-local org-src--source-type type) (setq-local org-src--block-indentation ind) (setq-local org-src--preserve-indentation preserve-ind) (setq-local org-src--overlay overlay) @@ -939,16 +962,7 @@ name of the sub-editing buffer." (org-src--construct-edit-buffer-name (buffer-name) lang)) lang-f (and (null code) - `(lambda () - (unless ,(or org-src-preserve-indentation - (org-element-property :preserve-indent element)) - (when (> org-edit-src-content-indentation 0) - (while (not (eobp)) - (unless (looking-at "[ \t]*$") - (indent-line-to (+ (org-get-indentation) - org-edit-src-content-indentation))) - (forward-line)))) - (org-escape-code-in-region (point-min) (point-max)))) + (lambda () (org-escape-code-in-region (point-min) (point-max)))) (and code (org-unescape-code-in-string code))) ;; Finalize buffer. (setq-local org-coderef-label-format diff --git a/testing/lisp/test-org-src.el b/testing/lisp/test-org-src.el index 1386f2237..853e15b45 100644 --- a/testing/lisp/test-org-src.el +++ b/testing/lisp/test-org-src.el @@ -266,5 +266,104 @@ This is a tab:\t. (string-match-p (org-src-coderef-regexp "; ref:%s" "label2") "#+BEGIN_SRC emacs-lisp\n0; ref:label\n#+END_SRC"))) +(ert-deftest test-org-src/indented-blocks () + "Test editing indented blocks." + ;; Editing a block should preserve its global indentation, unless + ;; `org-src-preserve-indentation' is non-nil. + (should + (equal + "- Item\n #+BEGIN_SRC emacs-lisp\n Foo\n #+END_SRC" + (org-test-with-temp-text + "- Item\n #+BEGIN_SRC emacs-lisp\n (+ 1 1)\n #+END_SRC" + (let ((org-edit-src-content-indentation 2) + (org-src-preserve-indentation nil)) + (org-edit-special) + (erase-buffer) + (insert "Foo") + (org-edit-src-exit) + (buffer-string))))) + (should + (equal + "- Item\n #+BEGIN_SRC emacs-lisp\n Foo\n #+END_SRC" + (org-test-with-temp-text + "- Item\n #+BEGIN_SRC emacs-lisp\n (+ 1 1)\n #+END_SRC" + (let ((org-src-preserve-indentation t)) + (org-edit-special) + (erase-buffer) + (insert " Foo") + (org-edit-src-exit) + (buffer-string))))) + ;; Global indentation obeys `indent-tabs-mode' from the original + ;; buffer. + (should + (string-match-p + "^\t+\s*argument2" + (org-test-with-temp-text + " +- Item + #+BEGIN_SRC emacs-lisp + (progn + (function argument1 + argument2)) + #+END_SRC" + (setq-local indent-tabs-mode t) + (let ((org-edit-src-content-indentation 2) + (org-src-preserve-indentation nil)) + (org-edit-special) + (org-edit-src-exit) + (buffer-string))))) + (should + (string-match-p + "^\s+argument2" + (org-test-with-temp-text + " +- Item + #+BEGIN_SRC emacs-lisp + (progn\n (function argument1\n\t\targument2)) + #+END_SRC" + (setq-local indent-tabs-mode nil) + (let ((org-edit-src-content-indentation 2) + (org-src-preserve-indentation nil)) + (org-edit-special) + (org-edit-src-exit) + (buffer-string))))) + ;; Global indentation also obeys `tab-width' from original buffer. + (should + (string-match-p + "^\t\\{3\\}\s\\{2\\}argument2" + (org-test-with-temp-text + " +- Item + #+BEGIN_SRC emacs-lisp + (progn + (function argument1 + argument2)) + #+END_SRC" + (setq-local indent-tabs-mode t) + (setq-local tab-width 4) + (let ((org-edit-src-content-indentation 0) + (org-src-preserve-indentation nil)) + (org-edit-special) + (org-edit-src-exit) + (buffer-string))))) + (should + (string-match-p + "^\t\s\\{6\\}argument2" + (org-test-with-temp-text + " +- Item + #+BEGIN_SRC emacs-lisp + (progn + (function argument1 + argument2)) + #+END_SRC" + (setq-local indent-tabs-mode t) + (setq-local tab-width 8) + (let ((org-edit-src-content-indentation 0) + (org-src-preserve-indentation nil)) + (org-edit-special) + (org-edit-src-exit) + (buffer-string)))))) + (provide 'test-org-src) ;;; test-org-src.el ends here