Closed rickalex21 closed 1 year ago
It sounds like you want a matching style that appends [^/]+$
to a component and then matches it as a regexp. Let me know if that is enough of a hint or if you need help implementing it.
Thanks @oantolin , I am aware that I can do regex in the minibuffer. I'm not sure how to do this
automatically. I was under the impression that completion-category-overrides
file styles would
help me with this? Sorry, I still consider myself a newbie. I find embark easier to understand.
Well, you have to decide exactly what kind of matching you want to support, but for example say you only want to match text literally and only against basenames. Then given a string like a.b
what regexp do you actually want to match against the filepath? That would be a\.b[^/]*$
(the dot is escaped to make the dot match literally). A function that converts an input string into the regexp you actually want to match against is called a "matching style" in orderless, and this particular matching style would be defined as the function +match-basename
below:
(defun +match-basename-literally (string)
(format "\\(%s\\)[^/]*\\'" (regexp-quote string)))
(orderless-define-completion-style +orderless-basename-only
"Completion style for matching against base file names only."
(orderless-matching-styles '(+match-basename-literally)))
(setq completion-category-overrides
'((project-file (styles +orderless-basename-only))
(file (styles +orderless-basename-only))))
The rest of that code block defines a new completion style, +orderless-basename-only
that exclusively uses this style of matching text literally and then the completion-category-overrides
variable is set so that that completion style is tried first when completing either file
s or project-file
s (which also tend to have long paths).
You could add support for other styles of matching against basenames, like, maybe you want to match regexps against basenames, or maybe shell-style file globs (which is easy to implement thanks to the dired-glob-regexp
function which turns a file glob into an equivalent regexp! —Emacs Lisp has one of the most ridiculously complete "standard libraries" of any language I've used).
@oantolin This makes more sense, however I'm not getting the expected results. Is there something
wrong with my config here? I updated it. I also comment out orderless-style-dispatchers
just to
be on the safe side.
As far as I'm aware all of these steps are done.
+match-basename-literally
converts input to a regex.+orderless-basename-only
with +match-basename-literally
(file (styles +orderless-basename-only))
to completion-category-overrides
(orderless-define-completion-style +orderless-with-initialism
(orderless-matching-styles '(orderless-initialism
orderless-literal
orderless-regexp)))
(defun +match-basename-literally (string)
(format "\\$%s\$[^/]\*\\\\'" (regexp-quote string)))
(orderless-define-completion-style +orderless-basename-only
"Completion style for matching against base file names only."
(orderless-matching-styles '(+match-basename-literally)))
(setq completion-styles '(orderless basic)
completion-category-defaults nil
completion-category-overrides '((file (styles +orderless-basename-only))
(project-file (styles +orderless-basename-only))
;;(file (styles partial-completion)) ;; partial-completion is tried first
(command (styles +orderless-with-initialism))
(variable (styles +orderless-with-initialism))
(symbol (styles +orderless-with-initialism)))
orderless-component-separator #'orderless-escapable-split-on-space ;; allow escaping space with backslash!
;; Comment out for now till I get +orderless-basename-only working
;;orderless-style-dispatchers '(+orderless-dispatch)
)
I'm not matching agenda-org.el
, I'm matching emacs.org
and others. Your regex works, I
tested it in the picture. I imagine the single quote at the end of the regex is a suffix
to match literately?
How do I know if something is explicitly file,command, variable, X, Y, Z etc..? I need to know this info for
completion-category-overrides
. I've seen other configs that have consult-something
in there. I did read the part in the docs that talk about Marginalia which I have installed but I'm not so sure. Obviously this picture is file
because I can see the file permissions. Obviously if I do Describe variable
I know it's a variable. command
I presume is M-x
.
Thanks
You changed the regexp! In my function I used "\\(%s\\)[^/]*\\'"
and you used instead the very different "\\$%s\$[^/]\*\\\\'"
! You were getting the correct results for the (weird) regexp you chose.
I imagine the single quote at the end of the regex is a suffix to match literately?
No, \'
matches the end of the string, just like $
matches the end of a line.
@oantolin Sorry about that, it's the markdown exporter that I'm using, it's not my code. It needs to be fixed. Here is my code again without using the markdown exporter. As shown in the picture above I'm not matching agenda-org.el.
;; FIXME: Markdown exporter adding backslashes
(orderless-define-completion-style +orderless-with-initialism
(orderless-matching-styles '(orderless-initialism
orderless-literal
orderless-regexp)))
(defun +match-basename-literally (string)
(format "\\(%s\\)[^/]*\\'" (regexp-quote string)))
(orderless-define-completion-style +orderless-basename-only
"Completion style for matching against base file names only."
(orderless-matching-styles '(+match-basename-literally)))
(setq completion-styles '(orderless basic)
completion-category-defaults nil
completion-category-overrides '((file (styles +orderless-basename-only))
(project-file (styles +orderless-basename-only))
;;(file (styles partial-completion)) ;; partial-completion is tried first
(command (styles +orderless-with-initialism))
(variable (styles +orderless-with-initialism))
(symbol (styles +orderless-with-initialism)))
orderless-component-separator #'orderless-escapable-split-on-space ;; allow escaping space with backslash!
;; Comment out for now till I get +orderless-basename-only working
;;orderless-style-dispatchers '(+orderless-dispatch)
)
As shown in the picture above I'm not matching agenda-org.el.
I'm confused: the picture clearly shows that agenda-org.el
does match. Maybe instead of saying that agenda-org.el
doesn't match you meant to say that you want the other files shown to not match?
If that is what you meant, one way to achieve that would be to anchor the regexps to the beginning of the basename. You could do that by changing the regexp from "\\(%s\\)[^/]*\\'"
to "\\(?:^\\|/\\)\\(%s\\)[^/]*\\'"
.
Is that what you wanted?
Yes ! Thanks alot Omar, that's what I needed. Hopefully this post will help someone else along the way. I understand Orderless a bit better now.
BTW
How do I know what category something is in: file,command, variable, X, Y, Z etc..? I need to know this info for
completion-category-overrides
. I've seen other configs that have consult-something
in there. I did read the part in the docs that talk about Marginalia which I have installed but I'm not so sure. Obviously the picture above is file
because I can see the file permissions. Obviously if I do Describe variable
I know it's a variable and command
I presume is M-x
.
How do I know what category something is in?
I wish Emacs made that a little easier than it is. I'd suggest you bind the following command to some key in minibuffer-local-map
, then when you are in the minibuffer you can use the command to find out the category.
(defun show-completion-category ()
(interactive)
(message "Category: %s"
(alist-get 'category
(cdr
(completion-metadata
(buffer-substring-no-properties
(minibuffer-prompt-end)
(max (minibuffer-prompt-end) (point)))
minibuffer-completion-table
minibuffer-completion-predicate)))))
(keymap-set minibuffer-local-map "M-?" #'show-completion-category)
I see, thanks for the function. Is there something I need to autoload or do to use keymap-set
?
I understand that define-key
is legacy now. It's really weird cause I get void function keymap-set
before doom-mode-line but after it it works fine. There's something in doom-modeline that enables
keymap-set
or emacs loads it at that time.
I even tried (autoload 'keymap-set "compat-29")
, don't work. I get:
Symbol's function definition is void: keymap-set
BTW I'm using: GNU Emacs 28.2 (build 2, x86_64-unknown-linux-gnu, GTK+ Version 3.24.34, cairo version 1.16.0) of 2022-09-13
I don't have anything weird in my doom-modeline config:
;; Don't work before
;; (keymap-set minibuffer-local-map "C-c o s" #'oantolin/show-completion-category)
(use-package doom-modeline
:straight t
:demand t
:custom-face
(doom-modeline-bar-inactive ((t (:background "dim gray"))))
(doom-modeline-buffer-modified ((t (:inherit (error bold) ))))
(doom-modeline-lsp-error ((t (:inherit error :foreground "dark red" :weight normal))))
(doom-modeline-urgent ((t (:inherit (error bold) :foreground "orange red"))))
(doom-modeline-notification ((t (:foreground "dark orange" :background "black"))))
(mode-line ((t (:family "Noto Sans" :height 140 ))))
(mode-line-active ((t (:inherit (doom-modeline-project-parent-dir) ))))
(mode-line-inactive ((t (:inherit (doom-modeline-project-parent-dir)))))
:config
(setq doom-modeline-project-detection 'auto)
(setq doom-modeline-minor-modes t)
(setq doom-modeline-lsp t)
;; (setq doom-modeline-buffer-file-name-style 'truncate-upto-root)
(setq doom-modeline-buffer-file-name-style 'truncate-upto-project)
:init (doom-modeline-mode 1))
;; Works after
(keymap-set minibuffer-local-map "C-c o s" #'oantolin/show-completion-category)
You can easily rewrite that line to use define-key or general or whatever Doom uses to define key bindings instead of keymap-set, if you want.
Alternatively load compat
which has a definition of keymap-set. Consult, for example, depends o compat.
I use vanilla emacs, I had to install compat
, that fixed it. Turns out it's required by doom-modeline
.
UPDATE: I noticed the aqua color is the second match after space. Perhaps I misunderstood the way that the faces work but what I describe below would be a nice feature if it does not already exist.
I created a test function to use with your function. Are the matches suppose to be colored
differently based on the style? They look the same color. ode
matches my/orderless-test
and
org.org
is matched by +match-basename-literally
.
If this is the case then the first match ode
should be color mangeta orderless-match-face-0
and the second match org.org
should be aqua
color orderless-match-face-1
? However,
everything looks the same color.
Not sure I understood this part of the readme.
You will only see these particular faces when the orderless completion is the one that ends up being used, of course.
These are the faces:
(orderless-match-face-0 ((t (:foreground "#d33682" :background "#073642"))))
(orderless-match-face-1 ((t (:foreground "#2aa198" :background "#073642"))))
(orderless-match-face-2 ((t (:foreground "#b58900" :background "#073642"))))
(orderless-match-face-3 ((t (:foreground "#859900" :background "#073642"))))
The code of interest:
(defun my/orderless-test (string)
(format "\\(%sd\\)\\([a-z]+\\)" (regexp-quote string)))
(orderless-define-completion-style my/path-with-basename
(orderless-matching-styles '( +match-basename-literally my/orderless-test)))
Are the matches suppose to be colored differently based on the style?
No, they are colored by component. So if your minibuffer input is ab cd
then the matches of ab
get one color and the matches of cd
get a different color. For each component, each matching style is tried in order and the first matching style that makes that component match is used; but the color is the one that correspond to that number of component, not depending on the style that matched.
Would it be difficult to implement something like that? If I'm matching +orderless-with-initialism
color
it yellow, if I'm matching +orderless-other-style
color it green? Not sure what use cases but
interesting for sure.
It would not be too difficult, but I don't think it would be useful. Nor has anyone ever suggested it before. The point of highlighting the matches of different components of the input string in different colors is that they can match out of order (this is orderless
after all), so the color helps you see were each component matched.
I also think coloring based on matching style would be a little confusing with style dispatchers in the mix: people use style dispatchers to invent "query syntax" by translating input to appropriate string & matching-style pairs. The matching style used is really an implementation detail, and very often is just orderless-regexp
. Is it helpful to highlight all matches that are implement via orderless-regexp
in the color assigned to orderless-regexp
? Probably not. The current system, where the color indicates the component number (mod 4) seems more informative to me.
In order to get the completion category one can also use marginalia-cycle
which prints a message.
@oantolin That makes sense, thanks for all your help. Thanks for the tip @minad.
Hello, how do I complete on file basenames only? I'm using vertico and consult-recent-file.
When I type 'a' I don't want a path with emacs to come up, only agenda.org because that's the basename of the file.
I have changed this part of the code to initialism and it does not work with initialism. I would like to complete with the file basename but if it doesn't work with initialism it's probably not going to work with anything else. When I use the comma it does work because I have the variable
+orderless-dispatch-alist
.Here is the full config. I've configured most packages myself but this is one config that I've copied and pasted. This seems to be everywhere on the internet.
Thanks