oantolin / embark Goto Github PK
View Code? Open in Web Editor NEWEmacs Mini-Buffer Actions Rooted in Keymaps
License: GNU General Public License v3.0
Emacs Mini-Buffer Actions Rooted in Keymaps
License: GNU General Public License v3.0
make predicate- and regexp-based classifiers (or a generator for these)
reuse thingatpt.el machinery somehow (can't verbalise the exact idea yet)
to be continued …
When executing embark-exit-and-act
the minibuffer closes immediately and if the action invokes the minibuffer again it reopens. With selectrum the minibuffer bumps down and after you invoke the action bumps up again. Ideally one would keep the minibuffer open to avoid the visual noise and also the target wouldn't vanish in front of your eyes before you press the action key but that doesn't seem to be possible within the constraints of the minibuffer API.
Emacs wouldn't be Emacs if there wouldn't be a way to get what you want ;) I experimented a bit and found a way to avoid that visual noise:
(add-hook 'embark-pre-action-hook
(lambda () (setq inhibit-redisplay nil)))
(defun embark-exit-and-act ()
"Exit the minibuffer and embark upon an action."
(interactive)
(run-at-time 0 nil (lambda ()
(setq inhibit-redisplay t)))
(embark--setup) ; setup now
(run-at-time 0 nil #'embark--start) ; start action later
(top-level))
Note that with selectrum the embark act message
triggered from embark--start
shows in the echo area and does not flash the prompt. But if a message is shown could be configurable as well.
The only problem I have with this hack is that I haven't figured out how to still show the which-key
help with this. Now I'm not sure how you think about including this but maybe this could be an opt-in user option. Or maybe there are better ways to get the desired behaviour?
The package currently includes the basic actions off the top of my head. I'd be very interested in suggestions for other common actions. I do want to only include very basic actions most people would find useful: it's easy enough for users to add their own most exotic actions.
(And of course, embark
doesn't keep you from using any command as an action! So, by "including actions" I really mean assigning them single-letter shortcuts.)
M-x xyz...
everything is fast in both icomplete and selectrum.M-x M-x M-x M-x xyz...
everything is fast in both icomplete and selectrum.M-x M-SPC M-x abc...
, the M-x abc...
completion is really slow as if there is some interaction between the recursive completion sessions going on. In my setup M-SPC
is bound to embark-act-noexit
.You will probably only observe this with selectrum since there the completion speed becomes more noticeable. But it is also noticeable when using icomplete with delay=0.
I tested without marginalia-mode
, the annotators are not the issue here. I did some profiling but it wasn't obvious directly where the problem lies. Do you observe this too?
Now I feel bad for asking this, since you removed all variables so nicely - can you make the current embark-keymap available again for the indicators. Alternatively (and probably better, but breaking the current indicator function), pass the keymap as argument to the indicator? I need this to show the keymap via which-key-show-keymap via the indicator function.
embark-act
on files from the completion buffer:
rename-file
in the completions buffer selects the file (with full path) correctly. However the prompt for the new name is always "~/", not the value of default-directory
in the completions buffer.
embark-insert
on a file inserts only the base name of the file. It should be appended with the default-directory
in the completions buffer before insertion.
embark-save
on a file name has the same behavior as embark-insert
.
EDIT:
delete-file
and delete-directory
when called using embark-act
from the completions buffer. It looks for the base name of the file/directory in the default directory of the minibuffer instead of the completions buffer.Originally posted by @karthink in #3 (comment)
@oantolin based on our discussion at https://www.reddit.com/r/emacs/comments/k3c0u7/consult_counselswiper_alternative_for/gef1jxo?utm_source=share&utm_medium=web2x&context=3
So far this looks to me as if we can create two relatively simply and fully independent packages. Maybe this does not work out but it seems to be worth a try.
The purpose of this package is to add (or refine?) the category completion metadata of commands which don't provide a category. There are various ad-hoc possibilities to infer the category information as has been done in the Embark classifier system.
The purpose of this package is to add or refine annotations of completion metadata. Ideally the annotation functions should only rely on category information, keeping everything simple this way.
Another one which may or may not be useful. Would fit together with consult-kmacro. For me the main motivation to have such actions is to make them accessible like a cheat sheet.
Do you think there are there any blockers to publish this as a package? I'm glad to help if you need a hand. Other then adding some autoload cookies and cleanup there shouldn't be much needed, I think? If you are interested I will provide a PR for that.
Here's the thing that is stopping me from switching to embark. How can I accomplish something like the following with it? Thanks!
(defun reddit-browser ()
(interactive)
(ivy-read
"Reddit: "
(mapcar 'car subreddit-list)
:action
'(1
("o" (lambda (x)
(browse-subreddit x "new"))
"new")
("t" (lambda (x)
(browse-subreddit x "top"))
"top")
("r" (lambda (x)
(browse-subreddit x "rising"))
"rising")
("c" (lambda (x)
(browse-subreddit x "controversial"))
"controversial"))))
Hi, I've spent the past few days looking through this package on a transition from Ivy to Selectrum, its been very useful and informative! I put together a keymap and classifier for Straight.el and figured it may be useful for others.
(defvar embark-straight-map
(embark-keymap
'(("u" . straight-visit-package-website)
("r" . straight-get-recipe)
("i" . straight-use-package)
("c" . straight-check-package)
("F" . straight-pull-package)
("f" . straight-fetch-package)
("p" . straight-push-package)
("n" . straight-normalize-package)
("m" . straight-merge-package))
embark-general-map))
(cl-pushnew '(straight . embark-straight-map) embark-keymap-alist)
(defun embark-straight-type ()
"Determine if currently completing package names."
(when (or (string-suffix-p "recipe? " (or (minibuffer-prompt) ""))
(string-suffix-p "package: " (or (minibuffer-prompt) "")))
'straight))
(cl-pushnew 'embark-straight-type embark-classifiers)
I'd love to help contribute where I can. But I'm still pretty green with elisp. If there's anything simple you all need help with let me know!
Is it possible at all?
When using an unbound key in the transient map the transient map exits and the usually bound command gets executed. This also means that after such a mistyped key embark--cleanup
doesn't run because embark--target
is still set. This way you can end up in a state where you think you quitted embark
but next time you open a minibuffer embark--inject
does its thing.
I think any wrong pressed key should probably call a command like embark-undefined
which would be like embark-cancel
but showing an additional message to inform the user that the key is undefined. One way I can think of implementing this would be to use [t]
to bind this command. This is a feature of keymaps to use this command as the default for any unbound key.
In embark--setup-action:
embark.el:645:43: Warning: reference to free variable `embark-general-map'
In embark-annotation-function-metadatum:
embark.el:919:28: Warning: reference to free variable
`embark-occur-annotation-func'
In embark-embark-occur-candidates:
embark.el:988:5: Warning: reference to free variable `embark-occur-candidates'
In embark-occur-choose:
embark.el:1069:38: Warning: reference to free variable `embark-occur-from'
To reproduce with emacs -q
, execute:
(load "~/src/pomembno/emacs/embark/embark.el")
(define-key minibuffer-local-map (kbd "M-o") 'embark-act)
And then type C-x C-f ~/test1/~/test2 M-o e
.
Eshell is started in "~/test1/~/test2"
instead of "~/test2"
.
Hello:
I think it would be nice to be able to go forward or go backward completion candidates in the embark live occur buffer, like what icomplete-forward-completions
and icomplete-backward-completions
do.
So I wrote two functions and modified embark-minibuffer-candidates
to provide this functionality. I am wondering what do you think of this?
Thanks for the nice package in any case. :)
(defun embark-forward-completions (&optional n)
"Step forward completions by N entry.
The N-th entry becomes the first and can be selected with
`minibuffer-force-complete-and-exit'."
(interactive "p")
(let* ((beg (if (window-minibuffer-p)
(minibuffer-prompt-end)
(nth 0 completion-in-region--data)))
(end (if (window-minibuffer-p)
(point-max)
(nth 1 completion-in-region--data)))
(comps (completion-all-sorted-completions beg end))
(tail (cdr (last comps))))
(when comps
;; make it a real list
(setf (cdr (last comps)) nil)
;; Rotating an entire round is the same as no rotation at all.
(setf n (mod n (length comps)))
;; rotating
(setf comps (append (nthcdr n comps)
(butlast comps (- (length comps) n)))
;; adjoin the tail back
(cdr (last comps)) tail)
(completion--cache-all-sorted-completions
beg end comps))
(embark-occur--update-linked)))
(defun embark-backward-completions (&optional n)
"Step backward completions by N entry.
The N-th-to-last entry becomes the first and can be selected with
`minibuffer-force-complete-and-exit'."
(interactive "p")
(let* ((beg (if (window-minibuffer-p)
(minibuffer-prompt-end)
(nth 0 completion-in-region--data)))
(end (if (window-minibuffer-p)
(point-max)
(nth 1 completion-in-region--data)))
(comps (completion-all-sorted-completions beg end))
(tail (cdr (last comps))))
(when comps
;; make it a real list
(setf (cdr (last comps)) nil)
;; Rotating an entire round is the same as no rotation at all.
(setf n (mod n (length comps)))
;; rotate
(setf comps (append (nthcdr (- (length comps) n) comps)
(butlast comps n))
;; adjoin the tail back
(cdr (last comps)) tail)
(completion--cache-all-sorted-completions beg end comps))
(embark-occur--update-linked)))
(defun embark-minibuffer-candidates ()
"Return all current completion candidates from the minibuffer.
Modified by Durand."
(when (minibufferp)
(let* ((all (completion-all-sorted-completions
(minibuffer-prompt-end)
(point-max)))
(last (last all)))
(when last (setcdr last nil))
all)))
Gotcha and yes I found embark-occur-initial-view-alist
. I appreciate the annotations that marginalia adds to an embark-occur
buffer of files (in list mode). I think I'd also like a way to get a list (one line per item) without any annotations (even if annotations are usually enabled). Maybe embark-occur-toggle-view
becomes embark-occur-cycle
? I'm not sure if this should be a marginalia or an embark feature?
Originally posted by @hmelman in #48 (comment)
Embark's symbol and package classifiers have been added to Marginalia now. I'd like to remove them from Embark to avoid duplication (I already removed everything to do with annotations!). I only want to remove the classifiers, not the actions or action keymaps.
Now, how important is it that Embark's package and symbol actions work out of the box?
What do you think @minad?
embark
executes actions before exiting the minibuffer. This has some downsides for example when I call embark-describe-symbol
and exit the minibuffer afterwards the info page goes away because the recursive edit exits. Many times I want to execute an action like an exit command, that means exit the minibuffer and call the action with the previously selected target. One way to achieve this within the constraints of the minibuffer API is to write commands like this:
(defun embark-info-emacs-command ()
(interactive)
(let ((node (embark-target)))
(run-at-time 0 nil
(lambda ()
(Info-goto-emacs-command-node node))))
(abort-recursive-edit))
I think this is useful enough to generalize it somehow in a command like embark-exit-and-act
command.
My current "vision" would be to keep the current behaviour via embark-act
but
when called from embark-exit-and-act
behave like the version above passing embark-target
as the target argument (so it automatically works with find-file-other-window
and such). So commands called from this new embark-exit-and-act
command would have the requirement to accept the target as first argument.
What do you think?
Hi there :)
Your recent improvements are great and solve another problem I had: Make it easy to use commands as actions which don't consume the target. I still use embark-undefined
in my config though because it's kind of annoying to me if self insert commands get accepted:
(define-key embark-general-map
[remap self-insert-command]
(defun embark-undefined ()
(interactive)
(minibuffer-message "Not an action")))
(define-key embark-region-map
[remap self-insert-command]
'embark-undefined)
This way they are blocked which I prefer, is there something you don't like about blocking them by default? Related to that could the general Not an action:...
message in embark-cleanup
be removed? Maybe we could assume that if the user binds a command intentionally in action maps it can be treated as a valid action (even if it doesn't consume the target).
What is the policy for using external libraries which are not included in Emacs?
I see several approaches:
denied, let the user decide what to bind. In this case, I think it's worth to add some examples to README
use something like
(when (require 'feature-name nil t)
'("x" . feature-name-magic-command))
embark-feature-name
with explicit dependencies@oantolin I decided to create another issue for the RET behavior, extracted from #62.
I am pressing the keys M-x consult-line M-SPC RET
. In my config M-SPC
is bound to embark-act-noexit
.
The expected behavior would be that RET
executes the default action and performs the jump to the line. This works with icomplete. But Selectrum behaves differently. Instead the minibuffer shows Go to line: [some full line candidate]
. Then I have to press <down>
and RET
to select the candidate and to perform the jump.
Since this is Selectrum-related, ping @clemera .
Hi Omar,
When using org$
as matching regexp in find-file
(completion-metadata-get (embark--metadata) 'category)
returns environment-variable
while you would expect the file
category which allows export to dired for example.
What do you mean? I'm afraid I don't understand.
What I want is add two binding for embark-occur-mode-map
(define-key embark-occur-mode-map (kbd "/") 'hide-item-not-matching)
(define-key embark-occur-mode-map (kbd "z") 'hide-item-matching)
something like dired-narrow
http://pragmaticemacs.com/emacs/dynamically-filter-directory-listing-with-dired-narrow/
How can I make the commands run via Embark not use the file dialogs?
The variable use-file-dialog
can be set to nil, but that also affects commands that use the mouse, such as the menu bar and tool bar. Is there a way to set this variable only for commands run via Embark?
Thank you.
Right now embark--metadata
uses minibuffer-contents
. For selectrum and other frameworks another function would be needed so maybe an embark-input-finders
option would do?
Does not work for me anymore on 9af3035
@oantolin I can add those if you like.
As discussed in #52. From what I see now it is not totally trivial and needs some changes to the logic around the transient keymap. But I still would some like to have this since it makes actions standard commands, for example embark-insert-unicode-character would just go away and be replaced by insert-char. I am sure there are more examples.
It would be nice if this package were available on Melpa.
In newer Emacsen, project-switch-project
will, after selecting the project, show a dispatch menu with possible options. It would be nice if Embark did this after using embark-act
.
Trigger warning - I am about to ask for more type errors.
Since I am using Embark as a context menu in the minibuffer I would like to bind both marginalia-cycle-annotators
and consult-preview-mode
there (this could also go into the marginalia or consult docs as examples!). But obviously these are type-errors. My question is if it somehow possible to avoid the consumption of embark--target. If it turns out that this is possible, this could also be used for embark-occur, embark-live-occur and embark-export since they all consume and ignore embark--target afaik. I asked this before, but I guess it somehow got lost in the discussions. Basically this is the thing which is missing to make the embark keymaps fully "general purpose" and easier to use.
PS: @oantolin Sorry for flooding you here with questions and issues. There is no hurry in answering those, but I think it is better to have things documented/reported somewhere instead of them getting lost.
After calling embark-occur on consult-buffer, I have two windows, the original one and the occur window. When I close the original window such that I am left with the occur window, I get an error window-valid-p, wrong type argument. Is this Embark's fault?
~/path/to/directory
by calling embark-act
and then i
inserts:~/path/to/directory/./
: When called from find-file
(C-x C-f)
~/path/to/directory/some-subdir/
: When called from dired
(C-x d)
embark-act
is not running the action when calling with prefix-arg. Trying to kill buffer and exit from switch-to-buffer
menu:Not sure if I'm doing something wrong. Tested both on emacs -Q
.
Keybinding "B" maybe. I tried to add it but it does not work, probably another "type error"?
When doing consult-line, using either embark-act or embark-act-noexit and then pressing RET for the default action, I am observing multiple issues:
embark-act/embark-act-noexit on selectrum does not perform the jump but hangs due to how RET is handled:
embark-act-noexit default action throws an error (happens also with icomplete):
Xref provides helpful commands for code navigation and discovery, such as xref-find-definitions
and project-find-regexp
,and project-find-regexp
support ripgrep
now( on emacs master) .
When a xref query hits multiple candidates Emacs shows a Xref
buffer with the various candidates and their location.
On emacs master, it support using completing-read
showing the multiple candidates now with xref--show-defs-minibuffer
https://lists.gnu.org/archive/html/emacs-devel/2020-11/msg00707.html
(with-eval-after-load 'xref
(setq xref-search-program 'ripgrep) ;default `grep`, for project-find-regexp
(when (functionp 'xref--show-defs-minibuffer)
(setq xref-show-definitions-function 'xref--show-defs-minibuffer) ; using completing-read
(setq xref-show-xrefs-function 'xref--show-defs-minibuffer)))
this is a screenshot of project-find-regexp
in minibuffer
And I can export the result to "Embark Occur" with embark-occur
So I think it is great for "Embark Occur" support wgrep
like ivy-occur/grep,
I think minad/consult#46 can benefit from it too, once consult-rg/ag is implemented.
If you use embark-act
with selectrum the candidate list shown in the minibuffer does not update (for example when renaming/deleting a file). An easy way to fix that for selectrum would be to set the transient exit function to something else (selectrum needs to get informed it needs to update the candidate list before the action gets executed because usually this only done on when the input changed).
Currently you use a lambda for the exit function so I can't use advice. Could you use a named function so I can advice it? Or if you are OK with it even use a variable to bind the function to use, or maybe a hook? This way it would look less hackish in user configuration for selectrum users.
Is there a reason why you don't put all the possible embark operations into the actions keymap directly? I am experimenting with the new embark version now - everything seems to work great so far, but it could be that I have more beginner questions, since I am not accustomed to actions.
EDIT: Is the reason that embark-occur is an action which acts on all candidates at once in contrast to acting on a single candidate?
embark-default-action
doesn't DWIM with M-x.
embark-occur
)embark-act
on target command (find-file-other-window
, say)embark-default-action
Opens M-x again instead of running find-file-other-window
.
Perhaps embark-default-action
should run the thing at point instead on the completion category of 'command
?
I found a way to enhance the consult narrowing menu such that which-key shows a description instead of the command name. E.g. for the buffer narrowing it shows "< Widen b Buffer f File ..." generated by which-key.
See here https://github.com/minad/consult/blob/eaebab54450d859bbc5eb8c83c2fd7585ad2e2e6/consult.el#L395.
Something like this could be an option for embark too!
Could you add autoload to the functions? It will be nice when we load them with use-package. Thank you.
Please help me understand how to setup embark for custom functions. For example, I get "unknown embark action" when I try something like this:
(let ((embark-custom-map))
(embark-keymap
'(("t" . my-embark))
embark-custom-map)
(completing-read "prompt: " '("red" "green" "blue")))
(defun my-embark ()
(interactive)
(message "You chose %s" (embark-target)))
So that's definitely not the correct way, but then what is the right way for creating embark keymaps for custom functions?
Sometimes when you are in a command which uses a minibuffer you want to switch to a related command but keep the current input. For example from switch-to-buffer
to find-file
or project-find-file
and so on. One could define swapping commands for embark classes and then swap commands using an embak-swap-command
. A similar feature request exists for ivy. What do you think?
I also mentioned this before but then it has been gone for a while but now I am seeing the bug reproducible every time I press RET in the occur buffer. The error occurs in embark-default-action
and call-interactively
.
Debugger entered--Lisp error: (wrong-type-argument commandp nil)
call-interactively(nil)
embark-default-action()
embark-occur-choose(#<marker (moves after insertion) at 225 in *Embark Occur*>)
This only happens for the first call of consult-line
and embark-occur
!
Since embark
already includes the logic and general "infrastructure" for this an occur command could be added for embark-general-map
which would exit the minibuffer and pop the current completion candidates to a buffer specialized to work on those candidates (similar to ivy-occur
).
Files could open in a dired buffer, buffers in ibuffer, packages in package menu and so on. This could be configurable via an embark-occur-alist
mapping embark classifier types to handlers.
There are times when I want to display keys of an alist for the completions in the minibuffer but act upon the associated value after selecting. For example, displaying a list of filenames, and then acting on the filepath.
How can I achieve that with embark in a custom function like this:
(completing-read "prompt: " '(("red" . "first")("green" . "second")("blue" . "third")))
I would like to be able to to it with a let binding which can read from (cdr (assoc KEY ALIST))
Sorry if the question is not clear, but for example I can do this in ivy like this:
(ivy-read
"prompt: " '(("red" . "first")("green" . "second")("blue" . "third"))
:action '(1
("o" (lambda (x)
(mpv-enqueue-play
(cdr x) t))
"play")
("x" (lambda (x)
(mpv-music-player
(cdr x) nil nil t))
"music")
("b" (lambda (x)
(qutebrowser
(cdr x)))
"browse")
("w" (lambda (x)
(kill-new
(cdr x)))
"copy url")
("d" (lambda (x)
(youtube-dl
(cdr x)))
"download")))
Thank you @oantolin for making this package!
I think in terms of usability, it would be nice to have the feedback of (message "Act on %s %s" kind embark--target)
as an overlay (which could be less verbose—perhaps as an option).
What I have in mind is the way recursive minibuffers show their depth level when minibuffer-depth-indicate-mode
is enabled. This offers you the chance to put the feedback outside the area of the minibuffer's input, which can make it feel less cluttered (especially if the indicator is just "Act"—again, if there is an option for that).
I believe this is the relevant code from mb-depth.el
:
(defun minibuffer-depth-setup ()
"Set up a minibuffer for `minibuffer-depth-indicate-mode'.
The prompt should already have been inserted."
(let ((depth (minibuffer-depth)))
(when (> depth 1)
(let ((pos (point-min)))
(setq minibuffer-depth-overlay (make-overlay pos (1+ pos))))
(overlay-put minibuffer-depth-overlay 'before-string
(if minibuffer-depth-indicator-function
(funcall minibuffer-depth-indicator-function depth)
(propertize (format "[%d]" depth) 'face 'highlight)))
(overlay-put minibuffer-depth-overlay 'evaporate t))))
I wrote in minad/marginalia#10:
This embark-act/embark-act-noexit distinction is something which I wondered about the first time, when I looked into embark. It looks to me as if I have to chose too early what it will do after the action. At the point where I enter the action menu, I might not even know yet which action to execute. Wouldn't it make sense to associate the exit/no-exit behavior with the executed action in order to get some kind of hydra-style interface? Alternatively one could use a toggle/prefix-like command which toggles the behavior for the next command being executed. Default is exit, but you could press some key, then next command will be no-exit. I think I would prefer a hydra style menu though.
As I wrote I wonder if occur and live occur could be unified. If not (you probably have good reasons to keep them separate?), it would still be good to have some option which prevents closing the live occur buffer. Like having an extra function embark-live-occur-noclose. Does that make sense?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.