emacs-orgmode@gnu.org archives
 help / color / mirror / code / Atom feed
From: Brent Goodrick <bgoodr@gmail.com>
To: Nicolas Goaziou <mail@nicolasgoaziou.fr>
Cc: emacs-orgmode@gnu.org
Subject: Re: 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	[thread overview]
Message-ID: <CAJj=8EFkL9=QXGf3AdD8FUM=yK4_4p_ZHB-Bp2q5qq0-ELZWow@mail.gmail.com> (raw)
In-Reply-To: <877f2cvnpe.fsf@nicolasgoaziou.fr>

On Sat, Apr 22, 2017 at 1:41 AM, Nicolas Goaziou <mail@nicolasgoaziou.fr> 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	other threads:[~2017-04-23  3:21 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-04-03  4:15 org-src--contents-for-write-back : preserve original major-mode, and avoid mixing tabs and spaces in org-mode buffers Brent Goodrick
2017-04-22  8:41 ` Nicolas Goaziou
2017-04-23  3:21   ` Brent Goodrick [this message]
2017-04-27 23:04     ` Nicolas Goaziou
2017-04-28 15:29       ` Brent Goodrick
2017-04-29  9:59         ` Nicolas Goaziou
2017-04-29 16:41           ` Brent Goodrick

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.orgmode.org/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to='CAJj=8EFkL9=QXGf3AdD8FUM=yK4_4p_ZHB-Bp2q5qq0-ELZWow@mail.gmail.com' \
    --to=bgoodr@gmail.com \
    --cc=emacs-orgmode@gnu.org \
    --cc=mail@nicolasgoaziou.fr \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).