skip to content

zsh's completion system is configured with `zstyle` on context strings such as `':completion:*:*:git:*'`. What is a zstyle context, how does zsh decide which of several matching zstyle lines applies, and what would you set to get case-insensitive matching and an arrow-key menu?

level: middleimportance: must knowfreq 55%

answer

  1. configuration is a lookup, not a set of options
  2. a colon-separated description of the situation
  3. patterns compete; specificity decides
  4. one style takes match specifications
  5. another turns the list into a navigable menu

basics

~20 s

A zstyle context is a colon-separated string describing the exact situation being completed, and zstyle lines are patterns matched against it, with the most specific matching pattern winning. Case-insensitive matching comes from the matcher-list style; the arrow-key menu comes from the menu style set to select.

solid answer

~40 s

compsys has almost no options; it is configured by looking up **styles** in a **context**. Every time a completion runs, zsh builds a context string of the shape `:completion:<function>:<completer>:<command>:<argument>:<tag>` — for example the tag `files` while completing an argument of `git`. Your `zstyle` lines register a pattern plus a style name and value, and at lookup time zsh picks the value from the most specific pattern that matches the current context, which is why a broad `':completion:*'` line acts as a default that a narrower `':completion:*:*:git:*'` line overrides. Two styles cover most of what people actually want: `zstyle ':completion:*' matcher-list 'm:{a-zA-Z}={A-Za-z}'` makes matching case-insensitive, and `zstyle ':completion:*' menu select` turns the completion list into a menu you move through with the arrow keys.

code

bash · 4 lines
bash
zstyle ':completion:*' matcher-list '' 'm:{a-zA-Z}={A-Za-z}'
zstyle ':completion:*' menu select
zstyle ':completion:*' group-name ''
zstyle ':completion:*:descriptions' format '%F{yellow}-- %d --%f'

go deeper

for a junior

Recognise that zsh completion is configured with zstyle lines rather than options, and be able to name the two everyday ones: matcher-list for case-insensitive matching and menu select for an arrow-key list.

for a middle

Break a context string into its parts — function, completer, command, argument, tag — and explain that the most specific matching pattern supplies the value, not the last one defined.

for a senior

Show how you would target a real situation precisely: use _complete_help to read the live context, scope the style to one command or one tag, and know that matcher-list is a fallback list rather than a single rule.

for a principal

Think about the shared-configuration angle: broad ':completion:*' defaults in a team dotfile silently override nothing but are overridden by anything narrower, so document which contexts are reserved and keep the layering shallow enough to debug.

## Why zstyle instead of options Completion behaviour varies enormously by situation: you may want fuzzy matching for filenames but exact matching for git branches, a different listing format for processes, caching for one slow command only. Encoding that as shell options would need hundreds of them. Instead compsys uses `zstyle`, a general zsh mechanism for context-sensitive configuration, and asks it a question every time it needs a decision: *in this exact situation, what is the value of style X?* `zstyle` itself is not part of the completion system — `vcs_info` and other zsh components use it too, which is why every completion context begins with the literal component `:completion:`. ## The shape of a context A completion context is a colon-separated string: ``` :completion:<function>:<completer>:<command>:<argument>:<tag> ``` - **function** — the widget or calling function, empty for ordinary completion. - **completer** — which completer produced this attempt: `complete`, `approximate`, `correct` and so on. - **command** — the command being completed, e.g. `git`, `kill`, `ssh`. - **argument** — which argument position or option is being completed. - **tag** — what *kind* of match is being offered: `files`, `directories`, `options`, `processes`, `users`. Tags are the key idea: one command can offer several tags at the same point, and styles can target one of them. So `':completion:*:*:kill:*:processes'` reads as: any function, any completer, the `kill` command, any argument, but only when offering process matches. ## Lookup: most specific wins You write `zstyle <pattern> <style> <value...>`. The pattern is matched against the context string at lookup time; when several patterns match, zsh uses the value from the **most specific** one, not the last one defined and not the first. That is what makes the common layered configuration work: ```bash zstyle ':completion:*' menu select zstyle ':completion:*:*:git:*' menu no ``` Here the menu is on everywhere except while completing `git`. Order in the file does not matter; specificity does. `zstyle -L` prints all currently defined styles in a re-usable form, which is the fastest way to see what a framework or a shared dotfile has already set for you. ## The two styles people actually want **Case-insensitive matching** is not a flag but a *matcher specification* passed through the `matcher-list` style: ```bash zstyle ':completion:*' matcher-list 'm:{a-zA-Z}={A-Za-z}' ``` The `m:` matcher says: a character on the left-hand side, as typed, may match any of the characters on the right-hand side in the candidate. Mapping both cases in both directions gives symmetric case-insensitivity. `matcher-list` takes a *list*: zsh tries each specification in turn and stops at the first that produces matches, so `matcher-list '' 'm:{a-zA-Z}={A-Za-z}'` means "try exact first, then fall back to case-insensitive" — usually what you want, because it stops a case-insensitive near-match from hiding an exact one. **Menu selection** turns the printed list into an interactive selection you move through with the arrow keys and accept with Return: ```bash zstyle ':completion:*' menu select ``` The interactive selection itself is implemented by the `zsh/complist` module; some configurations load it explicitly with `zmodload zsh/complist`. Note the distinction between *menu completion* (repeated Tab cycles through candidates on the command line) and *menu selection* (a highlighted list you navigate) — `select` is the latter. ## Presentation styles worth knowing ```bash zstyle ':completion:*' group-name '' zstyle ':completion:*:descriptions' format '%F{yellow}-- %d --%f' ``` Setting `group-name` to the empty string groups matches under their own tag, and the `descriptions` `format` style gives each group a header (`%d` is the description; `%F`/`%f` are colour escapes). Together they turn an undifferentiated blob of matches into a labelled list — the visual difference people notice first when they see someone else's zsh. ## Finding the context you need to target Guessing context strings is miserable. compsys ships a helper widget, `_complete_help`, bound to Ctrl-X h by default: type a command line, press it, and zsh prints the tags and context strings in play at that point. Copy the context from that output into your `zstyle` line and you are configuring exactly the situation you meant, rather than a pattern you hope matches.

  • If two zstyle patterns both match the current context, which value is used?
    The one from the most specific matching pattern. Definition order is irrelevant, which is what makes the common layering work: a broad `':completion:*'` line sets a default and a narrower per-command or per-tag line overrides it. `zstyle -L` shows everything currently defined when you need to see who set what.
  • What is a completion tag, and why do styles target them?
    A tag names the kind of match being offered at that point — `files`, `directories`, `options`, `processes`, `users`. A single command position often offers several tags at once, so tags let you style or order them independently: format process listings one way, hide a tag entirely, or set the `tag-order` style to decide which kind is offered first.
  • Why is `matcher-list '' 'm:{a-zA-Z}={A-Za-z}'` often better than the case-insensitive matcher alone?
    matcher-list is a list of specifications tried in order until one yields matches. Putting the empty specification first means an exact match is preferred, and case-insensitive matching only kicks in when exact matching produced nothing — so a file that matches exactly is never buried among differently-cased alternatives.
  • How do you find out which context string applies to a given completion point?
    Use the `_complete_help` widget, bound to Ctrl-X h by default. Type the command line, press it, and zsh prints the tags and full context strings being used at the cursor. Copying that context into your zstyle line beats guessing at pattern shapes.

saying these in an interview costs you the question

  • Thinks the last matching zstyle line wins
  • Treats zstyle as a completion-only builtin
  • Confuses menu completion with menu selection
  • Expects a single option to switch on case-insensitivity
  • Cannot name any component of a context string

context