194 lines
9.9 KiB
C#
194 lines
9.9 KiB
C#
|
using QFSW.QC.Utilities;
|
|||
|
using System;
|
|||
|
using System.Collections.Generic;
|
|||
|
using System.Linq;
|
|||
|
using System.Reflection;
|
|||
|
|
|||
|
namespace QFSW.QC
|
|||
|
{
|
|||
|
public static partial class QuantumConsoleProcessor
|
|||
|
{
|
|||
|
|
|||
|
private const string helpStr = "Welcome to Quantum Console! In order to see specific help about any specific command, " +
|
|||
|
"please use the 'man' command. Use 'man man' to see more about the man command. To see a full list of all " +
|
|||
|
"commands, use 'all-commands'.\n\n" +
|
|||
|
"mono-targets\nVarious commands may show a mono-target in their command signature.\n" +
|
|||
|
"This means they are not static commands, and instead requires instance(s) of the class in order to invoke the command." +
|
|||
|
"\nEach mono-target works differently as follows:" +
|
|||
|
"\n - single: uses the first instance of the type found in the scene" +
|
|||
|
"\n - all: uses all instances of the type found in the scene" +
|
|||
|
"\n - registry: uses all instances of the type found in the registry" +
|
|||
|
"\n - singleton: creates and manages a single instance automatically" +
|
|||
|
"\n\nThe registry is a part of the Quantum Registry that allows you to decide which specific instances of the class " +
|
|||
|
"should be used when invoking the command. In order to add an object to the registry, either use " +
|
|||
|
"QFSW.QC.QuantumRegistry.RegisterObject<T> or the runtime command 'register-object<T>'.";
|
|||
|
|
|||
|
[Command("help", "Shows a basic help guide for Quantum Console")]
|
|||
|
private static string GetHelp()
|
|||
|
{
|
|||
|
return helpStr;
|
|||
|
}
|
|||
|
|
|||
|
[Command("manual")]
|
|||
|
[Command("man")]
|
|||
|
private static string ManualHelp()
|
|||
|
{
|
|||
|
return "To use the man command, simply put the desired command name in front of it. For example, 'man my-command' will generate the manual for 'my-command'";
|
|||
|
}
|
|||
|
|
|||
|
[CommandDescription("Generates a user manual for any given command, including built in ones. To use the man command, simply put the desired command name infront of it. For example, 'man my-command' will generate the manual for 'my-command'")]
|
|||
|
[Command("help")]
|
|||
|
[Command("manual")]
|
|||
|
[Command("man")]
|
|||
|
private static string GenerateCommandManual(string commandName)
|
|||
|
{
|
|||
|
string[] matchingCommands = _commandTable.Keys.Where((string key) => key.Split('(')[0] == commandName).OrderBy((string key) => key).ToArray();
|
|||
|
if (matchingCommands.Length == 0) { throw new ArgumentException($"No command with the name {commandName} was found."); }
|
|||
|
else
|
|||
|
{
|
|||
|
Dictionary<string, ParameterInfo> foundParams = new Dictionary<string, ParameterInfo>();
|
|||
|
Dictionary<string, Type> foundGenericArguments = new Dictionary<string, Type>();
|
|||
|
Dictionary<string, CommandParameterDescriptionAttribute> foundParamDescriptions = new Dictionary<string, CommandParameterDescriptionAttribute>();
|
|||
|
List<Type> declaringTypes = new List<Type>(1);
|
|||
|
|
|||
|
string manual = $"Generated user manual for {commandName}\nAvailable command signatures:";
|
|||
|
|
|||
|
for (int i = 0; i < matchingCommands.Length; i++)
|
|||
|
{
|
|||
|
CommandData currentCommand = _commandTable[matchingCommands[i]];
|
|||
|
declaringTypes.Add(currentCommand.MethodData.DeclaringType);
|
|||
|
|
|||
|
manual += $"\n - {currentCommand.CommandSignature}";
|
|||
|
if (!currentCommand.IsStatic) { manual += $" (mono-target = {currentCommand.MonoTarget.ToString().ToLower()})"; }
|
|||
|
for (int j = 0; j < currentCommand.ParamCount; j++)
|
|||
|
{
|
|||
|
ParameterInfo param = currentCommand.MethodParamData[j];
|
|||
|
if (!foundParams.ContainsKey(param.Name)) { foundParams.Add(param.Name, param); }
|
|||
|
if (!foundParamDescriptions.ContainsKey(param.Name))
|
|||
|
{
|
|||
|
CommandParameterDescriptionAttribute descriptionAttribute = param.GetCustomAttribute<CommandParameterDescriptionAttribute>();
|
|||
|
if (descriptionAttribute != null && descriptionAttribute.Valid) { foundParamDescriptions.Add(param.Name, descriptionAttribute); }
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (currentCommand.IsGeneric)
|
|||
|
{
|
|||
|
Type[] genericArgs = currentCommand.GenericParamTypes;
|
|||
|
for (int j = 0; j < genericArgs.Length; j++)
|
|||
|
{
|
|||
|
Type arg = genericArgs[j];
|
|||
|
if (!foundGenericArguments.ContainsKey(arg.Name)) { foundGenericArguments.Add(arg.Name, arg); }
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (foundParams.Count > 0)
|
|||
|
{
|
|||
|
manual += "\nParameter info:";
|
|||
|
ParameterInfo[] commandParams = foundParams.Values.ToArray();
|
|||
|
for (int i = 0; i < commandParams.Length; i++)
|
|||
|
{
|
|||
|
ParameterInfo currentParam = commandParams[i];
|
|||
|
manual += $"\n - {currentParam.Name}: {currentParam.ParameterType.GetDisplayName()}";
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
string genericConstraintInformation = "";
|
|||
|
if (foundGenericArguments.Count > 0)
|
|||
|
{
|
|||
|
Type[] genericArgs = foundGenericArguments.Values.ToArray();
|
|||
|
for (int i = 0; i < genericArgs.Length; i++)
|
|||
|
{
|
|||
|
Type arg = genericArgs[i];
|
|||
|
Type[] typeConstraints = arg.GetGenericParameterConstraints();
|
|||
|
GenericParameterAttributes attributes = arg.GenericParameterAttributes;
|
|||
|
|
|||
|
List<string> formattedConstraints = new List<string>();
|
|||
|
if (attributes.HasFlag(GenericParameterAttributes.NotNullableValueTypeConstraint)) { formattedConstraints.Add("struct"); }
|
|||
|
if (attributes.HasFlag(GenericParameterAttributes.ReferenceTypeConstraint)) { formattedConstraints.Add("class"); }
|
|||
|
for (int j = 0; j < typeConstraints.Length; j++) { formattedConstraints.Add(typeConstraints[i].GetDisplayName()); }
|
|||
|
if (attributes.HasFlag(GenericParameterAttributes.DefaultConstructorConstraint)) { formattedConstraints.Add("new()"); }
|
|||
|
|
|||
|
if (formattedConstraints.Count > 0)
|
|||
|
{
|
|||
|
genericConstraintInformation += $"\n - {arg.Name}: {string.Join(", ", formattedConstraints)}";
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
if (!string.IsNullOrWhiteSpace(genericConstraintInformation)) { manual += $"\nGeneric constraints:{genericConstraintInformation}"; }
|
|||
|
|
|||
|
for (int i = 0; i < matchingCommands.Length; i++)
|
|||
|
{
|
|||
|
CommandData currentCommand = _commandTable[matchingCommands[i]];
|
|||
|
if (currentCommand.HasDescription)
|
|||
|
{
|
|||
|
manual += $"\n\nCommand description:\n{currentCommand.CommandDescription}";
|
|||
|
i = matchingCommands.Length;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (foundParamDescriptions.Count > 0)
|
|||
|
{
|
|||
|
manual += "\n\nParameter descriptions:";
|
|||
|
ParameterInfo[] commandParams = foundParams.Values.ToArray();
|
|||
|
for (int i = 0; i < commandParams.Length; i++)
|
|||
|
{
|
|||
|
ParameterInfo currentParam = commandParams[i];
|
|||
|
if (foundParamDescriptions.ContainsKey(currentParam.Name))
|
|||
|
{
|
|||
|
manual += $"\n - {currentParam.Name}: {foundParamDescriptions[currentParam.Name].Description}";
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
declaringTypes = declaringTypes.Distinct().ToList();
|
|||
|
manual += "\n\nDeclared in";
|
|||
|
if (declaringTypes.Count == 1) { manual += $" {declaringTypes[0].GetDisplayName(true)}"; }
|
|||
|
else
|
|||
|
{
|
|||
|
manual += ":";
|
|||
|
foreach (Type type in declaringTypes)
|
|||
|
{
|
|||
|
manual += $"\n - {type.GetDisplayName(true)}";
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return manual;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Gets all loaded unique commands. Unique excludes multiple overloads of the same command from appearing.
|
|||
|
/// </summary>
|
|||
|
/// <returns>All loaded unique commands.</returns>
|
|||
|
public static IEnumerable<CommandData> GetUniqueCommands()
|
|||
|
{
|
|||
|
return GetAllCommands()
|
|||
|
.DistinctBy(x => x.CommandName)
|
|||
|
.OrderBy(x => x.CommandName);
|
|||
|
}
|
|||
|
|
|||
|
[CommandDescription("Generates a list of all commands currently loaded by the Quantum Console Processor")]
|
|||
|
[Command("commands")]
|
|||
|
[Command("all-commands")]
|
|||
|
private static string GenerateCommandList()
|
|||
|
{
|
|||
|
string output = "List of all commands loaded by the Quantum Processor. Use 'man' on any command to see more:";
|
|||
|
foreach (CommandData command in GetUniqueCommands())
|
|||
|
{
|
|||
|
output += $"\n - {command.CommandName}";
|
|||
|
}
|
|||
|
|
|||
|
return output;
|
|||
|
}
|
|||
|
|
|||
|
[Command("user-commands", "Generates a list of all commands added by the user")]
|
|||
|
private static IEnumerable<string> GenerateUserCommandList()
|
|||
|
{
|
|||
|
return GetUniqueCommands()
|
|||
|
.Where(x => !x.MethodData.DeclaringType.Assembly.FullName.StartsWith("QFSW.QC"))
|
|||
|
.Select(x => $" - {x.CommandName}");
|
|||
|
}
|
|||
|
}
|
|||
|
}
|