Skip to content

Commit

Permalink
Use span tokenization in multi-line emission logic
Browse files Browse the repository at this point in the history
  • Loading branch information
layomia committed Mar 21, 2023
1 parent af4468f commit 9f8adbd
Show file tree
Hide file tree
Showing 7 changed files with 170 additions and 125 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,8 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Text.RegularExpressions;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Text;

namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
Expand Down Expand Up @@ -79,8 +77,7 @@ public void Emit()

EmitGenerationNamespaceAndHelpers();

SourceText source = SourceText.From(_writer.GetSource(), Encoding.UTF8);
_context.AddSource($"{Identifier.GeneratedConfigurationBinder}.g.cs", source);
_context.AddSource($"{Identifier.GeneratedConfigurationBinder}.g.cs", _writer.ToSourceText());
}

private void EmitConfigureMethod()
Expand Down Expand Up @@ -275,10 +272,10 @@ private void EmitBindCoreImplForArray(EnumerableSpec type)

// Resize array and copy fill with additional
_writer.WriteBlock($$"""
{{Identifier.Int32}} {{Identifier.originalCount}} = {{Identifier.obj}}.{{Identifier.Length}};
{{Identifier.Array}}.{{Identifier.Resize}}(ref {{Identifier.obj}}, {{Identifier.originalCount}} + {{tempVarName}}.{{Identifier.Count}});
{{tempVarName}}.{{Identifier.CopyTo}}({{Identifier.obj}}, {{Identifier.originalCount}});
""");
{{Identifier.Int32}} {{Identifier.originalCount}} = {{Identifier.obj}}.{{Identifier.Length}};
{{Identifier.Array}}.{{Identifier.Resize}}(ref {{Identifier.obj}}, {{Identifier.originalCount}} + {{tempVarName}}.{{Identifier.Count}});
{{tempVarName}}.{{Identifier.CopyTo}}({{Identifier.obj}}, {{Identifier.originalCount}});
""");
}

private void EmitBindCoreImplForDictionary(DictionarySpec type)
Expand Down Expand Up @@ -675,11 +672,11 @@ private void EmitObjectInit(TypeSpec type, string expressionForMemberAccess, Ini
private void EmitIConfigurationHasValueOrChildrenCheck()
{
_writer.WriteBlock($$"""
if (!{{GetHelperMethodDisplayString(Identifier.HasValueOrChildren)}}({{Identifier.configuration}}))
{
return default;
}
""");
if (!{{GetHelperMethodDisplayString(Identifier.HasValueOrChildren)}}({{Identifier.configuration}}))
{
return default;
}
""");
_writer.WriteBlankLine();
}

Expand All @@ -700,31 +697,29 @@ private void EmitHelperMethods()
private void EmitHasValueOrChildrenMethod()
{
_writer.WriteBlock($$"""
public static bool {{Identifier.HasValueOrChildren}}({{Identifier.IConfiguration}} {{Identifier.configuration}})
{
if (({{Identifier.configuration}} as {{Identifier.IConfigurationSection}})?.{{Identifier.Value}} is not null)
{
return true;
}
return {{Identifier.HasChildren}}({{Identifier.configuration}});
}
""");
public static bool {{Identifier.HasValueOrChildren}}({{Identifier.IConfiguration}} {{Identifier.configuration}})
{
if (({{Identifier.configuration}} as {{Identifier.IConfigurationSection}})?.{{Identifier.Value}} is not null)
{
return true;
}
return {{Identifier.HasChildren}}({{Identifier.configuration}});
}
""");
}

private void EmitHasChildrenMethod()
{
_writer.WriteBlock($$"""
public static bool {{Identifier.HasChildren}}({{Identifier.IConfiguration}} {{Identifier.configuration}})
{
foreach ({{Identifier.IConfigurationSection}} {{Identifier.section}} in {{Identifier.configuration}}.{{Identifier.GetChildren}}())
{
return true;
}
return false;
}
""");
public static bool {{Identifier.HasChildren}}({{Identifier.IConfiguration}} {{Identifier.configuration}})
{
foreach ({{Identifier.IConfigurationSection}} {{Identifier.section}} in {{Identifier.configuration}}.{{Identifier.GetChildren}}())
{
return true;
}
return false;
}
""");
}

private void EmitVarDeclaration(TypeSpec type, string varName) => _writer.WriteLine($"{type.MinimalDisplayString} {varName};");
Expand All @@ -747,11 +742,11 @@ private void EmitCastToIConfigurationSection()
}

_writer.WriteBlock($$"""
if ({{Identifier.configuration}} is not {{sectionTypeDisplayString}} {{Identifier.section}})
{
throw new {{exceptionTypeDisplayString}}();
}
""");
if ({{Identifier.configuration}} is not {{sectionTypeDisplayString}} {{Identifier.section}})
{
throw new {{exceptionTypeDisplayString}}();
}
""");
}

private void Emit_NotSupportedException_UnableToBindType(string reason, string typeDisplayString = "{typeof(T)}") =>
Expand All @@ -772,11 +767,11 @@ private void EmitCheckForNullArgument_WithBlankLine(string argName, bool useFull
: Identifier.ArgumentNullException;

_writer.WriteBlock($$"""
if ({{argName}} is null)
{
throw new {{exceptionTypeDisplayString}}(nameof({{argName}}));
}
""");
if ({{argName}} is null)
{
throw new {{exceptionTypeDisplayString}}(nameof({{argName}}));
}
""");

