aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-08-02 11:33:44 +0900
committernsfisis <nsfisis@gmail.com>2026-08-02 11:33:44 +0900
commitf1dee6362f9b25e78f7e6417e957c0ca34356ecb (patch)
treec25c07d81153b4fb12ae840db9c3eb71f5d7fab4 /crates
parent876f1bf158cf03944ba56fee351fa97b34fda9e0 (diff)
downloadphp-shirabe-f1dee6362f9b25e78f7e6417e957c0ca34356ecb.tar.gz
php-shirabe-f1dee6362f9b25e78f7e6417e957c0ca34356ecb.tar.zst
php-shirabe-f1dee6362f9b25e78f7e6417e957c0ca34356ecb.zip
feat(command): wire suggested values into every command definition
Ports the per-command completion metadata that PHP passes as the suggestedValues constructor argument, resolving all TODO(cli-completion) markers: - CompletionTrait providers on 18 argument/option sites (installed/root/ available package names, package types, prefer-install) - static value lists (--format on show/outdated/search/fund/licenses/ check-platform-reqs, archive's FORMATS, audit --ignore-severity, update --bump-after-update, repository's action list) - command-specific closures: ConfigCommand::suggest_setting_keys, ShowCommand::suggest_package_based_on_mode, RepositoryCommand's suggest_repo_names/suggest_type_for_add, exec/run-script inline closures (downcast from the this argument, as the closures are bound to their concrete command in PHP) - GlobalCommand::complete, delegating completion to the wrapped subcommand through CompletionInput::from_string - a complete() override on every Composer command forwarding to base_command_complete (BaseCommand inheritance restoration) Also fixes CompleteCommand to call merge_application_definition(true) as PHP's default-argument call does; with false the application-level "command" argument was missing from the bound definition, shifting every argument-position detection by one. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Diffstat (limited to 'crates')
-rw-r--r--crates/shirabe-external-packages/src/symfony/console/command/complete_command.rs3
-rw-r--r--crates/shirabe/src/command/about_command.rs8
-rw-r--r--crates/shirabe/src/command/archive_command.rs15
-rw-r--r--crates/shirabe/src/command/audit_command.rs17
-rw-r--r--crates/shirabe/src/command/bump_command.rs13
-rw-r--r--crates/shirabe/src/command/check_platform_reqs_command.rs12
-rw-r--r--crates/shirabe/src/command/clear_cache_command.rs8
-rw-r--r--crates/shirabe/src/command/config_command.rs133
-rw-r--r--crates/shirabe/src/command/create_project_command.rs14
-rw-r--r--crates/shirabe/src/command/depends_command.rs13
-rw-r--r--crates/shirabe/src/command/diagnose_command.rs8
-rw-r--r--crates/shirabe/src/command/dump_autoload_command.rs8
-rw-r--r--crates/shirabe/src/command/exec_command.rs22
-rw-r--r--crates/shirabe/src/command/fund_command.rs12
-rw-r--r--crates/shirabe/src/command/global_command.rs71
-rw-r--r--crates/shirabe/src/command/home_command.rs13
-rw-r--r--crates/shirabe/src/command/init_command.rs14
-rw-r--r--crates/shirabe/src/command/install_command.rs12
-rw-r--r--crates/shirabe/src/command/licenses_command.rs16
-rw-r--r--crates/shirabe/src/command/outdated_command.rs17
-rw-r--r--crates/shirabe/src/command/prohibits_command.rs13
-rw-r--r--crates/shirabe/src/command/reinstall_command.rs16
-rw-r--r--crates/shirabe/src/command/remove_command.rs15
-rw-r--r--crates/shirabe/src/command/repository_command.rs103
-rw-r--r--crates/shirabe/src/command/require_command.rs14
-rw-r--r--crates/shirabe/src/command/run_script_command.rs27
-rw-r--r--crates/shirabe/src/command/script_alias_command.rs8
-rw-r--r--crates/shirabe/src/command/search_command.rs12
-rw-r--r--crates/shirabe/src/command/self_update_command.rs8
-rw-r--r--crates/shirabe/src/command/show_command.rs44
-rw-r--r--crates/shirabe/src/command/status_command.rs8
-rw-r--r--crates/shirabe/src/command/suggests_command.rs13
-rw-r--r--crates/shirabe/src/command/update_command.rs16
-rw-r--r--crates/shirabe/src/command/validate_command.rs8
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"