|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Concurrent;
using System.CommandLine;
using Microsoft.DotNet.Cli.Commands.Test.IPC.Models;
using Microsoft.TemplateEngine.Cli.Help;
namespace Microsoft.DotNet.Cli.Commands.Test;
internal partial class MicrosoftTestingPlatformTestCommand
{
private readonly ConcurrentDictionary<string, CommandLineOptionMessage> _commandLineOptionNameToModuleNames = [];
private readonly ConcurrentDictionary<bool, List<(string, string[])>> _moduleNamesToCommandLineOptions = [];
private static readonly string Indent = " ";
public IEnumerable<Action<HelpContext>> CustomHelpLayout()
{
yield return (context) =>
{
WriteHelpOptions(context);
Console.WriteLine(CliCommandStrings.HelpWaitingForOptionsAndExtensions);
Run(context.ParseResult, isHelp: true);
if (_commandLineOptionNameToModuleNames.IsEmpty)
{
return;
}
var (builtInOptions, nonBuiltInOptions) = GetAllOptions(context.Command.Options);
Dictionary<bool, List<(string[], string[])>> moduleToMissingOptions = GetModulesToMissingOptions(_moduleNamesToCommandLineOptions, builtInOptions.Select(option => option.Name!), nonBuiltInOptions.Select(option => option.Name!));
_output!.WritePlatformAndExtensionOptions(context, builtInOptions!, nonBuiltInOptions!, moduleToMissingOptions);
};
}
private static void WriteHelpOptions(HelpContext context)
{
HelpBuilder.Default.SynopsisSection()(context);
context.Output.WriteLine();
WriteUsageSection(context);
context.Output.WriteLine();
HelpBuilder.Default.OptionsSection()(context);
context.Output.WriteLine();
}
private static void WriteUsageSection(HelpContext context)
{
context.Output.WriteLine(CliCommandStrings.CmdHelpUsageTitle);
context.Output.WriteLine(Indent + string.Join(" ", GetCustomUsageParts(context.Command)));
}
private static IEnumerable<string> GetCustomUsageParts(Command command, bool showOptions = true, bool showPlatformOptions = true, bool showExtensionOptions = true)
{
var parentCommands = new List<Command>();
var nextCommand = command;
while (nextCommand is not null)
{
parentCommands.Add(nextCommand);
nextCommand = nextCommand.Parents.FirstOrDefault(c => c is Command) as Command;
}
parentCommands.Reverse();
foreach (var parentCommand in parentCommands)
{
yield return parentCommand.Name;
}
if (showOptions)
{
yield return FormatHelpOption(CliCommandStrings.HelpOptions);
}
if (showPlatformOptions)
{
yield return FormatHelpOption(CliCommandStrings.HelpPlatformOptions);
}
if (showExtensionOptions)
{
yield return FormatHelpOption(CliCommandStrings.HelpExtensionOptions);
}
}
private static string FormatHelpOption(string option)
{
return $"[{option.Trim(':').ToLower()}]";
}
private void OnHelpRequested(CommandLineOptionMessages commandLineOptionMessages)
{
string moduleName = commandLineOptionMessages.ModulePath!;
List<string> builtInOptions = [];
List<string> nonBuiltInOptions = [];
foreach (CommandLineOptionMessage commandLineOption in commandLineOptionMessages.CommandLineOptionMessageList!)
{
if (commandLineOption.IsHidden.HasValue && commandLineOption.IsHidden.Value) continue;
if (commandLineOption.IsBuiltIn.HasValue && commandLineOption.IsBuiltIn.Value)
{
builtInOptions.Add(commandLineOption.Name!);
}
else
{
nonBuiltInOptions.Add(commandLineOption.Name!);
}
_commandLineOptionNameToModuleNames.AddOrUpdate(
commandLineOption.Name!,
commandLineOption,
(optionName, value) => (value));
}
_moduleNamesToCommandLineOptions.AddOrUpdate(true,
[(moduleName, builtInOptions.ToArray())],
(isBuiltIn, value) => [.. value, (moduleName, builtInOptions.ToArray())]);
_moduleNamesToCommandLineOptions.AddOrUpdate(false,
[(moduleName, nonBuiltInOptions.ToArray())],
(isBuiltIn, value) => [.. value, (moduleName, nonBuiltInOptions.ToArray())]);
}
private (List<CommandLineOptionMessage> BuiltInOptions, List<CommandLineOptionMessage> NonBuiltInOptions) GetAllOptions(IList<Option> commandOptions)
{
List<CommandLineOptionMessage> builtInOptions = [];
List<CommandLineOptionMessage> nonBuiltInOptions = [];
// Create a set of option names from the command's options for efficient lookup
var commandOptionNames = commandOptions.Select(o => o.Name.TrimStart('-')).ToHashSet(StringComparer.OrdinalIgnoreCase);
foreach (KeyValuePair<string, CommandLineOptionMessage> option in _commandLineOptionNameToModuleNames)
{
// Only include options that are NOT already present in the command's options
if (!commandOptionNames.Contains(option.Value.Name!))
{
if (option.Value.IsBuiltIn!.Value)
{
builtInOptions.Add(option.Value);
}
else
{
nonBuiltInOptions.Add(option.Value);
}
}
}
// Sort options alphabetically by name
builtInOptions.Sort((x, y) => string.Compare(x.Name, y.Name, StringComparison.OrdinalIgnoreCase));
nonBuiltInOptions.Sort((x, y) => string.Compare(x.Name, y.Name, StringComparison.OrdinalIgnoreCase));
return (builtInOptions, nonBuiltInOptions);
}
private static Dictionary<bool, List<(string[], string[])>> GetModulesToMissingOptions(
ConcurrentDictionary<bool, List<(string, string[])>> moduleNamesToCommandLineOptions,
IEnumerable<string> builtInOptions,
IEnumerable<string> nonBuiltInOptions)
{
var modulesWithMissingOptions = new Dictionary<bool, List<(string[], string[])>>();
foreach (var group in moduleNamesToCommandLineOptions)
{
bool isBuiltIn = group.Key;
var groupedModules = new List<(string[], string[])>();
var missingOptionsToModules = new Dictionary<string, List<string>>();
var allOptions = new HashSet<string>(isBuiltIn ? builtInOptions : nonBuiltInOptions);
foreach ((string module, string[] relatedOptions) in group.Value)
{
var missingOptions = new HashSet<string>(allOptions);
missingOptions.ExceptWith(relatedOptions);
if (missingOptions.Count > 0)
{
var missingKey = string.Join(",", missingOptions.OrderBy(option => option));
if (!missingOptionsToModules.TryGetValue(missingKey, out var modules))
{
modules = [];
missingOptionsToModules[missingKey] = modules;
}
modules.Add(module);
}
}
foreach (var kvp in missingOptionsToModules)
{
groupedModules.Add(([.. kvp.Value], kvp.Key.Split(',')));
}
if (groupedModules.Count > 0)
{
modulesWithMissingOptions.Add(isBuiltIn, groupedModules);
}
}
return modulesWithMissingOptions;
}
}
|