diff --git a/sdk.slnx b/sdk.slnx index 0246a040f447..4ea79fb126df 100644 --- a/sdk.slnx +++ b/sdk.slnx @@ -59,6 +59,7 @@ + diff --git a/src/BuiltInTools/dotnet-watch/CommandLine/CommandLineOptions.cs b/src/BuiltInTools/dotnet-watch/CommandLine/CommandLineOptions.cs index 95e98a584af6..0679d27d6897 100644 --- a/src/BuiltInTools/dotnet-watch/CommandLine/CommandLineOptions.cs +++ b/src/BuiltInTools/dotnet-watch/CommandLine/CommandLineOptions.cs @@ -8,6 +8,7 @@ using System.Diagnostics; using Microsoft.Build.Logging; using Microsoft.DotNet.Cli; +using Microsoft.DotNet.Cli.CommandLine; using Microsoft.DotNet.Cli.Commands.Run; using Microsoft.DotNet.Cli.Extensions; using Microsoft.Extensions.Logging; @@ -119,7 +120,7 @@ internal sealed class CommandLineOptions // determine subcommand: var explicitCommand = TryGetSubcommand(parseResult); var command = explicitCommand ?? RunCommandParser.GetCommand(); - var buildOptions = command.Options.Where(o => o is IForwardedOption); + var buildOptions = command.Options.Where(o => o.ForwardingFunction is not null); foreach (var buildOption in buildOptions) { @@ -161,7 +162,7 @@ internal sealed class CommandLineOptions var commandArguments = GetCommandArguments(parseResult, watchOptions, explicitCommand, out var binLogToken, out var binLogPath); // We assume that forwarded options, if any, are intended for dotnet build. - var buildArguments = buildOptions.Select(option => ((IForwardedOption)option).GetForwardingFunction()(parseResult)).SelectMany(args => args).ToList(); + var buildArguments = buildOptions.Select(option => option.ForwardingFunction!(parseResult)).SelectMany(args => args).ToList(); if (binLogToken != null) { diff --git a/src/Cli/Microsoft.DotNet.Cli.CommandLine/ArgumentBuilderExtensions.cs b/src/Cli/Microsoft.DotNet.Cli.CommandLine/ArgumentBuilderExtensions.cs new file mode 100644 index 000000000000..a88efd6259bd --- /dev/null +++ b/src/Cli/Microsoft.DotNet.Cli.CommandLine/ArgumentBuilderExtensions.cs @@ -0,0 +1,19 @@ +using System.CommandLine; +using System.CommandLine.Completions; + +namespace Microsoft.DotNet.Cli.CommandLine; + +/// +/// Extension methods that make it easier to chain argument configuration methods when building arguments. +/// +public static class ArgumentBuilderExtensions +{ + extension(Argument argument) + { + public Argument AddCompletions(Func> completionSource) + { + argument.CompletionSources.Add(completionSource); + return argument; + } + } +} diff --git a/src/Cli/Microsoft.DotNet.Cli.CommandLine/ForwardedOptionExtensions.cs b/src/Cli/Microsoft.DotNet.Cli.CommandLine/ForwardedOptionExtensions.cs new file mode 100644 index 000000000000..040f4595f7b8 --- /dev/null +++ b/src/Cli/Microsoft.DotNet.Cli.CommandLine/ForwardedOptionExtensions.cs @@ -0,0 +1,219 @@ +using System.CommandLine; +using System.CommandLine.Parsing; + +namespace Microsoft.DotNet.Cli.CommandLine; + +/// +/// Extensions for tracking and invoking forwarding functions on options and arguments. +/// Forwarding functions are used to translate the parsed value of an option or argument +/// into a set of zero or more string values that will be passed to an inner command. +/// +public static class ForwardedOptionExtensions +{ + private static readonly Dictionary>> s_forwardingFunctions = []; + private static readonly Lock s_lock = new(); + + extension(Option option) + { + /// + /// If this option has a forwarding function, this property will return it; otherwise, it will be null. + /// + /// + /// This getter is on the untyped Option because much of the _processing_ of option forwarding + /// is done at the ParseResult level, where we don't have the generic type parameter. + /// + public Func>? ForwardingFunction => s_forwardingFunctions.GetValueOrDefault(option); + } + + extension(Option option) + { + /// + /// Internal-only helper function that ensures the provided forwarding function is only called + /// if the option actually has a value. + /// + private Func> GetForwardingFunction(Func> func) + { + return (ParseResult parseResult) => + { + if (parseResult.GetResult(option) is OptionResult r) + { + if (r.GetValueOrDefault() is TValue value) + { + return func(value); + } + else + { + return []; + } + } + return []; + }; + } + + /// + /// Internal-only helper function that ensures the provided forwarding function is only called + /// if the option actually has a value. + /// + private Func> GetForwardingFunction(Func> func) + { + return (ParseResult parseResult) => + { + if (parseResult.GetResult(option) is OptionResult r) + { + if (r.GetValueOrDefault() is TValue value) + { + return func(value, parseResult); + } + else + { + return []; + } + } + return []; + }; + } + + /// + /// Forwards the option using the provided function to convert the option's value to zero or more string values. + /// The function will only be called if the option has a value. + /// + public Option SetForwardingFunction(Func> func) + { + lock (s_lock) + { + s_forwardingFunctions[option] = option.GetForwardingFunction(func); + } + return option; + } + + /// + /// Forward the option using the provided function to convert the option's value to a single string value. + /// The function will only be called if the option has a value. + /// + public Option SetForwardingFunction(Func format) + { + lock (s_lock) + { + s_forwardingFunctions[option] = option.GetForwardingFunction(o => [format(o)]); + } + return option; + } + + /// + /// Forward the option using the provided function to convert the option's value to a single string value. + /// The function will only be called if the option has a value. + /// + public Option SetForwardingFunction(Func> func) + { + lock (s_lock) + { + s_forwardingFunctions[option] = option.GetForwardingFunction(func); + } + return option; + } + + /// + /// Forward the option as multiple calculated string values from whatever the option's value is. + /// + /// + /// + public Option ForwardAsMany(Func> format) => option.SetForwardingFunction(format); + + /// + /// Forward the option as its own name. + /// + /// + public Option Forward() => option.SetForwardingFunction((TValue? o) => [option.Name]); + + /// + /// Forward the option as a string value. This value will be forwarded as long as the option has a OptionResult - which means that + /// any implicit value calculation will cause the string value to be forwarded. + /// + public Option ForwardAs(string value) => option.SetForwardingFunction((TValue? o) => [value]); + + /// + /// Forward the option as a singular calculated string value. + /// + public Option ForwardAsSingle(Func format) => option.SetForwardingFunction(format); + } + + extension(Option option) + { + /// + /// Forward the boolean option as a string value. This value will be forwarded as long as the option has a OptionResult - which means that + /// any implicit value calculation will cause the string value to be forwarded. For boolean options specifically, if the option is zero arity + /// and has no default value factory, S.CL will synthesize a true or false value based on whether the option was provided or not, so we need to + /// add an additional implicit 'value is true' check to prevent accidentally forwarding the option for flags that are absent.. + /// + public Option ForwardIfEnabled(string value) => option.SetForwardingFunction((bool o) => o ? [value] : []); + /// + /// Forward the boolean option as a string value. This value will be forwarded as long as the option has a OptionResult - which means that + /// any implicit value calculation will cause the string value to be forwarded. For boolean options specifically, if the option is zero arity + /// and has no default value factory, S.CL will synthesize a true or false value based on whether the option was provided or not, so we need to + /// add an additional implicit 'value is true' check to prevent accidentally forwarding the option for flags that are absent.. + /// + public Option ForwardIfEnabled(string[] value) => option.SetForwardingFunction((bool o) => o ? value : []); + + /// + /// Forward the boolean option as a string value. This value will be forwarded as long as the option has a OptionResult - which means that + /// any implicit value calculation will cause the string value to be forwarded. For boolean options specifically, if the option is zero arity + /// and has no default value factory, S.CL will synthesize a true or false value based on whether the option was provided or not, so we need to + /// add an additional implicit 'value is true' check to prevent accidentally forwarding the option for flags that are absent.. + /// + public Option ForwardAs(string value) => option.ForwardIfEnabled(value); + } + + extension(Option> option) + { + /// + /// Foreach argument in the option's value, yield the followed by the argument. + /// + public Option> ForwardAsManyArgumentsEachPrefixedByOption(string alias) => + option.ForwardAsMany(o => ForwardedArguments(alias, o)); + } + + extension(ParseResult parseResult) + { + /// + /// Calls the forwarding functions for all options that have declared a forwarding function (via 's extension members) in the provided . + /// + /// + /// If not provided, uses the 's . + /// + public IEnumerable OptionValuesToBeForwarded(Command? command = null) => + (command ?? parseResult.CommandResult.Command) + .Options + .Select(o => o.ForwardingFunction) + .SelectMany(f => f is not null ? f(parseResult) : []); + + /// + /// Tries to find the first option named in , and if found, + /// invokes its forwarding function (if any) and returns the result. If no option with that name is found, or if the option + /// has no forwarding function, returns an empty enumeration. + /// + /// + /// + /// + public IEnumerable ForwardedOptionValues(Command command, string alias) + { + var func = command.Options? + .Where(o => + (o.Name.Equals(alias) || o.Aliases.Contains(alias)) + && o.ForwardingFunction is not null) + .FirstOrDefault()?.ForwardingFunction; + return func?.Invoke(parseResult) ?? []; + } + } + + /// + /// For each argument in , yield the followed by the argument. + /// + private static IEnumerable ForwardedArguments(string alias, IEnumerable? arguments) + { + foreach (string arg in arguments ?? []) + { + yield return alias; + yield return arg; + } + } +} diff --git a/src/Cli/Microsoft.DotNet.Cli.CommandLine/Microsoft.DotNet.Cli.CommandLine.csproj b/src/Cli/Microsoft.DotNet.Cli.CommandLine/Microsoft.DotNet.Cli.CommandLine.csproj new file mode 100644 index 000000000000..4e2669b28bad --- /dev/null +++ b/src/Cli/Microsoft.DotNet.Cli.CommandLine/Microsoft.DotNet.Cli.CommandLine.csproj @@ -0,0 +1,19 @@ + + + + $(SdkTargetFramework) + enable + enable + MicrosoftAspNetCore + true + true + + + + + + + + + + diff --git a/src/Cli/Microsoft.DotNet.Cli.CommandLine/OptionBuilderExtensions.cs b/src/Cli/Microsoft.DotNet.Cli.CommandLine/OptionBuilderExtensions.cs new file mode 100644 index 000000000000..19b502a4b70d --- /dev/null +++ b/src/Cli/Microsoft.DotNet.Cli.CommandLine/OptionBuilderExtensions.cs @@ -0,0 +1,46 @@ +using System.CommandLine; +using System.CommandLine.Completions; + +namespace Microsoft.DotNet.Cli.CommandLine; + +/// +/// Extension methods that make it easier to chain option configuration methods when building options. +/// +public static class OptionBuilderExtensions +{ + extension(T option) where T : Option + { + /// + /// Forces an option that represents a collection-type to only allow a single + /// argument per instance of the option. This means that you'd have to + /// use the option multiple times to pass multiple values. + /// This prevents ambiguity in parsing when argument tokens may appear after the option. + /// + /// + /// + /// + public T AllowSingleArgPerToken() + { + option.AllowMultipleArgumentsPerToken = false; + return option; + } + + + public T AggregateRepeatedTokens() + { + option.AllowMultipleArgumentsPerToken = true; + return option; + } + + public T Hide() + { + option.Hidden = true; + return option; + } + public T AddCompletions(Func> completionSource) + { + option.CompletionSources.Add(completionSource); + return option; + } + } +} diff --git a/src/Cli/Microsoft.DotNet.Cli.CommandLine/README.md b/src/Cli/Microsoft.DotNet.Cli.CommandLine/README.md new file mode 100644 index 000000000000..82e8afb6e8de --- /dev/null +++ b/src/Cli/Microsoft.DotNet.Cli.CommandLine/README.md @@ -0,0 +1,12 @@ +# Microsoft.Dotnet.Cli.CommandLine + +This project contains extensions and utilities for building command line applications. + +These extensions are layered on top of core System.CommandLine concepts and types, and +do not directly reference concepts that are specific to the `dotnet` CLI. We hope that +these would be published separately as a NuGet package for use by other command line +applications in the future. + +From a layering perspective, everything that is specific to the `dotnet` CLI should +be in the `src/Cli/dotnet` or `src/Cli/Microsoft.DotNet.Cli.Utils` projects, which +reference this project. Keep this one generally-speaking clean. diff --git a/src/Cli/Microsoft.DotNet.Cli.CommandLine/ResultNavigationExtensions.cs b/src/Cli/Microsoft.DotNet.Cli.CommandLine/ResultNavigationExtensions.cs new file mode 100644 index 000000000000..b4bd612cc6c6 --- /dev/null +++ b/src/Cli/Microsoft.DotNet.Cli.CommandLine/ResultNavigationExtensions.cs @@ -0,0 +1,94 @@ +using System.CommandLine; +using System.CommandLine.Parsing; + +namespace Microsoft.DotNet.Cli.CommandLine; + +/// +/// Extension methods for safely navigating ParseResult and SymbolResult to get option values. +/// +public static class ResultNavigationExtensions +{ + /// + /// Only returns the value for this option if the option is present and there are no parse errors for that option. + /// This allows cross-cutting code like the telemetry filters to safely get the value without throwing on null-ref errors. + /// If you are inside a command handler or 'normal' System.CommandLine code then you don't need this - the parse error handling + /// will have covered these cases. + /// + public static T? SafelyGetValueForOption(this ParseResult parseResult, Option optionToGet) + { + if (parseResult.GetResult(optionToGet) is OptionResult optionResult // only return a value if there _is_ a value - default or otherwise + && !parseResult.Errors.Any(e => e.SymbolResult == optionResult) // only return a value if this isn't a parsing error + && optionResult.Option.ValueType.IsAssignableTo(typeof(T))) // only return a value if coercing the type won't error + { + // shouldn't happen because of the above checks, but we can be safe since this is only used in telemetry, and should + // be resistant to errors + try + { + return optionResult.GetValue(optionToGet); + } + catch + { + return default; + } + } + else + { + return default; + } + } + + /// + /// Only returns the value for this option if the option is present and there are no parse errors for that option. + /// This allows cross-cutting code like the telemetry filters to safely get the value without throwing on null-ref errors. + /// If you are inside a command handler or 'normal' System.CommandLine code then you don't need this - the parse error handling + /// will have covered these cases. + /// + public static T? SafelyGetValueForOption(this ParseResult parseResult, string name) + { + if (parseResult.GetResult(name) is OptionResult optionResult // only return a value if there _is_ a value - default or otherwise + && !parseResult.Errors.Any(e => e.SymbolResult == optionResult) // only return a value if this isn't a parsing error + && optionResult.Option.ValueType.IsAssignableTo(typeof(T))) // only return a value if coercing the type won't error + { + // shouldn't happen because of the above checks, but we can be safe since this is only used in telemetry, and should + // be resistant to errors + try + { + return optionResult.GetValue(name); + } + catch + { + return default; + } + } + else + { + return default; + } + } + + /// + /// Checks if the option is present and not implicit (i.e. not set by default). + /// This is useful for checking if the user has explicitly set an option, as opposed to it being set by default. + /// + public static bool HasOption(this ParseResult parseResult, Option option) => parseResult.GetResult(option) is OptionResult or && !or.Implicit; + + /// + /// Checks if the option with given name is present and not implicit (i.e. not set by default). + /// This is useful for checking if the user has explicitly set an option, as opposed to it being set by default. + /// + public static bool HasOption(this ParseResult parseResult, string name) + => parseResult.GetResult(name) is OptionResult or && !or.Implicit; + + /// + /// Checks if the option is present and not implicit (i.e. not set by default). + /// This is useful for checking if the user has explicitly set an option, as opposed to it being set by default. + /// + public static bool HasOption(this SymbolResult symbolResult, Option option) => symbolResult.GetResult(option) is OptionResult or && !or.Implicit; + + /// + /// Checks if the option with given name is present and not implicit (i.e. not set by default). + /// This is useful for checking if the user has explicitly set an option, as opposed to it being set by default. + /// + public static bool HasOption(this SymbolResult symbolResult, string name) + => symbolResult.GetResult(name) is OptionResult or && !or.Implicit; +} diff --git a/src/Cli/Microsoft.DotNet.Cli.CommandLine/SpanParsableExtensions.cs b/src/Cli/Microsoft.DotNet.Cli.CommandLine/SpanParsableExtensions.cs new file mode 100644 index 000000000000..9419a038ccd9 --- /dev/null +++ b/src/Cli/Microsoft.DotNet.Cli.CommandLine/SpanParsableExtensions.cs @@ -0,0 +1,153 @@ +using System.Collections.Immutable; +using System.CommandLine; +using System.CommandLine.Parsing; + +namespace Microsoft.DotNet.Cli.CommandLine; + +public static class SpanParserExtensions +{ + extension(Option o) where T : ISpanParsable + { + /// + /// Configures the option with a custom parser that uses the implementation to parse the tokens provided. + /// Will parse a single token with , and if the option allows multiple tokens will take the 'last one wins' approach. + /// + /// + /// Without this, Options will fall-back to using potentially-reflection-based parsing in S.CL, or + /// if the type doesn't have built-in S.CL parsing support will fail to parse at runtime. + /// + public Option AsSpanParsable() + { + o.CustomParser = StaticSingleItemParser; + return o; + } + } + + extension(Option> o) where T : ISpanParsable + { + /// + /// Configures the option with a custom parser that uses the implementation to parse the tokens provided. + /// This parser handles multiple tokens, using for each token. + /// + /// + /// Without this, Options will fall-back to using potentially-reflection-based parsing in S.CL, or + /// if the type doesn't have built-in S.CL parsing support will fail to parse at runtime. + /// + public Option> AsSpanParsable() + { + o.CustomParser = StaticMultiItemItemParser; + return o; + } + } + + extension(Argument a) where T : ISpanParsable + { + /// + /// Configures the argument with a custom parser that uses the implementation to parse the value. + /// Will parse a single token with , and if the argument allows multiple tokens will take the 'last one wins' approach. + /// + /// + /// Without this, Arguments will fall-back to using potentially-reflection-based parsing in S.CL, or + /// if the type doesn't have built-in S.CL parsing support will fail to parse at runtime. + /// + public Argument AsSpanParsable() + { + a.CustomParser = StaticSingleItemParser; + return a; + } + } + + extension(Argument> a) where T : ISpanParsable + { + /// + /// Configures the argument with a custom parser that uses the implementation to parse the value. + /// This parser handles multiple tokens, using for each token. + /// + /// + /// Without this, Arguments will fall-back to using potentially-reflection-based parsing in S.CL, or + /// if the type doesn't have built-in S.CL parsing support will fail to parse at runtime. + /// + public Argument> AsSpanParsable() + { + a.CustomParser = StaticMultiItemItemParser; + return a; + } + } + + internal static IReadOnlyCollection? StaticMultiItemItemParser(ArgumentResult tokenizationResult) + where T : ISpanParsable + { + if (tokenizationResult.Tokens.Count == 0) + { + return default; + } + + var parentName = + tokenizationResult.Parent switch + { + OptionResult optionResult => optionResult.Option.Name, + ArgumentResult argumentResult => argumentResult.Argument.Name, + CommandResult or null => tokenizationResult.Argument.Name, + _ => "" + }; + var coll = ImmutableArray.CreateBuilder(tokenizationResult.Tokens.Count); + + foreach (var token in tokenizationResult.Tokens) + { + var tokenToParse = token.Value; + + if (string.IsNullOrEmpty(tokenToParse)) + { + tokenizationResult.AddError($"Cannot parse null or empty value for symbol '{parentName}'"); + continue; + } + + if (!T.TryParse(tokenToParse, null, out var result)) + { + tokenizationResult.AddError($"Cannot parse value '{tokenToParse}' for symbol '{parentName}' as a {typeof(T).Name}"); + continue; + } + + coll.Add(result); + } + + return coll.ToImmutableArray(); + } + + internal static T? StaticSingleItemParser(ArgumentResult tokenizationResult) + where T : ISpanParsable + { + if (tokenizationResult.Tokens.Count == 0) + { + return default; + } + + var parentName = + tokenizationResult.Parent switch + { + OptionResult optionResult => optionResult.Option.Name, + ArgumentResult argumentResult => argumentResult.Argument.Name, + CommandResult or null => tokenizationResult.Argument.Name, + _ => "" + }; + // we explicitly only support parsing one token, so let's do a last-one-wins approach here + var tokenToParse = + tokenizationResult.Tokens switch + { + [var onlyToken] => onlyToken.Value, + _ => tokenizationResult.Tokens[^1].Value + }; + + if (string.IsNullOrEmpty(tokenToParse)) + { + tokenizationResult.AddError($"Cannot parse null or empty value for symbol '{parentName}'"); + } + + if (!T.TryParse(tokenToParse, null, out var result)) + { + tokenizationResult.AddError($"Cannot parse value '{tokenToParse}' for symbol '{parentName}' as a {typeof(T).Name}"); + } + + return result; + } +} diff --git a/src/Cli/Microsoft.DotNet.Cli.CommandLine/SymbolDocumentationExtensions.cs b/src/Cli/Microsoft.DotNet.Cli.CommandLine/SymbolDocumentationExtensions.cs new file mode 100644 index 000000000000..908a8911b4c7 --- /dev/null +++ b/src/Cli/Microsoft.DotNet.Cli.CommandLine/SymbolDocumentationExtensions.cs @@ -0,0 +1,40 @@ +using System.CommandLine; + +namespace Microsoft.DotNet.Cli.CommandLine; + +/// +/// Extension methods for adding documentation links to commands, options and arguments. +/// +public static class SymbolDocumentationExtensions +{ + private static readonly Dictionary s_documentationLinks = new(); + private static readonly Lock s_lock = new(); + + extension(Symbol symbol) + { + /// + /// Gets or sets the documentation link for this command, option or argument. + /// This link is intended to be shown in help or error messages to point users to more information. + /// It is not used by the command line parser itself. + /// + public string? DocsLink + { + get => s_documentationLinks.TryGetValue(symbol, out var link) ? link : null; + set + { + lock (s_lock) + { + if (string.IsNullOrEmpty(value)) + { + s_documentationLinks.Remove(symbol); + } + else + { + s_documentationLinks[symbol] = value; + } + } + } + } + } + +} diff --git a/src/Cli/Microsoft.TemplateEngine.Cli/Commands/ICommandDocument.cs b/src/Cli/Microsoft.TemplateEngine.Cli/Commands/ICommandDocument.cs deleted file mode 100644 index dc11c8327f36..000000000000 --- a/src/Cli/Microsoft.TemplateEngine.Cli/Commands/ICommandDocument.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.CommandLine; - -namespace Microsoft.TemplateEngine.Cli.Commands; - -/// -/// If a implements this interface, it can open -/// its documentation page online. -/// -public interface ICommandDocument -{ - /// - /// The URL to the documentation page for this command. - /// - string DocsLink { get; } -} diff --git a/src/Cli/Microsoft.TemplateEngine.Cli/Commands/NewCommand.cs b/src/Cli/Microsoft.TemplateEngine.Cli/Commands/NewCommand.cs index 3db21ea63d74..e8883d6a9f6f 100644 --- a/src/Cli/Microsoft.TemplateEngine.Cli/Commands/NewCommand.cs +++ b/src/Cli/Microsoft.TemplateEngine.Cli/Commands/NewCommand.cs @@ -3,18 +3,20 @@ using System.CommandLine; using System.CommandLine.Completions; +using Microsoft.DotNet.Cli.CommandLine; using Microsoft.TemplateEngine.Abstractions; using Microsoft.TemplateEngine.Edge.Settings; namespace Microsoft.TemplateEngine.Cli.Commands { - internal partial class NewCommand : BaseCommand, ICustomHelp, ICommandDocument + internal partial class NewCommand : BaseCommand, ICustomHelp { internal NewCommand( string commandName, Func hostBuilder) : base(hostBuilder, commandName, SymbolStrings.Command_New_Description) { + this.DocsLink = "https://aka.ms/dotnet-new"; TreatUnmatchedTokensAsErrors = true; //it is important that legacy commands are built before non-legacy, as non legacy commands are building validators that rely on legacy stuff @@ -44,8 +46,6 @@ internal NewCommand( Options.Add(SharedOptions.ProjectPathOption); } - public string DocsLink { get; } = "https://aka.ms/dotnet-new"; - internal static Option DebugCustomSettingsLocationOption { get; } = new("--debug:custom-hive") { Description = SymbolStrings.Option_Debug_CustomSettings, diff --git a/src/Cli/Microsoft.TemplateEngine.Cli/Microsoft.TemplateEngine.Cli.csproj b/src/Cli/Microsoft.TemplateEngine.Cli/Microsoft.TemplateEngine.Cli.csproj index 228c9cd613d2..e9140b0e10fc 100644 --- a/src/Cli/Microsoft.TemplateEngine.Cli/Microsoft.TemplateEngine.Cli.csproj +++ b/src/Cli/Microsoft.TemplateEngine.Cli/Microsoft.TemplateEngine.Cli.csproj @@ -21,10 +21,11 @@ + - + True @@ -65,6 +66,6 @@ - + diff --git a/src/Cli/dotnet/Commands/Build/BuildCommand.cs b/src/Cli/dotnet/Commands/Build/BuildCommand.cs index 871ead794e84..3d8943315a94 100644 --- a/src/Cli/dotnet/Commands/Build/BuildCommand.cs +++ b/src/Cli/dotnet/Commands/Build/BuildCommand.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.CommandLine; +using Microsoft.DotNet.Cli.CommandLine; using Microsoft.DotNet.Cli.Commands.Restore; using Microsoft.DotNet.Cli.Commands.Run; using Microsoft.DotNet.Cli.Extensions; diff --git a/src/Cli/dotnet/Commands/Build/BuildCommandParser.cs b/src/Cli/dotnet/Commands/Build/BuildCommandParser.cs index 5b87474d7548..74b4efa2a712 100644 --- a/src/Cli/dotnet/Commands/Build/BuildCommandParser.cs +++ b/src/Cli/dotnet/Commands/Build/BuildCommandParser.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.CommandLine; +using Microsoft.DotNet.Cli.CommandLine; using Microsoft.DotNet.Cli.Commands.Restore; using Microsoft.DotNet.Cli.Extensions; @@ -17,25 +18,25 @@ internal static class BuildCommandParser Arity = ArgumentArity.ZeroOrMore }; - public static readonly Option OutputOption = new ForwardedOption("--output", "-o") + public static readonly Option OutputOption = new Option("--output", "-o") { Description = CliCommandStrings.BuildOutputOptionDescription, HelpName = CliCommandStrings.OutputOptionName }.ForwardAsOutputPath("OutputPath"); - public static readonly Option NoIncrementalOption = new ForwardedOption("--no-incremental") + public static readonly Option NoIncrementalOption = new Option("--no-incremental") { Description = CliCommandStrings.NoIncrementalOptionDescription, Arity = ArgumentArity.Zero }.ForwardAs("--target:Rebuild"); - public static readonly Option NoDependenciesOption = new ForwardedOption("--no-dependencies") + public static readonly Option NoDependenciesOption = new Option("--no-dependencies") { Description = CliCommandStrings.NoDependenciesOptionDescription, Arity = ArgumentArity.Zero }.ForwardAs("--property:BuildProjectReferences=false"); - public static readonly Option NoLogoOption = new ForwardedOption("--nologo") + public static readonly Option NoLogoOption = new Option("--nologo") { Description = CliCommandStrings.BuildCmdNoLogo, Arity = ArgumentArity.Zero @@ -69,7 +70,10 @@ public static Command GetCommand() private static Command ConstructCommand() { - DocumentedCommand command = new("build", DocsLink, CliCommandStrings.BuildAppFullName); + Command command = new("build", CliCommandStrings.BuildAppFullName) + { + DocsLink = DocsLink + }; command.Arguments.Add(SlnOrProjectOrFileArgument); RestoreCommandParser.AddImplicitRestoreOptions(command, includeRuntimeOption: false, includeNoDependenciesOption: false); diff --git a/src/Cli/dotnet/Commands/BuildServer/BuildServerCommandParser.cs b/src/Cli/dotnet/Commands/BuildServer/BuildServerCommandParser.cs index 60d71d049054..468e60ad4fe1 100644 --- a/src/Cli/dotnet/Commands/BuildServer/BuildServerCommandParser.cs +++ b/src/Cli/dotnet/Commands/BuildServer/BuildServerCommandParser.cs @@ -5,6 +5,7 @@ using System.CommandLine; using Microsoft.DotNet.Cli.Commands.BuildServer.Shutdown; +using Microsoft.DotNet.Cli.CommandLine; using Microsoft.DotNet.Cli.Extensions; namespace Microsoft.DotNet.Cli.Commands.BuildServer; @@ -22,7 +23,10 @@ public static Command GetCommand() private static Command ConstructCommand() { - var command = new DocumentedCommand("build-server", DocsLink, CliCommandStrings.BuildServerCommandDescription); + var command = new Command("build-server", CliCommandStrings.BuildServerCommandDescription) + { + DocsLink = DocsLink + }; command.Subcommands.Add(BuildServerShutdownCommandParser.GetCommand()); diff --git a/src/Cli/dotnet/Commands/Clean/CleanCommandParser.cs b/src/Cli/dotnet/Commands/Clean/CleanCommandParser.cs index 19b1854a1e16..24e83acc8b64 100644 --- a/src/Cli/dotnet/Commands/Clean/CleanCommandParser.cs +++ b/src/Cli/dotnet/Commands/Clean/CleanCommandParser.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.CommandLine; +using Microsoft.DotNet.Cli.CommandLine; using Microsoft.DotNet.Cli.Commands.Clean.FileBasedAppArtifacts; using Microsoft.DotNet.Cli.Extensions; @@ -17,13 +18,13 @@ internal static class CleanCommandParser Arity = ArgumentArity.ZeroOrMore }; - public static readonly Option OutputOption = new ForwardedOption("--output", "-o") + public static readonly Option OutputOption = new Option("--output", "-o") { Description = CliCommandStrings.CleanCmdOutputDirDescription, HelpName = CliCommandStrings.CleanCmdOutputDir }.ForwardAsOutputPath("OutputPath"); - public static readonly Option NoLogoOption = new ForwardedOption("--nologo") + public static readonly Option NoLogoOption = new Option("--nologo") { Description = CliCommandStrings.CleanCmdNoLogo, Arity = ArgumentArity.Zero @@ -47,7 +48,10 @@ public static Command GetCommand() private static Command ConstructCommand() { - DocumentedCommand command = new("clean", DocsLink, CliCommandStrings.CleanAppFullName); + Command command = new("clean", CliCommandStrings.CleanAppFullName) + { + DocsLink = DocsLink + }; command.Arguments.Add(SlnOrProjectOrFileArgument); command.Options.Add(FrameworkOption); diff --git a/src/Cli/dotnet/Commands/CommandFactory.cs b/src/Cli/dotnet/Commands/CommandFactory.cs index 143de4cd4137..40bc500082b3 100644 --- a/src/Cli/dotnet/Commands/CommandFactory.cs +++ b/src/Cli/dotnet/Commands/CommandFactory.cs @@ -2,8 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.CommandLine; +using Microsoft.DotNet.Cli.CommandLine; using Microsoft.DotNet.Cli.Commands.Run; -using Microsoft.DotNet.Cli.Extensions; using Microsoft.DotNet.Cli.Utils; namespace Microsoft.DotNet.Cli.Commands; diff --git a/src/Cli/dotnet/Commands/Format/FormatCommandParser.cs b/src/Cli/dotnet/Commands/Format/FormatCommandParser.cs index 7cc23f3d7ce9..dcbeb642e4a8 100644 --- a/src/Cli/dotnet/Commands/Format/FormatCommandParser.cs +++ b/src/Cli/dotnet/Commands/Format/FormatCommandParser.cs @@ -4,6 +4,7 @@ #nullable disable using System.CommandLine; +using Microsoft.DotNet.Cli.CommandLine; namespace Microsoft.DotNet.Cli.Commands.Format; @@ -22,9 +23,10 @@ public static Command GetCommand() private static Command ConstructCommand() { - var formatCommand = new DocumentedCommand("format", DocsLink) + var formatCommand = new Command("format") { - Arguments + Arguments = { Arguments }, + DocsLink = DocsLink, }; formatCommand.SetAction((parseResult) => FormatCommand.Run(parseResult.GetValue(Arguments))); return formatCommand; diff --git a/src/Cli/dotnet/Commands/Fsi/FsiCommandParser.cs b/src/Cli/dotnet/Commands/Fsi/FsiCommandParser.cs index 6ca8b23b0aef..c81c34e79566 100644 --- a/src/Cli/dotnet/Commands/Fsi/FsiCommandParser.cs +++ b/src/Cli/dotnet/Commands/Fsi/FsiCommandParser.cs @@ -4,6 +4,7 @@ #nullable disable using System.CommandLine; +using Microsoft.DotNet.Cli.CommandLine; namespace Microsoft.DotNet.Cli.Commands.Fsi; @@ -22,8 +23,10 @@ public static Command GetCommand() private static Command ConstructCommand() { - DocumentedCommand command = new("fsi", DocsLink) { Arguments }; - + Command command = new("fsi") { + Arguments = { Arguments }, + DocsLink = DocsLink, + }; command.SetAction((parseResult) => FsiCommand.Run(parseResult.GetValue(Arguments))); return command; diff --git a/src/Cli/dotnet/Commands/Help/HelpCommand.cs b/src/Cli/dotnet/Commands/Help/HelpCommand.cs index 416516aa9d9e..a2646a3764d4 100644 --- a/src/Cli/dotnet/Commands/Help/HelpCommand.cs +++ b/src/Cli/dotnet/Commands/Help/HelpCommand.cs @@ -1,14 +1,13 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#nullable disable - using System.CommandLine; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using Microsoft.DotNet.Cli.CommandLine; using Microsoft.DotNet.Cli.Extensions; using Microsoft.DotNet.Cli.Utils; using Microsoft.DotNet.Cli.Utils.Extensions; -using Microsoft.TemplateEngine.Cli.Commands; using NuGetDocumentedCommand = NuGet.CommandLine.XPlat.Commands.DocumentedCommand; namespace Microsoft.DotNet.Cli.Commands.Help; @@ -103,12 +102,12 @@ public int Execute() } } - private static bool TryGetDocsLink(string[] command, out string docsLink) + private static bool TryGetDocsLink(string[] command, [NotNullWhen(true)] out string? docsLink) { var parsedCommand = Parser.Parse(["dotnet", .. command]); - if (parsedCommand?.CommandResult?.Command is ICommandDocument dc) + if (parsedCommand?.CommandResult?.Command?.DocsLink is { } link) { - docsLink = dc.DocsLink; + docsLink = link; return true; } else if (parsedCommand?.CommandResult?.Command is NuGetDocumentedCommand ndc) diff --git a/src/Cli/dotnet/Commands/Help/HelpCommandParser.cs b/src/Cli/dotnet/Commands/Help/HelpCommandParser.cs index d465ae0a398f..8334e262381f 100644 --- a/src/Cli/dotnet/Commands/Help/HelpCommandParser.cs +++ b/src/Cli/dotnet/Commands/Help/HelpCommandParser.cs @@ -4,6 +4,7 @@ #nullable disable using System.CommandLine; +using Microsoft.DotNet.Cli.CommandLine; namespace Microsoft.DotNet.Cli.Commands.Help; @@ -26,7 +27,10 @@ public static Command GetCommand() private static Command ConstructCommand() { - DocumentedCommand command = new("help", DocsLink, CliCommandStrings.HelpAppFullName); + Command command = new("help", CliCommandStrings.HelpAppFullName) + { + DocsLink = DocsLink + }; command.Arguments.Add(Argument); diff --git a/src/Cli/dotnet/Commands/Hidden/Add/AddCommandParser.cs b/src/Cli/dotnet/Commands/Hidden/Add/AddCommandParser.cs index 43cb90737cd6..c2d21405b1f2 100644 --- a/src/Cli/dotnet/Commands/Hidden/Add/AddCommandParser.cs +++ b/src/Cli/dotnet/Commands/Hidden/Add/AddCommandParser.cs @@ -5,6 +5,7 @@ using Microsoft.DotNet.Cli.Commands.Hidden.Add.Package; using Microsoft.DotNet.Cli.Commands.Hidden.Add.Reference; using Microsoft.DotNet.Cli.Commands.Package; +using Microsoft.DotNet.Cli.CommandLine; using Microsoft.DotNet.Cli.Extensions; namespace Microsoft.DotNet.Cli.Commands.Hidden.Add; @@ -22,9 +23,10 @@ public static Command GetCommand() private static Command ConstructCommand() { - var command = new DocumentedCommand("add", DocsLink, CliCommandStrings.NetAddCommand) + var command = new Command("add", CliCommandStrings.NetAddCommand) { - Hidden = true + Hidden = true, + DocsLink = DocsLink }; command.Arguments.Add(PackageCommandParser.ProjectOrFileArgument); diff --git a/src/Cli/dotnet/Commands/Hidden/List/ListCommandParser.cs b/src/Cli/dotnet/Commands/Hidden/List/ListCommandParser.cs index 5ce44933360c..f471c20b62d1 100644 --- a/src/Cli/dotnet/Commands/Hidden/List/ListCommandParser.cs +++ b/src/Cli/dotnet/Commands/Hidden/List/ListCommandParser.cs @@ -7,6 +7,7 @@ using Microsoft.DotNet.Cli.Commands.Hidden.List.Package; using Microsoft.DotNet.Cli.Commands.Hidden.List.Reference; using Microsoft.DotNet.Cli.Extensions; +using Microsoft.DotNet.Cli.CommandLine; namespace Microsoft.DotNet.Cli.Commands.Hidden.List; @@ -32,9 +33,10 @@ public static Command GetCommand() private static Command ConstructCommand() { - var command = new DocumentedCommand("list", DocsLink, CliCommandStrings.NetListCommand) + var command = new Command("list", CliCommandStrings.NetListCommand) { - Hidden = true + Hidden = true, + DocsLink = DocsLink }; command.Arguments.Add(SlnOrProjectArgument); diff --git a/src/Cli/dotnet/Commands/Hidden/Parse/ParseCommand.cs b/src/Cli/dotnet/Commands/Hidden/Parse/ParseCommand.cs index 262944805cdd..24c5fe6ec3eb 100644 --- a/src/Cli/dotnet/Commands/Hidden/Parse/ParseCommand.cs +++ b/src/Cli/dotnet/Commands/Hidden/Parse/ParseCommand.cs @@ -4,6 +4,7 @@ #nullable disable using System.CommandLine; +using Microsoft.DotNet.Cli.CommandLine; using Microsoft.DotNet.Cli.Extensions; namespace Microsoft.DotNet.Cli.Commands.Hidden.Parse; diff --git a/src/Cli/dotnet/Commands/Hidden/Remove/RemoveCommandParser.cs b/src/Cli/dotnet/Commands/Hidden/Remove/RemoveCommandParser.cs index 0ac1f1834dbb..a91ed69540f8 100644 --- a/src/Cli/dotnet/Commands/Hidden/Remove/RemoveCommandParser.cs +++ b/src/Cli/dotnet/Commands/Hidden/Remove/RemoveCommandParser.cs @@ -5,6 +5,7 @@ using Microsoft.DotNet.Cli.Commands.Hidden.Remove.Package; using Microsoft.DotNet.Cli.Commands.Hidden.Remove.Reference; using Microsoft.DotNet.Cli.Commands.Package; +using Microsoft.DotNet.Cli.CommandLine; using Microsoft.DotNet.Cli.Extensions; namespace Microsoft.DotNet.Cli.Commands.Hidden.Remove; @@ -22,9 +23,10 @@ public static Command GetCommand() private static Command ConstructCommand() { - var command = new DocumentedCommand("remove", DocsLink, CliCommandStrings.NetRemoveCommand) + var command = new Command("remove", CliCommandStrings.NetRemoveCommand) { - Hidden = true + Hidden = true, + DocsLink = DocsLink }; command.Arguments.Add(PackageCommandParser.ProjectOrFileArgument); diff --git a/src/Cli/dotnet/Commands/MSBuild/MSBuildCommand.cs b/src/Cli/dotnet/Commands/MSBuild/MSBuildCommand.cs index cf0b7e06c660..d7edaea57478 100644 --- a/src/Cli/dotnet/Commands/MSBuild/MSBuildCommand.cs +++ b/src/Cli/dotnet/Commands/MSBuild/MSBuildCommand.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.CommandLine; +using Microsoft.DotNet.Cli.CommandLine; using Microsoft.DotNet.Cli.Extensions; using Microsoft.DotNet.Cli.Utils; diff --git a/src/Cli/dotnet/Commands/MSBuild/MSBuildCommandParser.cs b/src/Cli/dotnet/Commands/MSBuild/MSBuildCommandParser.cs index 2638d7fefc71..5893570d7be2 100644 --- a/src/Cli/dotnet/Commands/MSBuild/MSBuildCommandParser.cs +++ b/src/Cli/dotnet/Commands/MSBuild/MSBuildCommandParser.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.CommandLine; +using Microsoft.DotNet.Cli.CommandLine; namespace Microsoft.DotNet.Cli.Commands.MSBuild; @@ -21,9 +22,10 @@ public static Command GetCommand() private static Command ConstructCommand() { - var command = new DocumentedCommand("msbuild", DocsLink, CliCommandStrings.BuildAppFullName) + var command = new Command("msbuild", CliCommandStrings.BuildAppFullName) { - Arguments + Arguments = { Arguments }, + DocsLink = DocsLink, }; command.Options.Add(CommonOptions.DisableBuildServersOption); diff --git a/src/Cli/dotnet/Commands/New/NewCommandParser.cs b/src/Cli/dotnet/Commands/New/NewCommandParser.cs index 5bb9db983a84..45337f19ea72 100644 --- a/src/Cli/dotnet/Commands/New/NewCommandParser.cs +++ b/src/Cli/dotnet/Commands/New/NewCommandParser.cs @@ -3,6 +3,7 @@ using System.CommandLine; using System.CommandLine.Parsing; +using Microsoft.DotNet.Cli.CommandLine; using Microsoft.DotNet.Cli.Commands.New.MSBuildEvaluation; using Microsoft.DotNet.Cli.Commands.New.PostActions; using Microsoft.DotNet.Cli.Commands.Workload; diff --git a/src/Cli/dotnet/Commands/NuGet/NuGetCommandParser.cs b/src/Cli/dotnet/Commands/NuGet/NuGetCommandParser.cs index 89af3722dd9a..814ad45f8544 100644 --- a/src/Cli/dotnet/Commands/NuGet/NuGetCommandParser.cs +++ b/src/Cli/dotnet/Commands/NuGet/NuGetCommandParser.cs @@ -2,7 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.CommandLine; -using Microsoft.DotNet.Cli.Extensions; +using Microsoft.DotNet.Cli.CommandLine; using NuGetWhyCommand = NuGet.CommandLine.XPlat.Commands.Why.WhyCommand; namespace Microsoft.DotNet.Cli.Commands.NuGet; @@ -22,10 +22,11 @@ public static Command GetCommand() private static Command ConstructCommand() { - var command = new DocumentedCommand("nuget", DocsLink) + var command = new Command("nuget") { // some subcommands are not defined here and just forwarded to NuGet app - TreatUnmatchedTokensAsErrors = false + TreatUnmatchedTokensAsErrors = false, + DocsLink = DocsLink }; command.Options.Add(new Option("--version") @@ -149,7 +150,7 @@ private static Command GetVerifyCommand() { Arity = ArgumentArity.Zero }); - verifyCommand.Options.Add(new ForwardedOption>(fingerprint) + verifyCommand.Options.Add(new Option>(fingerprint) .ForwardAsManyArgumentsEachPrefixedByOption(fingerprint) .AllowSingleArgPerToken()); verifyCommand.Options.Add(CommonOptions.VerbosityOption(Utils.VerbosityOptions.normal)); diff --git a/src/Cli/dotnet/Commands/Pack/PackCommand.cs b/src/Cli/dotnet/Commands/Pack/PackCommand.cs index 1e88f22688f5..57342f67bad2 100644 --- a/src/Cli/dotnet/Commands/Pack/PackCommand.cs +++ b/src/Cli/dotnet/Commands/Pack/PackCommand.cs @@ -4,6 +4,7 @@ using System.Collections.ObjectModel; using System.CommandLine; using System.CommandLine.Parsing; +using Microsoft.DotNet.Cli.CommandLine; using Microsoft.DotNet.Cli.Commands.Build; using Microsoft.DotNet.Cli.Commands.Restore; using Microsoft.DotNet.Cli.Commands.Run; @@ -92,14 +93,14 @@ public static int RunPackCommand(ParseResult parseResult) if (args.Count != 1) { - Console.Error.WriteLine(CliStrings.PackCmd_OneNuspecAllowed); + Console.Error.WriteLine(CliStrings.PackCmd_OneNuspecAllowed); return 1; } var nuspecPath = args[0]; var packArgs = new PackArgs() - { + { Logger = new NuGetConsoleLogger(), Exclude = new List(), OutputDirectory = parseResult.GetValue(PackCommandParser.OutputOption), diff --git a/src/Cli/dotnet/Commands/Pack/PackCommandParser.cs b/src/Cli/dotnet/Commands/Pack/PackCommandParser.cs index c0ee6b1a8b41..a57aa2fabfcd 100644 --- a/src/Cli/dotnet/Commands/Pack/PackCommandParser.cs +++ b/src/Cli/dotnet/Commands/Pack/PackCommandParser.cs @@ -2,9 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.CommandLine; +using Microsoft.DotNet.Cli.CommandLine; using Microsoft.DotNet.Cli.Commands.Build; using Microsoft.DotNet.Cli.Commands.Restore; -using Microsoft.DotNet.Cli.Extensions; using NuGet.Versioning; namespace Microsoft.DotNet.Cli.Commands.Pack; @@ -19,37 +19,37 @@ internal static class PackCommandParser Arity = ArgumentArity.ZeroOrMore }; - public static readonly Option OutputOption = new ForwardedOption("--output", "-o") + public static readonly Option OutputOption = new Option("--output", "-o") { Description = CliCommandStrings.PackCmdOutputDirDescription, HelpName = CliCommandStrings.PackCmdOutputDir }.ForwardAsSingle(o => $"-property:PackageOutputPath={CommandDirectoryContext.GetFullPath(o)}"); - public static readonly Option NoBuildOption = new ForwardedOption("--no-build") + public static readonly Option NoBuildOption = new Option("--no-build") { Description = CliCommandStrings.CmdNoBuildOptionDescription, Arity = ArgumentArity.Zero }.ForwardAs("-property:NoBuild=true"); - public static readonly Option IncludeSymbolsOption = new ForwardedOption("--include-symbols") + public static readonly Option IncludeSymbolsOption = new Option("--include-symbols") { Description = CliCommandStrings.CmdIncludeSymbolsDescription, Arity = ArgumentArity.Zero }.ForwardAs("-property:IncludeSymbols=true"); - public static readonly Option IncludeSourceOption = new ForwardedOption("--include-source") + public static readonly Option IncludeSourceOption = new Option("--include-source") { Description = CliCommandStrings.CmdIncludeSourceDescription, Arity = ArgumentArity.Zero }.ForwardAs("-property:IncludeSource=true"); - public static readonly Option ServiceableOption = new ForwardedOption("--serviceable", "-s") + public static readonly Option ServiceableOption = new Option("--serviceable", "-s") { Description = CliCommandStrings.CmdServiceableDescription, Arity = ArgumentArity.Zero }.ForwardAs("-property:Serviceable=true"); - public static readonly Option NoLogoOption = new ForwardedOption("--nologo") + public static readonly Option NoLogoOption = new Option("--nologo") { Description = CliCommandStrings.PackCmdNoLogo, Arity = ArgumentArity.Zero @@ -63,7 +63,7 @@ internal static class PackCommandParser public static readonly Option VerbosityOption = BuildCommandParser.VerbosityOption; public static Option VersionOption = - new ForwardedOption("--version") + new Option("--version") { Description = CliCommandStrings.PackCmdVersionDescription, HelpName = CliCommandStrings.PackCmdVersion, @@ -90,7 +90,10 @@ public static Command GetCommand() private static Command ConstructCommand() { - var command = new DocumentedCommand("pack", DocsLink, CliCommandStrings.PackAppFullName); + var command = new Command("pack", CliCommandStrings.PackAppFullName) + { + DocsLink = DocsLink + }; command.Arguments.Add(SlnOrProjectOrFileArgument); command.Options.Add(OutputOption); diff --git a/src/Cli/dotnet/Commands/Package/Add/PackageAddCommand.cs b/src/Cli/dotnet/Commands/Package/Add/PackageAddCommand.cs index d1c73fb5bb1e..7a9aa9dd1a64 100644 --- a/src/Cli/dotnet/Commands/Package/Add/PackageAddCommand.cs +++ b/src/Cli/dotnet/Commands/Package/Add/PackageAddCommand.cs @@ -9,6 +9,7 @@ using Microsoft.DotNet.Cli.Commands.MSBuild; using Microsoft.DotNet.Cli.Commands.NuGet; using Microsoft.DotNet.Cli.Commands.Run; +using Microsoft.DotNet.Cli.CommandLine; using Microsoft.DotNet.Cli.Extensions; using Microsoft.DotNet.Cli.Utils; using NuGet.ProjectModel; diff --git a/src/Cli/dotnet/Commands/Package/Add/PackageAddCommandParser.cs b/src/Cli/dotnet/Commands/Package/Add/PackageAddCommandParser.cs index 1d24fd9c10de..ad5c1270896f 100644 --- a/src/Cli/dotnet/Commands/Package/Add/PackageAddCommandParser.cs +++ b/src/Cli/dotnet/Commands/Package/Add/PackageAddCommandParser.cs @@ -4,7 +4,8 @@ using System.CommandLine; using System.CommandLine.Completions; using System.CommandLine.Parsing; -using Microsoft.DotNet.Cli.Extensions; +using System.CommandLine.StaticCompletions; +using Microsoft.DotNet.Cli.CommandLine; using Microsoft.Extensions.EnvironmentAbstractions; using NuGet.Versioning; @@ -12,7 +13,7 @@ namespace Microsoft.DotNet.Cli.Commands.Package.Add; public static class PackageAddCommandParser { - public static readonly Option PrereleaseOption = new ForwardedOption("--prerelease") + public static readonly Option PrereleaseOption = new Option("--prerelease") { Description = CliStrings.CommandPrereleaseOptionDescription, Arity = ArgumentArity.Zero @@ -26,10 +27,11 @@ public static class PackageAddCommandParser return QueryNuGet(context.WordToComplete, allowPrerelease, CancellationToken.None).Result.Select(packageId => new CompletionItem(packageId)); }); - public static readonly Option VersionOption = new DynamicForwardedOption("--version", "-v") + public static readonly Option VersionOption = new Option("--version", "-v") { Description = CliCommandStrings.CmdVersionDescription, - HelpName = CliCommandStrings.CmdVersion + HelpName = CliCommandStrings.CmdVersion, + IsDynamic = true }.ForwardAsSingle(o => $"--version {o}") .AddCompletions((context) => { @@ -48,7 +50,7 @@ public static class PackageAddCommandParser } }); - public static readonly Option FrameworkOption = new ForwardedOption("--framework", "-f") + public static readonly Option FrameworkOption = new Option("--framework", "-f") { Description = CliCommandStrings.PackageAddCmdFrameworkDescription, HelpName = CliCommandStrings.PackageAddCmdFramework @@ -60,13 +62,13 @@ public static class PackageAddCommandParser Arity = ArgumentArity.Zero }; - public static readonly Option SourceOption = new ForwardedOption("--source", "-s") + public static readonly Option SourceOption = new Option("--source", "-s") { Description = CliCommandStrings.PackageAddCmdSourceDescription, HelpName = CliCommandStrings.PackageAddCmdSource }.ForwardAsSingle(o => $"--source {o}"); - public static readonly Option PackageDirOption = new ForwardedOption("--package-directory") + public static readonly Option PackageDirOption = new Option("--package-directory") { Description = CliCommandStrings.CmdPackageDirectoryDescription, HelpName = CliCommandStrings.CmdPackageDirectory diff --git a/src/Cli/dotnet/Commands/Package/List/PackageListCommand.cs b/src/Cli/dotnet/Commands/Package/List/PackageListCommand.cs index 27520377cad2..4ff46b804d2c 100644 --- a/src/Cli/dotnet/Commands/Package/List/PackageListCommand.cs +++ b/src/Cli/dotnet/Commands/Package/List/PackageListCommand.cs @@ -6,6 +6,7 @@ using System.CommandLine; using Microsoft.DotNet.Cli.Commands.Hidden.List; using Microsoft.DotNet.Cli.Commands.NuGet; +using Microsoft.DotNet.Cli.CommandLine; using Microsoft.DotNet.Cli.Extensions; using Microsoft.DotNet.Cli.Utils; using System.Globalization; diff --git a/src/Cli/dotnet/Commands/Package/List/PackageListCommandParser.cs b/src/Cli/dotnet/Commands/Package/List/PackageListCommandParser.cs index 6c6970fcb6c1..2ce93a3c7e80 100644 --- a/src/Cli/dotnet/Commands/Package/List/PackageListCommandParser.cs +++ b/src/Cli/dotnet/Commands/Package/List/PackageListCommandParser.cs @@ -4,68 +4,68 @@ #nullable disable using System.CommandLine; -using Microsoft.DotNet.Cli.Extensions; +using Microsoft.DotNet.Cli.CommandLine; namespace Microsoft.DotNet.Cli.Commands.Package.List; internal static class PackageListCommandParser { - public static readonly Option OutdatedOption = new ForwardedOption("--outdated") + public static readonly Option OutdatedOption = new Option("--outdated") { Description = CliCommandStrings.CmdOutdatedDescription, Arity = ArgumentArity.Zero }.ForwardAs("--outdated"); - public static readonly Option DeprecatedOption = new ForwardedOption("--deprecated") + public static readonly Option DeprecatedOption = new Option("--deprecated") { Description = CliCommandStrings.CmdDeprecatedDescription, Arity = ArgumentArity.Zero }.ForwardAs("--deprecated"); - public static readonly Option VulnerableOption = new ForwardedOption("--vulnerable") + public static readonly Option VulnerableOption = new Option("--vulnerable") { Description = CliCommandStrings.CmdVulnerableDescription, Arity = ArgumentArity.Zero }.ForwardAs("--vulnerable"); - public static readonly Option FrameworkOption = new ForwardedOption>("--framework", "-f") + public static readonly Option FrameworkOption = new Option>("--framework", "-f") { Description = CliCommandStrings.PackageListCmdFrameworkDescription, HelpName = CliCommandStrings.PackageListCmdFramework }.ForwardAsManyArgumentsEachPrefixedByOption("--framework") .AllowSingleArgPerToken(); - public static readonly Option TransitiveOption = new ForwardedOption("--include-transitive") + public static readonly Option TransitiveOption = new Option("--include-transitive") { Description = CliCommandStrings.CmdTransitiveDescription, Arity = ArgumentArity.Zero }.ForwardAs("--include-transitive"); - public static readonly Option PrereleaseOption = new ForwardedOption("--include-prerelease") + public static readonly Option PrereleaseOption = new Option("--include-prerelease") { Description = CliCommandStrings.CmdPrereleaseDescription, Arity = ArgumentArity.Zero }.ForwardAs("--include-prerelease"); - public static readonly Option HighestPatchOption = new ForwardedOption("--highest-patch") + public static readonly Option HighestPatchOption = new Option("--highest-patch") { Description = CliCommandStrings.CmdHighestPatchDescription, Arity = ArgumentArity.Zero }.ForwardAs("--highest-patch"); - public static readonly Option HighestMinorOption = new ForwardedOption("--highest-minor") + public static readonly Option HighestMinorOption = new Option("--highest-minor") { Description = CliCommandStrings.CmdHighestMinorDescription, Arity = ArgumentArity.Zero }.ForwardAs("--highest-minor"); - public static readonly Option ConfigOption = new ForwardedOption("--config", "--configfile") + public static readonly Option ConfigOption = new Option("--config", "--configfile") { Description = CliCommandStrings.CmdConfigDescription, HelpName = CliCommandStrings.CmdConfig }.ForwardAsMany(o => ["--config", o]); - public static readonly Option SourceOption = new ForwardedOption>("--source", "-s") + public static readonly Option SourceOption = new Option>("--source", "-s") { Description = CliCommandStrings.PackageListCmdSourceDescription, HelpName = CliCommandStrings.PackageListCmdSource @@ -80,18 +80,18 @@ internal static class PackageListCommandParser Arity = ArgumentArity.Zero }; - public static readonly Option VerbosityOption = new ForwardedOption("--verbosity", "-v") + public static readonly Option VerbosityOption = new Option("--verbosity", "-v") { Description = CliStrings.VerbosityOptionDescription, HelpName = CliStrings.LevelArgumentName }.ForwardAsSingle(o => $"--verbosity:{o}"); - public static readonly Option FormatOption = new ForwardedOption("--format") + public static readonly Option FormatOption = new Option("--format") { Description = CliCommandStrings.CmdFormatDescription }.ForwardAsSingle(o => $"--format:{o}"); - public static readonly Option OutputVersionOption = new ForwardedOption("--output-version") + public static readonly Option OutputVersionOption = new Option("--output-version") { Description = CliCommandStrings.CmdOutputVersionDescription }.ForwardAsSingle(o => $"--output-version:{o}"); diff --git a/src/Cli/dotnet/Commands/Package/PackageCommandParser.cs b/src/Cli/dotnet/Commands/Package/PackageCommandParser.cs index a6e44f5bccbe..16eb5873794d 100644 --- a/src/Cli/dotnet/Commands/Package/PackageCommandParser.cs +++ b/src/Cli/dotnet/Commands/Package/PackageCommandParser.cs @@ -7,6 +7,7 @@ using Microsoft.DotNet.Cli.Commands.Package.Remove; using Microsoft.DotNet.Cli.Commands.Package.Search; using Microsoft.DotNet.Cli.Commands.Run; +using Microsoft.DotNet.Cli.CommandLine; using Microsoft.DotNet.Cli.Extensions; using Microsoft.DotNet.Cli.Utils; using Command = System.CommandLine.Command; @@ -37,7 +38,10 @@ internal class PackageCommandParser public static Command GetCommand() { - Command command = new DocumentedCommand("package", DocsLink); + Command command = new Command("package") + { + DocsLink = DocsLink + }; command.SetAction((parseResult) => parseResult.HandleMissingCommand()); command.Subcommands.Add(PackageSearchCommandParser.GetCommand()); command.Subcommands.Add(PackageAddCommandParser.GetCommand()); diff --git a/src/Cli/dotnet/Commands/Package/Remove/PackageRemoveCommand.cs b/src/Cli/dotnet/Commands/Package/Remove/PackageRemoveCommand.cs index 2706bed56b21..3ed3ed573416 100644 --- a/src/Cli/dotnet/Commands/Package/Remove/PackageRemoveCommand.cs +++ b/src/Cli/dotnet/Commands/Package/Remove/PackageRemoveCommand.cs @@ -5,7 +5,7 @@ using System.Diagnostics; using Microsoft.DotNet.Cli.Commands.NuGet; using Microsoft.DotNet.Cli.Commands.Run; -using Microsoft.DotNet.Cli.Extensions; +using Microsoft.DotNet.Cli.CommandLine; using Microsoft.DotNet.Cli.Utils; namespace Microsoft.DotNet.Cli.Commands.Package.Remove; diff --git a/src/Cli/dotnet/Commands/Package/Remove/PackageRemoveCommandParser.cs b/src/Cli/dotnet/Commands/Package/Remove/PackageRemoveCommandParser.cs index e1b020f7695f..20354b4f0658 100644 --- a/src/Cli/dotnet/Commands/Package/Remove/PackageRemoveCommandParser.cs +++ b/src/Cli/dotnet/Commands/Package/Remove/PackageRemoveCommandParser.cs @@ -2,7 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.CommandLine; -using Microsoft.DotNet.Cli.Extensions; +using Microsoft.DotNet.Cli.CommandLine; namespace Microsoft.DotNet.Cli.Commands.Package.Remove; diff --git a/src/Cli/dotnet/Commands/Package/Search/PackageSearchCommand.cs b/src/Cli/dotnet/Commands/Package/Search/PackageSearchCommand.cs index 4317f96329be..d26a96fab237 100644 --- a/src/Cli/dotnet/Commands/Package/Search/PackageSearchCommand.cs +++ b/src/Cli/dotnet/Commands/Package/Search/PackageSearchCommand.cs @@ -4,7 +4,7 @@ #nullable disable using Microsoft.DotNet.Cli.Commands.NuGet; -using Microsoft.DotNet.Cli.Extensions; +using Microsoft.DotNet.Cli.CommandLine; using System.CommandLine; namespace Microsoft.DotNet.Cli.Commands.Package.Search; diff --git a/src/Cli/dotnet/Commands/Package/Search/PackageSearchCommandParser.cs b/src/Cli/dotnet/Commands/Package/Search/PackageSearchCommandParser.cs index 52e15de7089e..54b0749cca3d 100644 --- a/src/Cli/dotnet/Commands/Package/Search/PackageSearchCommandParser.cs +++ b/src/Cli/dotnet/Commands/Package/Search/PackageSearchCommandParser.cs @@ -4,6 +4,7 @@ #nullable disable using System.CommandLine; +using Microsoft.DotNet.Cli.CommandLine; using Microsoft.DotNet.Cli.Extensions; namespace Microsoft.DotNet.Cli.Commands.Package.Search; @@ -17,26 +18,26 @@ internal static class PackageSearchCommandParser Arity = ArgumentArity.ZeroOrOne }; - public static readonly Option Sources = new ForwardedOption>("--source") + public static readonly Option Sources = new Option>("--source") { Description = CliCommandStrings.SourceDescription, HelpName = CliCommandStrings.SourceArgumentName }.ForwardAsManyArgumentsEachPrefixedByOption("--source") .AllowSingleArgPerToken(); - public static readonly Option Take = new ForwardedOption("--take") + public static readonly Option Take = new Option("--take") { Description = CliCommandStrings.PackageSearchTakeDescription, HelpName = CliCommandStrings.PackageSearchTakeArgumentName }.ForwardAsSingle(o => $"--take:{o}"); - public static readonly Option Skip = new ForwardedOption("--skip") + public static readonly Option Skip = new Option("--skip") { Description = CliCommandStrings.PackageSearchSkipDescription, HelpName = CliCommandStrings.PackageSearchSkipArgumentName }.ForwardAsSingle(o => $"--skip:{o}"); - public static readonly Option ExactMatch = new ForwardedOption("--exact-match") + public static readonly Option ExactMatch = new Option("--exact-match") { Description = CliCommandStrings.ExactMatchDescription, Arity = ArgumentArity.Zero @@ -44,25 +45,25 @@ internal static class PackageSearchCommandParser public static readonly Option Interactive = CommonOptions.InteractiveOption().ForwardIfEnabled("--interactive"); - public static readonly Option Prerelease = new ForwardedOption("--prerelease") + public static readonly Option Prerelease = new Option("--prerelease") { Description = CliCommandStrings.PackageSearchPrereleaseDescription, Arity = ArgumentArity.Zero }.ForwardAs("--prerelease"); - public static readonly Option ConfigFile = new ForwardedOption("--configfile") + public static readonly Option ConfigFile = new Option("--configfile") { Description = CliCommandStrings.ConfigFileDescription, HelpName = CliCommandStrings.ConfigFileArgumentName }.ForwardAsSingle(o => $"--configfile:{o}"); - public static readonly Option Format = new ForwardedOption("--format") + public static readonly Option Format = new Option("--format") { Description = CliCommandStrings.FormatDescription, HelpName = CliCommandStrings.FormatArgumentName }.ForwardAsSingle(o => $"--format:{o}"); - public static readonly Option Verbosity = new ForwardedOption("--verbosity") + public static readonly Option Verbosity = new Option("--verbosity") { Description = CliCommandStrings.VerbosityDescription, HelpName = CliCommandStrings.VerbosityArgumentName diff --git a/src/Cli/dotnet/Commands/Publish/PublishCommand.cs b/src/Cli/dotnet/Commands/Publish/PublishCommand.cs index 45dc32c84300..2bfb835871a9 100644 --- a/src/Cli/dotnet/Commands/Publish/PublishCommand.cs +++ b/src/Cli/dotnet/Commands/Publish/PublishCommand.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.CommandLine; +using Microsoft.DotNet.Cli.CommandLine; using Microsoft.DotNet.Cli.Commands.Restore; using Microsoft.DotNet.Cli.Commands.Run; using Microsoft.DotNet.Cli.Extensions; diff --git a/src/Cli/dotnet/Commands/Publish/PublishCommandParser.cs b/src/Cli/dotnet/Commands/Publish/PublishCommandParser.cs index 7be9784f5a15..9fbe305d4877 100644 --- a/src/Cli/dotnet/Commands/Publish/PublishCommandParser.cs +++ b/src/Cli/dotnet/Commands/Publish/PublishCommandParser.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.CommandLine; +using Microsoft.DotNet.Cli.CommandLine; using Microsoft.DotNet.Cli.Commands.Build; using Microsoft.DotNet.Cli.Commands.Restore; using Microsoft.DotNet.Cli.Extensions; @@ -18,26 +19,26 @@ internal static class PublishCommandParser Arity = ArgumentArity.ZeroOrMore }; - public static readonly Option OutputOption = new ForwardedOption("--output", "-o") + public static readonly Option OutputOption = new Option("--output", "-o") { Description = CliCommandStrings.PublishOutputOptionDescription, HelpName = CliCommandStrings.PublishOutputOption }.ForwardAsOutputPath("PublishDir"); - public static readonly Option> ManifestOption = new ForwardedOption>("--manifest") + public static readonly Option> ManifestOption = new Option>("--manifest") { Description = CliCommandStrings.ManifestOptionDescription, HelpName = CliCommandStrings.ManifestOption }.ForwardAsSingle(o => $"-property:TargetManifestFiles={string.Join("%3B", o.Select(CommandDirectoryContext.GetFullPath))}") .AllowSingleArgPerToken(); - public static readonly Option NoBuildOption = new ForwardedOption("--no-build") + public static readonly Option NoBuildOption = new Option("--no-build") { Description = CliCommandStrings.NoBuildOptionDescription, Arity = ArgumentArity.Zero }.ForwardAs("-property:NoBuild=true"); - public static readonly Option NoLogoOption = new ForwardedOption("--nologo") + public static readonly Option NoLogoOption = new Option("--nologo") { Description = CliCommandStrings.PublishCmdNoLogo, Arity = ArgumentArity.Zero @@ -67,7 +68,10 @@ public static Command GetCommand() private static Command ConstructCommand() { - var command = new DocumentedCommand("publish", DocsLink, CliCommandStrings.PublishAppDescription); + var command = new Command("publish", CliCommandStrings.PublishAppDescription) + { + DocsLink = DocsLink + }; command.Arguments.Add(SlnOrProjectOrFileArgument); RestoreCommandParser.AddImplicitRestoreOptions(command, includeRuntimeOption: false, includeNoDependenciesOption: true); diff --git a/src/Cli/dotnet/Commands/Reference/Add/ReferenceAddCommand.cs b/src/Cli/dotnet/Commands/Reference/Add/ReferenceAddCommand.cs index b60050fb12cc..10340392c0db 100644 --- a/src/Cli/dotnet/Commands/Reference/Add/ReferenceAddCommand.cs +++ b/src/Cli/dotnet/Commands/Reference/Add/ReferenceAddCommand.cs @@ -5,8 +5,8 @@ using System.CommandLine; using Microsoft.Build.Evaluation; +using Microsoft.DotNet.Cli.CommandLine; using Microsoft.DotNet.Cli.Commands.Package; -using Microsoft.DotNet.Cli.Extensions; using Microsoft.DotNet.Cli.Utils; using NuGet.Frameworks; diff --git a/src/Cli/dotnet/Commands/Reference/Add/ReferenceAddCommandParser.cs b/src/Cli/dotnet/Commands/Reference/Add/ReferenceAddCommandParser.cs index 5e5690fdd655..777f74585018 100644 --- a/src/Cli/dotnet/Commands/Reference/Add/ReferenceAddCommandParser.cs +++ b/src/Cli/dotnet/Commands/Reference/Add/ReferenceAddCommandParser.cs @@ -4,6 +4,8 @@ #nullable disable using System.CommandLine; +using System.CommandLine.StaticCompletions; +using Microsoft.DotNet.Cli.CommandLine; namespace Microsoft.DotNet.Cli.Commands.Reference.Add; @@ -21,12 +23,13 @@ internal static class ReferenceAddCommandParser } }; - public static readonly Option FrameworkOption = new DynamicOption("--framework", "-f") + public static readonly Option FrameworkOption = new Option("--framework", "-f") { Description = CliCommandStrings.ReferenceAddCmdFrameworkDescription, - HelpName = CliStrings.CommonCmdFramework - - }.AddCompletions(CliCompletion.TargetFrameworksFromProjectFile); + HelpName = CliStrings.CommonCmdFramework, + IsDynamic = true, + } + .AddCompletions(CliCompletion.TargetFrameworksFromProjectFile); public static readonly Option InteractiveOption = CommonOptions.InteractiveOption(); diff --git a/src/Cli/dotnet/Commands/Reference/List/ReferenceListCommand.cs b/src/Cli/dotnet/Commands/Reference/List/ReferenceListCommand.cs index 55d54b3d1103..cbd7a84e3a73 100644 --- a/src/Cli/dotnet/Commands/Reference/List/ReferenceListCommand.cs +++ b/src/Cli/dotnet/Commands/Reference/List/ReferenceListCommand.cs @@ -8,8 +8,8 @@ using Microsoft.Build.Evaluation; using Microsoft.Build.Exceptions; using Microsoft.Build.Execution; +using Microsoft.DotNet.Cli.CommandLine; using Microsoft.DotNet.Cli.Commands.Hidden.List; -using Microsoft.DotNet.Cli.Extensions; using Microsoft.DotNet.Cli.Utils; namespace Microsoft.DotNet.Cli.Commands.Reference.List; diff --git a/src/Cli/dotnet/Commands/Reference/ReferenceCommandParser.cs b/src/Cli/dotnet/Commands/Reference/ReferenceCommandParser.cs index 7756b35edf04..e26cd6cd8c35 100644 --- a/src/Cli/dotnet/Commands/Reference/ReferenceCommandParser.cs +++ b/src/Cli/dotnet/Commands/Reference/ReferenceCommandParser.cs @@ -7,6 +7,7 @@ using Microsoft.DotNet.Cli.Commands.Reference.Add; using Microsoft.DotNet.Cli.Commands.Reference.List; using Microsoft.DotNet.Cli.Commands.Reference.Remove; +using Microsoft.DotNet.Cli.CommandLine; using Microsoft.DotNet.Cli.Extensions; namespace Microsoft.DotNet.Cli.Commands.Reference; @@ -30,7 +31,10 @@ public static Command GetCommand() private static Command ConstructCommand() { - var command = new DocumentedCommand("reference", DocsLink, CliCommandStrings.NetRemoveCommand); + var command = new Command("reference", CliCommandStrings.NetRemoveCommand) + { + DocsLink = DocsLink + }; command.Subcommands.Add(ReferenceAddCommandParser.GetCommand()); command.Subcommands.Add(ReferenceListCommandParser.GetCommand()); diff --git a/src/Cli/dotnet/Commands/Reference/Remove/ReferenceRemoveCommand.cs b/src/Cli/dotnet/Commands/Reference/Remove/ReferenceRemoveCommand.cs index 64db0bec340d..3a71f6b761ac 100644 --- a/src/Cli/dotnet/Commands/Reference/Remove/ReferenceRemoveCommand.cs +++ b/src/Cli/dotnet/Commands/Reference/Remove/ReferenceRemoveCommand.cs @@ -5,8 +5,8 @@ using System.CommandLine; using Microsoft.Build.Evaluation; +using Microsoft.DotNet.Cli.CommandLine; using Microsoft.DotNet.Cli.Commands.Package; -using Microsoft.DotNet.Cli.Extensions; using Microsoft.DotNet.Cli.Utils; namespace Microsoft.DotNet.Cli.Commands.Reference.Remove; diff --git a/src/Cli/dotnet/Commands/Reference/Remove/ReferenceRemoveCommandParser.cs b/src/Cli/dotnet/Commands/Reference/Remove/ReferenceRemoveCommandParser.cs index 9cb48cdfff5f..03e67bdbb37c 100644 --- a/src/Cli/dotnet/Commands/Reference/Remove/ReferenceRemoveCommandParser.cs +++ b/src/Cli/dotnet/Commands/Reference/Remove/ReferenceRemoveCommandParser.cs @@ -4,15 +4,19 @@ #nullable disable using System.CommandLine; +using System.CommandLine.StaticCompletions; + +using Microsoft.DotNet.Cli.CommandLine; namespace Microsoft.DotNet.Cli.Commands.Reference.Remove; internal static class ReferenceRemoveCommandParser { - public static readonly Argument> ProjectPathArgument = new DynamicArgument>(CliCommandStrings.ReferenceRemoveProjectPathArgumentName) + public static readonly Argument> ProjectPathArgument = new Argument>(CliCommandStrings.ReferenceRemoveProjectPathArgumentName) { Description = CliCommandStrings.ReferenceRemoveProjectPathArgumentDescription, Arity = ArgumentArity.OneOrMore, + IsDynamic = true, }.AddCompletions(CliCompletion.ProjectReferencesFromProjectFile); public static readonly Option FrameworkOption = new("--framework", "-f") diff --git a/src/Cli/dotnet/Commands/Restore/RestoreCommandParser.cs b/src/Cli/dotnet/Commands/Restore/RestoreCommandParser.cs index 5477584f5734..7e1f4a48106d 100644 --- a/src/Cli/dotnet/Commands/Restore/RestoreCommandParser.cs +++ b/src/Cli/dotnet/Commands/Restore/RestoreCommandParser.cs @@ -2,7 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.CommandLine; -using Microsoft.DotNet.Cli.Extensions; +using System.CommandLine.StaticCompletions; +using Microsoft.DotNet.Cli.CommandLine; using NuGet.Packaging; namespace Microsoft.DotNet.Cli.Commands.Restore; @@ -17,7 +18,7 @@ internal static class RestoreCommandParser Arity = ArgumentArity.ZeroOrMore }; - public static readonly Option> SourceOption = new ForwardedOption>("--source", "-s") + public static readonly Option> SourceOption = new Option>("--source", "-s") { Description = CliCommandStrings.CmdSourceOptionDescription, HelpName = CliCommandStrings.CmdSourceOption @@ -33,22 +34,22 @@ private static IEnumerable diff --git a/src/System.CommandLine.StaticCompletions/DynamicSymbolExtensions.cs b/src/System.CommandLine.StaticCompletions/DynamicSymbolExtensions.cs new file mode 100644 index 000000000000..c59d18c7bec1 --- /dev/null +++ b/src/System.CommandLine.StaticCompletions/DynamicSymbolExtensions.cs @@ -0,0 +1,55 @@ +namespace System.CommandLine.StaticCompletions; + +/// +/// Extensions for marking options or arguments require dynamic completions. Such symbols get special handling +/// in the static completion generation logic. +/// +public static class DynamicSymbolExtensions +{ + /// + /// The state that is used to track which symbols are dynamic. + /// + private static readonly Dictionary s_dynamicSymbols = []; + + extension(Option option) + { + /// + /// Indicates whether this option requires a dynamic call into the dotnet process to compute completions. + /// + public bool IsDynamic + { + get => s_dynamicSymbols.GetValueOrDefault(option, false); + set => s_dynamicSymbols[option] = value; + } + + /// + /// Mark this option as requiring dynamic completions. + /// + /// + public Option RequiresDynamicCompletion() + { + option.IsDynamic = true; + return option; + } + } + + extension(Argument argument) + { + /// Indicates whether this argument requires a dynamic call into the dotnet process to compute completions. + public bool IsDynamic + { + get => s_dynamicSymbols.GetValueOrDefault(argument, false); + set => s_dynamicSymbols[argument] = value; + } + + /// + /// Mark this argument as requiring dynamic completions. + /// + /// + public Argument RequiresDynamicCompletion() + { + argument.IsDynamic = true; + return argument; + } + } +} diff --git a/src/System.CommandLine.StaticCompletions/HelpGenerationExtensions.cs b/src/System.CommandLine.StaticCompletions/HelpGenerationExtensions.cs index a78e854e690e..193234dc60db 100644 --- a/src/System.CommandLine.StaticCompletions/HelpGenerationExtensions.cs +++ b/src/System.CommandLine.StaticCompletions/HelpGenerationExtensions.cs @@ -111,8 +111,5 @@ private static IEnumerable