From a99f0c232b30022db2978ddb24a593050962636a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Lev=C3=A9?= Date: Thu, 11 Jun 2026 16:23:42 +0200 Subject: [PATCH 1/2] Unify IndentedStringBuilder across MSTest source generators (#8987) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Generators/MetadataRegistryEmitter.cs | 3 +- .../Helpers/IndentedStringBuilder.cs | 88 ------------------- ...Test.AotReflection.SourceGeneration.csproj | 5 ++ .../Helpers/IndentedStringBuilder.cs | 27 ++++-- 4 files changed, 26 insertions(+), 97 deletions(-) delete mode 100644 src/Analyzers/MSTest.AotReflection.SourceGeneration/Helpers/IndentedStringBuilder.cs diff --git a/src/Analyzers/MSTest.AotReflection.SourceGeneration/Generators/MetadataRegistryEmitter.cs b/src/Analyzers/MSTest.AotReflection.SourceGeneration/Generators/MetadataRegistryEmitter.cs index e7ae151cb8..d62a57defd 100644 --- a/src/Analyzers/MSTest.AotReflection.SourceGeneration/Generators/MetadataRegistryEmitter.cs +++ b/src/Analyzers/MSTest.AotReflection.SourceGeneration/Generators/MetadataRegistryEmitter.cs @@ -1,7 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using MSTest.AotReflection.SourceGeneration.Helpers; +using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.SourceGeneration.Helpers; + using MSTest.AotReflection.SourceGeneration.Model; namespace MSTest.AotReflection.SourceGeneration.Generators; diff --git a/src/Analyzers/MSTest.AotReflection.SourceGeneration/Helpers/IndentedStringBuilder.cs b/src/Analyzers/MSTest.AotReflection.SourceGeneration/Helpers/IndentedStringBuilder.cs deleted file mode 100644 index 6899b7cce4..0000000000 --- a/src/Analyzers/MSTest.AotReflection.SourceGeneration/Helpers/IndentedStringBuilder.cs +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -namespace MSTest.AotReflection.SourceGeneration.Helpers; - -/// -/// A small wrapper that tracks an indentation level so the -/// generated code stays readable. Kept private to this PoC to avoid pulling in the -/// equivalent helper that lives in MSTest.SourceGeneration. -/// -internal sealed class IndentedStringBuilder -{ - private const string IndentUnit = " "; - private readonly StringBuilder _builder = new(); - private int _indent; - private bool _needsIndent = true; - - public IndentedStringBuilder AppendLine(string value) - { - EnsureIndent(); - _builder.AppendLine(value); - _needsIndent = true; - return this; - } - - public IndentedStringBuilder AppendLine() - { - _builder.AppendLine(); - _needsIndent = true; - return this; - } - - public IndentedStringBuilder Append(string value) - { - EnsureIndent(); - _builder.Append(value); - return this; - } - - public IDisposable Block(string? header = null) - { - if (header is not null) - { - AppendLine(header); - } - - AppendLine("{"); - _indent++; - return new BlockScope(this); - } - - public override string ToString() => _builder.ToString(); - - private void EnsureIndent() - { - if (!_needsIndent) - { - return; - } - - for (int i = 0; i < _indent; i++) - { - _builder.Append(IndentUnit); - } - - _needsIndent = false; - } - - private sealed class BlockScope : IDisposable - { - private readonly IndentedStringBuilder _owner; - private bool _closed; - - public BlockScope(IndentedStringBuilder owner) => _owner = owner; - - public void Dispose() - { - if (_closed) - { - return; - } - - _closed = true; - _owner._indent--; - _owner.AppendLine("}"); - } - } -} diff --git a/src/Analyzers/MSTest.AotReflection.SourceGeneration/MSTest.AotReflection.SourceGeneration.csproj b/src/Analyzers/MSTest.AotReflection.SourceGeneration/MSTest.AotReflection.SourceGeneration.csproj index dc9bbade05..b88c34a107 100644 --- a/src/Analyzers/MSTest.AotReflection.SourceGeneration/MSTest.AotReflection.SourceGeneration.csproj +++ b/src/Analyzers/MSTest.AotReflection.SourceGeneration/MSTest.AotReflection.SourceGeneration.csproj @@ -33,4 +33,9 @@ + + + + + diff --git a/src/Analyzers/MSTest.SourceGeneration/Helpers/IndentedStringBuilder.cs b/src/Analyzers/MSTest.SourceGeneration/Helpers/IndentedStringBuilder.cs index 6b7937ade5..f36af8269d 100644 --- a/src/Analyzers/MSTest.SourceGeneration/Helpers/IndentedStringBuilder.cs +++ b/src/Analyzers/MSTest.SourceGeneration/Helpers/IndentedStringBuilder.cs @@ -4,9 +4,8 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.SourceGeneration.Helpers; /// -/// Small helper that produces indented source text. Mirrors the helper used by the existing -/// MSTest.SourceGeneration project so that the generated output is consistent with the rest of -/// the MSTest source generators. +/// Canonical source-generator indentation helper. This file is linked by +/// MSTest.AotReflection.SourceGeneration so both generators use deterministic newlines. /// internal sealed class IndentedStringBuilder { @@ -17,30 +16,40 @@ internal sealed class IndentedStringBuilder public int IndentationLevel { get; internal set; } - public void Append(string value) + public IndentedStringBuilder Append(string value) { MaybeAppendIndent(); _builder.Append(value); _needsIndent = false; + + return this; } - public void AppendLine() + public IndentedStringBuilder AppendLine() { _builder.Append(Constants.NewLine); _needsIndent = true; + + return this; } - public void AppendLine(string value) + public IndentedStringBuilder AppendLine(string value) { MaybeAppendIndent(); _builder.Append(value); _builder.Append(Constants.NewLine); _needsIndent = true; + + return this; } - public IDisposable AppendBlock(string header) + public IDisposable AppendBlock(string? header) { - AppendLine(header); + if (header is not null) + { + AppendLine(header); + } + AppendLine("{"); IndentationLevel++; @@ -51,6 +60,8 @@ public IDisposable AppendBlock(string header) }); } + public IDisposable Block(string? header = null) => AppendBlock(header); + public override string ToString() => _builder.ToString(); private void MaybeAppendIndent() From bd7b2930dc6777d703b68e6ec4090ef08451d243 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Lev=C3=A9?= Date: Thu, 11 Jun 2026 19:23:56 +0200 Subject: [PATCH 2/2] Collapse AppendBlock into Block in IndentedStringBuilder AppendBlock had no remaining call sites: every consumer in MetadataRegistryEmitter.cs goes through Block. Inline AppendBlock's body into Block to leave a single, unambiguous entry point on this internal helper. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../MSTest.SourceGeneration/Helpers/IndentedStringBuilder.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Analyzers/MSTest.SourceGeneration/Helpers/IndentedStringBuilder.cs b/src/Analyzers/MSTest.SourceGeneration/Helpers/IndentedStringBuilder.cs index f36af8269d..cfe869617f 100644 --- a/src/Analyzers/MSTest.SourceGeneration/Helpers/IndentedStringBuilder.cs +++ b/src/Analyzers/MSTest.SourceGeneration/Helpers/IndentedStringBuilder.cs @@ -43,7 +43,7 @@ public IndentedStringBuilder AppendLine(string value) return this; } - public IDisposable AppendBlock(string? header) + public IDisposable Block(string? header = null) { if (header is not null) { @@ -60,8 +60,6 @@ public IDisposable AppendBlock(string? header) }); } - public IDisposable Block(string? header = null) => AppendBlock(header); - public override string ToString() => _builder.ToString(); private void MaybeAppendIndent()