Ihor Radchenko writes:
For the second scenario, no special treatment of current line is needed.
For the first scenario, why do we need to do it all the way in
`org-src--contents-for-write-back'? Why not directly in
`org-indent-line'?
Ah, yes, that is much better.
--
Sébastien Miquel
From 9d31a71bc0ab7cfd466ecad60037d00c62bdd9f6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=A9bastien=20Miquel?= <sebastien.miq...@posteo.eu>
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<point>
- (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<point>
+#+BEGIN_SRC emacs-lisp
(progn
- (function argument1
- argument2))
- #+END_SRC"
+ (list argument1\n \t<point>argument2))
+#+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<point>
+ "
+#+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 \t<point>argument2))
+#+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\t<point>argument2))
+#+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