Add System.CommandLine.StaticCompletions project#2799
Add System.CommandLine.StaticCompletions project#2799slang25 wants to merge 5 commits intodotnet:mainfrom
Conversation
Imports the StaticCompletions library and test project from dotnet/sdk, converting the System.CommandLine dependency from a NuGet PackageReference to a ProjectReference. Tests run against a real System.CommandLine build via Verify.Xunit snapshots.
|
@baronfel what do you think to this, does this need a bit more coordination? |
baronfel
left a comment
There was a problem hiding this comment.
First pass on some things I suspected would need changing before making part of this repo.
| /// <summary> | ||
| /// Generates a call to <code>dotnet complete <string> --position <int></code> for dynamic completions where necessary, but in a more generic way | ||
| /// </summary> | ||
| /// <returns></returns> | ||
| /// <remarks>TODO: this is currently bound to the .NET CLI's 'dotnet complete' command - this should be definable/injectable per-host instead.</remarks> |
There was a problem hiding this comment.
| /// <summary> | |
| /// Generates a call to <code>dotnet complete <string> --position <int></code> for dynamic completions where necessary, but in a more generic way | |
| /// </summary> | |
| /// <returns></returns> | |
| /// <remarks>TODO: this is currently bound to the .NET CLI's 'dotnet complete' command - this should be definable/injectable per-host instead.</remarks> | |
| /// <summary> | |
| /// Generates a call to <code><appname> complete <string> --position <int></code> for dynamic completions where necessary - for example when completing NuGet package identifiers. | |
| /// </summary> |
There was a problem hiding this comment.
I feel it should use a directive rather than a subcommand.
|
|
||
| private static readonly string _dynamicCompletionScript = | ||
| """ | ||
| # fish parameter completion for the dotnet CLI |
There was a problem hiding this comment.
I think we have an open PR for fish completions in the SDK right now that would need syncing here.
| /// <summary> | ||
| /// Generate a call into `dotnet complete` for dynamic argument completions, then binds the returned values as CompletionResults. | ||
| /// </summary> | ||
| /// <remarks>TODO: this is currently bound to the .NET CLI's 'dotnet complete' command - this should be definable/injectable per-host instead.</remarks> | ||
| private static void GenerateDynamicCompletionsCall(IndentedTextWriter writer) | ||
| { | ||
| writer.WriteLine("$text = $commandAst.ToString()"); | ||
| writer.WriteLine("$dotnetCompleteResults = @(dotnet complete --position $cursorPosition \"$text\") | Where-Object { $_ -NotMatch \"^-|^/\" }"); | ||
| writer.WriteLine("$dynamicCompletions = $dotnetCompleteResults | Foreach-Object { [CompletionResult]::new($_, $_, [CompletionResultType]::ParameterValue, $_) }"); | ||
| writer.WriteLine("$completions += $dynamicCompletions"); | ||
| } |
There was a problem hiding this comment.
This will need to be changed to not rely on dotnet.
| writer.Indent++; | ||
| writer.WriteLine("local completions=()"); | ||
| // TODO: we're directly calling dotnet complete here - we need something pluggable. | ||
| writer.WriteLine("local result=$(dotnet complete -- \"${original_args[@]}\")"); |
There was a problem hiding this comment.
We also need to change this invocation to not rely on dotnet.
Replaces the hardcoded `dotnet complete` invocation in the generated bash, zsh, pwsh, fish, and nushell scripts with a call to the application's own `[suggest]` directive (built into System.CommandLine). Makes the generated scripts work for any System.CommandLine host without requiring a separate `complete` subcommand or the dotnet CLI on PATH.
|
@slang25 I wouldn't use the suggest directive for this, and I'm not sure about using directives at all for the feature - the suggest directive requires that the tool support the directive, and have dotnet-suggest installed, which will almost certainly not be the case for AOT tools meant for non .NET audiences. The most resilient thing is for the app itself to also implement completion. |
|
Agreed that following the design of |
Adds CompletionInvocation, a strategy object on each shell provider that
controls how generated scripts call back into the application for dynamic
completions. Default remains the built-in [suggest] directive; tool
authors can opt into CompletionInvocation.Subcommand("complete") if they
prefer a subcommand — useful for tools targeting non-.NET audiences or
where a directive isn't wanted.
Updates XML docs on IsDynamic and internal comments in the bash and zsh providers that still described the SDK-era behaviour (calling into the dotnet process) or used the dotnet CLI as a load-bearing example. The emitted scripts themselves already call the application binary directly and never reference dotnet.
| /// Describes how generated shell completion scripts invoke the target application | ||
| /// to resolve dynamic completions at runtime. | ||
| /// </summary> | ||
| public abstract record CompletionInvocation |
There was a problem hiding this comment.
Good thinking on the strategy approach here - it has me thinking about the 'protocol' of each of these modes.
dotnet suggesthas a known calling 'API' expressed by its CLI grammar- 'Subcommand' would be defining a new calling protocol that happens to match what the
dotnetCLI has today. Is that API sufficient for a more general library? I suspect not because it only provides the name/label of the completion item today - for example NuGet package identifier completions don't provide any deeper integration into the various shell providers. - would these protocols need to be extensible by consumers of this library? If so, how would the shell providers in this library know about the new providers? It feels coupled today in a way that is hard to eject out of from a consumer's point of view.
Summary
Imports the
System.CommandLine.StaticCompletionslibrary and its test project fromdotnet/sdkinto this repo. The library generates static shell completion scripts (bash, zsh, pwsh, fish, nushell) from aSystem.CommandLineCommandtree. In-repo, theSystem.CommandLinedependency becomes aProjectReference; tests run against a real build viaVerify.Xunitsnapshots (replacing the SDK's Helix-specific test harness).This is step 1 of a 3-step migration: land here first, wait for Maestro to flow a package, then remove the sources from
dotnet/sdk.Test plan
dotnet test src/System.CommandLine.StaticCompletions.Testspasses all 15 snapshot tests