# Dynamic Shell Autocompletion for Artists/Albums/Tracks ## Problem `rsp play ` and `rsp queue ` accept freeform strings. No tab-completion for artists, albums, or tracks from the SQLite library DB. User must type full path or query blindly. ## Approach: Direct DB read (no daemon dependency) Hidden CLI subcommand `rsp complete [prefix]` that: 1. Opens DB directly (same as `rsp scan` at cli.rs:204-205) 2. Queries with prefix match 3. Prints newline-separated results to stdout 4. Returns empty on error (never crash completion) Works with or without daemon running. SQLite prefix query is ~1ms. ## Steps ### 1. Add completion query methods to `Database` (`src/database.rs`) ``` pub fn complete_artists(&self, prefix: &str) -> SqlResult> pub fn complete_albums(&self, prefix: &str) -> SqlResult> pub fn complete_tracks(&self, prefix: &str) -> SqlResult> ``` SQL: - artists: `SELECT name FROM artists WHERE name LIKE ? ORDER BY name` - albums: `SELECT title FROM albums WHERE title LIKE ? ORDER BY title` - tracks: `SELECT title FROM tracks WHERE title LIKE ? ORDER BY title` Prefix `"a"` → `LIKE 'a%'`. Limit results (e.g. 25). ### 2. Add hidden `DbComplete` subcommand (`src/cli.rs`) ``` #[clap(hide = true)] DbComplete { entity: String, // "artists", "albums", "tracks" prefix: Option, } ``` Dispatch in `Cli::run()`: ``` Commands::DbComplete { entity, prefix } => { let config = Config::load()?; let db = Database::open(&config)?; let results = match entity.as_str() { ... }; for r in results { println!("{r}"); } } ``` Errors → silent empty (stderr) so completion never breaks. ### 3. Filter `DbComplete` out of generated completions (like `Completions`) In `Completions` generation (cli.rs:238-253): filter `s.get_name() != "db-complete"`. ### 4. Dynamic zsh completion function Replace `/usr/share/zsh/site-functions/_rsp` with a wrapper that: ```zsh #compdef rsp # Static part (generated by clap) _rsp_command() { ... } # clap-generated # Dynamic overrides _rsp() { case $line[1] in play|queue) if compset -P 1; then # Subcommand → complete tracks/artists/albums local -a suggestions suggestions=(${(f)"$(rsp complete tracks "$words[CURRENT]")"}) suggestions+=(${(f)"$(rsp complete artists "$words[CURRENT]")"}) suggestions+=(${(f)"$(rsp complete albums "$words[CURRENT]")"}) compadd -a suggestions else _rsp_command fi ;; *) _rsp_command ;; esac } ``` For `play `, complete artists + albums + tracks. Where artists have the biggest priority, then comes albums and at the end tracks. Additionally, when we have autocompleted an artist, put a '/' after the name. Next autocompletions will autocomplete albums/tracks, do the same for albums. ### 5. Update `Completions` generation to produce the wrapper In `cli.rs`, after `generate()` for zsh, append the dynamic wrapper code. The generated file becomes: `clap-generated-code` + `_rsp() { ... dynamic ... }`. Or simpler: write the wrapper as `completions/_rsp.dynamic` and concatenate in install.sh. ### 6. Update `install.sh` Replace `rsp completions zsh | sudo tee ...` with: ```bash (rsp completions zsh; echo; cat completions/_rsp) | sudo tee "$dest" ``` ### 7. Update `TODO.md` Mark "[x] Dynamic autocompletion of artists, albums and tracks" ### 8. Tests Add tests in `tests/database.rs`: - `test_complete_artists_prefix` - `test_complete_albums_prefix` - `test_complete_tracks_prefix` - `test_complete_empty_db` - `test_complete_no_match` Need a test helper that creates a temp DB with known data, runs queries, checks results. ## Files changed | File | Change | |------|--------| | `src/database.rs` | +3 pub methods (complete_artists/albums/tracks) | | `src/cli.rs` | +DbComplete variant, +dispatch, +filter in Completions | | `completions/_rsp.dynamic` (new) | zsh dynamic completion wrapper | | `install.sh` | concatenate static + dynamic | | `tests/database.rs` | +completion query tests | ## Future: fish/bash Same pattern. Fish uses `complete -c rsp -n '__fish_seen_subcommand_from play' -a '(rsp complete tracks (commandline -ct))'`. Bash uses `COMPREPLY=($(compgen -W "$(rsp complete tracks "$cur")" -- "$cur"))`. Start with zsh only (user's shell).