diff --git a/src/Tools/Extensions.ApiDescription.Server/src/build/Microsoft.Extensions.ApiDescription.Server.props b/src/Tools/Extensions.ApiDescription.Server/src/build/Microsoft.Extensions.ApiDescription.Server.props index cef30558147a..71784e621a88 100644 --- a/src/Tools/Extensions.ApiDescription.Server/src/build/Microsoft.Extensions.ApiDescription.Server.props +++ b/src/Tools/Extensions.ApiDescription.Server/src/build/Microsoft.Extensions.ApiDescription.Server.props @@ -1,12 +1,13 @@ - + diff --git a/src/Tools/GetDocumentInsider/src/AnsiConsole.cs b/src/Tools/GetDocumentInsider/src/AnsiConsole.cs deleted file mode 100644 index 306b4ff45225..000000000000 --- a/src/Tools/GetDocumentInsider/src/AnsiConsole.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; - -namespace Microsoft.Extensions.ApiDescription.Tool -{ - internal class AnsiConsole - { - public static readonly AnsiTextWriter _out = new AnsiTextWriter(Console.Out); - - public static void WriteLine(string text) - => _out.WriteLine(text); - } -} diff --git a/src/Tools/GetDocumentInsider/src/AnsiConstants.cs b/src/Tools/GetDocumentInsider/src/AnsiConstants.cs deleted file mode 100644 index b54e15b75135..000000000000 --- a/src/Tools/GetDocumentInsider/src/AnsiConstants.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -namespace Microsoft.Extensions.ApiDescription.Tool -{ - internal static class AnsiConstants - { - public const string Reset = "\x1b[22m\x1b[39m"; - public const string Bold = "\x1b[1m"; - public const string Dark = "\x1b[22m"; - public const string Black = "\x1b[30m"; - public const string Red = "\x1b[31m"; - public const string Green = "\x1b[32m"; - public const string Yellow = "\x1b[33m"; - public const string Blue = "\x1b[34m"; - public const string Magenta = "\x1b[35m"; - public const string Cyan = "\x1b[36m"; - public const string Gray = "\x1b[37m"; - } -} diff --git a/src/Tools/GetDocumentInsider/src/AnsiTextWriter.cs b/src/Tools/GetDocumentInsider/src/AnsiTextWriter.cs deleted file mode 100644 index 066f711e2786..000000000000 --- a/src/Tools/GetDocumentInsider/src/AnsiTextWriter.cs +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Diagnostics; -using System.IO; -using System.Text.RegularExpressions; - -namespace Microsoft.Extensions.ApiDescription.Tool -{ - internal class AnsiTextWriter - { - private readonly TextWriter _writer; - - public AnsiTextWriter(TextWriter writer) => _writer = writer; - - public void WriteLine(string text) - { - Interpret(text); - _writer.Write(Environment.NewLine); - } - - private void Interpret(string value) - { - var matches = Regex.Matches(value, "\x1b\\[([0-9]+)?m"); - - var start = 0; - foreach (Match match in matches) - { - var length = match.Index - start; - if (length != 0) - { - _writer.Write(value.Substring(start, length)); - } - - Apply(match.Groups[1].Value); - - start = match.Index + match.Length; - } - - if (start != value.Length) - { - _writer.Write(value.Substring(start)); - } - } - - private static void Apply(string parameter) - { - switch (parameter) - { - case "1": - ApplyBold(); - break; - - case "22": - ResetBold(); - break; - - case "30": - ApplyColor(ConsoleColor.Black); - break; - - case "31": - ApplyColor(ConsoleColor.DarkRed); - break; - - case "32": - ApplyColor(ConsoleColor.DarkGreen); - break; - - case "33": - ApplyColor(ConsoleColor.DarkYellow); - break; - - case "34": - ApplyColor(ConsoleColor.DarkBlue); - break; - - case "35": - ApplyColor(ConsoleColor.DarkMagenta); - break; - - case "36": - ApplyColor(ConsoleColor.DarkCyan); - break; - - case "37": - ApplyColor(ConsoleColor.Gray); - break; - - case "39": - ResetColor(); - break; - - default: - Debug.Fail("Unsupported parameter: " + parameter); - break; - } - } - - private static void ApplyBold() - => Console.ForegroundColor = (ConsoleColor)((int)Console.ForegroundColor | 8); - - private static void ResetBold() - => Console.ForegroundColor = (ConsoleColor)((int)Console.ForegroundColor & 7); - - private static void ApplyColor(ConsoleColor color) - { - var wasBold = ((int)Console.ForegroundColor & 8) != 0; - - Console.ForegroundColor = color; - - if (wasBold) - { - ApplyBold(); - } - } - - private static void ResetColor() - { - var wasBold = ((int)Console.ForegroundColor & 8) != 0; - - Console.ResetColor(); - - if (wasBold) - { - ApplyBold(); - } - } - } -} diff --git a/src/Tools/GetDocumentInsider/src/CommandLineUtils/CommandArgument.cs b/src/Tools/GetDocumentInsider/src/CommandLineUtils/CommandArgument.cs deleted file mode 100644 index 3346ea0ecb82..000000000000 --- a/src/Tools/GetDocumentInsider/src/CommandLineUtils/CommandArgument.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Collections.Generic; -using System.Linq; - -namespace Microsoft.DotNet.Cli.CommandLine -{ - internal class CommandArgument - { - public CommandArgument() => Values = new List(); - - public string Name { get; set; } - public string Description { get; set; } - public List Values { get; private set; } - public bool MultipleValues { get; set; } - public string Value => Values.FirstOrDefault(); - } -} diff --git a/src/Tools/GetDocumentInsider/src/CommandLineUtils/CommandLineApplication.cs b/src/Tools/GetDocumentInsider/src/CommandLineUtils/CommandLineApplication.cs deleted file mode 100644 index facbb68ad05a..000000000000 --- a/src/Tools/GetDocumentInsider/src/CommandLineUtils/CommandLineApplication.cs +++ /dev/null @@ -1,604 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Microsoft.DotNet.Cli.CommandLine -{ - internal class CommandLineApplication - { - private enum ParseOptionResult - { - Succeeded, - ShowHelp, - ShowVersion, - UnexpectedArgs, - } - - // Indicates whether the parser should throw an exception when it runs into an unexpected argument. - // If this field is set to false, the parser will stop parsing when it sees an unexpected argument, and all - // remaining arguments, including the first unexpected argument, will be stored in RemainingArguments property. - private readonly bool _throwOnUnexpectedArg; - - public CommandLineApplication(bool throwOnUnexpectedArg = true) - { - _throwOnUnexpectedArg = throwOnUnexpectedArg; - Options = new List(); - Arguments = new List(); - Commands = new List(); - RemainingArguments = new List(); - Invoke = () => 0; - } - - public CommandLineApplication Parent { get; set; } - public string Name { get; set; } - public string FullName { get; set; } - public string Syntax { get; set; } - public string Description { get; set; } - public List Options { get; private set; } - public CommandOption OptionHelp { get; private set; } - public CommandOption OptionVersion { get; private set; } - public List Arguments { get; private set; } - public List RemainingArguments { get; private set; } - public bool IsShowingInformation { get; protected set; } // Is showing help or version? - public Func Invoke { get; set; } - public Func LongVersionGetter { get; set; } - public Func ShortVersionGetter { get; set; } - public List Commands { get; private set; } - public bool HandleResponseFiles { get; set; } - public bool AllowArgumentSeparator { get; set; } - public bool HandleRemainingArguments { get; set; } - public string ArgumentSeparatorHelpText { get; set; } - - public CommandLineApplication Command(string name, bool throwOnUnexpectedArg = true) - => Command(name, _ => { }, throwOnUnexpectedArg); - - public CommandLineApplication Command(string name, Action configuration, - bool throwOnUnexpectedArg = true) - { - var command = new CommandLineApplication(throwOnUnexpectedArg) { Name = name, Parent = this }; - Commands.Add(command); - configuration(command); - return command; - } - - public CommandOption Option(string template, string description, CommandOptionType optionType) - => Option(template, description, optionType, _ => { }); - - public CommandOption Option(string template, string description, CommandOptionType optionType, Action configuration) - { - var option = new CommandOption(template, optionType) { Description = description }; - Options.Add(option); - configuration(option); - return option; - } - - public CommandArgument Argument(string name, string description, bool multipleValues = false) - => Argument(name, description, _ => { }, multipleValues); - - public CommandArgument Argument(string name, string description, Action configuration, bool multipleValues = false) - { - var lastArg = Arguments.LastOrDefault(); - if (lastArg != null && lastArg.MultipleValues) - { - var message = string.Format("The last argument '{0}' accepts multiple values. No more argument can be added.", - lastArg.Name); - throw new InvalidOperationException(message); - } - - var argument = new CommandArgument { Name = name, Description = description, MultipleValues = multipleValues }; - Arguments.Add(argument); - configuration(argument); - return argument; - } - - public void OnExecute(Func invoke) => Invoke = invoke; - - public void OnExecute(Func> invoke) => Invoke = () => invoke().Result; - - public int Execute(params string[] args) - { - var command = this; - IEnumerator arguments = null; - - if (HandleResponseFiles) - { - args = ExpandResponseFiles(args).ToArray(); - } - - for (var index = 0; index < args.Length; index++) - { - var arg = args[index]; - - var isLongOption = arg.StartsWith("--"); - if (isLongOption || arg.StartsWith("-")) - { - var result = ParseOption(isLongOption, command, args, ref index, out var option); - if (result == ParseOptionResult.ShowHelp) - { - command.ShowHelp(); - return 0; - } - else if (result == ParseOptionResult.ShowVersion) - { - command.ShowVersion(); - return 0; - } - } - else - { - var subcommand = ParseSubCommand(arg, command); - if (subcommand != null) - { - command = subcommand; - } - else - { - if (arguments == null) - { - arguments = new CommandArgumentEnumerator(command.Arguments.GetEnumerator()); - } - - if (arguments.MoveNext()) - { - arguments.Current.Values.Add(arg); - } - else - { - HandleUnexpectedArg(command, args, index, argTypeName: "command or argument"); - } - } - } - } - - return command.Invoke(); - } - - private ParseOptionResult ParseOption( - bool isLongOption, - CommandLineApplication command, - string[] args, - ref int index, - out CommandOption option) - { - option = null; - var result = ParseOptionResult.Succeeded; - var arg = args[index]; - - var optionPrefixLength = isLongOption ? 2 : 1; - var optionComponents = arg.Substring(optionPrefixLength).Split(new[] { ':', '=' }, 2); - var optionName = optionComponents[0]; - - if (isLongOption) - { - option = command.Options.SingleOrDefault( - opt => string.Equals(opt.LongName, optionName, StringComparison.Ordinal)); - } - else - { - option = command.Options.SingleOrDefault( - opt => string.Equals(opt.ShortName, optionName, StringComparison.Ordinal)); - - if (option == null) - { - option = command.Options.SingleOrDefault( - opt => string.Equals(opt.SymbolName, optionName, StringComparison.Ordinal)); - } - } - - if (option == null) - { - if (isLongOption && string.IsNullOrEmpty(optionName) && - !command._throwOnUnexpectedArg && AllowArgumentSeparator) - { - // a stand-alone "--" is the argument separator, so skip it and - // handle the rest of the args as unexpected args - index++; - } - - HandleUnexpectedArg(command, args, index, argTypeName: "option"); - result = ParseOptionResult.UnexpectedArgs; - } - else if (command.OptionHelp == option) - { - result = ParseOptionResult.ShowHelp; - } - else if (command.OptionVersion == option) - { - result = ParseOptionResult.ShowVersion; - } - else - { - if (optionComponents.Length == 2) - { - if (!option.TryParse(optionComponents[1])) - { - command.ShowHint(); - throw new CommandParsingException(command, - $"Unexpected value '{optionComponents[1]}' for option '{optionName}'"); - } - } - else - { - if (option.OptionType == CommandOptionType.NoValue || - option.OptionType == CommandOptionType.BoolValue) - { - // No value is needed for this option - option.TryParse(null); - } - else - { - index++; - arg = args[index]; - if (!option.TryParse(arg)) - { - command.ShowHint(); - throw new CommandParsingException(command, $"Unexpected value '{arg}' for option '{optionName}'"); - - } - } - } - } - - return result; - } - - private static CommandLineApplication ParseSubCommand(string arg, CommandLineApplication command) - { - foreach (var subcommand in command.Commands) - { - if (string.Equals(subcommand.Name, arg, StringComparison.OrdinalIgnoreCase)) - { - return subcommand; - } - } - - return null; - } - - // Helper method that adds a help option - public CommandOption HelpOption(string template) - { - // Help option is special because we stop parsing once we see it - // So we store it separately for further use - OptionHelp = Option(template, "Show help information", CommandOptionType.NoValue); - - return OptionHelp; - } - - public CommandOption VersionOption(string template, - string shortFormVersion, - string longFormVersion = null) - { - if (longFormVersion == null) - { - return VersionOption(template, () => shortFormVersion); - } - else - { - return VersionOption(template, () => shortFormVersion, () => longFormVersion); - } - } - - // Helper method that adds a version option - public CommandOption VersionOption(string template, - Func shortFormVersionGetter, - Func longFormVersionGetter = null) - { - // Version option is special because we stop parsing once we see it - // So we store it separately for further use - OptionVersion = Option(template, "Show version information", CommandOptionType.NoValue); - ShortVersionGetter = shortFormVersionGetter; - LongVersionGetter = longFormVersionGetter ?? shortFormVersionGetter; - - return OptionVersion; - } - - // Show short hint that reminds users to use help option - public void ShowHint() - { - if (OptionHelp != null) - { - Console.WriteLine(string.Format("Specify --{0} for a list of available options and commands.", OptionHelp.LongName)); - } - } - - // Show full help - public void ShowHelp(string commandName = null) - { - var headerBuilder = new StringBuilder("Usage:"); - var usagePrefixLength = headerBuilder.Length; - for (var cmd = this; cmd != null; cmd = cmd.Parent) - { - cmd.IsShowingInformation = true; - if (cmd != this && cmd.Arguments.Any()) - { - var args = string.Join(" ", cmd.Arguments.Select(arg => arg.Name)); - headerBuilder.Insert(usagePrefixLength, string.Format(" {0} {1}", cmd.Name, args)); - } - else - { - headerBuilder.Insert(usagePrefixLength, string.Format(" {0}", cmd.Name)); - } - } - - CommandLineApplication target; - - if (commandName == null || string.Equals(Name, commandName, StringComparison.OrdinalIgnoreCase)) - { - target = this; - } - else - { - target = Commands.SingleOrDefault(cmd => string.Equals(cmd.Name, commandName, StringComparison.OrdinalIgnoreCase)); - - if (target != null) - { - headerBuilder.AppendFormat(" {0}", commandName); - } - else - { - // The command name is invalid so don't try to show help for something that doesn't exist - target = this; - } - - } - - var optionsBuilder = new StringBuilder(); - var commandsBuilder = new StringBuilder(); - var argumentsBuilder = new StringBuilder(); - var argumentSeparatorBuilder = new StringBuilder(); - - var maxArgLen = 0; - for (var cmd = target; cmd != null; cmd = cmd.Parent) - { - if (cmd.Arguments.Any()) - { - if (cmd == target) - { - headerBuilder.Append(" [arguments]"); - } - - if (argumentsBuilder.Length == 0) - { - argumentsBuilder.AppendLine(); - argumentsBuilder.AppendLine("Arguments:"); - } - - maxArgLen = Math.Max(maxArgLen, MaxArgumentLength(cmd.Arguments)); - } - } - - for (var cmd = target; cmd != null; cmd = cmd.Parent) - { - if (cmd.Arguments.Any()) - { - var outputFormat = " {0}{1}"; - foreach (var arg in cmd.Arguments) - { - argumentsBuilder.AppendFormat( - outputFormat, - arg.Name.PadRight(maxArgLen + 2), - arg.Description); - argumentsBuilder.AppendLine(); - } - } - } - - if (target.Options.Any()) - { - headerBuilder.Append(" [options]"); - - optionsBuilder.AppendLine(); - optionsBuilder.AppendLine("Options:"); - var maxOptLen = MaxOptionTemplateLength(target.Options); - var outputFormat = string.Format(" {{0, -{0}}}{{1}}", maxOptLen + 2); - foreach (var opt in target.Options) - { - optionsBuilder.AppendFormat(outputFormat, opt.Template, opt.Description); - optionsBuilder.AppendLine(); - } - } - - if (target.Commands.Any()) - { - headerBuilder.Append(" [command]"); - - commandsBuilder.AppendLine(); - commandsBuilder.AppendLine("Commands:"); - var maxCmdLen = MaxCommandLength(target.Commands); - var outputFormat = string.Format(" {{0, -{0}}}{{1}}", maxCmdLen + 2); - foreach (var cmd in target.Commands.OrderBy(c => c.Name)) - { - commandsBuilder.AppendFormat(outputFormat, cmd.Name, cmd.Description); - commandsBuilder.AppendLine(); - } - - if (OptionHelp != null) - { - commandsBuilder.AppendLine(); - commandsBuilder.AppendFormat("Use \"{0} [command] --help\" for more information about a command.", Name); - commandsBuilder.AppendLine(); - } - } - - if (target.AllowArgumentSeparator || target.HandleRemainingArguments) - { - if (target.AllowArgumentSeparator) - { - headerBuilder.Append(" [[--] ...]]"); - } - else - { - headerBuilder.Append(" [args]"); - } - - if (!string.IsNullOrEmpty(target.ArgumentSeparatorHelpText)) - { - argumentSeparatorBuilder.AppendLine(); - argumentSeparatorBuilder.AppendLine("Args:"); - argumentSeparatorBuilder.AppendLine($" {target.ArgumentSeparatorHelpText}"); - argumentSeparatorBuilder.AppendLine(); - } - } - - headerBuilder.AppendLine(); - - var nameAndVersion = new StringBuilder(); - nameAndVersion.AppendLine(GetFullNameAndVersion()); - nameAndVersion.AppendLine(); - - Console.Write("{0}{1}{2}{3}{4}{5}", nameAndVersion, headerBuilder, argumentsBuilder, optionsBuilder, commandsBuilder, argumentSeparatorBuilder); - } - - public void ShowVersion() - { - for (var cmd = this; cmd != null; cmd = cmd.Parent) - { - cmd.IsShowingInformation = true; - } - - Console.WriteLine(FullName); - Console.WriteLine(LongVersionGetter()); - } - - public string GetFullNameAndVersion() - => ShortVersionGetter == null ? FullName : string.Format("{0} {1}", FullName, ShortVersionGetter()); - - public void ShowRootCommandFullNameAndVersion() - { - var rootCmd = this; - while (rootCmd.Parent != null) - { - rootCmd = rootCmd.Parent; - } - - Console.WriteLine(rootCmd.GetFullNameAndVersion()); - Console.WriteLine(); - } - - private static int MaxOptionTemplateLength(IEnumerable options) - { - var maxLen = 0; - foreach (var opt in options) - { - maxLen = opt.Template.Length > maxLen ? opt.Template.Length : maxLen; - } - return maxLen; - } - - private static int MaxCommandLength(IEnumerable commands) - { - var maxLen = 0; - foreach (var cmd in commands) - { - maxLen = cmd.Name.Length > maxLen ? cmd.Name.Length : maxLen; - } - return maxLen; - } - - private static int MaxArgumentLength(IEnumerable arguments) - { - var maxLen = 0; - foreach (var arg in arguments) - { - maxLen = arg.Name.Length > maxLen ? arg.Name.Length : maxLen; - } - return maxLen; - } - - private static void HandleUnexpectedArg(CommandLineApplication command, string[] args, int index, string argTypeName) - { - if (command._throwOnUnexpectedArg) - { - command.ShowHint(); - throw new CommandParsingException(command, $"Unrecognized {argTypeName} '{args[index]}'"); - } - else - { - command.RemainingArguments.Add(args[index]); - } - } - - private IEnumerable ExpandResponseFiles(IEnumerable args) - { - foreach (var arg in args) - { - if (!arg.StartsWith("@", StringComparison.Ordinal)) - { - yield return arg; - } - else - { - var fileName = arg.Substring(1); - - var responseFileArguments = ParseResponseFile(fileName); - - // ParseResponseFile can suppress expanding this response file by - // returning null. In that case, we'll treat the response - // file token as a regular argument. - - if (responseFileArguments == null) - { - yield return arg; - } - else - { - foreach (var responseFileArgument in responseFileArguments) - { - yield return responseFileArgument.Trim(); - } - } - } - } - } - - private IEnumerable ParseResponseFile(string fileName) - { - if (!HandleResponseFiles) - { - return null; - } - - if (!File.Exists(fileName)) - { - throw new InvalidOperationException($"Response file '{fileName}' doesn't exist."); - } - - return File.ReadLines(fileName); - } - - private class CommandArgumentEnumerator : IEnumerator - { - private readonly IEnumerator _enumerator; - - public CommandArgumentEnumerator(IEnumerator enumerator) => _enumerator = enumerator; - - public CommandArgument Current => _enumerator.Current; - - object IEnumerator.Current => Current; - - public void Dispose() => _enumerator.Dispose(); - - public bool MoveNext() - { - if (Current == null || !Current.MultipleValues) - { - return _enumerator.MoveNext(); - } - - // If current argument allows multiple values, we don't move forward and - // all later values will be added to current CommandArgument.Values - return true; - } - - public void Reset() => _enumerator.Reset(); - } - } -} diff --git a/src/Tools/GetDocumentInsider/src/CommandLineUtils/CommandLineApplicationExtensions.cs b/src/Tools/GetDocumentInsider/src/CommandLineUtils/CommandLineApplicationExtensions.cs deleted file mode 100644 index 1c43455ee15c..000000000000 --- a/src/Tools/GetDocumentInsider/src/CommandLineUtils/CommandLineApplicationExtensions.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -namespace Microsoft.DotNet.Cli.CommandLine -{ - internal static class CommandLineApplicationExtensions - { - public static CommandOption Option(this CommandLineApplication command, string template, string description) - => command.Option( - template, - description, - template.IndexOf('<') != -1 - ? template.EndsWith(">...") - ? CommandOptionType.MultipleValue - : CommandOptionType.SingleValue - : CommandOptionType.NoValue); - } -} diff --git a/src/Tools/GetDocumentInsider/src/CommandLineUtils/CommandOption.cs b/src/Tools/GetDocumentInsider/src/CommandLineUtils/CommandOption.cs deleted file mode 100644 index 5ba4b78ae3f5..000000000000 --- a/src/Tools/GetDocumentInsider/src/CommandLineUtils/CommandOption.cs +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Microsoft.DotNet.Cli.CommandLine -{ - internal class CommandOption - { - public CommandOption(string template, CommandOptionType optionType) - { - Template = template; - OptionType = optionType; - Values = new List(); - - foreach (var part in Template.Split(new[] { ' ', '|' }, StringSplitOptions.RemoveEmptyEntries)) - { - if (part.StartsWith("--")) - { - LongName = part.Substring(2); - } - else if (part.StartsWith("-")) - { - var optName = part.Substring(1); - - // If there is only one char and it is not an English letter, it is a symbol option (e.g. "-?") - if (optName.Length == 1 && !IsEnglishLetter(optName[0])) - { - SymbolName = optName; - } - else - { - ShortName = optName; - } - } - else if (part.StartsWith("<") && part.EndsWith(">")) - { - ValueName = part.Substring(1, part.Length - 2); - } - else if (optionType == CommandOptionType.MultipleValue && part.StartsWith("<") && part.EndsWith(">...")) - { - ValueName = part.Substring(1, part.Length - 5); - } - else - { - throw new ArgumentException($"Invalid template pattern '{template}'", nameof(template)); - } - } - - if (string.IsNullOrEmpty(LongName) && string.IsNullOrEmpty(ShortName) && string.IsNullOrEmpty(SymbolName)) - { - throw new ArgumentException($"Invalid template pattern '{template}'", nameof(template)); - } - } - - public string Template { get; set; } - public string ShortName { get; set; } - public string LongName { get; set; } - public string SymbolName { get; set; } - public string ValueName { get; set; } - public string Description { get; set; } - public List Values { get; private set; } - public bool? BoolValue { get; private set; } - public CommandOptionType OptionType { get; private set; } - - public bool TryParse(string value) - { - switch (OptionType) - { - case CommandOptionType.MultipleValue: - Values.Add(value); - break; - case CommandOptionType.SingleValue: - if (Values.Any()) - { - return false; - } - Values.Add(value); - break; - case CommandOptionType.BoolValue: - if (Values.Any()) - { - return false; - } - - if (value == null) - { - // add null to indicate that the option was present, but had no value - Values.Add(null); - BoolValue = true; - } - else - { - if (!bool.TryParse(value, out var boolValue)) - { - return false; - } - - Values.Add(value); - BoolValue = boolValue; - } - break; - case CommandOptionType.NoValue: - if (value != null) - { - return false; - } - // Add a value to indicate that this option was specified - Values.Add("on"); - break; - default: - break; - } - return true; - } - - public bool HasValue() => Values.Any(); - - public string Value() => HasValue() ? Values[0] : null; - - private static bool IsEnglishLetter(char c) => (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); - } -} diff --git a/src/Tools/GetDocumentInsider/src/CommandLineUtils/CommandOptionType.cs b/src/Tools/GetDocumentInsider/src/CommandLineUtils/CommandOptionType.cs deleted file mode 100644 index 5f7d37f02979..000000000000 --- a/src/Tools/GetDocumentInsider/src/CommandLineUtils/CommandOptionType.cs +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -namespace Microsoft.DotNet.Cli.CommandLine -{ - internal enum CommandOptionType - { - MultipleValue, - SingleValue, - BoolValue, - NoValue - } -} diff --git a/src/Tools/GetDocumentInsider/src/CommandLineUtils/CommandParsingException.cs b/src/Tools/GetDocumentInsider/src/CommandLineUtils/CommandParsingException.cs deleted file mode 100644 index c735ecbf12d6..000000000000 --- a/src/Tools/GetDocumentInsider/src/CommandLineUtils/CommandParsingException.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; - -namespace Microsoft.DotNet.Cli.CommandLine -{ - internal class CommandParsingException : Exception - { - public CommandParsingException(CommandLineApplication command, string message) - : base(message) => Command = command; - - public CommandLineApplication Command { get; } - } -} diff --git a/src/Tools/GetDocumentInsider/src/Commands/CommandBase.cs b/src/Tools/GetDocumentInsider/src/Commands/CommandBase.cs index b5eddd978c88..2c0f66fa3f78 100644 --- a/src/Tools/GetDocumentInsider/src/Commands/CommandBase.cs +++ b/src/Tools/GetDocumentInsider/src/Commands/CommandBase.cs @@ -1,26 +1,43 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using Microsoft.DotNet.Cli.CommandLine; +using System; +using Microsoft.Extensions.CommandLineUtils; +using Microsoft.Extensions.Tools.Internal; namespace Microsoft.Extensions.ApiDescription.Tool.Commands { internal abstract class CommandBase { + private readonly IConsole _console; + + public bool IsQuiet { get; private set; } + + public bool IsVerbose { get; private set; } + + protected IReporter Reporter { get; private set; } + + protected CommandBase(IConsole console) + { + _console = console ?? throw new ArgumentNullException(nameof(console)); + Reporter = new ConsoleReporter(_console); + } + public virtual void Configure(CommandLineApplication command) { - var verbose = command.Option("-v|--verbose", Resources.VerboseDescription); - var noColor = command.Option("--no-color", Resources.NoColorDescription); var prefixOutput = command.Option("--prefix-output", Resources.PrefixDescription); - - command.HandleResponseFiles = true; + var quiet = command.Option("-q|--quiet", Resources.QuietDescription); + var verbose = command.VerboseOption(); command.OnExecute( () => { - Reporter.IsVerbose = verbose.HasValue(); - Reporter.NoColor = noColor.HasValue(); - Reporter.PrefixOutput = prefixOutput.HasValue(); + IsQuiet = quiet.HasValue(); + IsVerbose = verbose.HasValue() || CliContext.IsGlobalVerbose(); + ReporterExtensions.PrefixOutput = prefixOutput.HasValue(); + + // Update the reporter now that we know the option values. + Reporter = new ConsoleReporter(_console, IsVerbose, IsQuiet); Validate(); @@ -30,6 +47,10 @@ public virtual void Configure(CommandLineApplication command) protected virtual void Validate() { + if (IsQuiet && IsVerbose) + { + throw new CommandException(Resources.QuietAndVerboseSpecified); + } } protected virtual int Execute() => 0; diff --git a/src/Tools/GetDocumentInsider/src/Commands/GetDocumentCommand.cs b/src/Tools/GetDocumentInsider/src/Commands/GetDocumentCommand.cs index 145db50da6e0..cdebc8618865 100644 --- a/src/Tools/GetDocumentInsider/src/Commands/GetDocumentCommand.cs +++ b/src/Tools/GetDocumentInsider/src/Commands/GetDocumentCommand.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; @@ -8,7 +8,8 @@ #if NETCOREAPP2_1 using System.Runtime.Loader; #endif -using Microsoft.DotNet.Cli.CommandLine; +using Microsoft.Extensions.CommandLineUtils; +using Microsoft.Extensions.Tools.Internal; namespace Microsoft.Extensions.ApiDescription.Tool.Commands { @@ -17,6 +18,10 @@ internal class GetDocumentCommand : ProjectCommandBase private CommandOption _fileListPath; private CommandOption _output; + public GetDocumentCommand(IConsole console) : base(console) + { + } + public override void Configure(CommandLineApplication command) { base.Configure(command); @@ -124,13 +129,14 @@ protected override int Execute() FileListPath = _fileListPath.Value(), OutputDirectory = _output.Value(), ProjectName = ProjectName.Value(), + Reporter = Reporter, }; - return GetDocumentCommandWorker.Process(context); + return new GetDocumentCommandWorker(context).Process(); } catch (Exception ex) { - Console.Error.WriteLine(ex.ToString()); + Reporter.WriteError(ex.ToString()); return 2; } } diff --git a/src/Tools/GetDocumentInsider/src/Commands/GetDocumentCommandContext.cs b/src/Tools/GetDocumentInsider/src/Commands/GetDocumentCommandContext.cs index 3cf1da50d192..9ba3020f7db9 100644 --- a/src/Tools/GetDocumentInsider/src/Commands/GetDocumentCommandContext.cs +++ b/src/Tools/GetDocumentInsider/src/Commands/GetDocumentCommandContext.cs @@ -1,7 +1,8 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using Microsoft.Extensions.Tools.Internal; namespace Microsoft.Extensions.ApiDescription.Tool.Commands { @@ -17,5 +18,7 @@ public class GetDocumentCommandContext public string OutputDirectory { get; set; } public string ProjectName { get; set; } + + public IReporter Reporter { get; set; } } } diff --git a/src/Tools/GetDocumentInsider/src/Commands/GetDocumentCommandWorker.cs b/src/Tools/GetDocumentInsider/src/Commands/GetDocumentCommandWorker.cs index 6ad658888225..b7a9582855b9 100644 --- a/src/Tools/GetDocumentInsider/src/Commands/GetDocumentCommandWorker.cs +++ b/src/Tools/GetDocumentInsider/src/Commands/GetDocumentCommandWorker.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; @@ -8,6 +8,7 @@ using System.Text; using System.Threading.Tasks; using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Tools.Internal; namespace Microsoft.Extensions.ApiDescription.Tool.Commands { @@ -32,14 +33,23 @@ private static readonly Encoding UTF8EncodingWithoutBOM private static readonly Type[] GenerateMethodParameterTypes = new[] { typeof(string), typeof(TextWriter) }; private static readonly Type GenerateMethodReturnType = typeof(Task); - public static int Process(GetDocumentCommandContext context) + private readonly GetDocumentCommandContext _context; + private readonly IReporter _reporter; + + public GetDocumentCommandWorker(GetDocumentCommandContext context) + { + _context = context ?? throw new ArgumentNullException(nameof(context)); + _reporter = context.Reporter; + } + + public int Process() { - var assemblyName = new AssemblyName(context.AssemblyName); + var assemblyName = new AssemblyName(_context.AssemblyName); var assembly = Assembly.Load(assemblyName); var entryPointType = assembly.EntryPoint?.DeclaringType; if (entryPointType == null) { - Reporter.WriteError(Resources.FormatMissingEntryPoint(context.AssemblyPath)); + _reporter.WriteError(Resources.FormatMissingEntryPoint(_context.AssemblyPath)); return 3; } @@ -48,7 +58,7 @@ public static int Process(GetDocumentCommandContext context) var serviceFactory = HostFactoryResolver.ResolveServiceProviderFactory(assembly); if (serviceFactory == null) { - Reporter.WriteError(Resources.FormatMethodsNotFound( + _reporter.WriteError(Resources.FormatMethodsNotFound( HostFactoryResolver.BuildWebHost, HostFactoryResolver.CreateHostBuilder, HostFactoryResolver.CreateWebHostBuilder, @@ -60,7 +70,7 @@ public static int Process(GetDocumentCommandContext context) var services = serviceFactory(Array.Empty()); if (services == null) { - Reporter.WriteError(Resources.FormatServiceProviderNotFound( + _reporter.WriteError(Resources.FormatServiceProviderNotFound( typeof(IServiceProvider), HostFactoryResolver.BuildWebHost, HostFactoryResolver.CreateHostBuilder, @@ -70,7 +80,7 @@ public static int Process(GetDocumentCommandContext context) return 5; } - var success = GetDocuments(context, services); + var success = GetDocuments(services); if (!success) { return 6; @@ -78,14 +88,14 @@ public static int Process(GetDocumentCommandContext context) } catch (Exception ex) { - Reporter.WriteError(ex.ToString()); + _reporter.WriteError(ex.ToString()); return 7; } return 0; } - private static bool GetDocuments(GetDocumentCommandContext context, IServiceProvider services) + private bool GetDocuments(IServiceProvider services) { Type serviceType = null; foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) @@ -99,7 +109,7 @@ private static bool GetDocuments(GetDocumentCommandContext context, IServiceProv if (serviceType == null) { - Reporter.WriteError(Resources.FormatServiceTypeNotFound(DocumentService)); + _reporter.WriteError(Resources.FormatServiceTypeNotFound(DocumentService)); return false; } @@ -126,7 +136,7 @@ private static bool GetDocuments(GetDocumentCommandContext context, IServiceProv var service = services.GetService(serviceType); if (service == null) { - Reporter.WriteError(Resources.FormatServiceNotFound(DocumentService)); + _reporter.WriteError(Resources.FormatServiceNotFound(DocumentService)); return false; } @@ -138,14 +148,14 @@ private static bool GetDocuments(GetDocumentCommandContext context, IServiceProv // Write out the documents. var found = false; - Directory.CreateDirectory(context.OutputDirectory); + Directory.CreateDirectory(_context.OutputDirectory); var filePathList = new List(); foreach (var documentName in documentNames) { var filePath = GetDocument( documentName, - context.ProjectName, - context.OutputDirectory, + _context.ProjectName, + _context.OutputDirectory, generateMethod, service); if (filePath == null) @@ -158,26 +168,26 @@ private static bool GetDocuments(GetDocumentCommandContext context, IServiceProv } // Write out the cache file. - var stream = File.Create(context.FileListPath); + var stream = File.Create(_context.FileListPath); using var writer = new StreamWriter(stream); writer.WriteLine(string.Join(Environment.NewLine, filePathList)); if (!found) { - Reporter.WriteError(Resources.DocumentsNotFound); + _reporter.WriteError(Resources.DocumentsNotFound); } return found; } - private static string GetDocument( + private string GetDocument( string documentName, string projectName, string outputDirectory, MethodInfo generateMethod, object service) { - Reporter.WriteInformation(Resources.FormatGeneratingDocument(documentName)); + _reporter.WriteInformation(Resources.FormatGeneratingDocument(documentName)); using var stream = new MemoryStream(); using (var writer = new StreamWriter(stream, UTF8EncodingWithoutBOM, bufferSize: 1024, leaveOpen: true)) @@ -192,21 +202,21 @@ private static string GetDocument( var finished = resultTask.Wait(TimeSpan.FromMinutes(1)); if (!finished) { - Reporter.WriteError(Resources.FormatMethodTimedOut(GenerateMethodName, DocumentService, 1)); + _reporter.WriteError(Resources.FormatMethodTimedOut(GenerateMethodName, DocumentService, 1)); return null; } } if (stream.Length == 0L) { - Reporter.WriteError( + _reporter.WriteError( Resources.FormatMethodWroteNoContent(GenerateMethodName, DocumentService, documentName)); return null; } var filePath = GetDocumentPath(documentName, projectName, outputDirectory); - Reporter.WriteInformation(Resources.FormatWritingDocument(documentName, filePath)); + _reporter.WriteInformation(Resources.FormatWritingDocument(documentName, filePath)); try { stream.Position = 0L; @@ -256,24 +266,24 @@ private static string GetDocumentPath(string documentName, string projectName, s return path; } - private static MethodInfo GetMethod(string methodName, Type type, Type[] parameterTypes, Type returnType) + private MethodInfo GetMethod(string methodName, Type type, Type[] parameterTypes, Type returnType) { var method = type.GetMethod(methodName, parameterTypes); if (method == null) { - Reporter.WriteError(Resources.FormatMethodNotFound(methodName, type)); + _reporter.WriteError(Resources.FormatMethodNotFound(methodName, type)); return null; } if (method.IsStatic) { - Reporter.WriteError(Resources.FormatMethodIsStatic(methodName, type)); + _reporter.WriteError(Resources.FormatMethodIsStatic(methodName, type)); return null; } if (!returnType.IsAssignableFrom(method.ReturnType)) { - Reporter.WriteError( + _reporter.WriteError( Resources.FormatMethodReturnTypeUnsupported(methodName, type, method.ReturnType, returnType)); return null; @@ -282,12 +292,12 @@ private static MethodInfo GetMethod(string methodName, Type type, Type[] paramet return method; } - private static object InvokeMethod(MethodInfo method, object instance, object[] arguments) + private object InvokeMethod(MethodInfo method, object instance, object[] arguments) { var result = method.Invoke(instance, arguments); if (result == null) { - Reporter.WriteError( + _reporter.WriteError( Resources.FormatMethodReturnedNull(method.Name, method.DeclaringType, method.ReturnType)); } diff --git a/src/Tools/GetDocumentInsider/src/Commands/HelpCommandBase.cs b/src/Tools/GetDocumentInsider/src/Commands/HelpCommandBase.cs index bab529d82709..06dcdb0664e2 100644 --- a/src/Tools/GetDocumentInsider/src/Commands/HelpCommandBase.cs +++ b/src/Tools/GetDocumentInsider/src/Commands/HelpCommandBase.cs @@ -1,16 +1,21 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using Microsoft.DotNet.Cli.CommandLine; +using Microsoft.Extensions.CommandLineUtils; +using Microsoft.Extensions.Tools.Internal; namespace Microsoft.Extensions.ApiDescription.Tool.Commands { internal class HelpCommandBase : CommandBase { + public HelpCommandBase(IConsole console) : base(console) + { + } + public override void Configure(CommandLineApplication command) { - command.HelpOption("-h|--help"); - command.VersionOption("--version", ProductInfo.GetVersion); + command.HelpOption(); + command.VersionOptionFromAssemblyAttributes(); base.Configure(command); } } diff --git a/src/Tools/GetDocumentInsider/src/Commands/ProjectCommandBase.cs b/src/Tools/GetDocumentInsider/src/Commands/ProjectCommandBase.cs index f6f3b2094575..76e9087a06e9 100644 --- a/src/Tools/GetDocumentInsider/src/Commands/ProjectCommandBase.cs +++ b/src/Tools/GetDocumentInsider/src/Commands/ProjectCommandBase.cs @@ -1,12 +1,17 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using Microsoft.DotNet.Cli.CommandLine; +using Microsoft.Extensions.CommandLineUtils; +using Microsoft.Extensions.Tools.Internal; namespace Microsoft.Extensions.ApiDescription.Tool.Commands { internal abstract class ProjectCommandBase : HelpCommandBase { + public ProjectCommandBase(IConsole console) : base(console) + { + } + public CommandOption AssemblyPath { get; private set; } public CommandOption ProjectName { get; private set; } diff --git a/src/Tools/GetDocumentInsider/src/GetDocumentInsider.csproj b/src/Tools/GetDocumentInsider/src/GetDocumentInsider.csproj index 57732f4774ce..120eab2a8ce3 100644 --- a/src/Tools/GetDocumentInsider/src/GetDocumentInsider.csproj +++ b/src/Tools/GetDocumentInsider/src/GetDocumentInsider.csproj @@ -1,4 +1,4 @@ - + GetDocument.Insider GetDocument Command-line Tool inside man @@ -15,6 +15,9 @@ + + + diff --git a/src/Tools/GetDocumentInsider/src/ProductInfo.cs b/src/Tools/GetDocumentInsider/src/ProductInfo.cs deleted file mode 100644 index c57bc65d105f..000000000000 --- a/src/Tools/GetDocumentInsider/src/ProductInfo.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Reflection; - -namespace Microsoft.Extensions.ApiDescription.Tool -{ - internal static class ProductInfo - { - public static string GetVersion() - => typeof(ProductInfo) - .Assembly - .GetCustomAttribute() - .InformationalVersion; - } -} diff --git a/src/Tools/GetDocumentInsider/src/Program.cs b/src/Tools/GetDocumentInsider/src/Program.cs index b83e9b0d8dac..003bded0d75d 100644 --- a/src/Tools/GetDocumentInsider/src/Program.cs +++ b/src/Tools/GetDocumentInsider/src/Program.cs @@ -1,47 +1,24 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System; -using System.Text; -using Microsoft.DotNet.Cli.CommandLine; using Microsoft.Extensions.ApiDescription.Tool.Commands; +using Microsoft.Extensions.Tools.Internal; namespace Microsoft.Extensions.ApiDescription.Tool { - internal static class Program + internal class Program : ProgramBase { - private static int Main(string[] args) + public Program(IConsole console) : base(console) { - if (Console.IsOutputRedirected) - { - Console.OutputEncoding = Encoding.UTF8; - } - - var app = new CommandLineApplication() - { - FullName = Resources.CommandFullName, - Name = Resources.CommandFullName, - }; + } - new GetDocumentCommand().Configure(app); + private static int Main(string[] args) + { + DebugHelper.HandleDebugSwitch(ref args); - try - { - return app.Execute(args); - } - catch (Exception ex) - { - if (ex is CommandException || ex is CommandParsingException) - { - Reporter.WriteError(ex.Message); - } - else - { - Reporter.WriteError(ex.ToString()); - } + var console = GetConsole(); - return 1; - } + return new Program(console).Run(args, new GetDocumentCommand(console), throwOnUnexpectedArg: true); } } } diff --git a/src/Tools/GetDocumentInsider/src/ProgramBase.cs b/src/Tools/GetDocumentInsider/src/ProgramBase.cs new file mode 100644 index 000000000000..46daf32ef736 --- /dev/null +++ b/src/Tools/GetDocumentInsider/src/ProgramBase.cs @@ -0,0 +1,68 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Text; +using Microsoft.Extensions.ApiDescription.Tool.Commands; +using Microsoft.Extensions.CommandLineUtils; +using Microsoft.Extensions.Tools.Internal; + +namespace Microsoft.Extensions.ApiDescription.Tool +{ + internal abstract class ProgramBase + { + private readonly IConsole _console; + private readonly IReporter _reporter; + + public ProgramBase(IConsole console) + { + _console = console ?? throw new ArgumentNullException(nameof(console)); + _reporter = new ConsoleReporter(_console, verbose: false, quiet: false); + } + + protected static IConsole GetConsole() + { + var console = PhysicalConsole.Singleton; + if (console.IsOutputRedirected) + { + Console.OutputEncoding = Encoding.UTF8; + } + + return console; + } + + protected int Run(string[] args, CommandBase command, bool throwOnUnexpectedArg) + { + try + { + // AllowArgumentSeparator and continueAfterUnexpectedArg are ignored when !throwOnUnexpectedArg _except_ + // AllowArgumentSeparator=true changes the help text (ignoring throwOnUnexpectedArg). + var app = new CommandLineApplication(throwOnUnexpectedArg, continueAfterUnexpectedArg: true) + { + AllowArgumentSeparator = !throwOnUnexpectedArg, + Error = _console.Error, + FullName = Resources.CommandFullName, + Name = Resources.CommandFullName, + Out = _console.Out, + }; + + command.Configure(app); + + return app.Execute(args); + } + catch (Exception ex) + { + if (ex is CommandException || ex is CommandParsingException) + { + _reporter.WriteError(ex.Message); + } + else + { + _reporter.WriteError(ex.ToString()); + } + + return 1; + } + } + } +} diff --git a/src/Tools/GetDocumentInsider/src/Reporter.cs b/src/Tools/GetDocumentInsider/src/Reporter.cs deleted file mode 100644 index e0b9aa62fe65..000000000000 --- a/src/Tools/GetDocumentInsider/src/Reporter.cs +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Linq; -using static Microsoft.Extensions.ApiDescription.Tool.AnsiConstants; - -namespace Microsoft.Extensions.ApiDescription.Tool -{ - internal static class Reporter - { - private static AnsiTextWriter Error = new AnsiTextWriter(Console.Error); - private static AnsiTextWriter Out = new AnsiTextWriter(Console.Out); - - public static bool IsVerbose { get; set; } - public static bool NoColor { get; set; } - public static bool PrefixOutput { get; set; } - - public static string Colorize(string value, Func colorizeFunc) - => NoColor ? value : colorizeFunc(value); - - public static void WriteError(string message) - => WriteLine(Prefix("error: ", Colorize(message, x => Bold + Red + x + Reset)), isError: true); - - public static void WriteWarning(string message) - => WriteLine(Prefix("warn: ", Colorize(message, x => Bold + Yellow + x + Reset))); - - public static void WriteInformation(string message) - => WriteLine(Prefix("info: ", message)); - - public static void WriteData(string message) - => WriteLine(Prefix("data: ", Colorize(message, x => Bold + Gray + x + Reset))); - - public static void WriteVerbose(string message) - { - if (IsVerbose) - { - WriteLine(Prefix("verbose: ", Colorize(message, x => Gray + x + Reset))); - } - } - - private static string Prefix(string prefix, string value) - => PrefixOutput - ? string.Join( - Environment.NewLine, - value.Split(new[] { Environment.NewLine }, StringSplitOptions.None).Select(l => prefix + l)) - : value; - - private static void WriteLine(string value, bool isError = false) - { - if (NoColor) - { - (isError ? Console.Error : Console.Out).WriteLine(value); - } - else - { - (isError ? Error : Out).WriteLine(value); - } - } - } -} diff --git a/src/Tools/GetDocumentInsider/src/ReporterExtensions.cs b/src/Tools/GetDocumentInsider/src/ReporterExtensions.cs new file mode 100644 index 000000000000..bc42593ffba2 --- /dev/null +++ b/src/Tools/GetDocumentInsider/src/ReporterExtensions.cs @@ -0,0 +1,40 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Linq; +using Microsoft.Extensions.Tools.Internal; + +namespace Microsoft.Extensions.ApiDescription.Tool +{ + internal static class ReporterExtensions + { + public static bool PrefixOutput { get; set; } + + public static void WriteError(this IReporter reporter, string message) + => reporter.Error(Prefix("error: ", message)); + + public static void WriteWarning(this IReporter reporter, string message) + => reporter.Warn(Prefix("warn: ", message)); + + public static void WriteInformation(this IReporter reporter, string message) + => reporter.Output(Prefix("info: ", message)); + + public static void WriteVerbose(this IReporter reporter, string message) + => reporter.Verbose(Prefix("verbose: ", message)); + + private static string Prefix(string prefix, string value) + { + if (PrefixOutput) + { + return string.Join( + Environment.NewLine, + value + .Split(new[] { Environment.NewLine }, StringSplitOptions.None) + .Select(l => prefix + l)); + } + + return value; + } + } +} diff --git a/src/Tools/GetDocumentInsider/src/Resources.resx b/src/Tools/GetDocumentInsider/src/Resources.resx index 63fe310c9a54..6a7e8287afca 100644 --- a/src/Tools/GetDocumentInsider/src/Resources.resx +++ b/src/Tools/GetDocumentInsider/src/Resources.resx @@ -123,9 +123,6 @@ Missing required option '--{0}'. - - Do not colorize console output. - The directory where the document files should be written. Required. @@ -191,4 +188,10 @@ Unable to find any registered documents. Update the 'Startup' class to register a document. Do not translate 'Startup' + + Cannot specify both '--quiet' and '--verbose' options. + + + Suppresses all output except warnings and errors. + \ No newline at end of file diff --git a/src/Tools/Shared/CommandLine/CommandLineApplicationExtensions.cs b/src/Tools/Shared/CommandLine/CommandLineApplicationExtensions.cs index 6206c861c03f..97189d0f38f6 100644 --- a/src/Tools/Shared/CommandLine/CommandLineApplicationExtensions.cs +++ b/src/Tools/Shared/CommandLine/CommandLineApplicationExtensions.cs @@ -21,6 +21,17 @@ public static void OnExecute(this CommandLineApplication app, Action action) return 0; }); + public static CommandOption Option(this CommandLineApplication command, string template, string description) + => command.Option( + template, + description, + template.IndexOf('<') != -1 + ? template.EndsWith(">...") ? CommandOptionType.MultipleValue : CommandOptionType.SingleValue + : CommandOptionType.NoValue); + + public static void VersionOptionFromAssemblyAttributes(this CommandLineApplication app) + => app.VersionOptionFromAssemblyAttributes(typeof(CommandLineApplicationExtensions).Assembly); + public static void VersionOptionFromAssemblyAttributes(this CommandLineApplication app, Assembly assembly) => app.VersionOption("--version", GetInformationalVersion(assembly)); diff --git a/src/Tools/Shared/CommandLine/ConsoleReporter.cs b/src/Tools/Shared/CommandLine/ConsoleReporter.cs index ffcd1e87053e..a94efc70a44c 100644 --- a/src/Tools/Shared/CommandLine/ConsoleReporter.cs +++ b/src/Tools/Shared/CommandLine/ConsoleReporter.cs @@ -8,7 +8,7 @@ namespace Microsoft.Extensions.Tools.Internal { public class ConsoleReporter : IReporter { - private object _writeLock = new object(); + private readonly object _writeLock = new object(); public ConsoleReporter(IConsole console) : this(console, verbose: false, quiet: false) diff --git a/src/Tools/dotnet-getdocument/src/Commands/InvokeCommand.cs b/src/Tools/dotnet-getdocument/src/Commands/InvokeCommand.cs index 52df8b1ae508..c7b4444a1e42 100644 --- a/src/Tools/dotnet-getdocument/src/Commands/InvokeCommand.cs +++ b/src/Tools/dotnet-getdocument/src/Commands/InvokeCommand.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; @@ -6,7 +6,8 @@ using System.IO; using System.Linq; using System.Runtime.Versioning; -using Microsoft.DotNet.Cli.CommandLine; +using Microsoft.Extensions.CommandLineUtils; +using Microsoft.Extensions.Tools.Internal; using Newtonsoft.Json; using Newtonsoft.Json.Linq; @@ -19,6 +20,10 @@ internal class InvokeCommand : HelpCommandBase private readonly ProjectOptions _projectOptions = new ProjectOptions(); private IList _args; + public InvokeCommand(IConsole console) : base(console) + { + } + public override void Configure(CommandLineApplication command) { base.Configure(command); @@ -75,7 +80,7 @@ protected override int Execute() targetFramework.Version)); } - executable = "dotnet"; + executable = DotNetMuxer.MuxerPathOrDefault(); toolsDirectory = Path.Combine(thisPath, "netcoreapp2.1"); args.Add("exec"); @@ -133,22 +138,22 @@ protected override int Execute() args.Add("--tools-directory"); args.Add(toolsDirectory); - if (Reporter.IsVerbose) + if (ReporterExtensions.PrefixOutput) { - args.Add("--verbose"); + args.Add("--prefix-output"); } - if (Reporter.NoColor) + if (IsQuiet) { - args.Add("--no-color"); + args.Add("--quiet"); } - if (Reporter.PrefixOutput) + if (IsVerbose) { - args.Add("--prefix-output"); + args.Add("--verbose"); } - return Exe.Run(executable, args); + return Exe.Run(executable, args, Reporter); } finally { diff --git a/src/Tools/dotnet-getdocument/src/Exe.cs b/src/Tools/dotnet-getdocument/src/Exe.cs index 68df26cc318a..bfdf3caf6f4f 100644 --- a/src/Tools/dotnet-getdocument/src/Exe.cs +++ b/src/Tools/dotnet-getdocument/src/Exe.cs @@ -1,10 +1,11 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Diagnostics; -using System.Text; +using Microsoft.Extensions.CommandLineUtils; +using Microsoft.Extensions.Tools.Internal; namespace Microsoft.Extensions.ApiDescription.Tool { @@ -13,12 +14,13 @@ internal static class Exe public static int Run( string executable, IReadOnlyList args, + IReporter reporter, string workingDirectory = null, bool interceptOutput = false) { - var arguments = ToArguments(args); + var arguments = ArgumentEscaper.EscapeAndConcatenate(args); - Reporter.WriteVerbose(executable + " " + arguments); + reporter.WriteVerbose(executable + " " + arguments); var startInfo = new ProcessStartInfo { @@ -32,100 +34,30 @@ public static int Run( startInfo.WorkingDirectory = workingDirectory; } - using (var process = Process.Start(startInfo)) + using var process = Process.Start(startInfo); + if (interceptOutput) { - if (interceptOutput) + string line; + while ((line = process.StandardOutput.ReadLine()) != null) { - string line; - while ((line = process.StandardOutput.ReadLine()) != null) - { - Reporter.WriteVerbose(line); - } + reporter.WriteVerbose(line); } - - // Follow precedent set in Razor integration tests and ensure process events and output are complete. - // https://github.com/aspnet/Razor/blob/d719920fdcc7d1db3a6f74cd5404d66fa098f057/test/Microsoft.NET.Sdk.Razor.Test/IntegrationTests/MSBuildProcessManager.cs#L91-L102 - // Timeout is double how long the inside man waits for the IDocumentProcessor to wrap up. - if (!process.WaitForExit((int)(TimeSpan.FromMinutes(2).TotalMilliseconds))) - { - process.Kill(); - - // Should be unreachable in almost every case. - throw new TimeoutException($"Process {executable} timed out after 2 minutes."); - } - - process.WaitForExit(); - - return process.ExitCode; } - } - private static string ToArguments(IReadOnlyList args) - { - var builder = new StringBuilder(); - for (var i = 0; i < args.Count; i++) + // Follow precedent set in Razor integration tests and ensure process events and output are complete. + // https://github.com/aspnet/Razor/blob/d719920fdcc7d1db3a6f74cd5404d66fa098f057/test/Microsoft.NET.Sdk.Razor.Test/IntegrationTests/MSBuildProcessManager.cs#L91-L102 + // Timeout is double how long the inside man waits for the IDocumentProcessor to wrap up. + if (!process.WaitForExit((int)(TimeSpan.FromMinutes(2).TotalMilliseconds))) { - if (i != 0) - { - builder.Append(" "); - } - - if (args[i].IndexOf(' ') == -1) - { - builder.Append(args[i]); - - continue; - } - - builder.Append("\""); - - var pendingBackslashes = 0; - for (var j = 0; j < args[i].Length; j++) - { - switch (args[i][j]) - { - case '\"': - if (pendingBackslashes != 0) - { - builder.Append('\\', pendingBackslashes * 2); - pendingBackslashes = 0; - } - builder.Append("\\\""); - break; - - case '\\': - pendingBackslashes++; - break; - - default: - if (pendingBackslashes != 0) - { - if (pendingBackslashes == 1) - { - builder.Append("\\"); - } - else - { - builder.Append('\\', pendingBackslashes * 2); - } - - pendingBackslashes = 0; - } + process.Kill(); - builder.Append(args[i][j]); - break; - } - } - - if (pendingBackslashes != 0) - { - builder.Append('\\', pendingBackslashes * 2); - } - - builder.Append("\""); + // Should be unreachable in almost every case. + throw new TimeoutException($"Process {executable} timed out after 2 minutes."); } - return builder.ToString(); + process.WaitForExit(); + + return process.ExitCode; } } } diff --git a/src/Tools/dotnet-getdocument/src/Program.cs b/src/Tools/dotnet-getdocument/src/Program.cs index 58502ce5b880..108c862e68a1 100644 --- a/src/Tools/dotnet-getdocument/src/Program.cs +++ b/src/Tools/dotnet-getdocument/src/Program.cs @@ -1,41 +1,24 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System; -using Microsoft.DotNet.Cli.CommandLine; using Microsoft.Extensions.ApiDescription.Tool.Commands; +using Microsoft.Extensions.Tools.Internal; namespace Microsoft.Extensions.ApiDescription.Tool { - internal static class Program + internal class Program : ProgramBase { - private static int Main(string[] args) + public Program(IConsole console) : base(console) { - var app = new CommandLineApplication(throwOnUnexpectedArg: false) - { - FullName = Resources.CommandFullName, - Name = Resources.CommandFullName, - }; + } - new InvokeCommand().Configure(app); + private static int Main(string[] args) + { + DebugHelper.HandleDebugSwitch(ref args); - try - { - return app.Execute(args); - } - catch (Exception ex) - { - if (ex is CommandException || ex is CommandParsingException) - { - Reporter.WriteError(ex.Message); - } - else - { - Reporter.WriteError(ex.ToString()); - } + var console = GetConsole(); - return 1; - } + return new Program(console).Run(args, new InvokeCommand(console), throwOnUnexpectedArg: false); } } } diff --git a/src/Tools/dotnet-getdocument/src/ProjectOptions.cs b/src/Tools/dotnet-getdocument/src/ProjectOptions.cs index 84b32968e992..10042cdc48a1 100644 --- a/src/Tools/dotnet-getdocument/src/ProjectOptions.cs +++ b/src/Tools/dotnet-getdocument/src/ProjectOptions.cs @@ -1,7 +1,7 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using Microsoft.DotNet.Cli.CommandLine; +using Microsoft.Extensions.CommandLineUtils; namespace Microsoft.Extensions.ApiDescription.Tool { diff --git a/src/Tools/dotnet-getdocument/src/Resources.resx b/src/Tools/dotnet-getdocument/src/Resources.resx index 08f92d039730..3f0b6440ad1f 100644 --- a/src/Tools/dotnet-getdocument/src/Resources.resx +++ b/src/Tools/dotnet-getdocument/src/Resources.resx @@ -1,17 +1,17 @@  - @@ -129,9 +129,6 @@ Project '{0}' targets framework '.NETStandard'. There is no runtime associated with this framework and projects targeting it cannot be executed directly. To use the dotnet-getdocument tool with this project, update this project to target .NET Core and / or .NET Framework. - - Do not colorize console output. - Prefix console output with logging level. @@ -159,4 +156,10 @@ The assembly path to use. Required. + + Cannot specify both '--quiet' and '--verbose' options. + + + Suppresses all output except warnings and errors. + \ No newline at end of file diff --git a/src/Tools/dotnet-getdocument/src/dotnet-getdocument.csproj b/src/Tools/dotnet-getdocument/src/dotnet-getdocument.csproj index dfe94dc2b9c4..c5494e3999f2 100644 --- a/src/Tools/dotnet-getdocument/src/dotnet-getdocument.csproj +++ b/src/Tools/dotnet-getdocument/src/dotnet-getdocument.csproj @@ -1,4 +1,4 @@ - + dotnet-getdocument GetDocument Command-line Tool outside man @@ -12,14 +12,14 @@ - + - - - + + +