From 9d31a71bc0ab7cfd466ecad60037d00c62bdd9f6 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.el (org-indent-line): Simplify native indentation inside src block. Ensure we add the org indentation if the line is empty. * lisp/org-macs.el (org-do-remove-indentation): Preserve indentation (spaces vs tabs) past the common indentation to remove. Do not empty blank lines. * 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 | 9 +++-- lisp/org-src.el | 52 ++++++++++++++----------- lisp/org.el | 23 ++++------- testing/lisp/test-org-src.el | 75 ++++++++++++++++++++++-------------- 4 files changed, 89 insertions(+), 70 deletions(-) diff --git a/lisp/org-macs.el b/lisp/org-macs.el index 51dbfe118..ea210dc60 100644 --- a/lisp/org-macs.el +++ b/lisp/org-macs.el @@ -483,9 +483,12 @@ line. Return nil if it fails." (when skip-fl (forward-line)) (while (not (eobp)) (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)))) + (cond ((< ind n) + (if (eolp) (delete-region (line-beginning-position) (point)) + (throw :exit nil))) + (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 f15ba8e99..2beb49e63 100644 --- a/lisp/org-src.el +++ b/lisp/org-src.el @@ -318,9 +318,6 @@ is 0.") "File name associated to Org source buffer, or nil.") (put 'org-src-source-file-name 'permanent-local t) -(defvar-local org-src--preserve-blank-line nil) -(put 'org-src--preserve-blank-line 'permanent-local t) - (defun org-src--construct-edit-buffer-name (org-buffer-name lang) "Construct the buffer name for a source editing buffer. Format is \"*Org Src ORG-BUFFER-NAME[ LANG ]*\"." @@ -473,12 +470,15 @@ Assume point is in the corresponding edit buffer." (list (buffer-substring (point-min) eol) (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)) ;; Apply WRITE-BACK function on edit buffer contents. (insert (org-no-properties (car contents))) (setq marker (point-marker)) @@ -488,15 +488,11 @@ Assume point is in the corresponding edit buffer." ;; Add INDENTATION-OFFSET to every line in buffer, ;; unless indentation is meant to be preserved. (when (> indentation-offset 0) - (when preserve-fl (forward-line)) + ;; LaTeX-fragments are inline. Do not add indentation to their + ;; first line. + (when preserve-fl (forward-line)) (while (not (eobp)) - (skip-chars-forward " \t") - (when (or (not (eolp)) ; not a blank line - (and (eq (point) (marker-position marker)) ; current line - preserve-blank-line)) - (let ((i (current-column))) - (delete-region (line-beginning-position) (point)) - (indent-to (+ i indentation-offset)))) + (when (not (eolp)) (insert indent-str)) ; not an empty line (forward-line))) (set-marker marker nil)))) @@ -549,11 +545,6 @@ Leave point in edit buffer." (org-element-property :parent datum) nil)) (t (org-current-text-indentation))))) (content-ind org-edit-src-content-indentation) - (blank-line (save-excursion (beginning-of-line) - (looking-at-p "^[[:space:]]*$"))) - (empty-line (and blank-line (looking-at-p "^$"))) - (preserve-blank-line (or (and blank-line (not empty-line)) - (and empty-line (= (+ block-ind content-ind) 0)))) (preserve-ind (and (memq type '(example-block src-block)) (or (org-element-property :preserve-indent datum) @@ -603,7 +594,6 @@ Leave point in edit buffer." (setq org-src--overlay overlay) (setq org-src--allow-write-back write-back) (setq org-src-source-file-name source-file-name) - (setq org-src--preserve-blank-line preserve-blank-line) ;; Start minor mode. (org-src-mode) ;; Clear undo information so we cannot undo back to the @@ -637,7 +627,7 @@ 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) @@ -651,6 +641,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)) @@ -708,6 +699,21 @@ 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 native tab indentation characters as spaces + (save-excursion + (goto-char start) + (let ((indent-offset + (if org-src-preserve-indentation 0 + (+ (progn (backward-char) + (org-current-text-indentation)) + org-edit-src-content-indentation)))) + (while (re-search-forward "^[ ]*\t" end t) + (let* ((b (and (eq indent-offset (move-to-column indent-offset)) + (point))) + (e (progn (skip-chars-forward "\t") (point))) + (s (and b (make-string (* (- e b) native-tab-width) ? )))) + (when (and b (< b e)) (add-text-properties b e `(display ,s))) + (forward-char))))) ;; Clear abbreviated link folding. (org-fold-region start end nil 'org-link) (add-text-properties diff --git a/lisp/org.el b/lisp/org.el index 4063ba98f..cda674919 100644 --- a/lisp/org.el +++ b/lisp/org.el @@ -19086,21 +19086,14 @@ Also align node properties according to `org-property-format'." (org-with-point-at (org-element-property :end element) (skip-chars-backward " \t\n") (line-beginning-position)))) - ;; At the beginning of a blank line, do some preindentation. This - ;; signals org-src--edit-element to preserve the indentation on exit - (when (and (looking-at-p "^[[:space:]]*$") - (not org-src-preserve-indentation)) - (let (block-content-ind some-ind) - (org-with-point-at (org-element-property :begin element) - (setq block-content-ind (+ (org-current-text-indentation) - org-edit-src-content-indentation)) - (forward-line) - (save-match-data (re-search-forward "^[ \t]*\\S-" nil t)) - (backward-char) - (setq some-ind (if (looking-at-p "#\\+end_src") - block-content-ind (org-current-text-indentation)))) - (indent-line-to (min block-content-ind some-ind)))) - (org-babel-do-key-sequence-in-edit-buffer (kbd "TAB"))) + (let ((block-content-ind + (when (not org-src-preserve-indentation) + (org-with-point-at (org-element-property :begin element) + (+ (org-current-text-indentation) + org-edit-src-content-indentation))))) + (org-babel-do-key-sequence-in-edit-buffer (kbd "TAB")) + (when (and block-content-ind (looking-at-p "^$")) + (indent-line-to block-content-ind)))) (t (let ((column (org--get-expected-indentation element nil))) ;; Preserve current column. diff --git a/testing/lisp/test-org-src.el b/testing/lisp/test-org-src.el index 2a45ba66e..c4309ccfc 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 @@ -323,14 +323,15 @@ This is a tab:\t. (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 +339,59 @@ 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))))) + ;; The initial tab characters respect org's `tab-width'. + (should + (equal + 10 + (org-test-with-temp-text + " +#+BEGIN_SRC emacs-lisp +\t(progn +\t (list argument1\n\t\targument2)) +#+END_SRC" + (setq-local indent-tabs-mode t) + (setq-local tab-width 2) + (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