Change command aliasing to use an explicit new "CommandAliasAttribute".
authorMike McLaughlin <mikem@microsoft.com>
Fri, 15 Mar 2019 23:37:45 +0000 (16:37 -0700)
committerMike McLaughlin <mikem@microsoft.com>
Wed, 20 Mar 2019 00:35:51 +0000 (17:35 -0700)
src/Microsoft.Diagnostic.Repl/Command/Attributes.cs
src/Microsoft.Diagnostic.Repl/Command/CommandProcessor.cs
src/Tools/dotnet-dump/Commands/ExitCommand.cs
src/Tools/dotnet-dump/Commands/ModulesCommand.cs
src/Tools/dotnet-dump/Commands/SOSCommand.cs
src/Tools/dotnet-dump/Commands/SetThreadCommand.cs

index 8083b5e17d361cef7497de21a3038c7f9e3a606e..678e969d54b9ff5da239201a0982d096bda3d964 100644 (file)
@@ -35,6 +35,14 @@ namespace Microsoft.Diagnostic.Repl
         public string AliasExpansion;
     }
 
+    /// <summary>
+    /// Adds an alias to the previous command attribute
+    /// </summary>
+    [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
+    public class CommandAliasAttribute : BaseAttribute
+    {
+    }
+
     /// <summary>
     /// Marks the property as a Option.
     /// </summary>
index 957559932fa5471a2bc109da131031f8a8d443d7..5d42b35c93d63e2166f36c69fc989c4123a67391 100644 (file)
@@ -75,61 +75,67 @@ namespace Microsoft.Diagnostic.Repl
             {
                 Command command = null;
 
-                var commandAttributes = (CommandAttribute[])type.GetCustomAttributes(typeof(CommandAttribute), inherit: false);
-                foreach (CommandAttribute commandAttribute in commandAttributes)
+                var baseAttributes = (BaseAttribute[])type.GetCustomAttributes(typeof(BaseAttribute), inherit: false);
+                foreach (BaseAttribute baseAttribute in baseAttributes)
                 {
-                    // If there is a previous command and the current command doesn't have help or alias expansion, use "simple" aliasing
-                    if (command != null && commandAttribute.Help == null && commandAttribute.AliasExpansion == null) {
-                        command.AddAlias(commandAttribute.Name);
-                        continue;
-                    }
-                    command = new Command(commandAttribute.Name, commandAttribute.Help);
-                    var builder = new CommandLineBuilder(command);
-                    builder.UseHelp();
+                    if (baseAttribute is CommandAttribute commandAttribute)
+                    {
+                        command = new Command(commandAttribute.Name, commandAttribute.Help);
+                        var builder = new CommandLineBuilder(command);
+                        builder.UseHelp();
 
-                    var properties = new List<(PropertyInfo, Option)>();
-                    PropertyInfo argument = null;
+                        var properties = new List<(PropertyInfo, Option)>();
+                        PropertyInfo argument = null;
 
-                    foreach (PropertyInfo property in type.GetProperties().Where(p => p.CanWrite))
-                    {
-                        var argumentAttribute = (ArgumentAttribute)property.GetCustomAttributes(typeof(ArgumentAttribute), inherit: false).SingleOrDefault();
-                        if (argumentAttribute != null)
-                        {
-                            if (argument != null) {
-                                throw new ArgumentException($"More than one ArgumentAttribute in command class: {type.Name}");
-                            }
-                            command.Argument = new Argument {
-                                Name = argumentAttribute.Name ?? property.Name.ToLowerInvariant(),
-                                Description = argumentAttribute.Help,
-                                ArgumentType = property.PropertyType,
-                                Arity = new ArgumentArity(0, int.MaxValue)
-                            };
-                            argument = property;
-                        }
-                        else
+                        foreach (PropertyInfo property in type.GetProperties().Where(p => p.CanWrite))
                         {
-                            var optionAttribute = (OptionAttribute)property.GetCustomAttributes(typeof(OptionAttribute), inherit: false).SingleOrDefault();
-                            if (optionAttribute != null)
+                            var argumentAttribute = (ArgumentAttribute)property.GetCustomAttributes(typeof(ArgumentAttribute), inherit: false).SingleOrDefault();
+                            if (argumentAttribute != null)
                             {
-                                var option = new Option(optionAttribute.Name ?? BuildAlias(property.Name), optionAttribute.Help, new Argument { ArgumentType = property.PropertyType });
-                                command.AddOption(option);
-                                properties.Add((property, option));
-
-                                foreach (var optionAliasAttribute in (OptionAliasAttribute[])property.GetCustomAttributes(typeof(OptionAliasAttribute), inherit: false))
-                                {
-                                    option.AddAlias(optionAliasAttribute.Name);
+                                if (argument != null) {
+                                    throw new ArgumentException($"More than one ArgumentAttribute in command class: {type.Name}");
                                 }
+                                command.Argument = new Argument {
+                                    Name = argumentAttribute.Name ?? property.Name.ToLowerInvariant(),
+                                    Description = argumentAttribute.Help,
+                                    ArgumentType = property.PropertyType,
+                                    Arity = new ArgumentArity(0, int.MaxValue)
+                                };
+                                argument = property;
                             }
                             else
                             {
-                                // If not an option, add as just a settable properties
-                                properties.Add((property, null));
+                                var optionAttribute = (OptionAttribute)property.GetCustomAttributes(typeof(OptionAttribute), inherit: false).SingleOrDefault();
+                                if (optionAttribute != null)
+                                {
+                                    var option = new Option(optionAttribute.Name ?? BuildAlias(property.Name), optionAttribute.Help, new Argument { ArgumentType = property.PropertyType });
+                                    command.AddOption(option);
+                                    properties.Add((property, option));
+
+                                    foreach (var optionAliasAttribute in (OptionAliasAttribute[])property.GetCustomAttributes(typeof(OptionAliasAttribute), inherit: false))
+                                    {
+                                        option.AddAlias(optionAliasAttribute.Name);
+                                    }
+                                }
+                                else
+                                {
+                                    // If not an option, add as just a settable properties
+                                    properties.Add((property, null));
+                                }
                             }
                         }
+
+                        command.Handler = new Handler(this, commandAttribute.AliasExpansion, argument, properties, type);
+                        rootBuilder.AddCommand(command);
                     }
 
-                    command.Handler = new Handler(this, commandAttribute.AliasExpansion, argument, properties, type);
-                    rootBuilder.AddCommand(command);
+                    if (baseAttribute is CommandAliasAttribute commandAliasAttribute)
+                    {
+                        if (command == null) {
+                            throw new ArgumentException($"No previous CommandAttribute for this CommandAliasAttrbute: {type.Name}");
+                        }
+                        command.AddAlias(commandAliasAttribute.Name);
+                    }
                 }
             }
         }
index 99e73af78ab95fb34ef1a0967900edf36c4625b9..a9fe36a4194a8be5fb6f615881a27e0dc26209fb 100644 (file)
@@ -6,7 +6,7 @@ using System.Threading.Tasks;
 namespace Microsoft.Diagnostic.Tools.Dump
 {
     [Command(Name = "exit", Help = "Exit interactive mode.")]
-    [Command(Name = "quit")]
+    [CommandAlias(Name = "quit")]
     public class ExitCommand : CommandBase
     {
         public AnalyzeContext AnalyzeContext { get; set; }
index e8059045988d484b54338ff98fb49dd9f74f9b9b..8b4091f884894ba9a167757537247209eb1d6484 100644 (file)
@@ -8,7 +8,7 @@ using System.Threading.Tasks;
 namespace Microsoft.Diagnostic.Tools.Dump
 {
     [Command(Name = "modules", Help = "Displays the native modules in the process.")]
-    [Command(Name = "lm")]
+    [CommandAlias(Name = "lm")]
     public class ModulesCommand : CommandBase
     {
         [Option(Name = "--verbose", Help = "Displays more details.")]
index 0c8954d14d1667a56e80dd0ba9bb336167c3fd88..da500deb2a6b4f22c6488ff4b016843988a882be 100644 (file)
@@ -24,7 +24,7 @@ namespace Microsoft.Diagnostic.Tools.Dump
     [Command(Name = "dumpmt",           AliasExpansion = "DumpMT",              Help = "Displays information about a method table at the specified address.")]
     [Command(Name = "dumpobj",          AliasExpansion = "DumpObj",             Help = "Displays info about an object at the specified address.")]
     [Command(Name = "dumpstackobjects", AliasExpansion = "DumpStackObjects",    Help = "Displays all managed objects found within the bounds of the current stack.")]
-    [Command(Name = "dso")]
+    [CommandAlias(Name = "dso")]
     [Command(Name = "eeheap",           AliasExpansion = "EEHeap",              Help = "Displays info about process memory consumed by internal runtime data structures.")]
     [Command(Name = "finalizequeue",    AliasExpansion = "FinalizeQueue",       Help = "Displays all objects registered for finalization.")]
     [Command(Name = "gcroot",           AliasExpansion = "GCRoot",              Help = "Displays info about references (or roots) to an object at the specified address.")]
@@ -32,7 +32,7 @@ namespace Microsoft.Diagnostic.Tools.Dump
     [Command(Name = "ip2md",            AliasExpansion = "IP2MD",               Help = "Displays the MethodDesc structure at the specified address in code that has been JIT-compiled.")]
     [Command(Name = "name2ee",          AliasExpansion = "Name2EE",             Help = "Displays the MethodTable structure and EEClass structure for the specified type or method in the specified module.")]
     [Command(Name = "printexception",   AliasExpansion = "PrintException",      Help = "Displays and formats fields of any object derived from the Exception class at the specified address.")]
-    [Command(Name = "pe")]
+    [CommandAlias(Name = "pe")]
     [Command(Name = "syncblk",          AliasExpansion = "SyncBlk",             Help = "Displays the SyncBlock holder info.")]
     [Command(Name = "histclear",        AliasExpansion = "HistClear",           Help = "Releases any resources used by the family of Hist commands.")]
     [Command(Name = "histinit",         AliasExpansion = "HistInit",            Help = "Initializes the SOS structures from the stress log saved in the debuggee.")]
index 35b10da8d576f2ce97885a95652f1418c937632f..049ef6cd6771de5de118ef9d1ded29f00ad81e42 100644 (file)
@@ -6,7 +6,7 @@ using System.Threading.Tasks;
 namespace Microsoft.Diagnostic.Tools.Dump
 {
     [Command(Name = "setthread", Help = "Sets or displays the current thread id for the SOS commands.")]
-    [Command(Name = "threads")]
+    [CommandAlias(Name = "threads")]
     public class SetThreadCommand : CommandBase
     {
         [Argument(Help = "The thread id to set, otherwise displays the current id.")]