diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index a7c2f759e982..f9bfe0e6c5a1 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -25,18 +25,18 @@ https://github.com/dotnet/symreader 27e584661980ee6d82c419a2a471ae505b7d122e - + https://github.com/dotnet/command-line-api - 02fe27cd6a9b001c8feb7938e6ef4b3799745759 + 8374d5fca634a93458c84414b1604c12f765d1ab - + https://github.com/dotnet/command-line-api - 02fe27cd6a9b001c8feb7938e6ef4b3799745759 + 8374d5fca634a93458c84414b1604c12f765d1ab - + https://github.com/dotnet/command-line-api - 02fe27cd6a9b001c8feb7938e6ef4b3799745759 + 8374d5fca634a93458c84414b1604c12f765d1ab diff --git a/eng/Versions.props b/eng/Versions.props index d53cabee7b6c..a0fbac4322d3 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -17,8 +17,8 @@ - 2.0.0-beta4.23307.1 - 0.4.0-alpha.23307.1 + 2.0.0-beta4.22564.1 + 0.4.0-alpha.22564.1 10.3.0 diff --git a/src/CommandLineExtensions.cs b/src/CommandLineExtensions.cs new file mode 100644 index 000000000000..9cd803c68538 --- /dev/null +++ b/src/CommandLineExtensions.cs @@ -0,0 +1,102 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.CommandLine; +using System.CommandLine.Parsing; +using System.Diagnostics.CodeAnalysis; +using System.Linq; + +namespace Microsoft.CodeAnalysis.Tools +{ + internal static class CommandLineExtensions + { + internal static OptionResult? GetOptionResult(this ParseResult result, string alias) + { + return GetOptionResult(result.CommandResult, alias); + } + + internal static ArgumentResult? GetArgumentResult(this ParseResult result, string alias) + { + return GetArgumentResult(result.CommandResult, alias); + } + + internal static OptionResult? GetOptionResult(this CommandResult result, string alias) + { + return result.Children.GetByAlias(alias) as OptionResult; + } + + internal static ArgumentResult? GetArgumentResult(this CommandResult result, string alias) + { + return result.Children.GetByAlias(alias) as ArgumentResult; + } + + internal static SymbolResult? GetByAlias(this IReadOnlyList results, string alias) + { + return results.SingleOrDefault(result => result.Symbol.Name.Equals(alias) || result.Symbol is IdentifierSymbol id && id.HasAlias(alias)); + } + + [return: MaybeNull] + internal static T GetValueForArgument(this ParseResult result, string alias) + { + return GetValueForArgument(result.CommandResult, alias); + } + + [return: MaybeNull] + internal static T GetValueForArgument(this ParseResult result, Argument argument) + { + return GetValueForArgument(result.CommandResult, argument); + } + + [return: MaybeNull] + internal static T GetValueForOption(this ParseResult result, string alias) + { + return GetValueForOption(result.CommandResult, alias); + } + + [return: MaybeNull] + internal static T GetValueForArgument(this CommandResult result, Argument argumentDefinition) + { + var arguments = result.Children.Where(x => x.Symbol.Name == argumentDefinition.Name).ToArray(); + if (arguments.Length == 1 && + arguments.SingleOrDefault() is ArgumentResult argument && + argument.GetValueOrDefault() is T t) + { + return t; + } + + return default; + } + + [return: MaybeNull] + internal static T GetValueForArgument(this CommandResult result, string alias) + { + if (result.GetArgumentResult(alias) is ArgumentResult argument && + argument.GetValueOrDefault() is { } t) + { + return t; + } + + return default; + } + + [return: MaybeNull] + internal static T GetValueForOption(this CommandResult result, string alias) + { + if (result.GetOptionResult(alias) is OptionResult option && + option.GetValueOrDefault() is { } t) + { + return t; + } + + return default; + } + + internal static bool WasOptionUsed(this ParseResult result, params string[] aliases) + { + return result.Tokens + .Where(token => token.Type == TokenType.Option) + .Any(token => aliases.Contains(token.Value)); + } + } +} diff --git a/src/Commands/FormatAnalyzersCommand.cs b/src/Commands/FormatAnalyzersCommand.cs index 4ecbb180c3f1..994a73735d69 100644 --- a/src/Commands/FormatAnalyzersCommand.cs +++ b/src/Commands/FormatAnalyzersCommand.cs @@ -2,8 +2,8 @@ using System.Collections.Immutable; using System.CommandLine; -using System.CommandLine.IO; -using System.Threading; +using System.CommandLine.Invocation; +using System.CommandLine.Parsing; using System.Threading.Tasks; using Microsoft.Extensions.Logging; using static Microsoft.CodeAnalysis.Tools.FormatCommandCommon; @@ -14,43 +14,44 @@ internal static class FormatAnalyzersCommand { private static readonly FormatAnalyzersHandler s_analyzerHandler = new(); - internal static CliCommand GetCommand() + internal static Command GetCommand() { - var command = new CliCommand("analyzers", Resources.Run_3rd_party_analyzers__and_apply_fixes) + var command = new Command("analyzers", Resources.Run_3rd_party_analyzers__and_apply_fixes) { DiagnosticsOption, ExcludeDiagnosticsOption, SeverityOption, }; command.AddCommonOptions(); - command.Action = s_analyzerHandler; + command.Handler = s_analyzerHandler; return command; } - private class FormatAnalyzersHandler : CliAction + private class FormatAnalyzersHandler : ICommandHandler { - public override int Invoke(ParseResult parseResult) => InvokeAsync(parseResult, CancellationToken.None).GetAwaiter().GetResult(); + public int Invoke(InvocationContext context) => InvokeAsync(context).GetAwaiter().GetResult(); - public override async Task InvokeAsync(ParseResult parseResult, CancellationToken cancellationToken) + public async Task InvokeAsync(InvocationContext context) { + var parseResult = context.ParseResult; var formatOptions = parseResult.ParseVerbosityOption(FormatOptions.Instance); - var logger = new SystemConsole().SetupLogging(minimalLogLevel: formatOptions.LogLevel, minimalErrorLevel: LogLevel.Warning); + var logger = context.Console.SetupLogging(minimalLogLevel: formatOptions.LogLevel, minimalErrorLevel: LogLevel.Warning); formatOptions = parseResult.ParseCommonOptions(formatOptions, logger); formatOptions = parseResult.ParseWorkspaceOptions(formatOptions); - if (parseResult.GetResult(SeverityOption) is not null && + if (parseResult.HasOption(SeverityOption) && parseResult.GetValue(SeverityOption) is string { Length: > 0 } analyzerSeverity) { formatOptions = formatOptions with { AnalyzerSeverity = GetSeverity(analyzerSeverity) }; } - if (parseResult.GetResult(DiagnosticsOption) is not null && + if (parseResult.HasOption(DiagnosticsOption) && parseResult.GetValue(DiagnosticsOption) is string[] { Length: > 0 } diagnostics) { formatOptions = formatOptions with { Diagnostics = diagnostics.ToImmutableHashSet() }; } - if (parseResult.GetResult(ExcludeDiagnosticsOption) is not null && + if (parseResult.HasOption(ExcludeDiagnosticsOption) && parseResult.GetValue(ExcludeDiagnosticsOption) is string[] { Length: > 0 } excludeDiagnostics) { formatOptions = formatOptions with { ExcludeDiagnostics = excludeDiagnostics.ToImmutableHashSet() }; @@ -58,7 +59,7 @@ public override async Task InvokeAsync(ParseResult parseResult, Cancellatio formatOptions = formatOptions with { FixCategory = FixCategory.Analyzers }; - return await FormatAsync(formatOptions, logger, cancellationToken).ConfigureAwait(false); + return await FormatAsync(formatOptions, logger, context.GetCancellationToken()).ConfigureAwait(false); } } } diff --git a/src/Commands/FormatCommandCommon.cs b/src/Commands/FormatCommandCommon.cs index d2ed8fad0cbe..5cccf1bbde2a 100644 --- a/src/Commands/FormatCommandCommon.cs +++ b/src/Commands/FormatCommandCommon.cs @@ -2,6 +2,7 @@ using System; using System.CommandLine; +using System.CommandLine.Parsing; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; @@ -26,80 +27,44 @@ internal static class FormatCommandCommon private static string[] VerbosityLevels => new[] { "q", "quiet", "m", "minimal", "n", "normal", "d", "detailed", "diag", "diagnostic" }; private static string[] SeverityLevels => new[] { "info", "warn", "error" }; - public static readonly CliArgument SlnOrProjectArgument = new CliArgument(Resources.SolutionOrProjectArgumentName) + public static readonly Argument SlnOrProjectArgument = new Argument(Resources.SolutionOrProjectArgumentName) { Description = Resources.SolutionOrProjectArgumentDescription, Arity = ArgumentArity.ZeroOrOne }.DefaultToCurrentDirectory(); - internal static readonly CliOption FolderOption = new("--folder") + internal static readonly Option FolderOption = new(new[] { "--folder" }, Resources.Whether_to_treat_the_workspace_argument_as_a_simple_folder_of_files); + internal static readonly Option NoRestoreOption = new(new[] { "--no-restore" }, Resources.Doesnt_execute_an_implicit_restore_before_formatting); + internal static readonly Option VerifyNoChanges = new(new[] { "--verify-no-changes" }, Resources.Verify_no_formatting_changes_would_be_performed_Terminates_with_a_non_zero_exit_code_if_any_files_would_have_been_formatted); + internal static readonly Option DiagnosticsOption = new(new[] { "--diagnostics" }, () => Array.Empty(), Resources.A_space_separated_list_of_diagnostic_ids_to_use_as_a_filter_when_fixing_code_style_or_3rd_party_issues) { - Description = Resources.Whether_to_treat_the_workspace_argument_as_a_simple_folder_of_files, + AllowMultipleArgumentsPerToken = true }; - internal static readonly CliOption NoRestoreOption = new("--no-restore") + internal static readonly Option ExcludeDiagnosticsOption = new(new[] { "--exclude-diagnostics" }, () => Array.Empty(), Resources.A_space_separated_list_of_diagnostic_ids_to_ignore_when_fixing_code_style_or_3rd_party_issues) { - Description = Resources.Doesnt_execute_an_implicit_restore_before_formatting, + AllowMultipleArgumentsPerToken = true }; - internal static readonly CliOption VerifyNoChanges = new("--verify-no-changes") + internal static readonly Option SeverityOption = new Option("--severity", Resources.The_severity_of_diagnostics_to_fix_Allowed_values_are_info_warn_and_error).AcceptOnlyFromAmong(SeverityLevels); + internal static readonly Option IncludeOption = new(new[] { "--include" }, () => Array.Empty(), Resources.A_list_of_relative_file_or_folder_paths_to_include_in_formatting_All_files_are_formatted_if_empty) { - Description = Resources.Verify_no_formatting_changes_would_be_performed_Terminates_with_a_non_zero_exit_code_if_any_files_would_have_been_formatted, + AllowMultipleArgumentsPerToken = true }; - internal static readonly CliOption DiagnosticsOption = new("--diagnostics") + internal static readonly Option ExcludeOption = new(new[] { "--exclude" }, () => Array.Empty(), Resources.A_list_of_relative_file_or_folder_paths_to_exclude_from_formatting) { - AllowMultipleArgumentsPerToken = true, - DefaultValueFactory = _ => Array.Empty(), - Description = Resources.A_space_separated_list_of_diagnostic_ids_to_use_as_a_filter_when_fixing_code_style_or_3rd_party_issues, + AllowMultipleArgumentsPerToken = true }; - internal static readonly CliOption ExcludeDiagnosticsOption = new("--exclude-diagnostics") + internal static readonly Option IncludeGeneratedOption = new(new[] { "--include-generated" }, Resources.Format_files_generated_by_the_SDK); + internal static readonly Option VerbosityOption = new Option(new[] { "--verbosity", "-v" }, Resources.Set_the_verbosity_level_Allowed_values_are_quiet_minimal_normal_detailed_and_diagnostic).AcceptOnlyFromAmong(VerbosityLevels); + internal static readonly Option BinarylogOption = new Option(new[] { "--binarylog" }, Resources.Log_all_project_or_solution_load_information_to_a_binary_log_file) { - AllowMultipleArgumentsPerToken = true, - DefaultValueFactory = _ => Array.Empty(), - Description = Resources.A_space_separated_list_of_diagnostic_ids_to_ignore_when_fixing_code_style_or_3rd_party_issues, - }; - internal static readonly CliOption SeverityOption = new CliOption("--severity") - { - Description = Resources.The_severity_of_diagnostics_to_fix_Allowed_values_are_info_warn_and_error, - }; - internal static readonly CliOption IncludeOption = new("--include") - { - AllowMultipleArgumentsPerToken = true, - DefaultValueFactory = _ => Array.Empty(), - Description = Resources.A_list_of_relative_file_or_folder_paths_to_include_in_formatting_All_files_are_formatted_if_empty, - }; - internal static readonly CliOption ExcludeOption = new("--exclude") - { - AllowMultipleArgumentsPerToken = true, - DefaultValueFactory = _ => Array.Empty(), - Description = Resources.A_list_of_relative_file_or_folder_paths_to_exclude_from_formatting, - }; - internal static readonly CliOption IncludeGeneratedOption = new("--include-generated") - { - Description = Resources.Format_files_generated_by_the_SDK, - }; - internal static readonly CliOption VerbosityOption = new CliOption("--verbosity", "-v") - { - Description = Resources.Set_the_verbosity_level_Allowed_values_are_quiet_minimal_normal_detailed_and_diagnostic, - }; - internal static readonly CliOption BinarylogOption = new CliOption("--binarylog") - { - HelpName = "binary-log-path", - Arity = ArgumentArity.ZeroOrOne, - Description = Resources.Log_all_project_or_solution_load_information_to_a_binary_log_file, - }; - internal static readonly CliOption ReportOption = new CliOption("--report") - { - HelpName = "report-path", - Arity = ArgumentArity.ZeroOrOne, - Description = Resources.Accepts_a_file_path_which_if_provided_will_produce_a_json_report_in_the_given_directory, - }; - - static FormatCommandCommon() + ArgumentHelpName = "binary-log-path", + Arity = ArgumentArity.ZeroOrOne + }.AcceptLegalFilePathsOnly(); + internal static readonly Option ReportOption = new Option(new[] { "--report" }, Resources.Accepts_a_file_path_which_if_provided_will_produce_a_json_report_in_the_given_directory) { - SeverityOption.AcceptOnlyFromAmong(SeverityLevels); - VerbosityOption.AcceptOnlyFromAmong(VerbosityLevels); - BinarylogOption.AcceptLegalFilePathsOnly(); - ReportOption.AcceptLegalFilePathsOnly(); - } + ArgumentHelpName = "report-path", + Arity = ArgumentArity.ZeroOrOne + }.AcceptLegalFilePathsOnly(); internal static async Task FormatAsync(FormatOptions formatOptions, ILogger logger, CancellationToken cancellationToken) { @@ -133,22 +98,22 @@ internal static async Task FormatAsync(FormatOptions formatOptions, ILogger return formatResult.GetExitCode(formatOptions.ChangesAreErrors); } - public static void AddCommonOptions(this CliCommand command) + public static void AddCommonOptions(this Command command) { - command.Arguments.Add(SlnOrProjectArgument); - command.Options.Add(NoRestoreOption); - command.Options.Add(VerifyNoChanges); - command.Options.Add(IncludeOption); - command.Options.Add(ExcludeOption); - command.Options.Add(IncludeGeneratedOption); - command.Options.Add(VerbosityOption); - command.Options.Add(BinarylogOption); - command.Options.Add(ReportOption); + command.AddArgument(SlnOrProjectArgument); + command.AddOption(NoRestoreOption); + command.AddOption(VerifyNoChanges); + command.AddOption(IncludeOption); + command.AddOption(ExcludeOption); + command.AddOption(IncludeGeneratedOption); + command.AddOption(VerbosityOption); + command.AddOption(BinarylogOption); + command.AddOption(ReportOption); } - public static CliArgument DefaultToCurrentDirectory(this CliArgument arg) + public static Argument DefaultToCurrentDirectory(this Argument arg) { - arg.DefaultValueFactory = _ => EnsureTrailingSlash(Directory.GetCurrentDirectory()); + arg.SetDefaultValue(EnsureTrailingSlash(Directory.GetCurrentDirectory())); return arg; } @@ -172,7 +137,7 @@ public static int GetExitCode(this WorkspaceFormatResult formatResult, bool chec public static FormatOptions ParseVerbosityOption(this ParseResult parseResult, FormatOptions formatOptions) { - if (parseResult.GetResult(VerbosityOption) is not null && + if (parseResult.HasOption(VerbosityOption) && parseResult.GetValue(VerbosityOption) is string { Length: > 0 } verbosity) { formatOptions = formatOptions with { LogLevel = GetLogLevel(verbosity) }; @@ -187,23 +152,23 @@ public static FormatOptions ParseVerbosityOption(this ParseResult parseResult, F public static FormatOptions ParseCommonOptions(this ParseResult parseResult, FormatOptions formatOptions, ILogger logger) { - if (parseResult.GetResult(NoRestoreOption) is not null) + if (parseResult.HasOption(NoRestoreOption)) { formatOptions = formatOptions with { NoRestore = true }; } - if (parseResult.GetResult(VerifyNoChanges) is not null) + if (parseResult.HasOption(VerifyNoChanges)) { formatOptions = formatOptions with { ChangesAreErrors = true }; formatOptions = formatOptions with { SaveFormattedFiles = false }; } - if (parseResult.GetResult(IncludeGeneratedOption) is not null) + if (parseResult.HasOption(IncludeGeneratedOption)) { formatOptions = formatOptions with { IncludeGeneratedFiles = true }; } - if (parseResult.GetResult(IncludeOption) is not null || parseResult.GetResult(ExcludeOption) is not null) + if (parseResult.HasOption(IncludeOption) || parseResult.HasOption(ExcludeOption)) { var fileToInclude = parseResult.GetValue(IncludeOption) ?? Array.Empty(); var fileToExclude = parseResult.GetValue(ExcludeOption) ?? Array.Empty(); @@ -211,7 +176,7 @@ public static FormatOptions ParseCommonOptions(this ParseResult parseResult, For formatOptions = formatOptions with { FileMatcher = SourceFileMatcher.CreateMatcher(fileToInclude, fileToExclude) }; } - if (parseResult.GetResult(ReportOption) is not null) + if (parseResult.HasOption(ReportOption)) { formatOptions = formatOptions with { ReportPath = string.Empty }; @@ -221,7 +186,7 @@ public static FormatOptions ParseCommonOptions(this ParseResult parseResult, For } } - if (parseResult.GetResult(BinarylogOption) is not null) + if (parseResult.HasOption(BinarylogOption)) { formatOptions = formatOptions with { BinaryLogPath = "format.binlog" }; @@ -323,7 +288,7 @@ public static FormatOptions ParseWorkspaceOptions(this ParseResult parseResult, if (parseResult.GetValue(SlnOrProjectArgument) is string { Length: > 0 } slnOrProject) { - if (parseResult.GetResult(FolderOption) is not null) + if (parseResult.HasOption(FolderOption)) { formatOptions = formatOptions with { WorkspaceFilePath = slnOrProject }; formatOptions = formatOptions with { WorkspaceType = WorkspaceType.Folder }; diff --git a/src/Commands/FormatStyleCommand.cs b/src/Commands/FormatStyleCommand.cs index cddf23cad785..e915e9936b6d 100644 --- a/src/Commands/FormatStyleCommand.cs +++ b/src/Commands/FormatStyleCommand.cs @@ -2,8 +2,8 @@ using System.Collections.Immutable; using System.CommandLine; -using System.CommandLine.IO; -using System.Threading; +using System.CommandLine.Invocation; +using System.CommandLine.Parsing; using System.Threading.Tasks; using Microsoft.Extensions.Logging; using static Microsoft.CodeAnalysis.Tools.FormatCommandCommon; @@ -14,43 +14,44 @@ internal static class FormatStyleCommand { private static readonly FormatStyleHandler s_styleHandler = new(); - internal static CliCommand GetCommand() + internal static Command GetCommand() { - var command = new CliCommand("style", Resources.Run_code_style_analyzers_and_apply_fixes) + var command = new Command("style", Resources.Run_code_style_analyzers_and_apply_fixes) { DiagnosticsOption, ExcludeDiagnosticsOption, SeverityOption, }; command.AddCommonOptions(); - command.Action = s_styleHandler; + command.Handler = s_styleHandler; return command; } - private class FormatStyleHandler : CliAction + private class FormatStyleHandler : ICommandHandler { - public override int Invoke(ParseResult parseResult) => InvokeAsync(parseResult, CancellationToken.None).GetAwaiter().GetResult(); + public int Invoke(InvocationContext context) => InvokeAsync(context).GetAwaiter().GetResult(); - public override async Task InvokeAsync(ParseResult parseResult, CancellationToken cancellationToken) + public async Task InvokeAsync(InvocationContext context) { + var parseResult = context.ParseResult; var formatOptions = parseResult.ParseVerbosityOption(FormatOptions.Instance); - var logger = new SystemConsole().SetupLogging(minimalLogLevel: formatOptions.LogLevel, minimalErrorLevel: LogLevel.Warning); + var logger = context.Console.SetupLogging(minimalLogLevel: formatOptions.LogLevel, minimalErrorLevel: LogLevel.Warning); formatOptions = parseResult.ParseCommonOptions(formatOptions, logger); formatOptions = parseResult.ParseWorkspaceOptions(formatOptions); - if (parseResult.GetResult(SeverityOption) is not null && + if (parseResult.HasOption(SeverityOption) && parseResult.GetValue(SeverityOption) is string { Length: > 0 } styleSeverity) { formatOptions = formatOptions with { CodeStyleSeverity = GetSeverity(styleSeverity) }; } - if (parseResult.GetResult(DiagnosticsOption) is not null && + if (parseResult.HasOption(DiagnosticsOption) && parseResult.GetValue(DiagnosticsOption) is string[] { Length: > 0 } diagnostics) { formatOptions = formatOptions with { Diagnostics = diagnostics.ToImmutableHashSet() }; } - if (parseResult.GetResult(ExcludeDiagnosticsOption) is not null && + if (parseResult.HasOption(ExcludeDiagnosticsOption) && parseResult.GetValue(ExcludeDiagnosticsOption) is string[] { Length: > 0 } excludeDiagnostics) { formatOptions = formatOptions with { ExcludeDiagnostics = excludeDiagnostics.ToImmutableHashSet() }; @@ -58,7 +59,7 @@ public override async Task InvokeAsync(ParseResult parseResult, Cancellatio formatOptions = formatOptions with { FixCategory = FixCategory.CodeStyle }; - return await FormatAsync(formatOptions, logger, cancellationToken).ConfigureAwait(false); + return await FormatAsync(formatOptions, logger, context.GetCancellationToken()).ConfigureAwait(false); } } } diff --git a/src/Commands/FormatWhitespaceCommand.cs b/src/Commands/FormatWhitespaceCommand.cs index de62c51cd39e..953f4c018f9a 100644 --- a/src/Commands/FormatWhitespaceCommand.cs +++ b/src/Commands/FormatWhitespaceCommand.cs @@ -1,9 +1,8 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. using System.CommandLine; -using System.CommandLine.IO; +using System.CommandLine.Invocation; using System.CommandLine.Parsing; -using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; using static Microsoft.CodeAnalysis.Tools.FormatCommandCommon; @@ -29,53 +28,52 @@ internal delegate Task Handler( private static readonly FormatWhitespaceHandler s_formattingHandler = new(); - internal static CliCommand GetCommand() + internal static Command GetCommand() { - var command = new CliCommand("whitespace", Resources.Run_whitespace_formatting) + var command = new Command("whitespace", Resources.Run_whitespace_formatting) { FolderOption }; command.AddCommonOptions(); - command.Validators.Add(EnsureFolderNotSpecifiedWithNoRestore); - command.Validators.Add(EnsureFolderNotSpecifiedWhenLoggingBinlog); - command.Action = s_formattingHandler; + command.AddValidator(EnsureFolderNotSpecifiedWithNoRestore); + command.AddValidator(EnsureFolderNotSpecifiedWhenLoggingBinlog); + command.Handler = s_formattingHandler; return command; } internal static void EnsureFolderNotSpecifiedWithNoRestore(CommandResult symbolResult) { - var folder = symbolResult.GetValue(FolderOption); - var noRestore = symbolResult.GetResult(NoRestoreOption); - if (folder && noRestore != null) - { - symbolResult.AddError(Resources.Cannot_specify_the_folder_option_with_no_restore); - } + var folder = symbolResult.GetValueForOption("--folder"); + var noRestore = symbolResult.GetOptionResult("--no-restore"); + symbolResult.ErrorMessage = folder && noRestore != null + ? Resources.Cannot_specify_the_folder_option_with_no_restore + : null; } internal static void EnsureFolderNotSpecifiedWhenLoggingBinlog(CommandResult symbolResult) { - var folder = symbolResult.GetValue(FolderOption); - var binarylog = symbolResult.GetResult(BinarylogOption); - if (folder && binarylog is not null && !binarylog.Implicit) - { - symbolResult.AddError(Resources.Cannot_specify_the_folder_option_when_writing_a_binary_log); - } + var folder = symbolResult.GetValueForOption("--folder"); + var binarylog = symbolResult.GetOptionResult("--binarylog"); + symbolResult.ErrorMessage = folder && binarylog is not null && !binarylog.IsImplicit + ? Resources.Cannot_specify_the_folder_option_when_writing_a_binary_log + : null; } - private class FormatWhitespaceHandler : CliAction + private class FormatWhitespaceHandler : ICommandHandler { - public override int Invoke(ParseResult parseResult) => InvokeAsync(parseResult, CancellationToken.None).GetAwaiter().GetResult(); + public int Invoke(InvocationContext context) => InvokeAsync(context).GetAwaiter().GetResult(); - public override async Task InvokeAsync(ParseResult parseResult, CancellationToken cancellationToken) + public async Task InvokeAsync(InvocationContext context) { + var parseResult = context.ParseResult; var formatOptions = parseResult.ParseVerbosityOption(FormatOptions.Instance); - var logger = new SystemConsole().SetupLogging(minimalLogLevel: formatOptions.LogLevel, minimalErrorLevel: LogLevel.Warning); + var logger = context.Console.SetupLogging(minimalLogLevel: formatOptions.LogLevel, minimalErrorLevel: LogLevel.Warning); formatOptions = parseResult.ParseCommonOptions(formatOptions, logger); formatOptions = parseResult.ParseWorkspaceOptions(formatOptions); formatOptions = formatOptions with { FixCategory = FixCategory.Whitespace }; - return await FormatAsync(formatOptions, logger, cancellationToken).ConfigureAwait(false); + return await FormatAsync(formatOptions, logger, context.GetCancellationToken()).ConfigureAwait(false); } } } diff --git a/src/Commands/RootFormatCommand.cs b/src/Commands/RootFormatCommand.cs index 79faa8edb12b..7f04ecfa791d 100644 --- a/src/Commands/RootFormatCommand.cs +++ b/src/Commands/RootFormatCommand.cs @@ -2,8 +2,8 @@ using System.Collections.Immutable; using System.CommandLine; -using System.CommandLine.IO; -using System.Threading; +using System.CommandLine.Invocation; +using System.CommandLine.Parsing; using System.Threading.Tasks; using Microsoft.Extensions.Logging; using static Microsoft.CodeAnalysis.Tools.FormatCommandCommon; @@ -14,9 +14,9 @@ internal static class RootFormatCommand { private static readonly FormatCommandDefaultHandler s_formatCommandHandler = new(); - public static CliRootCommand GetCommand() + public static RootCommand GetCommand() { - var formatCommand = new CliRootCommand(Resources.Formats_code_to_match_editorconfig_settings) + var formatCommand = new RootCommand(Resources.Formats_code_to_match_editorconfig_settings) { FormatWhitespaceCommand.GetCommand(), FormatStyleCommand.GetCommand(), @@ -26,35 +26,36 @@ public static CliRootCommand GetCommand() SeverityOption, }; formatCommand.AddCommonOptions(); - formatCommand.Action = s_formatCommandHandler; + formatCommand.Handler = s_formatCommandHandler; return formatCommand; } - private class FormatCommandDefaultHandler : CliAction + private class FormatCommandDefaultHandler : ICommandHandler { - public override int Invoke(ParseResult parseResult) => InvokeAsync(parseResult, CancellationToken.None).GetAwaiter().GetResult(); + public int Invoke(InvocationContext context) => InvokeAsync(context).GetAwaiter().GetResult(); - public override async Task InvokeAsync(ParseResult parseResult, CancellationToken cancellationToken) + public async Task InvokeAsync(InvocationContext context) { + var parseResult = context.ParseResult; var formatOptions = parseResult.ParseVerbosityOption(FormatOptions.Instance); - var logger = new SystemConsole().SetupLogging(minimalLogLevel: formatOptions.LogLevel, minimalErrorLevel: LogLevel.Warning); + var logger = context.Console.SetupLogging(minimalLogLevel: formatOptions.LogLevel, minimalErrorLevel: LogLevel.Warning); formatOptions = parseResult.ParseCommonOptions(formatOptions, logger); formatOptions = parseResult.ParseWorkspaceOptions(formatOptions); - if (parseResult.GetResult(SeverityOption) is not null && + if (parseResult.HasOption(SeverityOption) && parseResult.GetValue(SeverityOption) is string { Length: > 0 } defaultSeverity) { formatOptions = formatOptions with { AnalyzerSeverity = GetSeverity(defaultSeverity) }; formatOptions = formatOptions with { CodeStyleSeverity = GetSeverity(defaultSeverity) }; } - if (parseResult.GetResult(DiagnosticsOption) is not null && + if (parseResult.HasOption(DiagnosticsOption) && parseResult.GetValue(DiagnosticsOption) is string[] { Length: > 0 } diagnostics) { formatOptions = formatOptions with { Diagnostics = diagnostics.ToImmutableHashSet() }; } - if (parseResult.GetResult(ExcludeDiagnosticsOption) is not null && + if (parseResult.HasOption(ExcludeDiagnosticsOption) && parseResult.GetValue(ExcludeDiagnosticsOption) is string[] { Length: > 0 } excludeDiagnostics) { formatOptions = formatOptions with { ExcludeDiagnostics = excludeDiagnostics.ToImmutableHashSet() }; @@ -62,7 +63,7 @@ public override async Task InvokeAsync(ParseResult parseResult, Cancellatio formatOptions = formatOptions with { FixCategory = FixCategory.Whitespace | FixCategory.CodeStyle | FixCategory.Analyzers }; - return await FormatAsync(formatOptions, logger, cancellationToken).ConfigureAwait(false); + return await FormatAsync(formatOptions, logger, context.GetCancellationToken()).ConfigureAwait(false); } } } diff --git a/src/Program.cs b/src/Program.cs index 91ec85b660c3..5ffb0f8dbf44 100644 --- a/src/Program.cs +++ b/src/Program.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. -using System.Threading; +using System.CommandLine; +using System.CommandLine.Parsing; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Tools.Commands; @@ -11,7 +12,7 @@ internal class Program private static async Task Main(string[] args) { var rootCommand = RootFormatCommand.GetCommand(); - return await rootCommand.Parse(args).InvokeAsync(CancellationToken.None); + return await rootCommand.InvokeAsync(args); } } } diff --git a/tests/ProgramTests.cs b/tests/ProgramTests.cs index 7c5181025268..488f4955d7d5 100644 --- a/tests/ProgramTests.cs +++ b/tests/ProgramTests.cs @@ -1,5 +1,8 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. +using System.Collections.Generic; +using System.CommandLine; +using System.CommandLine.Parsing; using Microsoft.CodeAnalysis.Tools.Commands; using Xunit; @@ -55,18 +58,18 @@ public void CommandLine_OptionsAreParsedCorrectly() Assert.Equal(0, result.Errors.Count); Assert.Equal(0, result.UnmatchedTokens.Count); Assert.Equal(0, result.UnmatchedTokens.Count); - result.GetValue(FormatCommandCommon.NoRestoreOption); - Assert.Collection(result.GetValue(FormatCommandCommon.IncludeOption), + result.GetValueForOption("--no-restore"); + Assert.Collection(result.GetValueForOption>("--include"), i0 => Assert.Equal("include1", i0), i1 => Assert.Equal("include2", i1)); - Assert.Collection(result.GetValue(FormatCommandCommon.ExcludeOption), + Assert.Collection(result.GetValueForOption>("--exclude"), i0 => Assert.Equal("exclude1", i0), i1 => Assert.Equal("exclude2", i1)); - Assert.True(result.GetValue(FormatCommandCommon.VerifyNoChanges)); - Assert.Equal("binary-log-path", result.GetValue(FormatCommandCommon.BinarylogOption)); - Assert.Equal("report", result.GetValue(FormatCommandCommon.ReportOption)); - Assert.Equal("detailed", result.GetValue(FormatCommandCommon.VerbosityOption)); - Assert.True(result.GetValue(FormatCommandCommon.IncludeGeneratedOption)); + Assert.True(result.GetValueForOption("--verify-no-changes")); + Assert.Equal("binary-log-path", result.GetValueForOption("--binarylog")); + Assert.Equal("report", result.GetValueForOption("--report")); + Assert.Equal("detailed", result.GetValueForOption("--verbosity")); + Assert.True(result.GetValueForOption("--include-generated")); } [Fact] @@ -80,7 +83,7 @@ public void CommandLine_ProjectArgument_Simple() // Assert Assert.Equal(0, result.Errors.Count); - Assert.Equal("workspaceValue", result.GetValue(FormatCommandCommon.SlnOrProjectArgument)); + Assert.Equal("workspaceValue", result.GetValueForArgument(FormatCommandCommon.SlnOrProjectArgument)); } [Fact] @@ -94,8 +97,8 @@ public void CommandLine_ProjectArgument_WithOption_AfterArgument() // Assert Assert.Equal(0, result.Errors.Count); - Assert.Equal("workspaceValue", result.GetValue(FormatCommandCommon.SlnOrProjectArgument)); - Assert.Equal("detailed", result.GetValue(FormatCommandCommon.VerbosityOption)); + Assert.Equal("workspaceValue", result.GetValueForArgument(FormatCommandCommon.SlnOrProjectArgument)); + Assert.Equal("detailed", result.GetValueForOption("--verbosity")); } [Fact] @@ -109,8 +112,8 @@ public void CommandLine_ProjectArgument_WithOption_BeforeArgument() // Assert Assert.Equal(0, result.Errors.Count); - Assert.Equal("workspaceValue", result.GetValue(FormatCommandCommon.SlnOrProjectArgument)); - Assert.Equal("detailed", result.GetValue(FormatCommandCommon.VerbosityOption)); + Assert.Equal("workspaceValue", result.GetValueForArgument(FormatCommandCommon.SlnOrProjectArgument)); + Assert.Equal("detailed", result.GetValueForOption("--verbosity")); } [Fact] @@ -176,7 +179,7 @@ public void CommandLine_BinaryLog_DoesNotFailIfPathNotSpecified() // Assert Assert.Equal(0, result.Errors.Count); - Assert.NotNull(result.GetResult(FormatCommandCommon.BinarylogOption)); + Assert.True(result.WasOptionUsed("--binarylog")); } [Fact] @@ -190,7 +193,7 @@ public void CommandLine_BinaryLog_DoesNotFailIfPathIsSpecified() // Assert Assert.Equal(0, result.Errors.Count); - Assert.NotNull(result.GetResult(FormatCommandCommon.BinarylogOption)); + Assert.True(result.WasOptionUsed("--binarylog")); } [Fact]