瀏覽代碼

org-src: Fix indentation when tabs are involved

* lisp/org-src.el (org-src--source-type):
(org-src--tab-width): New variables.
(org-src--edit-element): Set variables above.

(org-src--contents-for-write-back): Re-indent properly non-blank lines
before inserting contents back into the source buffer.
(org-edit-src-code): Delegate block indentation to
`org-src--contents-for-write-back'.

* testing/lisp/test-org-src.el (test-org-src/indented-blocks): New
  test.

Reported-by: Brent Goodrick <bgoodr@gmail.com>
<http://permalink.gmane.org/gmane.emacs.orgmode/113207>
Nicolas Goaziou 7 年之前
父節點
當前提交
91236a3db3
共有 2 個文件被更改,包括 132 次插入19 次删除
  1. 33 19
      lisp/org-src.el
  2. 99 0
      testing/lisp/test-org-src.el

+ 33 - 19
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)
@@ -917,16 +940,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

+ 99 - 0
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<point>  #+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<point>  #+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<point>
+  (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<point>
+    (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<point>
+  (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<point>
+  (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