Marco Ricci commited on 2025-01-01 00:21:55
Zeige 1 geänderte Dateien mit 51 Einfügungen und 0 Löschungen.
`click` already includes shell completion out of the box, so what remained to be done was to tell `click` what kind of values the arguments expect: * The `path` argument for `derivepassphrase export vault` will be completed with a filename or the string `VAULT_PATH`. Because this is not (as a whole) a supported standard category of completion items, we must generate the completion items ourselves in this case, including the partial filtering of results. * The `path` argument to the `--import` and `--export` options of `derivepassphrase vault` will be completed with a filename. `click` handles this for us. * The `service` argument to `devirepassphrase vault` will be completed with a service name, which we must manually extract and filter. The shell completion has so far only been tested interactively; in particular, it is currently excluded from coverage testing.
... | ... |
@@ -1343,6 +1343,23 @@ def _load_data( |
1343 | 1343 |
assert_never(fmt) |
1344 | 1344 |
|
1345 | 1345 |
|
1346 |
+def _shell_complete_vault_path( # pragma: no cover |
|
1347 |
+ ctx: click.Context, |
|
1348 |
+ param: click.Parameter, |
|
1349 |
+ incomplete: str, |
|
1350 |
+) -> list[str | click.shell_completion.CompletionItem]: |
|
1351 |
+ del ctx, param |
|
1352 |
+ if incomplete and 'VAULT_PATH'.startswith(incomplete): |
|
1353 |
+ ret: set[str | click.shell_completion.CompletionItem] = {'VAULT_PATH'} |
|
1354 |
+ for f in os.listdir(): |
|
1355 |
+ if f.startswith(incomplete): |
|
1356 |
+ ret.add(f + os.path.sep if os.path.isdir(f) else f) |
|
1357 |
+ return sorted(ret) |
|
1358 |
+ return [ |
|
1359 |
+ click.shell_completion.CompletionItem('', type='file'), |
|
1360 |
+ ] |
|
1361 |
+ |
|
1362 |
+ |
|
1346 | 1363 |
@derivepassphrase_export.command( |
1347 | 1364 |
'vault', |
1348 | 1365 |
context_settings={'help_option_names': ['-h', '--help']}, |
... | ... |
@@ -1401,6 +1418,7 @@ def _load_data( |
1401 | 1418 |
'path', |
1402 | 1419 |
metavar=_msg.TranslatedString(_msg.Label.EXPORT_VAULT_METAVAR_PATH), |
1403 | 1420 |
required=True, |
1421 |
+ shell_complete=_shell_complete_vault_path, |
|
1404 | 1422 |
) |
1405 | 1423 |
@click.pass_context |
1406 | 1424 |
def derivepassphrase_export_vault( |
... | ... |
@@ -2179,6 +2197,36 @@ def _validate_length( |
2179 | 2197 |
return int_value |
2180 | 2198 |
|
2181 | 2199 |
|
2200 |
+def _shell_complete_path( # pragma: no cover |
|
2201 |
+ ctx: click.Context, |
|
2202 |
+ parameter: click.Parameter, |
|
2203 |
+ incomplete: str, |
|
2204 |
+) -> list[str | click.shell_completion.CompletionItem]: |
|
2205 |
+ del ctx, parameter, incomplete |
|
2206 |
+ return [click.shell_completion.CompletionItem('', type='file')] |
|
2207 |
+ |
|
2208 |
+ |
|
2209 |
+def _shell_complete_service( # pragma: no cover |
|
2210 |
+ ctx: click.Context, |
|
2211 |
+ parameter: click.Parameter, |
|
2212 |
+ incomplete: str, |
|
2213 |
+) -> list[str | click.shell_completion.CompletionItem]: |
|
2214 |
+ del ctx, parameter |
|
2215 |
+ try: |
|
2216 |
+ config = _load_config() |
|
2217 |
+ return [sv for sv in config['services'] if sv.startswith(incomplete)] |
|
2218 |
+ except FileNotFoundError: |
|
2219 |
+ try: |
|
2220 |
+ config, _exc = _migrate_and_load_old_config() |
|
2221 |
+ return [ |
|
2222 |
+ sv for sv in config['services'] if sv.startswith(incomplete) |
|
2223 |
+ ] |
|
2224 |
+ except FileNotFoundError: |
|
2225 |
+ return [] |
|
2226 |
+ except Exception: # noqa: BLE001 |
|
2227 |
+ return [] |
|
2228 |
+ |
|
2229 |
+ |
|
2182 | 2230 |
DEFAULT_NOTES_TEMPLATE = """\ |
2183 | 2231 |
# Enter notes below the line with the cut mark (ASCII scissors and |
2184 | 2232 |
# dashes). Lines above the cut mark (such as this one) will be ignored. |
... | ... |
@@ -2416,6 +2464,7 @@ DEFAULT_NOTES_MARKER = '# - - - - - >8 - - - - -' |
2416 | 2464 |
), |
2417 | 2465 |
), |
2418 | 2466 |
cls=StorageManagementOption, |
2467 |
+ shell_complete=_shell_complete_path, |
|
2419 | 2468 |
) |
2420 | 2469 |
@click.option( |
2421 | 2470 |
'-i', |
... | ... |
@@ -2431,6 +2480,7 @@ DEFAULT_NOTES_MARKER = '# - - - - - >8 - - - - -' |
2431 | 2480 |
), |
2432 | 2481 |
), |
2433 | 2482 |
cls=StorageManagementOption, |
2483 |
+ shell_complete=_shell_complete_path, |
|
2434 | 2484 |
) |
2435 | 2485 |
@click.option( |
2436 | 2486 |
'--overwrite-existing/--merge-existing', |
... | ... |
@@ -2478,6 +2528,7 @@ DEFAULT_NOTES_MARKER = '# - - - - - >8 - - - - -' |
2478 | 2528 |
metavar=_msg.TranslatedString(_msg.Label.VAULT_METAVAR_SERVICE), |
2479 | 2529 |
required=False, |
2480 | 2530 |
default=None, |
2531 |
+ shell_complete=_shell_complete_service, |
|
2481 | 2532 |
) |
2482 | 2533 |
@click.pass_context |
2483 | 2534 |
def derivepassphrase_vault( # noqa: C901,PLR0912,PLR0913,PLR0914,PLR0915 |
2484 | 2535 |