From ab48e9671efdaba6566f406b1df81a441c72252c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Miquel?= Date: Tue, 27 Jun 2023 09:23:01 +0200 Subject: [PATCH] org-src.el: Use native value of `indent-tabs-mode' for indentation * lisp/org-macs.el (org-do-remove-indentation): Preserve indentation (spaces vs tabs) past the common indentation to remove. * lisp/org-src.el (org-src--contents-for-write-back): Preserve the native indentation (spaces vs tabs). If necessary, add a common org indentation to the block according to org's `indent-tabs-mode'. (org-src-font-lock-fontify-block): In case of mixed indentation, display the tab characters with a fixed width, according to the native tab width value. * testing/lisp/test-org-src.el (test-org-src/indented-blocks): Update tests. Indentation no longer obeys `indent-tabs-mode' from the org buffer, but is separated in two parts. --- lisp/org-macs.el | 4 ++- lisp/org-src.el | 36 ++++++++++++++------- testing/lisp/test-org-src.el | 62 +++++++++++++++++++----------------- 3 files changed, 60 insertions(+), 42 deletions(-) diff --git a/lisp/org-macs.el b/lisp/org-macs.el index 51dbfe118..f42e6b14b 100644 --- a/lisp/org-macs.el +++ b/lisp/org-macs.el @@ -485,7 +485,9 @@ line. Return nil if it fails." (let ((ind (progn (skip-chars-forward " \t") (current-column)))) (cond ((eolp) (delete-region (line-beginning-position) (point))) ((< ind n) (throw :exit nil)) - (t (indent-line-to (- ind n)))) + (t (delete-region (line-beginning-position) + (progn (move-to-column n t) + (point))))) (forward-line))) ;; Signal success. t)))) diff --git a/lisp/org-src.el b/lisp/org-src.el index 5c272c7f5..5a1030c42 100644 --- a/lisp/org-src.el +++ b/lisp/org-src.el @@ -473,11 +473,15 @@ Assume point is in the corresponding edit buffer." (buffer-substring eol (point-max)))))) (write-back org-src--allow-write-back) (preserve-blank-line org-src--preserve-blank-line) - marker) + marker indent-str) + (setq indent-str + (with-temp-buffer + ;; Reproduce indentation parameters from org buffer. + (setq indent-tabs-mode use-tabs?) + (when (> source-tab-width 0) (setq tab-width source-tab-width)) + (indent-to indentation-offset) + (buffer-string))) (with-current-buffer write-back-buf - ;; Reproduce indentation parameters from source buffer. - (setq indent-tabs-mode use-tabs?) - (when (> source-tab-width 0) (setq tab-width source-tab-width)) (insert (org-no-properties (car contents))) (setq marker (point-marker)) (insert (org-no-properties (car (cdr contents)))) @@ -486,13 +490,12 @@ Assume point is in the corresponding edit buffer." ;; unless indentation is meant to be preserved. (when (> indentation-offset 0) (while (not (eobp)) - (skip-chars-forward " \t") - (when (or (not (eolp)) ; not a blank line - (and (eq (point) (marker-position marker)) ; current line + (when (or (not (eolp)) ; not an empty line + ;; If the current line is empty, we may + ;; want to indent it. + (and (eq (point) (marker-position marker)) preserve-blank-line)) - (let ((i (current-column))) - (delete-region (line-beginning-position) (point)) - (indent-to (+ i indentation-offset)))) + (insert indent-str)) (forward-line))) ;; Apply WRITE-BACK function on edit buffer contents. (goto-char (point-min)) @@ -636,7 +639,8 @@ Leave point in edit buffer." "Fontify code block between START and END using LANG's syntax. This function is called by Emacs' automatic fontification, as long as `org-src-fontify-natively' is non-nil." - (let ((modified (buffer-modified-p))) + (let ((modified (buffer-modified-p)) + native-tab-width) (remove-text-properties start end '(face nil)) (let ((lang-mode (org-src-get-lang-mode lang))) (when (fboundp lang-mode) @@ -650,6 +654,7 @@ as `org-src-fontify-natively' is non-nil." ;; Add string and a final space to ensure property change. (insert string " ")) (unless (eq major-mode lang-mode) (funcall lang-mode)) + (setq native-tab-width tab-width) (font-lock-ensure) (let ((pos (point-min)) next) (while (setq next (next-property-change pos)) @@ -707,6 +712,15 @@ as `org-src-fontify-natively' is non-nil." (when (or (facep src-face) (listp src-face)) (font-lock-append-text-property start end 'face src-face)) (font-lock-append-text-property start end 'face 'org-block)) + ;; Display tab indentation characters preceded by spaces as spaces + (unless org-src-preserve-indentation + (save-excursion + (goto-char start) + (while (re-search-forward "^[ ]+\\([\t]+\\)" end t) + (let* ((b (match-beginning 1)) + (e (match-end 1)) + (s (make-string (* (- e b) native-tab-width) ? ))) + (add-text-properties b e `(display ,s)))))) ;; Clear abbreviated link folding. (org-fold-region start end nil 'org-link) (add-text-properties diff --git a/testing/lisp/test-org-src.el b/testing/lisp/test-org-src.el index 2a45ba66e..11d56209d 100644 --- a/testing/lisp/test-org-src.el +++ b/testing/lisp/test-org-src.el @@ -304,11 +304,11 @@ This is a tab:\t. (insert " Foo") (org-edit-src-exit) (buffer-string))))) - ;; Global indentation obeys `indent-tabs-mode' from the original - ;; buffer. - (should + ;; Global indentation does not obey `indent-tabs-mode' from the + ;; original buffer. + (should-not (string-match-p - "^\t+\s*argument2" + "\t" (org-test-with-temp-text " - Item @@ -318,19 +318,21 @@ This is a tab:\t. argument2)) #+END_SRC" (setq-local indent-tabs-mode t) - (let ((org-edit-src-content-indentation 2) + (let ((tab-width 8) + (org-edit-src-content-indentation 2) (org-src-preserve-indentation nil)) (org-edit-special) (org-edit-src-exit) (buffer-string))))) + ;; Tab character is preserved (should (string-match-p - "^\s+argument2" + "\targument2" (org-test-with-temp-text " - Item #+BEGIN_SRC emacs-lisp - (progn\n (function argument1\n\t\targument2)) + (progn\n (function argument1\n \targument2)) #+END_SRC" (setq-local indent-tabs-mode nil) (let ((org-edit-src-content-indentation 2) @@ -338,43 +340,43 @@ This is a tab:\t. (org-edit-special) (org-edit-src-exit) (buffer-string))))) - ;; Global indentation also obeys `tab-width' from original buffer. + ;; Indentation does not obey `tab-width' from org buffer. (should (string-match-p - "^\t\\{3\\}\s\\{2\\}argument2" + "^ \targument2" (org-test-with-temp-text " -- Item - #+BEGIN_SRC emacs-lisp +#+BEGIN_SRC emacs-lisp (progn - (function argument1 - argument2)) - #+END_SRC" + (list argument1\n \targument2)) +#+END_SRC" (setq-local indent-tabs-mode t) (setq-local tab-width 4) - (let ((org-edit-src-content-indentation 0) + (let ((org-edit-src-content-indentation 2) (org-src-preserve-indentation nil)) (org-edit-special) + (setq-local indent-tabs-mode t) + (setq-local tab-width 8) + (lisp-indent-line) (org-edit-src-exit) (buffer-string))))) + ;; Tab characters are displayed with `tab-width' from the native + ;; edit buffer. (should - (string-match-p - "^\t\s\\{6\\}argument2" + (equal + 10 (org-test-with-temp-text - " -- Item - #+BEGIN_SRC emacs-lisp + " +#+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)))))) + (list argument1\n \targument2)) +#+END_SRC" + (setq-local indent-tabs-mode t) + (setq-local tab-width 4) + (let ((org-edit-src-content-indentation 2) + (org-src-preserve-indentation nil)) + (font-lock-ensure) + (current-column)))))) (ert-deftest test-org-src/footnote-references () "Test editing footnote references." -- 2.41.0