0

I tried different combinations of save-excursion or ielm-change-working-buffer, but no success. I'm currently trying to see if there's a way to save a point in a buffer (using registers?) and run a function in IELM as if it was at that point.

Would anyone have suggestions on how I can make this work?

1
  • To move point, simply use goto-char.
    – phils
    Commented Dec 15, 2023 at 14:12

2 Answers 2

1

IIUC, you have a buffer, say "foo.bar", with point in that buffer at some position, say 23, and you have saved that position in a register, say register a, with C-x r <SPACE> a RET. If you do M-x get-register RET a RET you verify that register a contains #<marker at 23 in foo.bar>.

Now you go into ielm: M-x ielm . First verify that ielm knows about register a:

ELISP> (get-register ?a)
#<marker at 23 in foo.bar>

A marker is a position in a buffer. You can get the buffer out of the marker with marker-buffer and the position with marker-position. So we can make the marker buffer current temporarily, go to the marker position and evaluate any function we want there:


ELISP> (let ((m (get-register ?a)))
          (with-current-buffer (marker-buffer m)
            (goto-char (marker-position m))
            (format "point: %d, buffer: %S" (point) (current-buffer))))
"point: 23, buffer: #<buffer foo.bar>"

ELISP> (current-buffer)
#<buffer *ielm*>

In this case, it's a format call with the value of point and the current buffer as arguments to verify that we are indeed where we think we are. Instead of format, you can run whatever function you want. Then we are back in the ielm buffer after with-current-buffer is done.

You can of course package the boilerplate in a function:

(defun ielm-run-at-register (register func &rest args)
  (let ((m (get-register register)))
    (with-current-buffer (marker-buffer m)
      (goto-char (marker-position m))
      (apply func args))))

(defun foo ()
  (format "point: %d, buffer: %S" (point) (current-buffer)))

and then the invocation in ielm becomes:

ELISP> (ielm-run-at-register ?a 'foo)
"point: 23, buffer: #<buffer foo.bar>"
1

I can not find any hook that gets triggered before a buffer change. Therefore this solution updates a buffer-local last-buffer-position via the post-command-hook.

(defvar-local last-buffer-position nil)

(defun update-last-buffer-position ()
  (setq last-buffer-position (point)))

(add-hook 'post-command-hook #'update-last-buffer-position)

(defun ielm-eval-at-point-last-buffer ()
  (interactive)
  (let ((sexp (with-current-buffer (seq-find (lambda (b)
                                               (not (minibufferp b)))
                                             (cdr (buffer-list)))
                (goto-char last-buffer-position)
                (or (thing-at-point 'line) (thing-at-point 'sexp)))))
    (insert sexp)
    (ielm-return)))

To find the previous buffer, it simply searches the buffer-list for the first 'non-minibuffer' that is not the current buffer.

I am not sure how you would define 'function at point', I guess you mean some kind of 'sexp'. Here I chose to first look for a list at point and otherwise a sexp. You can call the command in ielm using M-x ielm-eval-at-point-last-buffer.

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.