diff --git a/samples/DotNetCampus.CommandLine.Sample/Program.cs b/samples/DotNetCampus.CommandLine.Sample/Program.cs index bbf7129f..a38d444b 100644 --- a/samples/DotNetCampus.CommandLine.Sample/Program.cs +++ b/samples/DotNetCampus.CommandLine.Sample/Program.cs @@ -1,28 +1,36 @@ +using DotNetCampus.Cli; +using DotNetCampus.Cli.Compiler; using DotNetCampus.Cli.Legacy; using DotNetCampus.Cli.Properties; -namespace DotNetCampus.Cli; - -class Program +var appState = new AppState { - static async Task Main(string[] args) + AppName = "DotNetCampus.CommandLine.Sample", +}; + +await CommandLine.Parse(args, CommandLineParsingOptions.Flexible) + .AddHandler(o => o.Run()) + .AddHandler() + .AddHandler() + .AddHandler() + .AddHandler() + .AddHelpHandler(new HelpConfigurations { - var appState = new AppState - { - AppName = "DotNetCampus.CommandLine.Sample", - }; + HelpTextLocalizer = key => LocalizableStrings.ResourceManager.GetString(key) ?? key, + }) + .ForState(appState).AddHandler() + .ForState() + .RunAsync(); + - await CommandLine.Parse(args, CommandLineParsingOptions.Flexible) - .AddHandler(o => o.Run()) - .AddHandler() - .AddHandler() - .AddHandler() - .AddHelpHandler(new HelpConfigurations - { - HelpTextLocalizer = key => LocalizableStrings.ResourceManager.GetString(key) ?? key, - }) - .ForState(appState).AddHandler() - .ForState() - .RunAsync(); +[Command("test")] +internal record TestHandler : ICommandHandler +{ + [Option("test")] + public int? Count { get; init; } + + public Task RunAsync() + { + return Task.FromResult(0); } } diff --git a/src/DotNetCampus.CommandLine.Analyzer/Generators/InterceptorGenerator.cs b/src/DotNetCampus.CommandLine.Analyzer/Generators/InterceptorGenerator.cs index a29f9faf..e9322514 100644 --- a/src/DotNetCampus.CommandLine.Analyzer/Generators/InterceptorGenerator.cs +++ b/src/DotNetCampus.CommandLine.Analyzer/Generators/InterceptorGenerator.cs @@ -151,7 +151,7 @@ private void GenerateCommandLineAddHandlerCode(TypeDeclarationSourceTextBuilder .AddTypeConstraints("where T : class, global::DotNetCampus.Cli.ICommandHandler") .AddRawStatement(GenerateComment(model)) .AddRawStatements($""" - return commandLine.AsRunner().AddHandler(global::{model.CommandObjectType.ContainingNamespace}.{model.GetBuilderTypeName()}.CommandNameGroup, new global::{model.CommandObjectType.ContainingNamespace}.{model.GetBuilderTypeName()}.Metadata()); + return commandLine.AsRunner().AddHandler({model.GetGlobalBuilderTypeName()}.CommandNameGroup, new {model.GetGlobalBuilderTypeName()}.Metadata()); """)); } @@ -166,7 +166,7 @@ private void GenerateCommandLineAddHandlerActionCode(TypeDeclarationSourceTextBu .AddTypeConstraints("where T : class") .AddRawStatement(GenerateComment(model)) .AddRawStatements($""" - return commandLine.AsRunner().AddHandler(handler, global::{model.CommandObjectType.ContainingNamespace}.{model.GetBuilderTypeName()}.CommandNameGroup, new global::{model.CommandObjectType.ContainingNamespace}.{model.GetBuilderTypeName()}.Metadata()); + return commandLine.AsRunner().AddHandler(handler, {model.GetGlobalBuilderTypeName()}.CommandNameGroup, new {model.GetGlobalBuilderTypeName()}.Metadata()); """)); } @@ -265,7 +265,7 @@ private void GenerateCommandLineAsCode(TypeDeclarationSourceTextBuilder builder, .AddRawStatement(GenerateComment(model)) .AddRawStatements($$""""" var context = new global::DotNetCampus.Cli.Compiler.CommandRunningContext { CommandLine = commandLine }; - var instance = new global::{{model.CommandObjectType.ContainingNamespace}}.{{model.GetBuilderTypeName()}}().Build(context); + var instance = new {{model.GetGlobalBuilderTypeName()}}().Build(context); {{( model.UseFullStackParser ? $""" @@ -293,7 +293,7 @@ private void GenerateCommandBuilderAddHandlerCode(TypeDeclarationSourceTextBuild .AddTypeConstraints("where T : class, global::DotNetCampus.Cli.ICommandHandler") .AddRawStatement(GenerateComment(model)) .AddRawStatements($""" - return global::DotNetCampus.Cli.CommandRunnerBuilderExtensions.AddHandler(builder, global::{model.CommandObjectType.ContainingNamespace}.{model.GetBuilderTypeName()}.CommandNameGroup, new global::{model.CommandObjectType.ContainingNamespace}.{model.GetBuilderTypeName()}.Metadata()); + return global::DotNetCampus.Cli.CommandRunnerBuilderExtensions.AddHandler(builder, {model.GetGlobalBuilderTypeName()}.CommandNameGroup, new {model.GetGlobalBuilderTypeName()}.Metadata()); """)); } @@ -310,7 +310,7 @@ private void GenerateStatedCommandBuilderAddHandlerCode(TypeDeclarationSourceTex .AddTypeConstraints("where T : class, global::DotNetCampus.Cli.ICommandHandler") .AddRawStatement(GenerateComment(model)) .AddRawStatements($""" - return builder.AddHandler(global::{model.CommandObjectType.ContainingNamespace}.{model.GetBuilderTypeName()}.CommandNameGroup, new global::{model.CommandObjectType.ContainingNamespace}.{model.GetBuilderTypeName()}.Metadata()); + return builder.AddHandler({model.GetGlobalBuilderTypeName()}.CommandNameGroup, new {model.GetGlobalBuilderTypeName()}.Metadata()); """)); } @@ -327,7 +327,7 @@ private void GenerateCommandBuilderAddHandlerActionCode(TypeDeclarationSourceTex .AddTypeConstraints("where T : class") .AddRawStatement(GenerateComment(model)) .AddRawStatements($""" - return global::DotNetCampus.Cli.CommandRunnerBuilderExtensions.AddHandler(builder, handler, global::{model.CommandObjectType.ContainingNamespace}.{model.GetBuilderTypeName()}.CommandNameGroup, new global::{model.CommandObjectType.ContainingNamespace}.{model.GetBuilderTypeName()}.Metadata()); + return global::DotNetCampus.Cli.CommandRunnerBuilderExtensions.AddHandler(builder, handler, {model.GetGlobalBuilderTypeName()}.CommandNameGroup, new {model.GetGlobalBuilderTypeName()}.Metadata()); """)); } diff --git a/src/DotNetCampus.CommandLine.Analyzer/Generators/ModelBuilderGenerator.cs b/src/DotNetCampus.CommandLine.Analyzer/Generators/ModelBuilderGenerator.cs index 7bc2a254..d0d854f6 100644 --- a/src/DotNetCampus.CommandLine.Analyzer/Generators/ModelBuilderGenerator.cs +++ b/src/DotNetCampus.CommandLine.Analyzer/Generators/ModelBuilderGenerator.cs @@ -128,7 +128,7 @@ private void GenerateCommandObjectMetadata(TypeDeclarationSourceTextBuilder buil { builder .AddMethodDeclaration("public object Build(global::DotNetCampus.Cli.Compiler.CommandRunningContext context)", m => m - .AddRawStatement($"return new {model.Namespace}.{model.GetBuilderTypeName()}().Build(context);")) + .AddRawStatement($"return new {model.GetGlobalBuilderTypeName()}().Build(context);")) .AddMethodDeclaration("public global::DotNetCampus.Cli.Compiler.CommandHelpMetadata? GetHelp()", m => m .AddRawStatement(GenerateGetHelpReturnStatement(model))); } diff --git a/src/DotNetCampus.CommandLine.Analyzer/Generators/ModelProviding/CommandModelProvider.cs b/src/DotNetCampus.CommandLine.Analyzer/Generators/ModelProviding/CommandModelProvider.cs index 2238ca6c..8a882719 100644 --- a/src/DotNetCampus.CommandLine.Analyzer/Generators/ModelProviding/CommandModelProvider.cs +++ b/src/DotNetCampus.CommandLine.Analyzer/Generators/ModelProviding/CommandModelProvider.cs @@ -69,7 +69,7 @@ public static IncrementalValuesProvider SelectComm return null; } - var @namespace = typeSymbol.ContainingNamespace.ToDisplayString(); + var @namespace = typeSymbol.GetNamespaceOrNull(); var commandNames = attribute?.ConstructorArguments.FirstOrDefault().Value?.ToString(); var commandDescription = attribute?.NamedArguments .FirstOrDefault(kv => kv.Key == "Description").Value.Value?.ToString(); diff --git a/src/DotNetCampus.CommandLine.Analyzer/Generators/ModelProviding/InterceptorModelProvider.cs b/src/DotNetCampus.CommandLine.Analyzer/Generators/ModelProviding/InterceptorModelProvider.cs index 022b9d6e..43d4aec1 100644 --- a/src/DotNetCampus.CommandLine.Analyzer/Generators/ModelProviding/InterceptorModelProvider.cs +++ b/src/DotNetCampus.CommandLine.Analyzer/Generators/ModelProviding/InterceptorModelProvider.cs @@ -169,6 +169,11 @@ string InvocationInfo public string GetBuilderTypeName() => CommandObjectGeneratingModel.GetBuilderTypeName(CommandObjectType); + /// + /// 获取创建器类型带 global:: 前缀的完整名称。支持命令行对象位于全局命名空间的情况(如使用顶级语句时)。 + /// + public string GetGlobalBuilderTypeName() => CommandObjectGeneratingModel.GetGlobalBuilderTypeName(CommandObjectType); + internal static IEqualityComparer CommandObjectTypeEqualityComparer { get; } = new PrivateTypeSymbolEqualityComparer(); diff --git a/src/DotNetCampus.CommandLine.Analyzer/Generators/Models/CommandObjectGeneratingModel.cs b/src/DotNetCampus.CommandLine.Analyzer/Generators/Models/CommandObjectGeneratingModel.cs index 4d62701d..ad4df9c2 100644 --- a/src/DotNetCampus.CommandLine.Analyzer/Generators/Models/CommandObjectGeneratingModel.cs +++ b/src/DotNetCampus.CommandLine.Analyzer/Generators/Models/CommandObjectGeneratingModel.cs @@ -1,4 +1,5 @@ using DotNetCampus.Cli.Utils; +using DotNetCampus.CommandLine.Utils.CodeAnalysis; using Microsoft.CodeAnalysis; namespace DotNetCampus.CommandLine.Generators.Models; @@ -10,7 +11,10 @@ internal record CommandObjectGeneratingModel typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypes, genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters); - public required string Namespace { get; init; } + /// + /// 命令行对象类型所在的命名空间。如果类型位于全局命名空间(例如顶级语句所在的项目中未声明命名空间的类型),则为 。 + /// + public required string? Namespace { get; init; } public required INamedTypeSymbol CommandObjectType { get; init; } @@ -41,6 +45,25 @@ public static string GetBuilderTypeName(INamedTypeSymbol commandObjectType) return $"{name}Builder"; } + /// + /// 获取创建器类型带 global:: 前缀的完整名称。支持命令行对象位于全局命名空间的情况。 + /// + public string GetGlobalBuilderTypeName() + { + return GetGlobalBuilderTypeName(CommandObjectType); + } + + /// + /// 获取创建器类型带 global:: 前缀的完整名称。支持命令行对象位于全局命名空间的情况。 + /// + public static string GetGlobalBuilderTypeName(INamedTypeSymbol commandObjectType) + { + var typeName = GetBuilderTypeName(commandObjectType); + return commandObjectType.GetNamespaceOrNull() is { } @namespace + ? $"global::{@namespace}.{typeName}" + : $"global::{typeName}"; + } + public int GetCommandLevel() => CommandNames switch { null => 0, diff --git a/src/DotNetCampus.CommandLine.Analyzer/Utils/CodeAnalysis/TypeSymbolExtensions.cs b/src/DotNetCampus.CommandLine.Analyzer/Utils/CodeAnalysis/TypeSymbolExtensions.cs index 6b37521b..bc31af16 100644 --- a/src/DotNetCampus.CommandLine.Analyzer/Utils/CodeAnalysis/TypeSymbolExtensions.cs +++ b/src/DotNetCampus.CommandLine.Analyzer/Utils/CodeAnalysis/TypeSymbolExtensions.cs @@ -7,6 +7,15 @@ namespace DotNetCampus.CommandLine.Utils.CodeAnalysis; /// internal static class TypeSymbolExtensions { + /// + /// 获取一个符号所在的命名空间。如果此符号位于全局命名空间(例如使用顶级语句时定义的类型),则返回 。 + /// + /// 要获取命名空间的符号。 + /// 命名空间的字符串形式;如果位于全局命名空间,则返回 + public static string? GetNamespaceOrNull(this ISymbol symbol) => symbol.ContainingNamespace is { IsGlobalNamespace: false } @namespace + ? @namespace.ToDisplayString() + : null; + /// /// 判断一个类型是否是指定类型的子类或实现了指定接口。 ///