Have zsh return case-insensitive auto-complete matches, but prefer exact matches
Asked Answered
H

3

67

I am using zsh with oh-my-zsh's rc file and there is some behavior I find particularly annoying. By default, oh-my-zsh is configured to return case-insensitive matches when auto-completing. This behavior is sometimes good, but other times it really sucks. Is there a way I can configure zsh to only use case-insenstive matching when there are no case-sensitive matches?

For instance, this case would use case-sensitive matching:

> ls
LICENSE.txt    lib/
> emacs l <-- should autocomplete to lib/

In this case, case-insensitive auto-completion would happen:

> ls
README    lib/
> emacs r <-- should autocomplete to README

Thanks!

Hemimorphite answered 15/6, 2014 at 4:54 Comment(0)
P
60

Just uncomment the following line in ~/.zshrc:

# Uncomment the following line to use case-sensitive completion.
# CASE_SENSITIVE="true"

It worked for me

Pete answered 19/1, 2015 at 9:45 Comment(2)
This only works with Oh My Zsh. github.com/robbyrussell/oh-my-zsh/blob/master/lib/… Edit: Oops, it was about Oh My Zsh... But this answer doesn't fully satisfy the needs.Echoism
This stops case insensitive completion completely. It does not fall back to case insensitive completion when there is no case sensitive match.Grounds
C
62

Create a file ~/.oh-my-zsh/custom/better-completion.zsh (assuming you are using default paths for oh-my-zsh) with the following line

zstyle ':completion:*' matcher-list '' 'm:{a-zA-Z}={A-Za-z}' 'r:|[._-]=* r:|=*' 'l:|=* r:|=*'

Explanation:

Rules for matches in zsh completion in general are defined in the matcher-list style. For oh-my-zsh this is defined in ~/.oh-my-zsh/lib/completion.zsh (once for case-sensitive and once for case-insensitive). You could change it there but it would probably be gone if you updated your oh-my-zsh. ~/.oh-my-zsh/custom is specifially intended for customization and files with extension .zsh are loaded from there by .oh-my-zsh/oh-my-zsh.sh at the end of the configuration.

The default (case-insensitive) settings for matcher-list in oh-my-zsh are:

zstyle ':completion:*' matcher-list 'm:{a-zA-Z}={A-Za-z}' 'r:|[._-]=* r:|=*' 'l:|=* r:|=*'

The first of which tells to handle upper and lower case interchangeable. As it is the first rule, it will be invariably used for every match.

The only change needed is to prepend '' for simple completion (it is even the first example in zshcompsys(1) for matcher-list)

zstyle ':completion:*' matcher-list '' 'm:{a-zA-Z}={A-Za-z}' 'r:|[._-]=* r:|=*' 'l:|=* r:|=*'

This tries first to complete the current word exactly as its written, before trying case-insensitive or other matches.

To be complete:

  • The second (original) rule allows for partial completion before ., _ or -, e.g. f.b -> foo.bar.
  • The third rule allows for completing on the left side of the written text, e.g. bar -> foobar)
Cataclysm answered 16/6, 2014 at 6:15 Comment(12)
You also have to set CASE_SENSITIVE to true I assume?Guildroy
Thanks @didibus. I removed the if-clause, so the setting of CASE_SENSITIVE does not matter anymore. I am not sure what I was thinking back then. oh-my-zsh's own behaviour depends on CASE_SENSITIVE, but as we want to change it either way, it does not make sense to even check for the original setting.Cataclysm
You forgot matcher-list after ':completion:*', didn't you?Macarthur
@Macarthur Yes, I did. Thank you, I have fixed it now.Cataclysm
Any reason r:|=*' is repeated?Stepdame
@TomHale It is not actually repeated on its own, each one is just a part of one of the matching rules, r:|[._-]=* r:|=* and l:|=* r:|=*. The single quote character ' is not a part of the rule but is needed in order to quote the special (to ZSH) characters in the rule string, like |, * and of course the space.Cataclysm
how do we know whether our zsh is oh-my-zsh or plain zsh?Responsibility
@Responsibility As long as your ~/.zshrc does not source a oh-my-zsh.sh (usually placed at ~/.oh-my-zsh/oh-my-zsh.sh) you are most likely not using Oh My Zsh. Of course, this does not necessarily mean, that you are using "plain" zsh, as there are other frameworks around. In most cases I would say that you are not using a Oh My Zsh (or another framework) unless you installed it yourself. If you forgot or if it is not your own machine, the only way to be absolutely sure would be to check any and all zsh configuration files (in your $HOME as well as in /etc or /etc/zsh).Cataclysm
@DidierA., yes you do. At least for me it did not work with only creating the better-completioin.zsh file. I also had to uncomment the CASE_SENSITIVE=true statement in ~/.zshrcPayoff
zstyle ':completion:*' matcher-list '' '+m:{a-zA-Z}={A-Za-z}' '+r:|[._-]=* r:|=*' '+l:|=* r:|=*' is better and works with more edge cases.Inhabitancy
> with the following lines I just want to check if "lines" is a typo or if there should be another line to that code?Finlay
@Finlay It is only a single line. "lines" was correct for the original code, which I edited almost 9 years ago…Cataclysm
P
60

Just uncomment the following line in ~/.zshrc:

# Uncomment the following line to use case-sensitive completion.
# CASE_SENSITIVE="true"

It worked for me

Pete answered 19/1, 2015 at 9:45 Comment(2)
This only works with Oh My Zsh. github.com/robbyrussell/oh-my-zsh/blob/master/lib/… Edit: Oops, it was about Oh My Zsh... But this answer doesn't fully satisfy the needs.Echoism
This stops case insensitive completion completely. It does not fall back to case insensitive completion when there is no case sensitive match.Grounds
H
44

For those not using oh-my-zsh, you can add the following two lines to ~/.zshrc

zstyle ':completion:*' matcher-list '' 'm:{a-zA-Z}={A-Za-z}' 'r:|=*' 'l:|=* r:|=*'
autoload -Uz compinit && compinit
Hutch answered 1/9, 2021 at 13:42 Comment(4)
Didn't work for me. For instance: cd d<Tab> doesn't even offer me "Dropbox"; it only match lowercase. BUT cd dr<Tab> does do the matches with "Dropbox". Am I missing something?Flores
@Flores You might want to add this extra line too setopt MENU_COMPLETE. Here is what it means: On an ambiguous completion, instead of listing possibilities or beeping, insert the first match immediately. Then when completion is requested again, remove the first match and insert the second match, etc. When there are no more matches, go back to the first one again.Hutch
this worked for me on macOS - thanks! was way too difficult to figure out how to configure thisSuperimposed
I got a rough idea about the r: part, but what is the l: part bringing to the table?Tergum

© 2022 - 2024 — McMap. All rights reserved.