_writer.WriteBlankLine();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Diagnostics;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Text;

namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
Expand Down Expand Up @@ -107,79 +108,5 @@ private static class TypeFullName

private static bool TypesAreEqual(ITypeSymbol first, ITypeSymbol second)
=> first.Equals(second, SymbolEqualityComparer.Default);

private sealed class SourceWriter
{
private readonly StringBuilder _sb = new();
private int _indentationLevel;

public int Length => _sb.Length;
public int IndentationLevel => _indentationLevel;

public void WriteBlockStart(string? declaration = null)
{
if (declaration is not null)
{
WriteLine(declaration);
}
WriteLine("{");
_indentationLevel++;
}

public void WriteBlockEnd(string? extra = null)
{
_indentationLevel--;
Debug.Assert(_indentationLevel > -1);
WriteLine($"}}{extra}");
}

public void WriteLine(string source)
{
_sb.Append(' ', 4 * _indentationLevel);
_sb.AppendLine(source);
}

public void WriteBlock(string source)
{
foreach (string value in source.Split('\n'))
{
string line = value.Trim();
switch (line)
{
case "{":
{
WriteBlockStart();
}
break;
case "}":
{
WriteBlockEnd();
}
break;
case "":
{
WriteBlankLine();
}
break;
default:
{
WriteLine(line);
}
break;
}
}
}

public void WriteBlankLine() => _sb.AppendLine();

public void RemoveBlankLine()
{
int newLineLength = Environment.NewLine.Length;
int lastNewLineStartIndex = Length - newLineLength;
_sb.Remove(lastNewLineStartIndex, newLineLength);
}

public string GetSource() => _sb.ToString();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@
<EnableDefaultEmbeddedResourceItems>false</EnableDefaultEmbeddedResourceItems>
<UsingToolXliff>true</UsingToolXliff>
<AnalyzerLanguage>cs</AnalyzerLanguage>
</PropertyGroup>

<PropertyGroup>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DefineConstants Condition="'$(LaunchDebugger)' == 'true'">$(DefineConstants);LAUNCH_DEBUGGER</DefineConstants>
</PropertyGroup>

Expand All @@ -34,6 +32,7 @@
<Compile Include="PopulationStrategy.cs" />
<Compile Include="PropertySpec.cs" />
<Compile Include="SourceGenerationSpec.cs" />
<Compile Include="SourceWriter.cs" />
<Compile Include="TypeSpecKind.cs" />
<Compile Include="TypeSpec.cs" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Diagnostics;
using System.Text;
using Microsoft.CodeAnalysis.Text;

namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
internal sealed class SourceWriter
{
private readonly StringBuilder _sb = new();
private int _indentationLevel;

public int Length => _sb.Length;
public int IndentationLevel => _indentationLevel;

private static readonly char[] s_newLine = Environment.NewLine.ToCharArray();

public void WriteBlockStart(string? declaration = null)
{
if (declaration is not null)
{
WriteLine(declaration);
}
WriteLine("{");
_indentationLevel++;
}

public void WriteBlockEnd(string? extra = null)
{
_indentationLevel--;
Debug.Assert(_indentationLevel > -1);
WriteLine($"}}{extra}");
}

public void WriteLine(string source)
{
_sb.Append(' ', 4 * _indentationLevel);
_sb.AppendLine(source);
}

public unsafe void WriteLine(ReadOnlySpan<char> source)
{
_sb.Append(' ', 4 * _indentationLevel);
fixed (char* ptr = source)
{
_sb.Append(ptr, source.Length);
WriteBlankLine();
}
}

public void WriteBlock(string source)
{
bool isFinalLine;
ReadOnlySpan<char> remainingText = source.AsSpan();

do
{
ReadOnlySpan<char> line = GetNextLine(ref remainingText, out isFinalLine);
switch (line)
{
case "{":
{
WriteBlockStart();
}
break;
case "}":
{
WriteBlockEnd();
}
break;
default:
{
WriteLine(line);
}
break;
}
} while (!isFinalLine);
}

public void WriteBlankLine() => _sb.AppendLine();

public void RemoveBlankLine()
{
int newLineLength = s_newLine.Length;
int lastNewLineStartIndex = Length - newLineLength;
_sb.Remove(lastNewLineStartIndex, newLineLength);
}

public SourceText ToSourceText()
{
Debug.Assert(_indentationLevel == 0 && _sb.Length > 0);
return SourceText.From(_sb.ToString(), Encoding.UTF8);
}

private static ReadOnlySpan<char> GetNextLine(ref ReadOnlySpan<char> remainingText, out bool isFinalLine)
{
if (remainingText.IsEmpty)
{
isFinalLine = true;
return default;
}

ReadOnlySpan<char> next;
ReadOnlySpan<char> rest;

remainingText = remainingText.Trim();

int lineLength = remainingText.IndexOf(s_newLine);
if (lineLength == -1)
{
lineLength = remainingText.Length;
isFinalLine = true;
rest = default;
}
else
{
rest = remainingText.Slice(lineLength + 1);
isFinalLine = false;
}

next = remainingText.Slice(0, lineLength);
remainingText = rest;
return next;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
return true;
}

return false;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
return true;
}

return HasChildren(configuration);
}

Expand All @@ -129,7 +128,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
return true;
}

return false;
}
}
Expand Down
Loading

0 comments on commit 9f8adbd

Please sign in to comment.