diff options
Diffstat (limited to 'crates')
34 files changed, 661 insertions, 73 deletions
diff --git a/crates/shirabe-external-packages/src/symfony/console/command/complete_command.rs b/crates/shirabe-external-packages/src/symfony/console/command/complete_command.rs index dcffb5f8..a4567829 100644 --- a/crates/shirabe-external-packages/src/symfony/console/command/complete_command.rs +++ b/crates/shirabe-external-packages/src/symfony/console/command/complete_command.rs @@ -332,7 +332,8 @@ impl Command for CompleteCommand { ); } Some(command) => { - command.borrow().merge_application_definition(false); + // PHP: $command->mergeApplicationDefinition() — $mergeArgs defaults to true. + command.borrow().merge_application_definition(true); completion_input.bind(&command.borrow().get_definition())?; if CompletionInput::TYPE_OPTION_NAME == completion_input.get_completion_type() { diff --git a/crates/shirabe/src/command/about_command.rs b/crates/shirabe/src/command/about_command.rs index 799b889f..a8653ad9 100644 --- a/crates/shirabe/src/command/about_command.rs +++ b/crates/shirabe/src/command/about_command.rs @@ -64,6 +64,14 @@ impl Command for AboutCommand { base_command_initialize(self, input, output) } + fn complete( + &self, + input: &shirabe_external_packages::symfony::console::completion::completion_input::CompletionInput, + suggestions: &mut shirabe_external_packages::symfony::console::completion::completion_suggestions::CompletionSuggestions, + ) -> anyhow::Result<()> { + crate::command::base_command::base_command_complete(self, input, suggestions) + } + shirabe_external_packages::delegate_command_trait_impls_to_inner!( base_command_data, "Composer\\Command\\AboutCommand" diff --git a/crates/shirabe/src/command/archive_command.rs b/crates/shirabe/src/command/archive_command.rs index b6425a27..fbca89f9 100644 --- a/crates/shirabe/src/command/archive_command.rs +++ b/crates/shirabe/src/command/archive_command.rs @@ -1,11 +1,13 @@ //! ref: composer/src/Composer/Command/ArchiveCommand.php +use crate::command::CompletionTrait; use crate::command::base_command::base_command_initialize; use crate::command::{BaseCommand, BaseCommandData}; use crate::composer::PartialComposerHandle; use crate::config::Config; use crate::console::input::InputArgument; use crate::console::input::InputOption; +use crate::console::input::SuggestedValues; use crate::factory::Factory; use crate::io::IOInterface; use crate::io::IOInterfaceImmutable; @@ -80,13 +82,12 @@ impl ArchiveCommand { impl Command for ArchiveCommand { fn configure(&self) -> anyhow::Result<()> { - // TODO(cli-completion): suggest_available_package(99) for `package` argument self.set_name("archive")?; self.set_description("Creates an archive of this composer package"); self.set_definition(&[ - InputArgument::new("package", Some(InputArgument::OPTIONAL), "The package to archive instead of the current project", None).unwrap().into(), + InputArgument::new5("package", Some(InputArgument::OPTIONAL), "The package to archive instead of the current project", None, self.suggest_available_package(99)).unwrap().into(), InputArgument::new("version", Some(InputArgument::OPTIONAL), "A version constraint to find the package to archive", None).unwrap().into(), - InputOption::new("format", Some(shirabe_php_shim::PhpMixed::String("f".to_string())), Some(InputOption::VALUE_REQUIRED), "Format of the resulting archive: tar, tar.gz, tar.bz2 or zip (default tar)", None).unwrap().into(), + InputOption::new6("format", Some(shirabe_php_shim::PhpMixed::String("f".to_string())), Some(InputOption::VALUE_REQUIRED), "Format of the resulting archive: tar, tar.gz, tar.bz2 or zip (default tar)", None, SuggestedValues::List(Self::FORMATS.iter().map(|s| s.to_string()).collect())).unwrap().into(), InputOption::new("dir", None, Some(InputOption::VALUE_REQUIRED), "Write the archive to this directory", None).unwrap().into(), InputOption::new("file", None, Some(InputOption::VALUE_REQUIRED), "Write the archive with the given file name. Note that the format will be appended.", None).unwrap().into(), InputOption::new("ignore-filters", None, Some(InputOption::VALUE_NONE), "Ignore filters when saving package", None).unwrap().into(), @@ -214,6 +215,14 @@ impl Command for ArchiveCommand { base_command_initialize(self, input, output) } + fn complete( + &self, + input: &shirabe_external_packages::symfony::console::completion::completion_input::CompletionInput, + suggestions: &mut shirabe_external_packages::symfony::console::completion::completion_suggestions::CompletionSuggestions, + ) -> anyhow::Result<()> { + crate::command::base_command::base_command_complete(self, input, suggestions) + } + shirabe_external_packages::delegate_command_trait_impls_to_inner!( base_command_data, "Composer\\Command\\ArchiveCommand" diff --git a/crates/shirabe/src/command/audit_command.rs b/crates/shirabe/src/command/audit_command.rs index 52307297..25ca433b 100644 --- a/crates/shirabe/src/command/audit_command.rs +++ b/crates/shirabe/src/command/audit_command.rs @@ -7,6 +7,7 @@ use crate::command::BaseCommandData; use crate::command::base_command::base_command_initialize; use crate::composer::PartialComposerHandle; use crate::console::input::InputOption; +use crate::console::input::SuggestedValues; use crate::io::IOInterfaceImmutable; use crate::repository::InstalledRepository; use crate::repository::RepositoryInterface; @@ -83,12 +84,18 @@ impl Command for AuditCommand { ) .unwrap() .into(), - InputOption::new( + InputOption::new6( "ignore-severity", None, Some(InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED), "Ignore advisories of a certain severity level.", Some(PhpMixed::Array(indexmap::IndexMap::new())), + SuggestedValues::List(vec![ + "low".to_string(), + "medium".to_string(), + "high".to_string(), + "critical".to_string(), + ]), ) .unwrap() .into(), @@ -223,6 +230,14 @@ impl Command for AuditCommand { base_command_initialize(self, input, output) } + fn complete( + &self, + input: &shirabe_external_packages::symfony::console::completion::completion_input::CompletionInput, + suggestions: &mut shirabe_external_packages::symfony::console::completion::completion_suggestions::CompletionSuggestions, + ) -> anyhow::Result<()> { + crate::command::base_command::base_command_complete(self, input, suggestions) + } + shirabe_external_packages::delegate_command_trait_impls_to_inner!( base_command_data, "Composer\\Command\\AuditCommand" diff --git a/crates/shirabe/src/command/bump_command.rs b/crates/shirabe/src/command/bump_command.rs index 5560a293..20df759f 100644 --- a/crates/shirabe/src/command/bump_command.rs +++ b/crates/shirabe/src/command/bump_command.rs @@ -1,5 +1,6 @@ //! ref: composer/src/Composer/Command/BumpCommand.php +use crate::command::CompletionTrait; use crate::command::base_command::base_command_initialize; use crate::command::{BaseCommand, BaseCommandData}; use crate::console::input::InputArgument; @@ -332,15 +333,15 @@ impl BumpCommand { impl Command for BumpCommand { fn configure(&self) -> anyhow::Result<()> { - // TODO(cli-completion): suggest_root_requirement() for `packages` argument self.set_name("bump")?; self.set_description("Increases the lower limit of your composer.json requirements to the currently installed versions"); self.set_definition(&[ - InputArgument::new( + InputArgument::new5( "packages", Some(InputArgument::IS_ARRAY | InputArgument::OPTIONAL), "Optional package name(s) to restrict which packages are bumped.", None, + self.suggest_root_requirement(), ) .unwrap() .into(), @@ -436,6 +437,14 @@ impl Command for BumpCommand { base_command_initialize(self, input, output) } + fn complete( + &self, + input: &shirabe_external_packages::symfony::console::completion::completion_input::CompletionInput, + suggestions: &mut shirabe_external_packages::symfony::console::completion::completion_suggestions::CompletionSuggestions, + ) -> anyhow::Result<()> { + crate::command::base_command::base_command_complete(self, input, suggestions) + } + shirabe_external_packages::delegate_command_trait_impls_to_inner!( base_command_data, "Composer\\Command\\BumpCommand" diff --git a/crates/shirabe/src/command/check_platform_reqs_command.rs b/crates/shirabe/src/command/check_platform_reqs_command.rs index f4017350..bcd16d57 100644 --- a/crates/shirabe/src/command/check_platform_reqs_command.rs +++ b/crates/shirabe/src/command/check_platform_reqs_command.rs @@ -3,6 +3,7 @@ use crate::command::base_command::base_command_initialize; use crate::command::{BaseCommand, BaseCommandData}; use crate::console::input::InputOption; +use crate::console::input::SuggestedValues; use crate::io::IOInterfaceImmutable; use crate::json::JsonFile; use crate::package::Link; @@ -171,12 +172,13 @@ impl Command for CheckPlatformReqsCommand { ) .unwrap() .into(), - InputOption::new( + InputOption::new6( "format", Some(shirabe_php_shim::PhpMixed::String("f".to_string())), Some(InputOption::VALUE_REQUIRED), "Format of the output: text or json", Some(shirabe_php_shim::PhpMixed::String("text".to_string())), + SuggestedValues::List(vec!["json".to_string(), "text".to_string()]), ) .unwrap() .into(), @@ -402,6 +404,14 @@ impl Command for CheckPlatformReqsCommand { base_command_initialize(self, input, output) } + fn complete( + &self, + input: &shirabe_external_packages::symfony::console::completion::completion_input::CompletionInput, + suggestions: &mut shirabe_external_packages::symfony::console::completion::completion_suggestions::CompletionSuggestions, + ) -> anyhow::Result<()> { + crate::command::base_command::base_command_complete(self, input, suggestions) + } + shirabe_external_packages::delegate_command_trait_impls_to_inner!( base_command_data, "Composer\\Command\\CheckPlatformReqsCommand" diff --git a/crates/shirabe/src/command/clear_cache_command.rs b/crates/shirabe/src/command/clear_cache_command.rs index d10bdb20..8aee3f33 100644 --- a/crates/shirabe/src/command/clear_cache_command.rs +++ b/crates/shirabe/src/command/clear_cache_command.rs @@ -184,6 +184,14 @@ impl Command for ClearCacheCommand { base_command_initialize(self, input, output) } + fn complete( + &self, + input: &shirabe_external_packages::symfony::console::completion::completion_input::CompletionInput, + suggestions: &mut shirabe_external_packages::symfony::console::completion::completion_suggestions::CompletionSuggestions, + ) -> anyhow::Result<()> { + crate::command::base_command::base_command_complete(self, input, suggestions) + } + shirabe_external_packages::delegate_command_trait_impls_to_inner!( base_command_data, "Composer\\Command\\ClearCacheCommand" diff --git a/crates/shirabe/src/command/config_command.rs b/crates/shirabe/src/command/config_command.rs index 5f56b274..a6ba2f76 100644 --- a/crates/shirabe/src/command/config_command.rs +++ b/crates/shirabe/src/command/config_command.rs @@ -8,6 +8,7 @@ use crate::config::ConfigSourceInterface; use crate::config::JsonConfigSource; use crate::console::input::InputArgument; use crate::console::input::InputOption; +use crate::factory::Factory; use crate::io::IOInterfaceImmutable; use crate::io::io_interface; use crate::json::JsonEncodeOptions; @@ -85,7 +86,6 @@ impl ConfigCommand { impl Command for ConfigCommand { fn configure(&self) -> anyhow::Result<()> { - // TODO(cli-completion): suggest_setting_keys() for `setting-key` argument self.set_name("config")?; self.set_description("Sets config options"); self.set_definition(&[ @@ -100,7 +100,7 @@ impl Command for ConfigCommand { InputOption::new("merge", Some(PhpMixed::String("m".to_string())), Some(InputOption::VALUE_NONE), "Merge the setting value with the current value, to be used with extra.* or audit.ignore[-abandoned] keys in combination with --json", None).unwrap().into(), InputOption::new("append", None, Some(InputOption::VALUE_NONE), "When adding a repository, append it (lowest priority) to the existing ones instead of prepending it (highest priority)", None).unwrap().into(), InputOption::new("source", None, Some(InputOption::VALUE_NONE), "Display where the config value is loaded from", None).unwrap().into(), - InputArgument::new("setting-key", None, "Setting key", None).unwrap().into(), + InputArgument::new5("setting-key", None, "Setting key", None, self.suggest_setting_keys()).unwrap().into(), InputArgument::new("setting-value", Some(InputArgument::IS_ARRAY), "Setting value", None).unwrap().into(), ]); self.set_help( @@ -1273,6 +1273,14 @@ impl Command for ConfigCommand { .into()) } + fn complete( + &self, + input: &shirabe_external_packages::symfony::console::completion::completion_input::CompletionInput, + suggestions: &mut shirabe_external_packages::symfony::console::completion::completion_suggestions::CompletionSuggestions, + ) -> anyhow::Result<()> { + crate::command::base_command::base_command_complete(self, input, suggestions) + } + shirabe_external_packages::delegate_command_trait_impls_to_inner!( base_command_data, "Composer\\Command\\ConfigCommand" @@ -1536,7 +1544,123 @@ impl ConfigCommand { } } - // TODO(cli-completion): fn suggest_setting_keys(&self) -> Box<dyn Fn(&CompletionInput) -> Vec<String>> + /// Suggest setting-keys, while taking given options in account. + fn suggest_setting_keys(&self) -> crate::console::input::SuggestedValues { + crate::console::input::SuggestedValues::Closure(Box::new(|this, input, _suggestions| { + if input.get_option("list")?.to_bool() + || input.get_option("editor")?.to_bool() + || input.get_option("auth")?.to_bool() + { + return Ok(vec![]); + } + + let this = this + .as_any() + .downcast_ref::<ConfigCommand>() + .expect("suggestSettingKeys is bound to ConfigCommand"); + // PHP passes the CompletionInput itself; the accessors only read from it, so a + // clone behind a fresh handle is equivalent. + let input_handle: std::rc::Rc< + std::cell::RefCell< + dyn shirabe_external_packages::symfony::console::input::InputInterface, + >, + > = std::rc::Rc::new(std::cell::RefCell::new(input.clone())); + + // initialize configuration + let mut config = Factory::create_config(None, None)?; + + // load configuration + let mut config_file = JsonFile::new( + this.get_composer_config_file(input_handle.clone(), &config)?, + None, + None, + )?; + if config_file.exists() { + let path = config_file.get_path().to_string(); + let data = config_file.read()?.as_array().cloned().unwrap_or_default(); + config.merge(&data, &path); + } + + // load auth-configuration + let mut auth_config_file = JsonFile::new( + this.get_auth_config_file(input_handle.clone(), &config)?, + None, + None, + )?; + if auth_config_file.exists() { + let path = auth_config_file.get_path().to_string(); + let mut data = IndexMap::new(); + data.insert("config".to_string(), auth_config_file.read()?); + config.merge(&data, &path); + } + + // collect all configuration setting-keys + let raw_config = config.raw(); + let mut keys = flatten_setting_keys( + raw_config.get("config").cloned().unwrap_or(PhpMixed::Null), + "", + ); + keys.extend(flatten_setting_keys( + raw_config + .get("repositories") + .cloned() + .unwrap_or(PhpMixed::Null), + "repositories.", + )); + + // if unsetting … + if input.get_option("unset")?.to_bool() { + // … keep only the currently customized setting-keys … + let sources = [ + config_file.get_path().to_string(), + auth_config_file.get_path().to_string(), + ]; + keys.retain(|key| sources.contains(&config.get_source_of_value(key))); + + // … else if showing or setting a value … + } else { + // … add all configurable package-properties, no matter if it exist + keys.extend( + Self::CONFIGURABLE_PACKAGE_PROPERTIES + .iter() + .map(|property| property.to_string()), + ); + + // it would be nice to distinguish between showing and setting + // a value, but that makes the implementation much more complex + // and partially impossible because symfony's implementation + // does not complete arguments followed by other arguments + } + + // add all existing configurable package-properties + if config_file.exists() { + let properties: IndexMap<String, PhpMixed> = config_file + .read()? + .as_array() + .cloned() + .unwrap_or_default() + .into_iter() + .filter(|(key, _)| { + Self::CONFIGURABLE_PACKAGE_PROPERTIES.contains(&key.as_str()) + }) + .collect(); + + keys.extend(flatten_setting_keys(PhpMixed::Array(properties), "")); + } + + // filter settings-keys by completion value + let completion_value = input.get_completion_value(); + + if !completion_value.is_empty() { + keys.retain(|key| key.starts_with(&completion_value)); + } + + keys.sort(); + + keys.dedup(); + Ok(keys) + })) + } } // PHP signature: function ($val): bool / ($val) -> bool/string @@ -2197,9 +2321,6 @@ fn flatten_setting_keys(config: PhpMixed, prefix: &str) -> Vec<String> { merged } -// TODO(cli-completion): get_composer_config_file_static / get_auth_config_file_static helpers -// were only used by suggest_setting_keys; dropped along with completion support. - // PHP key($value) — first key of an array fn key_first_key(value: &PhpMixed) -> Option<String> { if let Some(arr) = value.as_array() { diff --git a/crates/shirabe/src/command/create_project_command.rs b/crates/shirabe/src/command/create_project_command.rs index 82924a95..6da21f48 100644 --- a/crates/shirabe/src/command/create_project_command.rs +++ b/crates/shirabe/src/command/create_project_command.rs @@ -1,6 +1,7 @@ //! ref: composer/src/Composer/Command/CreateProjectCommand.php use crate::advisory::Auditor; +use crate::command::CompletionTrait; use crate::command::base_command::base_command_initialize; use crate::command::{BaseCommand, BaseCommandData}; use crate::config::Config; @@ -78,17 +79,16 @@ impl CreateProjectCommand { impl Command for CreateProjectCommand { fn configure(&self) -> anyhow::Result<()> { - // TODO(cli-completion): suggest_prefer_install / suggest_available_package self.set_name("create-project")?; self.set_description("Creates new project from a package into given directory"); self.set_definition(&[ - InputArgument::new("package", Some(InputArgument::OPTIONAL), "Package name to be installed", None).unwrap().into(), + InputArgument::new5("package", Some(InputArgument::OPTIONAL), "Package name to be installed", None, self.suggest_available_package(99)).unwrap().into(), InputArgument::new("directory", Some(InputArgument::OPTIONAL), "Directory where the files should be created", None).unwrap().into(), InputArgument::new("version", Some(InputArgument::OPTIONAL), "Version, will default to latest", None).unwrap().into(), InputOption::new("stability", Some(PhpMixed::String("s".to_string())), Some(InputOption::VALUE_REQUIRED), "Minimum-stability allowed (unless a version is specified).", None).unwrap().into(), InputOption::new("prefer-source", None, Some(InputOption::VALUE_NONE), "Forces installation from package sources when possible, including VCS information.", None).unwrap().into(), InputOption::new("prefer-dist", None, Some(InputOption::VALUE_NONE), "Forces installation from package dist (default behavior).", None).unwrap().into(), - InputOption::new("prefer-install", None, Some(InputOption::VALUE_REQUIRED), "Forces installation from package dist|source|auto (auto chooses source for dev versions, dist for the rest).", None).unwrap().into(), + InputOption::new6("prefer-install", None, Some(InputOption::VALUE_REQUIRED), "Forces installation from package dist|source|auto (auto chooses source for dev versions, dist for the rest).", None, self.suggest_prefer_install()).unwrap().into(), InputOption::new("repository", None, Some(InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY), "Add custom repositories to look the package up, either by URL or using JSON arrays", None).unwrap().into(), InputOption::new("repository-url", None, Some(InputOption::VALUE_REQUIRED), "DEPRECATED: Use --repository instead.", None).unwrap().into(), InputOption::new("add-repository", None, Some(InputOption::VALUE_NONE), "Add the custom repository in the composer.json. If a lock file is present it will be deleted and an update will be run instead of install.", None).unwrap().into(), @@ -262,6 +262,14 @@ impl Command for CreateProjectCommand { base_command_initialize(self, input, output) } + fn complete( + &self, + input: &shirabe_external_packages::symfony::console::completion::completion_input::CompletionInput, + suggestions: &mut shirabe_external_packages::symfony::console::completion::completion_suggestions::CompletionSuggestions, + ) -> anyhow::Result<()> { + crate::command::base_command::base_command_complete(self, input, suggestions) + } + shirabe_external_packages::delegate_command_trait_impls_to_inner!( base_command_data, "Composer\\Command\\CreateProjectCommand" diff --git a/crates/shirabe/src/command/depends_command.rs b/crates/shirabe/src/command/depends_command.rs index ff01b3af..145d174a 100644 --- a/crates/shirabe/src/command/depends_command.rs +++ b/crates/shirabe/src/command/depends_command.rs @@ -3,6 +3,7 @@ use crate::command::BaseCommand; use crate::command::BaseCommandData; use crate::command::BaseDependencyCommand; +use crate::command::CompletionTrait; use crate::command::base_command::base_command_initialize; use crate::console::input::InputArgument; use crate::console::input::InputOption; @@ -48,16 +49,16 @@ impl BaseDependencyCommand for DependsCommand { impl Command for DependsCommand { fn configure(&self) -> anyhow::Result<()> { - // TODO(cli-completion): suggest_installed_package(true, true) for `package` argument self.set_name("depends")?; self.set_aliases(vec!["why".to_string()])?; self.set_description("Shows which packages cause the given package to be installed"); self.set_definition(&[ - InputArgument::new( + InputArgument::new5( crate::command::ARGUMENT_PACKAGE, Some(InputArgument::REQUIRED), "Package to inspect", None, + self.suggest_installed_package(true, true), ) .unwrap() .into(), @@ -113,6 +114,14 @@ impl Command for DependsCommand { base_command_initialize(self, input, output) } + fn complete( + &self, + input: &shirabe_external_packages::symfony::console::completion::completion_input::CompletionInput, + suggestions: &mut shirabe_external_packages::symfony::console::completion::completion_suggestions::CompletionSuggestions, + ) -> anyhow::Result<()> { + crate::command::base_command::base_command_complete(self, input, suggestions) + } + shirabe_external_packages::delegate_command_trait_impls_to_inner!( base_command_data, "Composer\\Command\\DependsCommand" diff --git a/crates/shirabe/src/command/diagnose_command.rs b/crates/shirabe/src/command/diagnose_command.rs index d0f8f592..047b8377 100644 --- a/crates/shirabe/src/command/diagnose_command.rs +++ b/crates/shirabe/src/command/diagnose_command.rs @@ -444,6 +444,14 @@ impl Command for DiagnoseCommand { base_command_initialize(self, input, output) } + fn complete( + &self, + input: &shirabe_external_packages::symfony::console::completion::completion_input::CompletionInput, + suggestions: &mut shirabe_external_packages::symfony::console::completion::completion_suggestions::CompletionSuggestions, + ) -> anyhow::Result<()> { + crate::command::base_command::base_command_complete(self, input, suggestions) + } + shirabe_external_packages::delegate_command_trait_impls_to_inner!( base_command_data, "Composer\\Command\\DiagnoseCommand" diff --git a/crates/shirabe/src/command/dump_autoload_command.rs b/crates/shirabe/src/command/dump_autoload_command.rs index 02c25ba9..d4855d79 100644 --- a/crates/shirabe/src/command/dump_autoload_command.rs +++ b/crates/shirabe/src/command/dump_autoload_command.rs @@ -317,6 +317,14 @@ impl Command for DumpAutoloadCommand { base_command_initialize(self, input, output) } + fn complete( + &self, + input: &shirabe_external_packages::symfony::console::completion::completion_input::CompletionInput, + suggestions: &mut shirabe_external_packages::symfony::console::completion::completion_suggestions::CompletionSuggestions, + ) -> anyhow::Result<()> { + crate::command::base_command::base_command_complete(self, input, suggestions) + } + shirabe_external_packages::delegate_command_trait_impls_to_inner!( base_command_data, "Composer\\Command\\DumpAutoloadCommand" diff --git a/crates/shirabe/src/command/exec_command.rs b/crates/shirabe/src/command/exec_command.rs index 36101bc3..23e4b91f 100644 --- a/crates/shirabe/src/command/exec_command.rs +++ b/crates/shirabe/src/command/exec_command.rs @@ -77,11 +77,19 @@ impl Command for ExecCommand { self.set_description("Executes a vendored binary/script"); self.set_definition(&[ InputOption::new("list", Some(PhpMixed::String("l".to_string())), Some(InputOption::VALUE_NONE), "", None).unwrap().into(), - // TODO(cli-completion): suggest installed binary names (via get_binaries) for `binary` argument - InputArgument::new("binary", + // PHP passes an inline closure here (it takes no arguments; PHP tolerates the + // extra ones the caller passes). + InputArgument::new5("binary", Some(InputArgument::OPTIONAL), "The binary to run, e.g. phpunit", - None).unwrap().into(), + None, + crate::console::input::SuggestedValues::Closure(Box::new(|this, _input, _suggestions| { + let this = this + .as_any() + .downcast_ref::<ExecCommand>() + .expect("the binary suggestions are bound to ExecCommand"); + this.get_binaries(false) + }))).unwrap().into(), InputArgument::new("args", Some(InputArgument::IS_ARRAY | InputArgument::OPTIONAL), "Arguments to pass to the binary. Use <info>--</info> to separate from composer arguments", @@ -245,6 +253,14 @@ impl Command for ExecCommand { base_command_initialize(self, input, output) } + fn complete( + &self, + input: &shirabe_external_packages::symfony::console::completion::completion_input::CompletionInput, + suggestions: &mut shirabe_external_packages::symfony::console::completion::completion_suggestions::CompletionSuggestions, + ) -> anyhow::Result<()> { + crate::command::base_command::base_command_complete(self, input, suggestions) + } + shirabe_external_packages::delegate_command_trait_impls_to_inner!( base_command_data, "Composer\\Command\\ExecCommand" diff --git a/crates/shirabe/src/command/fund_command.rs b/crates/shirabe/src/command/fund_command.rs index c4fdaa55..8e460f4b 100644 --- a/crates/shirabe/src/command/fund_command.rs +++ b/crates/shirabe/src/command/fund_command.rs @@ -3,6 +3,7 @@ use crate::command::base_command::base_command_initialize; use crate::command::{BaseCommand, BaseCommandData}; use crate::console::input::InputOption; +use crate::console::input::SuggestedValues; use crate::io::IOInterfaceImmutable; use crate::json::JsonFile; use crate::package::base_package::{self}; @@ -83,12 +84,13 @@ impl Command for FundCommand { fn configure(&self) -> anyhow::Result<()> { self.set_name("fund")?; self.set_description("Discover how to help fund the maintenance of your dependencies"); - self.set_definition(&[InputOption::new( + self.set_definition(&[InputOption::new6( "format", Some(PhpMixed::String("f".to_string())), Some(InputOption::VALUE_REQUIRED), "Format of the output: text or json", Some(PhpMixed::String("text".to_string())), + SuggestedValues::List(vec!["text".to_string(), "json".to_string()]), ) .unwrap() .into()]); @@ -223,6 +225,14 @@ impl Command for FundCommand { base_command_initialize(self, input, output) } + fn complete( + &self, + input: &shirabe_external_packages::symfony::console::completion::completion_input::CompletionInput, + suggestions: &mut shirabe_external_packages::symfony::console::completion::completion_suggestions::CompletionSuggestions, + ) -> anyhow::Result<()> { + crate::command::base_command::base_command_complete(self, input, suggestions) + } + shirabe_external_packages::delegate_command_trait_impls_to_inner!( base_command_data, "Composer\\Command\\FundCommand" diff --git a/crates/shirabe/src/command/global_command.rs b/crates/shirabe/src/command/global_command.rs index 0414fd76..2795b778 100644 --- a/crates/shirabe/src/command/global_command.rs +++ b/crates/shirabe/src/command/global_command.rs @@ -11,6 +11,9 @@ use crate::util::Platform; use shirabe_external_packages::composer::pcre::Preg; use shirabe_external_packages::symfony::console::command::command::Command; use shirabe_external_packages::symfony::console::completion::completion_input::CompletionInput; +use shirabe_external_packages::symfony::console::completion::completion_suggestions::{ + CompletionSuggestions, StringOrSuggestion, +}; use shirabe_external_packages::symfony::console::input::ArgvInput; use shirabe_external_packages::symfony::console::input::ArrayInput; use shirabe_external_packages::symfony::console::input::InputInterface; @@ -41,8 +44,6 @@ impl GlobalCommand { command } - // TODO(cli-completion): pub fn complete(&self, input: &CompletionInput, suggestions: &mut CompletionSuggestions) - // TODO remove for Symfony 6+ as it is then in the interface. // Mirrors PHP's `method_exists($input, '__toString')` guard followed by // `$input->__toString()`. `InputInterface` does not declare `__toString`, so the @@ -151,6 +152,72 @@ impl Command for GlobalCommand { true } + fn complete( + &self, + input: &CompletionInput, + suggestions: &mut CompletionSuggestions, + ) -> anyhow::Result<()> { + let application = self + .get_application() + .expect("a proxy command is always attached to its application"); + if input.must_suggest_argument_values_for("command-name") { + // The application borrow must be dropped before suggest_values (harmless) and + // before any command re-entry below. + let values: Vec<StringOrSuggestion> = { + let mut app_ref = application.borrow_mut(); + let app = app_ref + .as_any_mut() + .downcast_mut::<Application>() + .expect("shirabe always installs its own Application"); + app.all(None)? + .values() + // PHP: $command->isHidden() ? null : $command->getName(), then + // array_filter drops the nulls. + .filter(|command| !command.borrow().is_hidden()) + .filter_map(|command| command.borrow().get_name()) + .map(StringOrSuggestion::String) + .collect() + }; + suggestions.suggest_values(values); + + return Ok(()); + } + + let command_name = input.get_argument("command-name")?.to_string(); + let has = { + let mut app_ref = application.borrow_mut(); + let app = app_ref + .as_any_mut() + .downcast_mut::<Application>() + .expect("shirabe always installs its own Application"); + app.has(&command_name) + }; + if has { + let prepared = self.prepare_subcommand_input( + std::rc::Rc::new(std::cell::RefCell::new(input.clone())), + true, + )?; + let mut input = CompletionInput::from_string(&prepared.to_string(), 2)?; + let command = { + let mut app_ref = application.borrow_mut(); + let app = app_ref + .as_any_mut() + .downcast_mut::<Application>() + .expect("shirabe always installs its own Application"); + app.find(&command_name)? + }; + command.borrow().merge_application_definition(true); + + { + let command_ref = command.borrow(); + let definition = command_ref.get_definition(); + input.bind(&definition)?; + } + command.borrow().complete(&input, suggestions)?; + } + Ok(()) + } + fn run( &self, input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>, diff --git a/crates/shirabe/src/command/home_command.rs b/crates/shirabe/src/command/home_command.rs index 77abb6ac..d06dcda7 100644 --- a/crates/shirabe/src/command/home_command.rs +++ b/crates/shirabe/src/command/home_command.rs @@ -2,6 +2,7 @@ use crate::command::BaseCommand; use crate::command::BaseCommandData; +use crate::command::CompletionTrait; use crate::command::base_command::base_command_initialize; use crate::console::input::InputArgument; use crate::console::input::InputOption; @@ -132,16 +133,16 @@ impl HomeCommand { impl Command for HomeCommand { fn configure(&self) -> anyhow::Result<()> { - // TODO(cli-completion): suggest_installed_package() for `packages` argument self.set_name("browse")?; self.set_aliases(vec!["home".to_string()])?; self.set_description("Opens the package's repository URL or homepage in your browser"); self.set_definition(&[ - InputArgument::new( + InputArgument::new5( "packages", Some(InputArgument::IS_ARRAY), "Package(s) to browse to.", None, + self.suggest_installed_package(true, false), ) .unwrap() .into(), @@ -260,6 +261,14 @@ impl Command for HomeCommand { base_command_initialize(self, input, output) } + fn complete( + &self, + input: &shirabe_external_packages::symfony::console::completion::completion_input::CompletionInput, + suggestions: &mut shirabe_external_packages::symfony::console::completion::completion_suggestions::CompletionSuggestions, + ) -> anyhow::Result<()> { + crate::command::base_command::base_command_complete(self, input, suggestions) + } + shirabe_external_packages::delegate_command_trait_impls_to_inner!( base_command_data, "Composer\\Command\\HomeCommand" diff --git a/crates/shirabe/src/command/init_command.rs b/crates/shirabe/src/command/init_command.rs index 17ed8c09..53b8915b 100644 --- a/crates/shirabe/src/command/init_command.rs +++ b/crates/shirabe/src/command/init_command.rs @@ -1,5 +1,6 @@ //! ref: composer/src/Composer/Command/InitCommand.php +use crate::command::CompletionTrait; use crate::command::PackageDiscoveryTrait; use crate::command::base_command::base_command_initialize; use crate::command::{BaseCommand, BaseCommandData}; @@ -84,7 +85,6 @@ impl InitCommand { impl Command for InitCommand { fn configure(&self) -> anyhow::Result<()> { - // TODO(cli-completion): suggest_available_package_incl_platform() for `require` / `require-dev` self.set_name("init")?; self.set_description("Creates a basic composer.json file in current directory"); self.set_definition(&[ @@ -93,8 +93,8 @@ impl Command for InitCommand { InputOption::new("author", None, Some(InputOption::VALUE_REQUIRED), "Author name of package", None).unwrap().into(), InputOption::new("type", None, Some(InputOption::VALUE_REQUIRED), "Type of package (e.g. library, project, metapackage, composer-plugin)", None).unwrap().into(), InputOption::new("homepage", None, Some(InputOption::VALUE_REQUIRED), "Homepage of package", None).unwrap().into(), - InputOption::new("require", None, Some(InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED), "Package to require with a version constraint, e.g. foo/bar:1.0.0 or foo/bar=1.0.0 or \"foo/bar 1.0.0\"", None).unwrap().into(), - InputOption::new("require-dev", None, Some(InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED), "Package to require for development with a version constraint, e.g. foo/bar:1.0.0 or foo/bar=1.0.0 or \"foo/bar 1.0.0\"", None).unwrap().into(), + InputOption::new6("require", None, Some(InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED), "Package to require with a version constraint, e.g. foo/bar:1.0.0 or foo/bar=1.0.0 or \"foo/bar 1.0.0\"", None, self.suggest_available_package_incl_platform()).unwrap().into(), + InputOption::new6("require-dev", None, Some(InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED), "Package to require for development with a version constraint, e.g. foo/bar:1.0.0 or foo/bar=1.0.0 or \"foo/bar 1.0.0\"", None, self.suggest_available_package_incl_platform()).unwrap().into(), InputOption::new("stability", Some(PhpMixed::String("s".to_string())), Some(InputOption::VALUE_REQUIRED), &format!("Minimum stability (empty or one of: {})", implode(", ", &base_package::STABILITIES.keys().map(|k| k.to_string()).collect::<Vec<_>>())), None).unwrap().into(), InputOption::new("license", Some(PhpMixed::String("l".to_string())), Some(InputOption::VALUE_REQUIRED), "License of package", None).unwrap().into(), InputOption::new("repository", None, Some(InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY), "Add custom repositories, either by URL or using JSON arrays", None).unwrap().into(), @@ -900,6 +900,14 @@ impl Command for InitCommand { })(); } + fn complete( + &self, + input: &shirabe_external_packages::symfony::console::completion::completion_input::CompletionInput, + suggestions: &mut shirabe_external_packages::symfony::console::completion::completion_suggestions::CompletionSuggestions, + ) -> anyhow::Result<()> { + crate::command::base_command::base_command_complete(self, input, suggestions) + } + shirabe_external_packages::delegate_command_trait_impls_to_inner!( base_command_data, "Composer\\Command\\InitCommand" diff --git a/crates/shirabe/src/command/install_command.rs b/crates/shirabe/src/command/install_command.rs index 2e65be1f..7bac166c 100644 --- a/crates/shirabe/src/command/install_command.rs +++ b/crates/shirabe/src/command/install_command.rs @@ -3,6 +3,7 @@ use crate::advisory::Auditor; use crate::command::BaseCommand; use crate::command::BaseCommandData; +use crate::command::CompletionTrait; use crate::command::base_command::base_command_initialize; use crate::console::input::InputArgument; use crate::console::input::InputOption; @@ -41,14 +42,13 @@ impl InstallCommand { impl Command for InstallCommand { fn configure(&self) -> anyhow::Result<()> { - // TODO(cli-completion): suggest_prefer_install() for `prefer-install` option self.set_name("install")?; self.set_aliases(vec!["i".to_string()])?; self.set_description("Installs the project dependencies from the composer.lock file if present, or falls back on the composer.json"); self.set_definition(&[ InputOption::new("prefer-source", None, Some(InputOption::VALUE_NONE), "Forces installation from package sources when possible, including VCS information.", None).unwrap().into(), InputOption::new("prefer-dist", None, Some(InputOption::VALUE_NONE), "Forces installation from package dist (default behavior).", None).unwrap().into(), - InputOption::new("prefer-install", None, Some(InputOption::VALUE_REQUIRED), "Forces installation from package dist|source|auto (auto chooses source for dev versions, dist for the rest).", None).unwrap().into(), + InputOption::new6("prefer-install", None, Some(InputOption::VALUE_REQUIRED), "Forces installation from package dist|source|auto (auto chooses source for dev versions, dist for the rest).", None, self.suggest_prefer_install()).unwrap().into(), InputOption::new("dry-run", None, Some(InputOption::VALUE_NONE), "Outputs the operations but will not execute anything (implicitly enables --verbose).", None).unwrap().into(), InputOption::new("download-only", None, Some(InputOption::VALUE_NONE), "Download only, do not install packages.", None).unwrap().into(), InputOption::new("dev", None, Some(InputOption::VALUE_NONE), "DEPRECATED: Enables installation of require-dev packages (enabled by default, only present for BC).", None).unwrap().into(), @@ -269,6 +269,14 @@ impl Command for InstallCommand { base_command_initialize(self, input, output) } + fn complete( + &self, + input: &shirabe_external_packages::symfony::console::completion::completion_input::CompletionInput, + suggestions: &mut shirabe_external_packages::symfony::console::completion::completion_suggestions::CompletionSuggestions, + ) -> anyhow::Result<()> { + crate::command::base_command::base_command_complete(self, input, suggestions) + } + shirabe_external_packages::delegate_command_trait_impls_to_inner!( base_command_data, "Composer\\Command\\InstallCommand" diff --git a/crates/shirabe/src/command/licenses_command.rs b/crates/shirabe/src/command/licenses_command.rs index ba97a055..6dc12559 100644 --- a/crates/shirabe/src/command/licenses_command.rs +++ b/crates/shirabe/src/command/licenses_command.rs @@ -3,6 +3,7 @@ use crate::command::base_command::base_command_initialize; use crate::command::{BaseCommand, BaseCommandData}; use crate::console::input::InputOption; +use crate::console::input::SuggestedValues; use crate::io::IOInterfaceImmutable; use crate::json::JsonFile; use crate::plugin::CommandEvent; @@ -49,12 +50,17 @@ impl Command for LicensesCommand { self.set_name("licenses")?; self.set_description("Shows information about licenses of dependencies"); self.set_definition(&[ - InputOption::new( + InputOption::new6( "format", Some(PhpMixed::String("f".to_string())), Some(InputOption::VALUE_REQUIRED), "Format of the output: text, json or summary", Some(PhpMixed::String("text".to_string())), + SuggestedValues::List(vec![ + "text".to_string(), + "json".to_string(), + "summary".to_string(), + ]), ) .unwrap() .into(), @@ -328,6 +334,14 @@ impl Command for LicensesCommand { base_command_initialize(self, input, output) } + fn complete( + &self, + input: &shirabe_external_packages::symfony::console::completion::completion_input::CompletionInput, + suggestions: &mut shirabe_external_packages::symfony::console::completion::completion_suggestions::CompletionSuggestions, + ) -> anyhow::Result<()> { + crate::command::base_command::base_command_complete(self, input, suggestions) + } + shirabe_external_packages::delegate_command_trait_impls_to_inner!( base_command_data, "Composer\\Command\\LicensesCommand" diff --git a/crates/shirabe/src/command/outdated_command.rs b/crates/shirabe/src/command/outdated_command.rs index 1c03bfdb..202b0762 100644 --- a/crates/shirabe/src/command/outdated_command.rs +++ b/crates/shirabe/src/command/outdated_command.rs @@ -2,9 +2,11 @@ use crate::command::BaseCommand; use crate::command::BaseCommandData; +use crate::command::CompletionTrait; use crate::command::base_command::base_command_initialize; use crate::console::input::InputArgument; use crate::console::input::InputOption; +use crate::console::input::SuggestedValues; use indexmap::IndexMap; use shirabe_external_packages::symfony::console::command::command::Command; use shirabe_external_packages::symfony::console::input::ArrayInput; @@ -37,11 +39,10 @@ impl OutdatedCommand { impl Command for OutdatedCommand { fn configure(&self) -> anyhow::Result<()> { - // TODO(cli-completion): suggest_installed_package(false, false) for `package` argument and `--ignore` option self.set_name("outdated")?; self.set_description("Shows a list of installed packages that have updates available, including their latest version"); self.set_definition(&[ - InputArgument::new("package", Some(InputArgument::OPTIONAL), "Package to inspect. Or a name including a wildcard (*) to filter lists of packages instead.", None).unwrap().into(), + InputArgument::new5("package", Some(InputArgument::OPTIONAL), "Package to inspect. Or a name including a wildcard (*) to filter lists of packages instead.", None, self.suggest_installed_package(false, false)).unwrap().into(), InputOption::new("outdated", Some(PhpMixed::String("o".to_string())), Some(InputOption::VALUE_NONE), "Show only packages that are outdated (this is the default, but present here for compat with `show`", None).unwrap().into(), InputOption::new("all", Some(PhpMixed::String("a".to_string())), Some(InputOption::VALUE_NONE), "Show all installed packages with their latest versions", None).unwrap().into(), InputOption::new("locked", None, Some(InputOption::VALUE_NONE), "Shows updates for packages from the lock file, regardless of what is currently in vendor dir", None).unwrap().into(), @@ -51,8 +52,8 @@ impl Command for OutdatedCommand { InputOption::new("minor-only", Some(PhpMixed::String("m".to_string())), Some(InputOption::VALUE_NONE), "Show only packages that have minor SemVer-compatible updates.", None).unwrap().into(), InputOption::new("patch-only", Some(PhpMixed::String("p".to_string())), Some(InputOption::VALUE_NONE), "Show only packages that have patch SemVer-compatible updates.", None).unwrap().into(), InputOption::new("sort-by-age", Some(PhpMixed::String("A".to_string())), Some(InputOption::VALUE_NONE), "Displays the installed version's age, and sorts packages oldest first.", None).unwrap().into(), - InputOption::new("format", Some(PhpMixed::String("f".to_string())), Some(InputOption::VALUE_REQUIRED), "Format of the output: text or json", Some(PhpMixed::String("text".to_string()))).unwrap().into(), - InputOption::new("ignore", None, Some(InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY), "Ignore specified package(s). Can contain wildcards (*). Use it if you don't want to be informed about new versions of some packages.", None).unwrap().into(), + InputOption::new6("format", Some(PhpMixed::String("f".to_string())), Some(InputOption::VALUE_REQUIRED), "Format of the output: text or json", Some(PhpMixed::String("text".to_string())), SuggestedValues::List(vec!["json".to_string(), "text".to_string()])).unwrap().into(), + InputOption::new6("ignore", None, Some(InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY), "Ignore specified package(s). Can contain wildcards (*). Use it if you don't want to be informed about new versions of some packages.", None, self.suggest_installed_package(false, false)).unwrap().into(), InputOption::new("no-dev", None, Some(InputOption::VALUE_NONE), "Disables search in require-dev packages.", None).unwrap().into(), InputOption::new("ignore-platform-req", None, Some(InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY), "Ignore a specific platform requirement (php & ext- packages). Use with the --outdated option", None).unwrap().into(), InputOption::new("ignore-platform-reqs", None, Some(InputOption::VALUE_NONE), "Ignore all platform requirements (php & ext- packages). Use with the --outdated option", None).unwrap().into(), @@ -230,6 +231,14 @@ impl Command for OutdatedCommand { base_command_initialize(self, input, output) } + fn complete( + &self, + input: &shirabe_external_packages::symfony::console::completion::completion_input::CompletionInput, + suggestions: &mut shirabe_external_packages::symfony::console::completion::completion_suggestions::CompletionSuggestions, + ) -> anyhow::Result<()> { + crate::command::base_command::base_command_complete(self, input, suggestions) + } + shirabe_external_packages::delegate_command_trait_impls_to_inner!( base_command_data, "Composer\\Command\\OutdatedCommand" diff --git a/crates/shirabe/src/command/prohibits_command.rs b/crates/shirabe/src/command/prohibits_command.rs index 23b693f3..fb37b7e7 100644 --- a/crates/shirabe/src/command/prohibits_command.rs +++ b/crates/shirabe/src/command/prohibits_command.rs @@ -1,6 +1,7 @@ //! ref: composer/src/Composer/Command/ProhibitsCommand.php use crate::command::BaseDependencyCommand; +use crate::command::CompletionTrait; use crate::command::base_command::base_command_initialize; use crate::command::{BaseCommand, BaseCommandData}; use crate::console::input::InputArgument; @@ -47,16 +48,16 @@ impl BaseDependencyCommand for ProhibitsCommand { impl Command for ProhibitsCommand { fn configure(&self) -> anyhow::Result<()> { - // TODO(cli-completion): suggest_available_package() for `package` argument self.set_name("prohibits")?; self.set_aliases(vec!["why-not".to_string()])?; self.set_description("Shows which packages prevent the given package from being installed"); self.set_definition(&[ - InputArgument::new( + InputArgument::new5( <Self as BaseDependencyCommand>::ARGUMENT_PACKAGE, Some(InputArgument::REQUIRED), "Package to inspect", None, + self.suggest_available_package(99), ) .unwrap() .into(), @@ -120,6 +121,14 @@ impl Command for ProhibitsCommand { base_command_initialize(self, input, output) } + fn complete( + &self, + input: &shirabe_external_packages::symfony::console::completion::completion_input::CompletionInput, + suggestions: &mut shirabe_external_packages::symfony::console::completion::completion_suggestions::CompletionSuggestions, + ) -> anyhow::Result<()> { + crate::command::base_command::base_command_complete(self, input, suggestions) + } + shirabe_external_packages::delegate_command_trait_impls_to_inner!( base_command_data, "Composer\\Command\\ProhibitsCommand" diff --git a/crates/shirabe/src/command/reinstall_command.rs b/crates/shirabe/src/command/reinstall_command.rs index 6b5b021a..d47e4897 100644 --- a/crates/shirabe/src/command/reinstall_command.rs +++ b/crates/shirabe/src/command/reinstall_command.rs @@ -2,6 +2,7 @@ use crate::command::BaseCommand; use crate::command::BaseCommandData; +use crate::command::CompletionTrait; use crate::command::base_command::base_command_initialize; use crate::console::input::InputArgument; use crate::console::input::InputOption; @@ -45,13 +46,12 @@ impl ReinstallCommand { impl Command for ReinstallCommand { fn configure(&self) -> anyhow::Result<()> { - // TODO(cli-completion): suggest_prefer_install / suggest_installed_package_types / suggest_installed_package self.set_name("reinstall")?; self.set_description("Uninstalls and reinstalls the given package names"); self.set_definition(&[ InputOption::new("prefer-source", None, Some(InputOption::VALUE_NONE), "Forces installation from package sources when possible, including VCS information.", None).unwrap().into(), InputOption::new("prefer-dist", None, Some(InputOption::VALUE_NONE), "Forces installation from package dist (default behavior).", None).unwrap().into(), - InputOption::new("prefer-install", None, Some(InputOption::VALUE_REQUIRED), "Forces installation from package dist|source|auto (auto chooses source for dev versions, dist for the rest).", None).unwrap().into(), + InputOption::new6("prefer-install", None, Some(InputOption::VALUE_REQUIRED), "Forces installation from package dist|source|auto (auto chooses source for dev versions, dist for the rest).", None, self.suggest_prefer_install()).unwrap().into(), InputOption::new("no-autoloader", None, Some(InputOption::VALUE_NONE), "Skips autoloader generation", None).unwrap().into(), InputOption::new("no-progress", None, Some(InputOption::VALUE_NONE), "Do not output download progress.", None).unwrap().into(), InputOption::new("optimize-autoloader", Some(shirabe_php_shim::PhpMixed::String("o".to_string())), Some(InputOption::VALUE_NONE), "Optimize autoloader during autoloader dump", None).unwrap().into(), @@ -60,8 +60,8 @@ impl Command for ReinstallCommand { InputOption::new("apcu-autoloader-prefix", None, Some(InputOption::VALUE_REQUIRED), "Use a custom prefix for the APCu autoloader cache. Implicitly enables --apcu-autoloader", None).unwrap().into(), InputOption::new("ignore-platform-req", None, Some(InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY), "Ignore a specific platform requirement (php & ext- packages).", None).unwrap().into(), InputOption::new("ignore-platform-reqs", None, Some(InputOption::VALUE_NONE), "Ignore all platform requirements (php & ext- packages).", None).unwrap().into(), - InputOption::new("type", None, Some(InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY), "Filter packages to reinstall by type(s)", None).unwrap().into(), - InputArgument::new("packages", Some(InputArgument::IS_ARRAY), "List of package names to reinstall, can include a wildcard (*) to match any substring.", None).unwrap().into(), + InputOption::new6("type", None, Some(InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY), "Filter packages to reinstall by type(s)", None, self.suggest_installed_package_types(false)).unwrap().into(), + InputArgument::new5("packages", Some(InputArgument::IS_ARRAY), "List of package names to reinstall, can include a wildcard (*) to match any substring.", None, self.suggest_installed_package(false, false)).unwrap().into(), ]); self.set_help( "The <info>reinstall</info> command looks up installed packages by name,\n\ @@ -357,6 +357,14 @@ impl Command for ReinstallCommand { base_command_initialize(self, input, output) } + fn complete( + &self, + input: &shirabe_external_packages::symfony::console::completion::completion_input::CompletionInput, + suggestions: &mut shirabe_external_packages::symfony::console::completion::completion_suggestions::CompletionSuggestions, + ) -> anyhow::Result<()> { + crate::command::base_command::base_command_complete(self, input, suggestions) + } + shirabe_external_packages::delegate_command_trait_impls_to_inner!( base_command_data, "Composer\\Command\\ReinstallCommand" diff --git a/crates/shirabe/src/command/remove_command.rs b/crates/shirabe/src/command/remove_command.rs index ebd332a8..4eb4308a 100644 --- a/crates/shirabe/src/command/remove_command.rs +++ b/crates/shirabe/src/command/remove_command.rs @@ -1,6 +1,7 @@ //! ref: composer/src/Composer/Command/RemoveCommand.php use crate::advisory::Auditor; +use crate::command::CompletionTrait; use crate::command::base_command::base_command_initialize; use crate::command::{BaseCommand, BaseCommandData}; use crate::config::ConfigSourceInterface; @@ -47,15 +48,15 @@ impl RemoveCommand { impl Command for RemoveCommand { fn configure(&self) -> anyhow::Result<()> { - // TODO(cli-completion): suggest_root_requirement() for `packages` argument self.set_name("remove")?; self.set_aliases(vec!["rm".to_string(), "uninstall".to_string()])?; self.set_description("Removes a package from the require or require-dev"); self.set_definition(&[ - InputArgument::new("packages", + InputArgument::new5("packages", Some(InputArgument::IS_ARRAY), "Packages that should be removed.", - None).unwrap().into(), + None, + self.suggest_root_requirement()).unwrap().into(), InputOption::new("dev", None, Some(InputOption::VALUE_NONE), @@ -711,6 +712,14 @@ impl Command for RemoveCommand { base_command_initialize(self, input, output) } + fn complete( + &self, + input: &shirabe_external_packages::symfony::console::completion::completion_input::CompletionInput, + suggestions: &mut shirabe_external_packages::symfony::console::completion::completion_suggestions::CompletionSuggestions, + ) -> anyhow::Result<()> { + crate::command::base_command::base_command_complete(self, input, suggestions) + } + shirabe_external_packages::delegate_command_trait_impls_to_inner!( base_command_data, "Composer\\Command\\RemoveCommand" diff --git a/crates/shirabe/src/command/repository_command.rs b/crates/shirabe/src/command/repository_command.rs index 51089732..d0add253 100644 --- a/crates/shirabe/src/command/repository_command.rs +++ b/crates/shirabe/src/command/repository_command.rs @@ -119,13 +119,79 @@ impl RepositoryCommand { } } - // TODO(cli-completion): fn suggest_type_for_add() - // TODO(cli-completion): fn suggest_repo_names(&self) + /// PHP: private function suggestTypeForAdd(): \Closure (a static closure — `this` unused) + fn suggest_type_for_add(&self) -> crate::console::input::SuggestedValues { + crate::console::input::SuggestedValues::Closure(Box::new(|_this, input, _suggestions| { + if input.get_argument("action")?.to_string() == "add" { + return Ok(vec![ + "composer".to_string(), + "vcs".to_string(), + "artifact".to_string(), + "path".to_string(), + ]); + } + + Ok(vec![]) + })) + } + + fn suggest_repo_names(&self) -> crate::console::input::SuggestedValues { + crate::console::input::SuggestedValues::Closure(Box::new(|this, input, _suggestions| { + let action = input.get_argument("action")?.to_string(); + if ["enable", "disable"].contains(&action.as_str()) { + return Ok(vec!["packagist.org".to_string()]); + } + + if !["remove", "set-url", "get-url"].contains(&action.as_str()) { + return Ok(vec![]); + } + + let this = this + .as_any() + .downcast_ref::<RepositoryCommand>() + .expect("suggestRepoNames is bound to RepositoryCommand"); + // PHP passes the CompletionInput itself; the accessors only read from it, so a + // clone behind a fresh handle is equivalent. + let input_handle: std::rc::Rc< + std::cell::RefCell< + dyn shirabe_external_packages::symfony::console::input::InputInterface, + >, + > = std::rc::Rc::new(std::cell::RefCell::new(input.clone())); + let config = crate::factory::Factory::create_config(None, None)?; + let mut config_file = JsonFile::new( + this.get_composer_config_file(input_handle, &config)?, + None, + None, + )?; + + let data = config_file.read()?; + let mut repos: Vec<String> = vec![]; + + if let Some(repositories) = data.as_array().and_then(|d| d.get("repositories")) { + if let Some(list) = repositories.as_list() { + for repo in list { + if let Some(name) = repo.as_array().and_then(|r| r.get("name")) { + repos.push(name.to_string()); + } + } + } else if let Some(map) = repositories.as_array() { + for repo in map.values() { + if let Some(name) = repo.as_array().and_then(|r| r.get("name")) { + repos.push(name.to_string()); + } + } + } + } + + repos.sort(); + + Ok(repos) + })) + } } impl Command for RepositoryCommand { fn configure(&self) -> anyhow::Result<()> { - // TODO(cli-completion): suggest_repo_names() / suggest_type_for_add() self.set_name("repository")?; self.set_aliases(vec!["repo".to_string()])?; self.set_description("Manages repositories"); @@ -157,45 +223,58 @@ impl Command for RepositoryCommand { ) .unwrap() .into(), - InputOption::new( + InputOption::new6( "before", None, Some(InputOption::VALUE_REQUIRED), "When adding a repository, insert it before the given repository name", None, + self.suggest_repo_names(), ) .unwrap() .into(), - InputOption::new( + InputOption::new6( "after", None, Some(InputOption::VALUE_REQUIRED), "When adding a repository, insert it after the given repository name", None, + self.suggest_repo_names(), ) .unwrap() .into(), - InputArgument::new( + InputArgument::new5( "action", Some(InputArgument::OPTIONAL), "Action to perform: list, add, remove, set-url, get-url, enable, disable", Some(PhpMixed::String("list".to_string())), + crate::console::input::SuggestedValues::List(vec![ + "list".to_string(), + "add".to_string(), + "remove".to_string(), + "set-url".to_string(), + "get-url".to_string(), + "enable".to_string(), + "disable".to_string(), + ]), ) .unwrap() .into(), - InputArgument::new( + InputArgument::new5( "name", Some(InputArgument::OPTIONAL), "Repository name (or special name packagist.org for enable/disable)", None, + self.suggest_repo_names(), ) .unwrap() .into(), - InputArgument::new( + InputArgument::new5( "arg1", Some(InputArgument::OPTIONAL), "Type for add, or new URL for set-url, or JSON config for add", None, + self.suggest_type_for_add(), ) .unwrap() .into(), @@ -501,6 +580,14 @@ impl Command for RepositoryCommand { ) } + fn complete( + &self, + input: &shirabe_external_packages::symfony::console::completion::completion_input::CompletionInput, + suggestions: &mut shirabe_external_packages::symfony::console::completion::completion_suggestions::CompletionSuggestions, + ) -> anyhow::Result<()> { + crate::command::base_command::base_command_complete(self, input, suggestions) + } + shirabe_external_packages::delegate_command_trait_impls_to_inner!( base_command_data, "Composer\\Command\\RepositoryCommand" diff --git a/crates/shirabe/src/command/require_command.rs b/crates/shirabe/src/command/require_command.rs index d54ecaa4..d53ba9a0 100644 --- a/crates/shirabe/src/command/require_command.rs +++ b/crates/shirabe/src/command/require_command.rs @@ -1,6 +1,7 @@ //! ref: composer/src/Composer/Command/RequireCommand.php use crate::advisory::Auditor; +use crate::command::CompletionTrait; use crate::command::PackageDiscoveryTrait; use crate::command::base_command::base_command_initialize; use crate::command::{BaseCommand, BaseCommandData}; @@ -104,17 +105,16 @@ impl PackageDiscoveryTrait for RequireCommand { impl Command for RequireCommand { fn configure(&self) -> anyhow::Result<()> { - // TODO(cli-completion): suggest_available_package_incl_platform / suggest_prefer_install self.set_name("require")?; self.set_aliases(vec!["r".to_string()])?; self.set_description("Adds required packages to your composer.json and installs them"); self.set_definition(&[ - InputArgument::new("packages", Some(InputArgument::IS_ARRAY | InputArgument::OPTIONAL), "Optional package name can also include a version constraint, e.g. foo/bar or foo/bar:1.0.0 or foo/bar=1.0.0 or \"foo/bar 1.0.0\"", None).unwrap().into(), + InputArgument::new5("packages", Some(InputArgument::IS_ARRAY | InputArgument::OPTIONAL), "Optional package name can also include a version constraint, e.g. foo/bar or foo/bar:1.0.0 or foo/bar=1.0.0 or \"foo/bar 1.0.0\"", None, self.suggest_available_package_incl_platform()).unwrap().into(), InputOption::new("dev", None, Some(InputOption::VALUE_NONE), "Add requirement to require-dev.", None).unwrap().into(), InputOption::new("dry-run", None, Some(InputOption::VALUE_NONE), "Outputs the operations but will not execute anything (implicitly enables --verbose).", None).unwrap().into(), InputOption::new("prefer-source", None, Some(InputOption::VALUE_NONE), "Forces installation from package sources when possible, including VCS information.", None).unwrap().into(), InputOption::new("prefer-dist", None, Some(InputOption::VALUE_NONE), "Forces installation from package dist (default behavior).", None).unwrap().into(), - InputOption::new("prefer-install", None, Some(InputOption::VALUE_REQUIRED), "Forces installation from package dist|source|auto (auto chooses source for dev versions, dist for the rest).", None).unwrap().into(), + InputOption::new6("prefer-install", None, Some(InputOption::VALUE_REQUIRED), "Forces installation from package dist|source|auto (auto chooses source for dev versions, dist for the rest).", None, self.suggest_prefer_install()).unwrap().into(), InputOption::new("fixed", None, Some(InputOption::VALUE_NONE), "Write fixed version to the composer.json.", None).unwrap().into(), InputOption::new("no-suggest", None, Some(InputOption::VALUE_NONE), "DEPRECATED: This flag does not exist anymore.", None).unwrap().into(), InputOption::new("no-progress", None, Some(InputOption::VALUE_NONE), "Do not output download progress.", None).unwrap().into(), @@ -650,6 +650,14 @@ impl Command for RequireCommand { base_command_initialize(self, input, output) } + fn complete( + &self, + input: &shirabe_external_packages::symfony::console::completion::completion_input::CompletionInput, + suggestions: &mut shirabe_external_packages::symfony::console::completion::completion_suggestions::CompletionSuggestions, + ) -> anyhow::Result<()> { + crate::command::base_command::base_command_complete(self, input, suggestions) + } + shirabe_external_packages::delegate_command_trait_impls_to_inner!( base_command_data, "Composer\\Command\\RequireCommand" diff --git a/crates/shirabe/src/command/run_script_command.rs b/crates/shirabe/src/command/run_script_command.rs index 1ac9e9a4..b041f146 100644 --- a/crates/shirabe/src/command/run_script_command.rs +++ b/crates/shirabe/src/command/run_script_command.rs @@ -118,12 +118,27 @@ impl Command for RunScriptCommand { self.set_aliases(vec!["run".to_string()])?; self.set_description("Runs the scripts defined in composer.json"); self.set_definition(&[ - // TODO(cli-completion): script-name completion was provided via a closure suggesting runtime script names - InputArgument::new( + // PHP passes an inline closure here (it takes no arguments; PHP tolerates the + // extra ones the caller passes). + InputArgument::new5( "script", Some(InputArgument::OPTIONAL), "Script name to run.", None, + crate::console::input::SuggestedValues::Closure(Box::new( + |this, _input, _suggestions| { + let this = this + .as_any() + .downcast_ref::<RunScriptCommand>() + .expect("the script suggestions are bound to RunScriptCommand"); + // PHP: array_map(fn ($script) => $script['name'], $this->getScripts()) + Ok(this + .get_scripts()? + .into_iter() + .map(|(name, _description)| name) + .collect()) + }, + )), ) .unwrap() .into(), @@ -330,6 +345,14 @@ impl Command for RunScriptCommand { base_command_initialize(self, input, output) } + fn complete( + &self, + input: &shirabe_external_packages::symfony::console::completion::completion_input::CompletionInput, + suggestions: &mut shirabe_external_packages::symfony::console::completion::completion_suggestions::CompletionSuggestions, + ) -> anyhow::Result<()> { + crate::command::base_command::base_command_complete(self, input, suggestions) + } + shirabe_external_packages::delegate_command_trait_impls_to_inner!( base_command_data, "Composer\\Command\\RunScriptCommand" diff --git a/crates/shirabe/src/command/script_alias_command.rs b/crates/shirabe/src/command/script_alias_command.rs index 6a27507f..33f3ac3b 100644 --- a/crates/shirabe/src/command/script_alias_command.rs +++ b/crates/shirabe/src/command/script_alias_command.rs @@ -168,6 +168,14 @@ impl Command for ScriptAliasCommand { base_command_initialize(self, input, output) } + fn complete( + &self, + input: &shirabe_external_packages::symfony::console::completion::completion_input::CompletionInput, + suggestions: &mut shirabe_external_packages::symfony::console::completion::completion_suggestions::CompletionSuggestions, + ) -> anyhow::Result<()> { + crate::command::base_command::base_command_complete(self, input, suggestions) + } + shirabe_external_packages::delegate_command_trait_impls_to_inner!( base_command_data, "Composer\\Command\\ScriptAliasCommand" diff --git a/crates/shirabe/src/command/search_command.rs b/crates/shirabe/src/command/search_command.rs index 0d5fa4be..0ed220e0 100644 --- a/crates/shirabe/src/command/search_command.rs +++ b/crates/shirabe/src/command/search_command.rs @@ -4,6 +4,7 @@ use crate::command::base_command::base_command_initialize; use crate::command::{BaseCommand, BaseCommandData}; use crate::console::input::InputArgument; use crate::console::input::InputOption; +use crate::console::input::SuggestedValues; use crate::io::IOInterfaceImmutable; use crate::json::JsonFile; use crate::plugin::CommandEvent; @@ -74,12 +75,13 @@ impl Command for SearchCommand { ) .unwrap() .into(), - InputOption::new( + InputOption::new6( "format", Some(PhpMixed::String("f".to_string())), Some(InputOption::VALUE_REQUIRED), "Format of the output: text or json", Some(PhpMixed::String("text".to_string())), + SuggestedValues::List(vec!["json".to_string(), "text".to_string()]), ) .unwrap() .into(), @@ -301,6 +303,14 @@ impl Command for SearchCommand { base_command_initialize(self, input, output) } + fn complete( + &self, + input: &shirabe_external_packages::symfony::console::completion::completion_input::CompletionInput, + suggestions: &mut shirabe_external_packages::symfony::console::completion::completion_suggestions::CompletionSuggestions, + ) -> anyhow::Result<()> { + crate::command::base_command::base_command_complete(self, input, suggestions) + } + shirabe_external_packages::delegate_command_trait_impls_to_inner!( base_command_data, "Composer\\Command\\SearchCommand" diff --git a/crates/shirabe/src/command/self_update_command.rs b/crates/shirabe/src/command/self_update_command.rs index 29b08006..59c44830 100644 --- a/crates/shirabe/src/command/self_update_command.rs +++ b/crates/shirabe/src/command/self_update_command.rs @@ -91,6 +91,14 @@ impl Command for SelfUpdateCommand { base_command_initialize(self, input, output) } + fn complete( + &self, + input: &shirabe_external_packages::symfony::console::completion::completion_input::CompletionInput, + suggestions: &mut shirabe_external_packages::symfony::console::completion::completion_suggestions::CompletionSuggestions, + ) -> anyhow::Result<()> { + crate::command::base_command::base_command_complete(self, input, suggestions) + } + shirabe_external_packages::delegate_command_trait_impls_to_inner!( base_command_data, "Composer\\Command\\SelfUpdateCommand" diff --git a/crates/shirabe/src/command/show_command.rs b/crates/shirabe/src/command/show_command.rs index c5116b80..55ec5ddf 100644 --- a/crates/shirabe/src/command/show_command.rs +++ b/crates/shirabe/src/command/show_command.rs @@ -1,10 +1,12 @@ //! ref: composer/src/Composer/Command/ShowCommand.php +use crate::command::CompletionTrait; use crate::command::base_command::base_command_initialize; use crate::command::{BaseCommand, BaseCommandData}; use crate::composer::PartialComposerHandle; use crate::console::input::InputArgument; use crate::console::input::InputOption; +use crate::console::input::SuggestedValues; use crate::dependency_resolver::DefaultPolicy; use crate::dependency_resolver::PolicyInterface; use crate::filter::platform_requirement_filter::PlatformRequirementFilterInterface; @@ -84,9 +86,6 @@ impl Command for ShowCommand { self.set_name("show")?; self.set_aliases(vec!["info".to_string()])?; self.set_description("Shows information about packages"); - // TODO(cli-completion): the package/ignore suggestion closures (suggestPackageBasedOnMode / - // suggestInstalledPackage) and the format option's allowed-value list are dropped, matching - // the InputArgument/InputOption API which does not yet carry completion metadata. let opt_none = |name: &str, shortcut: Option<&str>, description: &str| { InputOption::new( name, @@ -99,11 +98,12 @@ impl Command for ShowCommand { .into() }; self.set_definition(&[ - InputArgument::new( + InputArgument::new5( "package", Some(InputArgument::OPTIONAL), "Package to inspect. Or a name including a wildcard (*) to filter lists of packages instead.", None, + self.suggest_package_based_on_mode(), ) .unwrap() .into(), @@ -134,12 +134,13 @@ impl Command for ShowCommand { Some("o"), "Show the latest version but only for packages that are outdated", ), - InputOption::new( + InputOption::new6( "ignore", None, Some(InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY), "Ignore specified package(s). Can contain wildcards (*). Use it with the --outdated option if you don't want to be informed about new versions of some packages.", None, + self.suggest_installed_package(false, false), ) .unwrap() .into(), @@ -173,12 +174,13 @@ impl Command for ShowCommand { None, "Return a non-zero exit code when there are outdated packages", ), - InputOption::new( + InputOption::new6( "format", Some(PhpMixed::String("f".to_string())), Some(InputOption::VALUE_REQUIRED), "Format of the output: text or json", Some(PhpMixed::String("text".to_string())), + SuggestedValues::List(vec!["json".to_string(), "text".to_string()]), ) .unwrap() .into(), @@ -1441,6 +1443,14 @@ impl Command for ShowCommand { base_command_initialize(self, input, output) } + fn complete( + &self, + input: &shirabe_external_packages::symfony::console::completion::completion_input::CompletionInput, + suggestions: &mut shirabe_external_packages::symfony::console::completion::completion_suggestions::CompletionSuggestions, + ) -> anyhow::Result<()> { + crate::command::base_command::base_command_complete(self, input, suggestions) + } + shirabe_external_packages::delegate_command_trait_impls_to_inner!( base_command_data, "Composer\\Command\\ShowCommand" @@ -1456,7 +1466,27 @@ impl BaseCommand for ShowCommand { } impl ShowCommand { - // TODO(cli-completion): pub fn suggest_package_based_on_mode(&self) -> Box<dyn Fn(&CompletionInput) -> Vec<String>> + /// PHP: protected function suggestPackageBasedOnMode(): \Closure + pub(crate) fn suggest_package_based_on_mode(&self) -> crate::console::input::SuggestedValues { + crate::console::input::SuggestedValues::Closure(Box::new(|this, input, suggestions| { + if input.get_option("available")?.to_bool() || input.get_option("all")?.to_bool() { + return this.suggest_available_package_incl_platform().call( + this, + input, + suggestions, + ); + } + + if input.get_option("platform")?.to_bool() { + return this + .suggest_platform_package() + .call(this, input, suggestions); + } + + this.suggest_installed_package(false, false) + .call(this, input, suggestions) + })) + } #[allow(clippy::too_many_arguments, reason = "to keep PHP signature")] fn print_packages( diff --git a/crates/shirabe/src/command/status_command.rs b/crates/shirabe/src/command/status_command.rs index 323a61d8..42ba74eb 100644 --- a/crates/shirabe/src/command/status_command.rs +++ b/crates/shirabe/src/command/status_command.rs @@ -385,6 +385,14 @@ impl Command for StatusCommand { base_command_initialize(self, input, output) } + fn complete( + &self, + input: &shirabe_external_packages::symfony::console::completion::completion_input::CompletionInput, + suggestions: &mut shirabe_external_packages::symfony::console::completion::completion_suggestions::CompletionSuggestions, + ) -> anyhow::Result<()> { + crate::command::base_command::base_command_complete(self, input, suggestions) + } + shirabe_external_packages::delegate_command_trait_impls_to_inner!( base_command_data, "Composer\\Command\\StatusCommand" diff --git a/crates/shirabe/src/command/suggests_command.rs b/crates/shirabe/src/command/suggests_command.rs index fc2b739a..65493f2f 100644 --- a/crates/shirabe/src/command/suggests_command.rs +++ b/crates/shirabe/src/command/suggests_command.rs @@ -1,5 +1,6 @@ //! ref: composer/src/Composer/Command/SuggestsCommand.php +use crate::command::CompletionTrait; use crate::command::base_command::base_command_initialize; use crate::command::{BaseCommand, BaseCommandData}; use crate::console::input::InputArgument; @@ -41,7 +42,6 @@ impl SuggestsCommand { impl Command for SuggestsCommand { fn configure(&self) -> anyhow::Result<()> { - // TODO(cli-completion): suggest_installed_package() for `packages` argument self.set_name("suggests")?; self.set_description("Shows package suggestions"); self.set_definition(&[ @@ -90,11 +90,12 @@ impl Command for SuggestsCommand { ) .unwrap() .into(), - InputArgument::new( + InputArgument::new5( "packages", Some(InputArgument::IS_ARRAY | InputArgument::OPTIONAL), "Packages that you want to list suggestions from.", None, + self.suggest_installed_package(true, false), ) .unwrap() .into(), @@ -219,6 +220,14 @@ impl Command for SuggestsCommand { base_command_initialize(self, input, output) } + fn complete( + &self, + input: &shirabe_external_packages::symfony::console::completion::completion_input::CompletionInput, + suggestions: &mut shirabe_external_packages::symfony::console::completion::completion_suggestions::CompletionSuggestions, + ) -> anyhow::Result<()> { + crate::command::base_command::base_command_complete(self, input, suggestions) + } + shirabe_external_packages::delegate_command_trait_impls_to_inner!( base_command_data, "Composer\\Command\\SuggestsCommand" diff --git a/crates/shirabe/src/command/update_command.rs b/crates/shirabe/src/command/update_command.rs index 7c89c19e..d4fda900 100644 --- a/crates/shirabe/src/command/update_command.rs +++ b/crates/shirabe/src/command/update_command.rs @@ -2,6 +2,7 @@ use crate::advisory::Auditor; use crate::command::BumpCommand; +use crate::command::CompletionTrait; use crate::command::base_command::base_command_initialize; use crate::command::{BaseCommand, BaseCommandData}; use crate::composer::PartialComposerHandle; @@ -62,16 +63,15 @@ impl UpdateCommand { impl Command for UpdateCommand { fn configure(&self) -> anyhow::Result<()> { - // TODO(cli-completion): suggest_installed_package(false, true) / suggest_prefer_install self.set_name("update")?; self.set_aliases(vec!["u".to_string(), "upgrade".to_string()])?; self.set_description("Updates your dependencies to the latest version according to composer.json, and updates the composer.lock file"); self.set_definition(&[ - InputArgument::new("packages", Some(InputArgument::IS_ARRAY | InputArgument::OPTIONAL), "Packages that should be updated, if not provided all packages are.", None).unwrap().into(), + InputArgument::new5("packages", Some(InputArgument::IS_ARRAY | InputArgument::OPTIONAL), "Packages that should be updated, if not provided all packages are.", None, self.suggest_installed_package(false, true)).unwrap().into(), InputOption::new("with", None, Some(InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED), "Temporary version constraint to add, e.g. foo/bar:1.0.0 or foo/bar=1.0.0", None).unwrap().into(), InputOption::new("prefer-source", None, Some(InputOption::VALUE_NONE), "Forces installation from package sources when possible, including VCS information.", None).unwrap().into(), InputOption::new("prefer-dist", None, Some(InputOption::VALUE_NONE), "Forces installation from package dist (default behavior).", None).unwrap().into(), - InputOption::new("prefer-install", None, Some(InputOption::VALUE_REQUIRED), "Forces installation from package dist|source|auto (auto chooses source for dev versions, dist for the rest).", None).unwrap().into(), + InputOption::new6("prefer-install", None, Some(InputOption::VALUE_REQUIRED), "Forces installation from package dist|source|auto (auto chooses source for dev versions, dist for the rest).", None, self.suggest_prefer_install()).unwrap().into(), InputOption::new("dry-run", None, Some(InputOption::VALUE_NONE), "Outputs the operations but will not execute anything (implicitly enables --verbose).", None).unwrap().into(), InputOption::new("dev", None, Some(InputOption::VALUE_NONE), "DEPRECATED: Enables installation of require-dev packages (enabled by default, only present for BC).", None).unwrap().into(), InputOption::new("no-dev", None, Some(InputOption::VALUE_NONE), "Disables installation of require-dev packages.", None).unwrap().into(), @@ -98,7 +98,7 @@ impl Command for UpdateCommand { InputOption::new("patch-only", None, Some(InputOption::VALUE_NONE), "Only allow patch version updates for currently installed dependencies.", None).unwrap().into(), InputOption::new("interactive", Some(PhpMixed::String("i".to_string())), Some(InputOption::VALUE_NONE), "Interactive interface with autocompletion to select the packages to update.", None).unwrap().into(), InputOption::new("root-reqs", None, Some(InputOption::VALUE_NONE), "Restricts the update to your first degree dependencies.", None).unwrap().into(), - InputOption::new("bump-after-update", None, Some(InputOption::VALUE_OPTIONAL), "Runs bump after performing the update.", Some(PhpMixed::Bool(false))).unwrap().into(), + InputOption::new6("bump-after-update", None, Some(InputOption::VALUE_OPTIONAL), "Runs bump after performing the update.", Some(PhpMixed::Bool(false)), crate::console::input::SuggestedValues::List(vec!["dev".to_string(), "no-dev".to_string(), "all".to_string()])).unwrap().into(), ]); self.set_help( "The <info>update</info> command reads the composer.json file from the\n\ @@ -578,6 +578,14 @@ impl Command for UpdateCommand { base_command_initialize(self, input, output) } + fn complete( + &self, + input: &shirabe_external_packages::symfony::console::completion::completion_input::CompletionInput, + suggestions: &mut shirabe_external_packages::symfony::console::completion::completion_suggestions::CompletionSuggestions, + ) -> anyhow::Result<()> { + crate::command::base_command::base_command_complete(self, input, suggestions) + } + shirabe_external_packages::delegate_command_trait_impls_to_inner!( base_command_data, "Composer\\Command\\UpdateCommand" diff --git a/crates/shirabe/src/command/validate_command.rs b/crates/shirabe/src/command/validate_command.rs index eee41018..061eed2c 100644 --- a/crates/shirabe/src/command/validate_command.rs +++ b/crates/shirabe/src/command/validate_command.rs @@ -307,6 +307,14 @@ impl Command for ValidateCommand { base_command_initialize(self, input, output) } + fn complete( + &self, + input: &shirabe_external_packages::symfony::console::completion::completion_input::CompletionInput, + suggestions: &mut shirabe_external_packages::symfony::console::completion::completion_suggestions::CompletionSuggestions, + ) -> anyhow::Result<()> { + crate::command::base_command::base_command_complete(self, input, suggestions) + } + shirabe_external_packages::delegate_command_trait_impls_to_inner!( base_command_data, "Composer\\Command\\ValidateCommand" |
