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): Display the native indentation tab
characters with a fixed width, according to the native tab width
value, to preserve vertical alignement in the org buffer.
* 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 an eventual org part, and the native part.

Link: https://list.orgmode.org/87a5wcez7e.fsf@localhost/T/#t
This commit is contained in:
Sébastien Miquel 2023-06-27 09:23:01 +02:00 committed by Ihor Radchenko
parent 501be358bb
commit 2e2ed40553
No known key found for this signature in database
GPG Key ID: 6470762A7DA11D8B
4 changed files with 94 additions and 70 deletions

View File

@ -402,9 +402,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))))

View File

@ -326,9 +326,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 ]*\"."
@ -481,12 +478,17 @@ 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)
;; Compute the exact sequence of tabs and spaces used to indent up
;; to `indentation-offset' in the Org buffer.
(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))
@ -496,15 +498,14 @@ 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))))
;; Keep empty src lines empty, even when src block is
;; indented on Org side.
;; See https://list.orgmode.org/725763.1632663635@apollo2.minshall.org/T/
(when (not (eolp)) (insert indent-str)) ; not an empty line
(forward-line)))
(set-marker marker nil))))
@ -557,11 +558,6 @@ Leave point in edit buffer."
(org-element-parent datum) nil))
(t (org-current-text-indentation)))))
(content-ind org-edit-src-content-indentation)
(blank-line (save-excursion (forward-line 0)
(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)
@ -611,7 +607,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
@ -645,7 +640,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)
@ -659,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))
@ -716,6 +712,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

View File

@ -19153,21 +19153,14 @@ Also align node properties according to `org-property-format'."
(org-with-point-at (org-element-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-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.

View File

@ -345,11 +345,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
@ -364,14 +364,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)
@ -379,43 +380,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/indented-latex-fragments ()
"Test editing multiline indented LaTeX fragment."