emacs-orgmode
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [O] org-src--contents-for-write-back : preserve original major-mode,


From: Brent Goodrick
Subject: Re: [O] org-src--contents-for-write-back : preserve original major-mode, and avoid mixing tabs and spaces in org-mode buffers
Date: Sat, 22 Apr 2017 20:21:07 -0700

On Sat, Apr 22, 2017 at 1:41 AM, Nicolas Goaziou <address@hidden> wrote:
>
> > The bug is in the `org-src--contents-for-write-back' function. It
> > uses a temp buffer. The temp buffer's major-mode is left to be
> > the default, which is fundamental-mode, which knows nothing about
> > how to indent lisp code properly.
>
> And it doesn't need to. This function doesn't care about the code, but
> indents it rigidly according to original source block.  IOW, I don't
> think changing the major mode is required.
>
> > So in the fix below, I run the major-mode function from the original
> > buffer. But even with that fix, the indentation must also use spaces
> > in order to avoid mixing tabs and spaces in the resulting Org buffer.
>
> Why do you think it is a good thing that tabs and spaces shouldn't be
> mixed. For example, imagineh that the source code requires
> `indent-tabs-mode' being non-nil, but Org source buffer indentation is
> space only, i.e., with `indent-tabs-mode' being nil.

Thanks for looking into this!

In the current implementation of org-src--contents-for-write-back, the
`with-temp-buffer` uses fundamental-mode.  Later, while inside that
temp buffer, spaces are inserted in to indent the entire source block
over to where it needs to be (in my original post, notice that I have
the source block within a list item so the source block needs to be
aligned properly under that list item, no matter to what depth that
list item is). It is in mode hook functions that certain changes to
indentation can occur, so that is why I'm switching into that
mode. But that is not enough: In order for the text to be aligned
properly inside the org mode buffer after indentation, there cannot be
a mix of tabs and spaces, as shown in my original post. IIRC,
`indent-to' is called within the `write-back' function, and
`indent-to' is affected by the `indent-tabs-mode' value, which by
default in emacs lisp mode buffers, is t.

You might try my implementation both without the change to
`indent-tabs-mode' to see how improperly indented the resulting source
block looks.

>
> Shouldn't the resulting block be indented with spaces from column 0 to
> block boundaries' indentation, and then follow with space indentation?
>

Yes, if I understand what you are meaning. In fact, I think that is,
in effect, what my replacement function is doing.  Basically the
bottom line is that you can't mix tabs and spaces in the final Org
buffer and have it look correctly indented in that buffer, and the
change to indent-tabs-mode to nil will be buffer local inside that
with-temp-buffer so nothing is affected in any other buffer.

For your reference, below is my replacement function that has
elaborated comments that hopefully clarify things a bit more:

(defun org-src--contents-for-write-back-fix-indentation ()
  "Return buffer contents in a format appropriate for write back.
        Assume point is in the corresponding edit buffer."
  (let ((indentation (or org-src--block-indentation 0))
        (preserve-indentation org-src--preserve-indentation)
        (contents (org-with-wide-buffer (buffer-string)))
        (write-back org-src--allow-write-back))
    ;; --- BEGIN CHANGES FROM ORIGINAL DEFINITION ---
    ;;
    ;; Save off the original mode into orig-major-mode:
    ;;
    (let ((orig-major-mode major-mode))
      (with-temp-buffer
        ;;
        ;; Insert the contents as was done before:
        ;;
        (insert (org-no-properties contents))
        ;;
        ;; First change: Switch to the original mode for indentation by
        ;; switching its mode to be the original one. This is because that mode
        ;; handles mode-specific indentation behavior:
        ;;
        (funcall orig-major-mode)
        ;;
        ;; Second change: Do not use tabs here. If the mode being switched to
        ;; has indent-tabs-mode set to t, that is fine for separate buffers, but
        ;; for when org source blocks are shown in Org buffers, mixing tabs and
        ;; spaces will mess up the view (see this for emacs lisp code blocks
        ;; when indent-tabs-mode is set to t) when write-back calls `indent-to'.
        ;;
        ;; The alternative is to require everyone to set indent-tabs-mode to nil
        ;; in their mode hooks for all modes used in Org mode, but that seems
        ;; slightly heavy-handed.
        ;;
        (setq indent-tabs-mode nil)
        ;; --- END CHANGES FROM ORIGINAL DEFINITION ---
        (goto-char (point-min))
        (when (functionp write-back) (funcall write-back))
        (unless (or preserve-indentation (= indentation 0))
          (let ((ind (make-string indentation ?\s)))
            (goto-char (point-min))
            (while (not (eobp))
              (when (looking-at-p "[ \t]*\\S-") (insert ind))
              (forward-line))))
        (buffer-string)))))

I am curious to see if there is a better fix that what I've coded up above.

Thanks again for your help,
-Brent



reply via email to

[Prev in Thread] Current Thread [Next in Thread]