diff --git a/src/DocoptNet/CodeGeneration/SourceGenerator.cs b/src/DocoptNet/CodeGeneration/SourceGenerator.cs index 05054fc..0c7b53c 100644 --- a/src/DocoptNet/CodeGeneration/SourceGenerator.cs +++ b/src/DocoptNet/CodeGeneration/SourceGenerator.cs @@ -166,7 +166,7 @@ public void Execute(GeneratorExecutionContext context) if (Generate(ns, name, parentNames, attribute?.HelpConstName, help, options) is { Length: > 0 } source) { hintNameBuilder.Clear(); - if (ns is { } someNamespace) + if (ns?.Trim() is { Length: > 0 } someNamespace) hintNameBuilder.Append(someNamespace).Append('.'); if (parentNames.Length > 0) { diff --git a/tests/DocoptNet.Tests/CodeGeneration/SourceGeneratorTests.cs b/tests/DocoptNet.Tests/CodeGeneration/SourceGeneratorTests.cs index cadf378..a41239f 100644 --- a/tests/DocoptNet.Tests/CodeGeneration/SourceGeneratorTests.cs +++ b/tests/DocoptNet.Tests/CodeGeneration/SourceGeneratorTests.cs @@ -54,6 +54,17 @@ public void Generate_with_usage_in_external_file() }); } + [Test] + public void Generate_with_empty_root_namespace() + { + AssertMatchesSnapshot( + analyzerConfigOptions: new(KeyValuePair.Create("build_property.RootNamespace", string.Empty)), + sources: new[] + { + ("Program.docopt.txt", SourceText.From(NavalFateUsage)) + }); + } + [Test] public void Generate_with_inline_usage() { @@ -256,9 +267,10 @@ sealed partial class ProgramArguments } void AssertMatchesSnapshot((string Path, SourceText Text)[] sources, + AnalyzerConfigOptions? analyzerConfigOptions = null, [CallerMemberName]string? callerName = null) { - var (driver, compilation) = PrepareForGeneration(sources); + var (driver, compilation) = PrepareForGeneration(analyzerConfigOptions ?? new(), sources); var grr = driver.RunGeneratorsAndUpdateCompilation(compilation, out var outputCompilation, out _) .GetRunResult().Results.Single(); @@ -340,7 +352,16 @@ where Path.GetFileName(fp) is { } fn || fn.EndsWith(".json", StringComparison.OrdinalIgnoreCase)) select Path.GetRelativePath(solutionDirPath, fp); - var actualFiles = EnumerateFiles(actualSourcesPath); + var actualFiles = EnumerateFiles(actualSourcesPath).ToImmutableArray(); + + Assert.That(from gs in grr.GeneratedSources + orderby gs.HintName + select gs.HintName, + Is.EqualTo(from af in actualFiles + select Path.GetFileName(af) into af + where af.EndsWith(".cs", StringComparison.OrdinalIgnoreCase) + orderby af + select af)); var expectedFiles = Directory.Exists(expectedSourcesPath) ? EnumerateFiles(expectedSourcesPath) @@ -553,7 +574,12 @@ public partial class " + ProgramArgumentsClassName + @" { } new AnalyzerConfigOptions(KeyValuePair.Create("build_metadata.AdditionalFiles.SourceItemType", "Docopt")); internal static (CSharpGeneratorDriver, CSharpCompilation) - PrepareForGeneration(params (string Path, SourceText Text)[] sources) + PrepareForGeneration(params (string Path, SourceText Text)[] sources) => + PrepareForGeneration(new AnalyzerConfigOptions(), sources); + + internal static (CSharpGeneratorDriver, CSharpCompilation) + PrepareForGeneration(AnalyzerConfigOptions analyzerConfigOptions, + params (string Path, SourceText Text)[] sources) { var trees = new List(); var additionalTexts = new List(); @@ -579,11 +605,9 @@ internal static (CSharpGeneratorDriver, CSharpCompilation) ISourceGenerator generator = new SourceGenerator(); - var globalOptions = Enumerable.Empty>(); - var optionsProvider = new AnalyzerConfigOptionsProvider( - new AnalyzerConfigOptions(globalOptions), + analyzerConfigOptions, additionalTexts.Select(at => KeyValuePair.Create(at, DocoptSourceItemTypeConfigOption)) .ToImmutableDictionary()); diff --git a/tests/DocoptNet.Tests/CodeGeneration/SourceGeneratorTests/Generate_with_empty_root_namespace/ProgramArguments.cs b/tests/DocoptNet.Tests/CodeGeneration/SourceGeneratorTests/Generate_with_empty_root_namespace/ProgramArguments.cs new file mode 100644 index 0000000..5f10428 --- /dev/null +++ b/tests/DocoptNet.Tests/CodeGeneration/SourceGeneratorTests/Generate_with_empty_root_namespace/ProgramArguments.cs @@ -0,0 +1,492 @@ +// + +#nullable enable + +using System.Collections; +using System.Collections.Generic; +using DocoptNet; +using DocoptNet.Internals; +using Leaves = DocoptNet.Internals.ReadOnlyList; + +partial class ProgramArguments : IEnumerable> +{ + public const string Help = @" +Naval Fate. + + Usage: + naval_fate.exe ship new ... + naval_fate.exe ship move [--speed=] + naval_fate.exe ship shoot + naval_fate.exe mine (set|remove) [--moored | --drifting] + naval_fate.exe (-h | --help) + naval_fate.exe --version + + Options: + -h --help Show this screen. + --version Show version. + --speed= Speed in knots [default: 10]. + --moored Moored (anchored) mine. + --drifting Drifting mine. +"; + + public const string Usage = @"Usage: + naval_fate.exe ship new ... + naval_fate.exe ship move [--speed=] + naval_fate.exe ship shoot + naval_fate.exe mine (set|remove) [--moored | --drifting] + naval_fate.exe (-h | --help) + naval_fate.exe --version"; + + static readonly IHelpFeaturingParser Parser = GeneratedSourceModule.CreateParser(Help, Parse).EnableHelp(); + + public static IHelpFeaturingParser CreateParser() => Parser; + + static IParser.IResult Parse(IEnumerable args, ParseFlags flags, string? version) + { + var options = new List