1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
|
# Dynamic Shell Autocompletion for Artists/Albums/Tracks
## Problem
`rsp play <query>` and `rsp queue <query>` 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 <entity> [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<Vec<String>>
pub fn complete_albums(&self, prefix: &str) -> SqlResult<Vec<String>>
pub fn complete_tracks(&self, prefix: &str) -> SqlResult<Vec<String>>
```
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<String>,
}
```
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 <query>`, 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).
|