oantolin / embark

Emacs Mini-Buffer Actions Rooted in Keymaps

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

embark-insert from M-: eval doesn't function correctly

jdtsmith opened this issue · comments

I tend to use M-: a lot to evaluate elisp snippets, but embark does not correctly insert from the mini-buffer when Eval is active:

  1. Recalling an old expression from M-:
  2. mark something with C-,
  3. use i=embark-insert

This either inserts nothing in the original buffer, or inserts at a random location near the top of the buffer instead of at point.

"Random location" sounds like a fun debugging session is in store for me. :)

Wait a minute, I think you are using an outdated Embark. A little while ago I changed things so that if a minibuffer is not in a completion session it gets treated as a regular buffer. So if you use embark-insert from within M-:, it should insert into the minibuffer itself.

We can argue whether that change is the right decision or not, but it does mean this bug is not currently present in Embark.

That explains why it inserted in minibuffer with emacs -Q. I updated to latest MELPA build, but apparently a restart is needed for that fix to take. Random is overstating it; it was usually somewhere non-obvious on the first line ;).

But yes, insert from minibuffer back to the minibuffer is... not so useful.

But yes, insert from minibuffer back to the minibuffer is... not so useful.

I use that to duplicate s-expressions before modifying the duplicate.

Why are you inserting from M-: into the buffer rather than typing into the buffer in the first place?

In general, I feel that at the M-: prompt I'm editing Lisp code so I want Embark to behave as it would in any lisp mode buffer.

I would be interested in knowing your use case for inserting from the M-: into the buffer you were in before. I'm open to changing my mind about wanting non-completing-read minibuffers to be treated as regular, non-mini buffers.

Well... I sort of abuse M-: as a tiny on-demand REPL, and experiment with lots of little pieces of elisp code there. If one of those becomes relevant for documenting something in a org document or inserting in "real" code in an elisp buffer, I tend to pull up my "mini-REPL", search back to find the code of interest, mark some part of it with embark, then (fail to) insert it with i. It's not much harder to copy and paste it, but my "get something from the mini-buffer into my main buffer" automatic habit kicks in, since I do this with function and variable names quite a bit.

But that makes me realize: what if I want to pull something from a python REPL buffer into an org buffer? Maybe what I really want is an "insert selection into the last selected buffer" action (i.e. into (other-buffer (current-buffer) t)). This could be used from any minibuffer or real buffer. Just leave point where you want it, switch to the other buffer or mini-buffer, find the thing of interest, then embark-insert-last-buffer.

There is an append-to-buffer command you could use for this. It would need some hooks though, the same as the hooks Embark configures for the similar append-to-file command: embark--ignore-target and embark--mark-target. So you could try something like:

