1

I want to copy text from one buffer to another. With point in the source buffer, I want to insert text in the target buffer. In the target, the text should insert at point, a new line be opened, and the point should move to the beginning of the new line. When I later switch to the target buffer, the point should be at the start of the new line.

I'm able to do all that except the last bit; when I eventually switch to the target buffer, the point is still on the first line, rather than on the newline.

Here's a reduced example. Open two buffers, a *scratch* buffer (target) and something else (source). When I run the following in the source buffer,

 (with-current-buffer "*scratch*"
          (insert "first")
          (end-of-line)
          (newline-and-indent))

 (with-current-buffer "*scratch*"
          (insert "second")
          (end-of-line)
          (newline-and-indent))

 (with-current-buffer "*scratch*"
          (insert "third")
          (end-of-line)
          (newline-and-indent))

I expect point to be here when I switch to *scratch* (target),

first
second
third
| <-- point

Instead, I get,

|first
second
third

When I execute insert, end-of-line and newline-and-indent with M-:, the behavior is as expected. Why does it work as expected with eval-expression but not when run programmatically?

Because there is (legitimate) concern of an X-Y problem, here's everything I'm working with. The problem, so far as I can tell, is in the with-current-buffer section, as demonstrated above. Maybe there's another way to insert text and step?

(defvar my-on-demand-window nil
  "Target on-demand window.

An on-demand window is one which you wish to return to within the
current Emacs session but whose importance doesn't warrant a
permanent binding.")

(defun my-set-on-demand-window ()
  "Set the value of the on-demand window to current window."
  (interactive)
  (setq my-on-demand-window (selected-window))
  (message "Set on-demand window to: %s" my-on-demand-window))

(defun my-send-line-or-region (&optional beg end buff)
  "Send region defined by BEG and END to BUFF.

Use current region if BEG and END not provided.  If no region
provided, send entire line.  Default BUFF is the buffer displayed
in `my-on-demand-window'."
  (interactive (if (use-region-p)
                   (list (region-beginning) (region-end) nil)
                 (list nil nil nil)))
  (let* ((beg (or beg (if (use-region-p) (region-beginning)) nil))
         (end (or end (if (use-region-p) (region-end)) nil))
         (substr (or (and beg end (buffer-substring-no-properties beg end))
                     (buffer-substring-no-properties (line-beginning-position) (line-end-position))))
         (buff (or buff (window-buffer my-on-demand-window))))
    (if substr
        (with-current-buffer buff
          (insert substr)
          (end-of-line)
          (newline-and-indent))
      (error "Invalid selection"))))

I am using GNU Emacs 27.1 (build 1, x86_64-w64-mingw32) of 2020-08-21.

3
  • To be clear, are you really looking for an explanation of why what you see is what happens, or are you really just trying to get point to end up at the end of the buffer? This kind of sounds like an X-Y problem.
    – Drew
    Dec 22, 2020 at 5:19
  • I am interested in both the how and the why. If someone shows me how to get the desired behavior, great. I would prefer, however, to understand why the unexpected behavior occurs so that I can solve related problems in the future. Jan 6, 2021 at 20:04
  • Seems related to emacs.stackexchange.com/questions/576/… Jan 6, 2021 at 20:17

1 Answer 1

1

Per (elisp) Marker Insertion Types,

When you insert text directly at the place where a marker points, there are two possible ways to relocate that marker: it can point before the inserted text, or point after it. You can specify which one a given marker should do by setting its insertion type.

Point and mark are buffer local, but the insertion type is controlled by the window. You can use the window-point-insertion-type variable to control whether insertions happen before (default) or after point.

Therefore, to get the desired behavior, first select the window associated with the target buffer, set the insertion type, and then do the insert:

(with-selected-window (get-buffer-window "*scratch*")
  (setq-local window-point-insertion-type t)
  (insert "first")
  (end-of-line)
  (newline-and-indent))

(with-selected-window (get-buffer-window "*scratch*")
  (setq-local window-point-insertion-type t)
  (insert "second")
  (end-of-line)
  (newline-and-indent))

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.