|
3 | 3 | //! This module defines the CLI structure using clap and routes commands |
4 | 4 | //! to their appropriate handlers. |
5 | 5 |
|
6 | | -use std::process::ExitStatus; |
| 6 | +use std::{ffi::OsStr, process::ExitStatus}; |
7 | 7 |
|
8 | 8 | use clap::{CommandFactory, FromArgMatches, Parser, Subcommand}; |
| 9 | +use clap_complete::ArgValueCompleter; |
| 10 | +use tokio::runtime::Runtime; |
9 | 11 | use vite_install::commands::{ |
10 | 12 | add::SaveDependencyType, install::InstallCommandOptions, outdated::Format, |
11 | 13 | }; |
@@ -615,7 +617,7 @@ pub enum Commands { |
615 | 617 | #[command(disable_help_flag = true)] |
616 | 618 | Run { |
617 | 619 | /// Additional arguments |
618 | | - #[arg(trailing_var_arg = true, allow_hyphen_values = true)] |
| 620 | + #[arg(trailing_var_arg = true, allow_hyphen_values = true, add = ArgValueCompleter::new(run_tasks_completions))] |
619 | 621 | args: Vec<String>, |
620 | 622 | }, |
621 | 623 |
|
@@ -1480,6 +1482,46 @@ fn should_force_global_delegate(command: &str, args: &[String]) -> bool { |
1480 | 1482 | } |
1481 | 1483 | } |
1482 | 1484 |
|
| 1485 | +/// Get available tasks for shell completion. |
| 1486 | +/// |
| 1487 | +/// Delegates to the local vite-plus CLI to run `vp run` without arguments, |
| 1488 | +/// which returns a list of available tasks in the format "task_name: description". |
| 1489 | +fn run_tasks_completions(current: &OsStr) -> Vec<clap_complete::CompletionCandidate> { |
| 1490 | + let Some(cwd) = std::env::current_dir() |
| 1491 | + .ok() |
| 1492 | + .and_then(AbsolutePathBuf::new) |
| 1493 | + .filter(|p| commands::has_vite_plus_dependency(p)) |
| 1494 | + else { |
| 1495 | + return vec![]; |
| 1496 | + }; |
| 1497 | + |
| 1498 | + // Unescape hashtag and trim quotes for better matching |
| 1499 | + let current = current |
| 1500 | + .to_string_lossy() |
| 1501 | + .replace("\\#", "#") |
| 1502 | + .trim_matches(|c| c == '"' || c == '\'') |
| 1503 | + .to_string(); |
| 1504 | + |
| 1505 | + let output = tokio::task::block_in_place(|| { |
| 1506 | + Runtime::new().ok().and_then(|rt| { |
| 1507 | + rt.block_on(async { commands::delegate::execute_output(cwd, "run", &[]).await.ok() }) |
| 1508 | + }) |
| 1509 | + }); |
| 1510 | + |
| 1511 | + output |
| 1512 | + .filter(|o| o.status.success()) |
| 1513 | + .map(|output| { |
| 1514 | + String::from_utf8_lossy(&output.stdout) |
| 1515 | + .lines() |
| 1516 | + .filter_map(|line| line.split_once(": ").map(|(name, _)| name.trim())) |
| 1517 | + .filter(|name| !name.is_empty()) |
| 1518 | + .filter(|name| name.starts_with(¤t) || current.is_empty()) |
| 1519 | + .map(|name| clap_complete::CompletionCandidate::new(name.to_string())) |
| 1520 | + .collect() |
| 1521 | + }) |
| 1522 | + .unwrap_or_default() |
| 1523 | +} |
| 1524 | + |
1483 | 1525 | /// Run the CLI command. |
1484 | 1526 | pub async fn run_command(cwd: AbsolutePathBuf, args: Args) -> Result<ExitStatus, Error> { |
1485 | 1527 | run_command_with_options(cwd, args, RenderOptions::default()).await |
|
0 commit comments