(push 'embark--ignore-target
      (alist-get 'append-to-buffer embark-target-injection-hooks))

(push 'embark--mark-target
      (alist-get 'append-to-buffer embark-around-action-hooks))

(keymap-set embark-general-map "j" #'append-to-buffer) ;; not sure what a good binding would be

I'll add the hook configuration to embark, but probably not the key binding.

Of course, you'd still have to select the buffer with the append-to-buffer command, but the extra flexibility might be nice.

But all in all, I think I want to keep things as they are with regards to treating non-completing-read minibuffers as normal buffers, specially M-:, since what you do there is edit Lisp code. And I agree that you should use a different action for the purpose you mentioned, either append-to-buffer or a custom action if you can get the other-buffer logic to work predictably enough (I tend to think (other-window-for-scrolling) is more predictable for Emacsers than (other-buffer (current-buffer) t)).

A good binding for append-to-buffer might be ^ since it looks like the proofreader's symbol for an insertion. I also like » if you can type it easily (I've setup my keyboard to type that easily because we use « and » for quotations in Spanish).

Good thoughts, I'll take a look at append-to-buffer, and I did not know of other-window-for-scrolling, good idea.

Gets me wondering though: what do you use insert at the M-: eval prompt for? To pluck from a recursive completing-read minibuffer? I use corfu in the mini-buffer during M-: so that might explain why I would rarely reach for that.

Gets me wondering though: what do you use insert at the M-: eval prompt for?

Well, I don't use it much, but sometimes to duplicate a symbol or s-expression, usually in order to modify the copy. For example, say I'm in some buffer and I want to know the buffer-local value of two similarly named variables, like embark--target-buffer and embark--target-window. I'll use M-: (list embark--target-buffer embark--target-window) where I get the second variable's name by C-. i C-<backspace> w <TAB>.

But notice that the change I made in Embark wasn't specific to embark-insert's behavior. Embark treats the minibuffer specially in several ways, one of which is the notion of "target buffer". Actions are run in the target buffer, and for minibuffer completion sessions, that target buffer is the buffer in which you ran the command that opened the minibuffer. For actions in a regular buffer, the target buffer is that same buffer. What about non-completing-read minibuffers? Without thinking about it I had chosen to follow the same reue as for completing minibuffers: the target buffer is the buffer from which the command that opened the minibuffer ran. But it doesn't really make sense: the idea behind that rule is that the completion candidates are often something somehow related to the buffer in which you ran the command, and that logic just doesn't apply to non-completion minibuffers.

So, from my point of view the determinantion of the target buffer for the non-completing-read case of minibuffers had been buggy for along time. It's not about embark-insert specifically. Indeed, what bothered me most was probably DEL: I often delete an s-expression via C-. DEL and in M-: this, very problematically, deleted some s-expression from the previous buffer!

To pluck from a recursive completing-read minibuffer?

That doesn't work like that: insert from a recursive completing-read minibuffer will insert into the previous buffer that is non-mini (technically into (window-buffer (minibuffer-selected-window))).

Thanks. I use lispy at the eval prompt so do all my sexp slinging with it (though looking to retire it).

I've been trying to get this to work. I added the following:

(push 'embark--ignore-target
      (alist-get 'my/append-to-buffer embark-target-injection-hooks))

(push 'embark--mark-target
      (alist-get 'my/append-to-other-buffer embark-around-action-hooks))
(keymap-set embark-general-map "^" (defalias 'my/append-to-other-buffer
                                     (lambda ()
                                       (interactive)
                                       (when-let ((win (or (minibuffer-selected-window)
							   (other-window-for-scrolling))))
					 (append-to-buffer (window-buffer win)
							   (region-beginning) (region-end))))))

with ignore/mark target set for this command. But it seems that general commands run from the mini-buffer are run with the main buffer active, even if it's not a completing-read minibuffer. I must be doing something silly. Or maybe that's eval-expression getting in the way by setting the original buffer as current.

But it seems that general commands run from the mini-buffer are run with the main buffer active, even if it's not a completing-read minibuffer.

I don't think that's the case as evidenced by embark-insert inserting into the minibuffer itself when called from eval-expression.

Your code works for me (though I only used mark-target; not ignore-target: since you pass all parameters to apend-to-buffer, it won't prompt for anything).

Oh, are you calling it from a recursive minibuffer where the outer minibuffer is in a completion session? In that case Embark is fooled into thinking the inner minibuffer is also in a completion session and will run the action in (minibuffer-selected-window). 😬

Good point about ignore-target. It does work well between buffers. But not from eval-expression. I'm calling it from within M-:, so not a completion. I invoke that, use arrow keys to browse to a prior eval input, mark something with C-,, then hit ^ (which is a really nice binding, btw). minibuffer-selected-window is always nil in my called custom command, and the current buffer is the original buffer from which M-: was invoked. So you can't pull anything out of the minibuffer. I also tried this in a minibuffer with completion; same behavior.

minibuffer-selected-window is always nil in my called custom command,

That's worrisome. I tried your code in my Emacs to insert from M-: and minibuffer-selected-window is, as expected, the buffer from which I ran M-:. I even added (message "MSW = %s" (minibuffer-selected-window)) to your function and it printed msw = #<window 3 on *scratch*>, which is what I expected.

I think it must have been from the unwanted target injection leaving things in a bad state. After a restart it works fine. Going to use ^ quite a bit I think.

OK, but one misbehavior remains: if I override quit-after-action with C-u, (minibuffer-selected-window) goes back to being nil, and (not sure this is relevant) Minibuf-0 is the current buffer instead of Minibuf-1.