Hi Ihor Thanks for the comments, I've attached the updated patch.
> 1. Use `insert' rather than `insert-char' that is for interactive use. > 2. Use more readable ?\t notation I realized that just inserting the spaces actually did mess up things in some edge-cases so I changed approach. It feels ugly having almost the exact same bit of code within a couple lines, but I don't see an obvious solution. This is also my first time messing with test writing in elisp so hope there's nothing too egregious. While working on it I did however run into two other issues in the indenting behavior that might also need fixing: 1. `org-indent-line' indents the end tag of some blocks to match the begin tag, but doesn't do so with src blocks, among others. MRE: With the pointer before =#+end_src= in the following, do `M-x org-indent-line' #+begin_src fundamental foo #+end_src Expected: line is indented by 2 Actual: line is indented by 4 I thought it was a bit weird since the code for calculating the src block content indent does handle it if the block itself is indented, and it worked fine for other block types. It seems this is because src blocks (+ export, example, et al) aren't greater elements and don't have a `contents-end' property. I was looking at fixing it for all cases but realized it would require messing with the element parsers which I'm honestly really not looking to get into, but I've attached a draft patch that works for src blocks. It's a pretty theoretical problem, but if you think it's worth fixing and that the patch makes sense I'd be happy to clean it up and add a test. 2. At first I just wanted to report that tabbing with `org-src-tab-acts-natively' at nil didn't follow `org-edit-src-content-indentation', but looking into it I realize there's a nuance to it. I think there is one "actual" bug and two ambiguities in intention/design. The description of `org-indent-line' says: > - In the code part of a source block, use language major mode to indent current line if ‘org-src-tab-acts-natively’ is non-nil. *If it is nil, do nothing.* (<-- This is the current behavior of `org-indent-region' and therefore `org-indent-block') > > - Otherwise, *indent like the first non-blank line above.* (<-- This is the > current behavior of `org-indent-line') MRE: With `org-src-tab-acts-natively' set to nil and pointer in content of the following, press TAB. #+begin_src fundamental foo bar #+end_src Expected: Nothing happens. Actual: Indent is set to 3, equal to line above. So that's the actual bug, nothing with the content indent specifically. However, I'm not sure that bringing it in line with the description is necessarily what makes best sense. Right now there's even a test that contradicts the function description, and the description of `org-src-tab-acts-natively' also doesn't say anything about what happens when it is set to nil. I can't speak for the people who already use this setting, but having TAB fall back to the current behavior of `org-indent-line' makes more intuitive sense to me than having TAB do nothing when you disable native indent. I think it needs to be clarified whether the intent of the customization is to completely disable all auto-indentation of code from the org-buffer; versus just disabling the use of temporary edit-buffers at every newline/TAB, e.g. to get around issues with language modes or slow-downs or whatever. (I guess having all three settings could also make sense, since just keeping your fingers off of TAB wouldn't fully achieve the `do nothing' mode.) On the content indent, my confusion arose due to the fact that nothing in the chain of descriptions related to it gave any indication that the customization on code indentation should impact it. 1. `org-src-tab-acts-natively' itself doesn't mention it. 2. The description of `org-edit-src-content-indentation' says nothing about `org-src-tab-acts-natively', it mentions `org-src-preserve-indentation'; but 3. That one also doesn't say anything about `org-src-tab-acts-natively'. It does specifically mention that `org-indent-block' should add the content indent; but 4. The descriptions of `org-indent-block' and `-region', which it relies on, are both very low on detail so the intended behavior is unclear; in any case `org-indent-region' only adds the content indent when native tab is enabled. Point being that it is unclear whether `tab-acts-natively' is at all supposed to change how and when the content indent is added. Currently the content indent is added when returning from the edit buffer, so, despite the bug, it's skipped when native indenting is disabled since the edit buffer isn't used. I personally don't think it makes a lot of sense that the value of `tab-acts-natively' should affect this behavior, see for example the MRE below. IMO it should only regard the code indentation and not whether org indents the block content. MRE for lack of content indentation: With `org-src-tab-acts-natively' and `org-src-preserve-indentation' set to nil and above-zero `org-edit-src-content-indentation', press enter at the end of a src block header. #+begin_src fundamental<pointer> #+end_src My expectation: you are placed at the content indent. Actual: You are placed at column 0. I didn't prepare a patch since my favored solutions would require changes to multiple functions including descriptions, so I thought I'd hear your thoughts before putting in the effort. LRA
>From 583eb40d1ef59261d5114c7e278324ae3774bdb4 Mon Sep 17 00:00:00 2001 From: Lukas Rudd Andersen <l...@phdk.org> Date: Tue, 25 Mar 2025 12:49:24 +0100 Subject: [PATCH] lisp/org.el Fix bug in source-block content indentation * org.el (org-indent-line): Indent to content before indenting with mode. Indent to content before the block contents are put in the edit buffer to indent according to mode, ensuring that the whole block is properly cleaned of content indentation, avoiding unintended over-indentation when the contents of the edit buffer are reinserted. * test-org.el (org-indent-line): Add test. TINYCHANGE --- lisp/org.el | 4 ++++ testing/lisp/test-org.el | 14 ++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/lisp/org.el b/lisp/org.el index e9f11db1e..3fcc03697 100644 --- a/lisp/org.el +++ b/lisp/org.el @@ -19469,6 +19469,10 @@ Also align node properties according to `org-property-format'." (org-with-point-at (org-element-property :begin element) (+ (org-current-text-indentation) org-edit-src-content-indentation))))) + ;; Avoid over-indenting when beginning of a new line is not empty. + ;; https://list.orgmode.org/omcpuwz--...@phdk.org/ + (when block-content-ind + (save-excursion (indent-line-to block-content-ind))) (ignore-errors ; do not err when there is no proper major mode ;; It is important to call `indent-according-to-mode' ;; rather than `indent-line-function' here or we may diff --git a/testing/lisp/test-org.el b/testing/lisp/test-org.el index 2487c9ace..a55c4162e 100644 --- a/testing/lisp/test-org.el +++ b/testing/lisp/test-org.el @@ -1220,8 +1220,9 @@ Otherwise, evaluate RESULT as an sexp and return its result." (org-indent-line) (org-get-indentation))))) ;; Within code part of a source block, use language major mode if - ;; `org-src-tab-acts-natively' is non-nil. Otherwise, indent - ;; according to line above. + ;; `org-src-tab-acts-natively' is non-nil, only add + ;; `org-edit-src-content-indentation' to lines with indentation that + ;; is lower. Otherwise, indent according to line above. (should (= 6 (org-test-with-temp-text @@ -1230,6 +1231,15 @@ Otherwise, evaluate RESULT as an sexp and return its result." (org-edit-src-content-indentation 0)) (org-indent-line)) (org-get-indentation)))) + (should + (= 2 + (org-test-with-temp-text + "#+BEGIN_SRC emacs-lisp\n (and A\n<point>B)\n#+END_SRC" + (let ((org-src-tab-acts-natively t) + (org-edit-src-content-indentation 2)) + (org-indent-line)) + (forward-line -1) + (org-get-indentation)))) (should (= 1 (org-test-with-temp-text -- 2.47.2
>From 8f02b2c9e5c661747afd766dc77fdf59b1b80e81 Mon Sep 17 00:00:00 2001 From: Lukas Rudd Andersen <l...@phdk.org> Date: Fri, 28 Mar 2025 03:16:09 +0100 Subject: [PATCH] lisp/org.el: Indent end of src-blocks. --- lisp/org.el | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/lisp/org.el b/lisp/org.el index e9f11db1e..f1b1e6bac 100644 --- a/lisp/org.el +++ b/lisp/org.el @@ -19354,10 +19354,15 @@ ELEMENT." ;; and contents. ((and post-affiliated (= (line-beginning-position) post-affiliated)) (org--get-expected-indentation element t)) - ;; POS is after contents in a greater element. Indent like - ;; the beginning of the element. - ((and (memq type org-element-greater-elements) - (let ((cend (org-element-contents-end element))) + ;; POS is after contents in a greater element or src-block. + ;; Indent like the beginning of the element. + ((and (or (memq type org-element-greater-elements) + (org-element-type-p element 'src-block)) + (let ((cend (or (org-element-contents-end element) + (org-with-wide-buffer + (goto-char (org-element-end element)) + (skip-chars-backward " \r\t\n") + (line-beginning-position))))) (and cend (<= cend pos)))) ;; As a special case, if point is at the end of a footnote ;; definition or an item, indent like the very last element -- 2.47.2