Comments (13)
What are you trying to do that Embark does not already do?
from consult.
There is probably nothing I want to do which embark doesn't already do. I looked at it and it is seems involved. Its code complexity is comparable to selectrum itself. Its goal is to provide full powered actions like ivy on top of e.g. icomplete. To achieve this embark resorts to classify the type you are completing and then binds the appropriate keymap. I am not entirely convinced of this approach. And I also think its a unique approach on top of the basic concept of actions, as I understand them. I would rather prefer something where I have to explicitly associate commands with keymaps as long as this is possible without much code and without writing wrappers.
In the scope of this library here I would like to have the possibility to add actions to commands, as in the commands in the selectrum wiki: https://github.com/raxod502/selectrum/wiki/Useful-Commands#misc-by-lorniu
There this is achieved with not much code. I could probably do the same with embark, but then I am tied to embark, which I didn't buy into yet.
Maybe it would be great to have an embark-core which only provides the key binding facilities without the classification mechanism, but I am not sure if this is possible since embark seems to be centered around the classification concept?
Maybe the way embark approaches the problem is the right one if you are coming from ivy. And it is indeed natural to have all the actions around automatically if you complete a certain type. My perspective is a bit different since I am used to a more barebone Emacs. This is also where for example the selectrum appeal lies for me. It has its narrow niche and only gives me a better completer without adding keybindings (except very few in the selectrum-minibuffer-map) and without adding any commands etc.
Overall I am just exploring. Motivated by looking at the commands in the selectrum wiki and the commands provided by counsel which are often equipped with useful actions...
from consult.
Another experiment where I am using the minibuffer-setup-hook
to bind the action keymap.
;;; -*- lexical-binding: t -*-
(defvar action-candidate-function #'selectrum-get-current-candidate)
(defvar action-key [C-return])
(defvar action-stack nil)
(defvar action-command nil)
(defvar action-command-maps nil)
(defvar action-category-maps nil)
(defun action-exit (command)
(let ((candidate (funcall action-candidate-function))
(orig-inhibit-message inhibit-message))
(run-at-time 0 nil (lambda ()
(setq inhibit-message orig-inhibit-message)
(letrec ((hook (lambda ()
(push 13 unread-command-events)
(setq minibuffer-setup-hook (delq hook minibuffer-setup-hook))
(delete-minibuffer-contents)
(insert candidate))))
(push hook minibuffer-setup-hook)
(unwind-protect
(call-interactively (setq this-command command))
(setq minibuffer-setup-hook (delq hook minibuffer-setup-hook))))))
(setq inhibit-message t)
(top-level)))
(defun define-action (name cmd &optional no-exit)
`(defun ,name ()
(interactive)
,(if no-exit
`(call-interactively ,(if (eq cmd :default) 'action-command `#',cmd))
`(action-exit ,(if (eq cmd :default) 'action-command `#',cmd)))
))
(defmacro define-action-map (name &rest bindings)
`(progn
,@(mapcar (pcase-lambda (`(,_key ,cmd . ,rest))
(define-action (intern (format "action-%s-%s" name cmd)) cmd rest))
bindings)
(defvar ,name
(let ((map (make-sparse-keymap)))
,@(mapcar (pcase-lambda (`(,key ,cmd . ,_rest))
`(define-key map ,(kbd key) #',(intern (format "action-%s-%s" name cmd))))
bindings)
map))))
(defun action-category ()
(completion-metadata-get
(completion-metadata
(buffer-substring-no-properties (field-beginning) (point))
minibuffer-completion-table
minibuffer-completion-predicate)
'category))
(defun action-minibuffer-setup ()
(push (cons action-command (lookup-key minibuffer-local-map action-key)) action-stack)
(setq action-command this-command)
(let ((map (or (alist-get this-command action-command-maps)
(alist-get (action-category) action-category-maps))))
(define-key minibuffer-local-map action-key (and map (symbol-value map)))))
(defun action-minibuffer-exit ()
(let ((state (pop action-stack)))
(setq action-command (car state))
(define-key minibuffer-local-map action-key (cdr state))))
(add-hook 'minibuffer-setup-hook #'action-minibuffer-setup)
(add-hook 'minibuffer-exit-hook #'action-minibuffer-exit)
(define-action-map buffer-action-map
("RET" :default)
("o" switch-to-buffer-other-window)
("x" ignore :no-exit)
("C-g" keyboard-quit))
(push '(buffer . buffer-action-map) action-category-maps)
;;(push '(consult-buffer . buffer-action-map) action-command-maps)
from consult.
I looked into embark again and there is also handling of a completion category which is part of the metadata. This is a nice concept. Probably this is what I've been looking for.
I want something which decouples the command from the associated actions. And then I would like "consult" to support this. Ideally I don't have to define the actions directly in consult, but they could be supplied later on by embark or some other provider.
So to conclude - actions should always be provided externally. This is in contrast to how ivy handles actions.
The only thing "consult" should do here is to play nice, i.e., provide completion metadata (like category) etc.
@clemera @okamsn Do you agree?
from consult.
See 277c014 for category support.
consult-buffer
is a bit of an odd one. This would need a special category I think.
from consult.
Embark is really great and it handles probably everything you might want to do with actions. I wouldn't recommend trying to replicate the hard work done there, it handles a lot of edge cases already and also comes with an API which makes it easy to integrate with different completion frameworks.
As you described embark makes use completion metadata this allows to offer the right actions for the current context otherwise you would have to define the correct actions for each command. If one uses read-buffer
or read-file-name
the metadata reports that you are completing buffers/files so embark can offer the corresponding actions.
I would rather prefer something where I have to explicitly associate commands with keymaps as long as this is possible without much code and without writing wrappers.
You can bypass the usual categorization:
(defun example-command ()
(interactive)
(let ((embark-overriding-keymap
(let ((map (make-sparse-keymap)))
(define-key map "d"
(defun example-action ()
(interactive)
(message "Do something with %s" (embark-target))))
map)))
(completing-read "Check: " '("this" "out"))))
Embark will offer you the general actions (like candidate insertion that always make sense like candidate insertion/copy) plus the ones defined in embark-overriding-keymap
.
So to conclude - actions should always be provided externally. This is in contrast to how ivy handles actions
Some actions are very command specific but in that case you can create a keymap for them and bind it like shown above. BTW I highly recommend the which-key integration as described on the selectrum and also on the embark wiki.
from consult.
Thank you @clemera for answering here. For me the question here was mainly if I can enhance commands after having them defined, since this matters for consult. And this is different from ivy, I think, where the actions seem to be fixed by the :actions
property (I assume it is still possible to overwrite them somehow dynamically).
Regarding consult, I like to avoid dependencies and ideally make this only dependent on Emacs builtins. Maybe we can allow some small enhancements for external library which are detected at runtime.
This means I want to avoid adding actions directly to the library and instead favor an external solution. But nevertheless I want consult to be at least compatible with actions and in principle allow them. Therefore I added the :category
property to consult--read
.
For more involved cases special keymaps are needed, as you already wrote:
Some actions are very command specific but in that case you can create a keymap for them and bind it like shown above. BTW I highly recommend the which-key integration as described on the selectrum and also on the embark wiki.
Is it possible in embark to associate keymaps (or maybe only add a few bindings) with commands like I've shown in the example above? Maybe this is possible using some embark-setup-hook, if something like this exists?
(add-hook 'minibuffer-setup-hook
(lambda ()
(push (cons action-command (lookup-key minibuffer-local-map action-key)) action-stack)
(setq action-command this-command)
(let ((map (alist-get this-command action-maps)))
(define-key minibuffer-local-map action-key (and map (symbol-value map))))))
from consult.
That would be a great feature, you could do the following (though checking for this-command does not always work reliably, see here):
(defvar my-action-map
(let ((map (make-sparse-keymap)))
(define-key map "d"
(defun example-action ()
(interactive)
(message "Do something with %s" (embark-target))))
map))
(defvar action-maps-alist
'((example-command . my-action-map)))
(add-hook 'minibuffer-setup-hook
(defun my-setup-actions ()
(when-let ((map (cdr (assoc this-command action-maps-alist))))
(setq-local embark-overriding-keymap (symbol-value map)))))
(defun example-command ()
(interactive)
(completing-read "Check: " '("this" "out")))
from consult.
Cool that its possible to do this with embark! My only concern with embark is its complexity. I looked at it to understand how everything fits together. There are some design decisions I would have to understand better to fully grasp this, for example why transient maps are used. But it also depends very much on the point of view, I am not very accustomed to actions so I am not using most things as of now. If you are coming from ivy or helm you have totally different expectations I guess.
Yes... this-command is not reliable since it is overwritten everywhere. But there is also last-command, this-original-command and last-repeatable-command to help us out. Who is responsible for this holy mess? Stallmann himself?
from consult.
To add to this there are also real-this-command
and real-last-command
which are more reliable since they should never be rebound like this-command
or last-command
but this doesn't help in this case as we would really need something like minibuffer-this-command
😄
from consult.
I knew there were more, but overlooked them when I did C-h v
just now 🤦
from consult.
Please don't propose adding another this-command
variable on the emacs-dev list!1!!
from consult.
Actually I think it would make sense to have a variable which records the command which is currently running (ignoring minibuffer commands) as otherwise we have to create one as mentioned in the linked selectrum issue. Ivy has the same problem and uses a keyword arg to pass this information. But don't worry I'm to lazy to propose that ;)
from consult.
Related Issues (20)
- Transform yanks before listing/previewing in consult-yank HOT 1
- Consult previews don't have any font highlighting
- consult-grep family integrates badly with literally-visited files
- consult--each-line not unquoted `end` symbol
- Sorting when buffer switching
- [feature request] preselect the item that is most close to current point in consult-imenu and consult-outline HOT 3
- Where is the cached stored?
- Intragration with package Breadcrumb
- Disable consult buffer preview for remote buffers
- Support project.el external roots
- consult-rg wiki advice
- [request] allow `:lookup` in `consult--multi` sources
- Handling buffer renames while switching buffers
- Decorating consult-imenu-multi output with the origin
- Add support to display the matched heading's sub-headings also as candidates in the consult-outline's completion HOT 3
- NetBSD/OpenBSD: find(1) does not support -iwholename
- `which-function-mode` not honored with `consult-line` together with `consult-preview-at-point-mode` (in `completion-list-mode`)
- Need a recipe for annotating `consult-line` ( `consult-grep` etc) with `which-function`
- Is there a way to remember the last search and Change the result layout?
- Possible to grep in other window
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from consult.