Overhaul zsh completion: caching, global flags, --flag=value, test suite#36
Overhaul zsh completion: caching, global flags, --flag=value, test suite#36b-per wants to merge 1 commit into
Conversation
Caching
-------
Replace the previous flat-file cache (target/.dbt_completion_cache.txt)
with zsh's native _retrieve_cache / _store_cache, enabled via:
zstyle ':completion:*:complete:dbt:*' use-cache yes
Cache keys are composed of two parts to prevent collisions:
path_hash — cksum of the manifest / binary path, so two projects whose
manifests have the same mtime (compiled in the same second)
don't share entries.
mtime — ensures the cache is invalidated whenever the file changes.
Three cache namespaces are used:
dbt_models_<path_hash>_<mtime> — model/tag/source selectors
dbt_selectors_<path_hash>_<mtime> — YAML named selectors
dbt_click_<bin_mtime>_<ctx_hash> — Click completion responses, keyed
by a hash of the normalised context
string so many real command lines
collapse onto a small number of
cache entries
Click context normalisation
---------------------------
Prior flags are stripped from COMP_WORDS before calling Click because Click
returns the same flag list regardless of which flags have already been given.
This means "dbt run --project-dir /foo --<TAB>" and "dbt run --<TAB>" share
one cache entry ("dbt run -"), with the real partial filtered locally.
Exception: when global flags precede the subcommand (e.g.
"dbt --profiles-dir /p run --<TAB>") we cannot safely strip them because we
don't know which take a value argument. In that case the full word sequence is
passed to Click verbatim. The current word is still normalised to "-" for
flag-name completions so the same global-flag prefix shares one cache entry.
Other fixes
-----------
- --flag=value style (used by Fusion / clap): compset -P '*=' strips the
prefix so model/selector completions work for --select=<TAB> etc.
- Click multi-line description parser: fixed stride-3 loop that broke
alignment when a description spanned multiple lines (--warn-error-options).
- Manifest path helper (_dbt_manifest_path) extracted to reduce duplication.
Test suite
----------
Add test.zsh: 72 unit tests covering all pure-logic functions without
requiring a live dbt binary or a real completion context. Mocks stub out
_describe, compadd, _files, compset, _retrieve_cache, and _store_cache.
|
Fantastic work on this, learnt a few more things about zsh completions -- something I've been meaning to look at. Completions for
Found a couple of issues with dbt-core, zsh 5.9 and MacOS. I'm writing some comments now. |
|
Thanks for giving it a spin! So, normally If it isn't for you there must be a bug in the caching mechanism somewhere. |
|
I've been wanting to delve further into zsh completions for a while, so I had a bit of a look, and commited my changes for my future reference. Give https://github.com/joshuataylor/dbt-completion.bash/blob/feature/zsh-completion-overhaul/_dbt a go and let me know if it works for you. I haven't tested dbt fusion at all. Take what you want :-). Rambly notes below from a fun night of zshnes.. Cache issue (as you noted)
Cache keys ended up containing literal
Bug fixes
Bugs found while working on the above (all zsh footguns)
Cleanup: stop spawning sub-processes per TAB
|
Summary
_retrieve_cache/_store_cache; fix cache-key collision bug where two projects compiled at the same second would share entriesdbt --profiles-dir /p run --<TAB>now correctly passes the full word sequence to Click instead of dropping everything after the global flag--flag=valuesupport:--select=<TAB>,--exclude=<TAB>, etc. now list model selectors (usingcompset -P '*='to strip the prefix)--warn-error-optionsemits a multi-line description that broke the old stride-3 parser; replaced with a forward-scan approachtest.zshwith 72 unit tests covering all pure-logic functions without requiring a live binaryCaching approach
Completions are expensive — Click spawns a Python process (~1 s each call). The caching strategy has two layers.
1. Manifest cache (
_dbt_list_models,_dbt_list_selectors)Cache key:
dbt_models_<path_hash>_<mtime>(same pattern for selectors).mtime— invalidate when the manifest changes on diskpath_hash—cksumof the manifest path; added to fix a cross-project collision: two projects whose manifests have the same mtime (compiled in the same second) would previously share a cache entryStorage: zsh's
~/.zcompcache/via_retrieve_cache/_store_cache.2. Click completion cache (
_dbt_core_fetch_completions)Cache key:
dbt_click_<bin_mtime>_<ctx_hash>.bin_mtime— invalidate when the dbt binary changes (upgrade, reinstall)ctx_hash—cksumof the normalisedCOMP_WORDSstringContext normalisation is the key insight. Click returns the same flag list for
dbt runregardless of which flags have already been typed. So instead of caching one entry per unique command line, the context is collapsed:COMP_WORDSdbt run --project-dir /foo --<TAB>dbt run -dbt run --<TAB>dbt run -dbt run --log-format te<TAB>dbt run --log-format tedbt ru<TAB>dbt ruThe real partial is filtered locally after the cache is read, so the hit rate is high even while the user is still typing.
Exception — global flags: when
dbt --profiles-dir /p run --<TAB>is typed, stripping global flags would shift the word positions Click sees (we can't know which flags take a value). In that case the full word sequence is passed verbatim. The current word is still normalised to-for flag-name completions so the same global-flag prefix shares one cache entry.3. Fusion binary cache (
_dbt_fusion_complete)The clap_complete script from
dbt completions zshis stored to~/.cache/dbt_fusion_completions.zshand regenerated only when the binary mtime changes. Unchanged from before.