Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 13 additions & 16 deletions eng/SnippetGenerator/CSharpProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Security;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace SnippetGenerator
{
Expand All @@ -19,34 +20,30 @@ public class CSharpProcessor
private static readonly Regex _snippetExampleRegex = new Regex("^(?<indent>\\s*)\\/{3}\\s*<example snippet=\"(?<name>[\\w:]+)\">.*?\\s*<\\/example>",
RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.Singleline | RegexOptions.IgnoreCase);

public static string Process(string markdown, Func<string, string> snippetProvider)
public static async ValueTask<string> ProcessAsync(string markdown, Func<string, ValueTask<string>> snippetProvider)
{
string CodeTagFormatter(Match match)
async ValueTask<string> CodeTagFormatter(Match match)
{
var name = BuildResult(snippetProvider, match, out var prefix, out var builder);

return string.Format(_snippetFormat, name, Environment.NewLine, builder, prefix);
return await BuildResult(snippetProvider, match, _snippetFormat);
}

string ExampleTagFormatter(Match match)
async ValueTask<string> ExampleTagFormatter(Match match)
{
var name = BuildResult(snippetProvider, match, out var prefix, out var builder);

return string.Format(_snippetExampleFormat, name, Environment.NewLine, builder, prefix);
return await BuildResult(snippetProvider, match, _snippetExampleFormat);
}

string result = _snippetRegex.Replace(markdown, CodeTagFormatter);
return result != markdown ? result : _snippetExampleRegex.Replace(markdown, ExampleTagFormatter);
string result = await _snippetRegex.ReplaceAsync(markdown, CodeTagFormatter);
return result != markdown ? result : await _snippetExampleRegex.ReplaceAsync(markdown, ExampleTagFormatter);
}

private static string BuildResult(Func<string, string> snippetProvider, Match match, out string prefix, out StringBuilder builder)
private static async ValueTask<string> BuildResult(Func<string, ValueTask<string>> snippetProvider, Match match, string format)
{
var name = match.Groups["name"].Value;
prefix = match.Groups["indent"].Value + "///";
var prefix = match.Groups["indent"].Value + "///";

var snippetText = snippetProvider(name);
var snippetText = await snippetProvider(name);

builder = new StringBuilder();
var builder = new StringBuilder();
foreach (var line in snippetText.Split(Environment.NewLine))
{
builder.Append(prefix);
Expand All @@ -63,7 +60,7 @@ private static string BuildResult(Func<string, string> snippetProvider, Match ma
builder.Length -= Environment.NewLine.Length;
}

return name;
return string.Format(format, name, Environment.NewLine, builder, prefix);
}
}
}
34 changes: 21 additions & 13 deletions eng/SnippetGenerator/DirectoryProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
Expand All @@ -18,7 +19,7 @@ public class DirectoryProcessor
{
private const string _snippetPrefix = "Snippet:";
private readonly string _directory;
private readonly Lazy<List<Snippet>> _snippets;
private readonly Lazy<Task<List<Snippet>>> _snippets;
private static readonly Regex _markdownOnlyRegex = new Regex(
@"(?<indent>\s*)//@@\s*(?<line>.*)",
RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.CultureInvariant);
Expand All @@ -33,10 +34,10 @@ public class DirectoryProcessor
public DirectoryProcessor(string directory)
{
_directory = directory;
_snippets = new Lazy<List<Snippet>>(DiscoverSnippets);
_snippets = new Lazy<Task<List<Snippet>>>(DiscoverSnippetsAsync);
}

public void Process()
public async Task ProcessAsync()
{

List<string> files = new List<string>();
Expand All @@ -45,9 +46,10 @@ public void Process()

foreach (var file in files)
{
string SnippetProvider(string s)
async ValueTask<string> SnippetProvider(string s)
{
var selectedSnippets = _snippets.Value.Where(snip => snip.Name == s).ToArray();
var snippets = await _snippets.Value;
var selectedSnippets = snippets.Where(snip => snip.Name == s).ToArray();
if (selectedSnippets.Length > 1)
{
throw new InvalidOperationException($"Multiple snippets with the name '{s}' defined '{_directory}'");
Expand All @@ -63,16 +65,16 @@ string SnippetProvider(string s)
return FormatSnippet(selectedSnippet.Text);
}

var originalText = File.ReadAllText(file);
var originalText = await File.ReadAllTextAsync(file);

string text;
switch (Path.GetExtension(file))
{
case ".md":
text = MarkdownProcessor.Process(originalText, SnippetProvider);
text = await MarkdownProcessor.ProcessAsync(originalText, SnippetProvider);
break;
case ".cs":
text = CSharpProcessor.Process(originalText, SnippetProvider);
text = await CSharpProcessor.ProcessAsync(originalText, SnippetProvider);
break;
default:
throw new NotSupportedException(file);
Expand All @@ -81,14 +83,14 @@ string SnippetProvider(string s)
if (text != originalText)
{
_utf8EncodingWithoutBOM = new UTF8Encoding(false);
File.WriteAllText(file, text, _utf8EncodingWithoutBOM);
await File.WriteAllTextAsync(file, text, _utf8EncodingWithoutBOM);
}
}
}

private List<Snippet> DiscoverSnippets()
private async Task<List<Snippet>> DiscoverSnippetsAsync()
{
var snippets = GetSnippetsInDirectory(_directory);
var snippets = await GetSnippetsInDirectoryAsync(_directory);
Console.WriteLine($"Discovered snippets:");

foreach (var snippet in snippets)
Expand Down Expand Up @@ -200,15 +202,21 @@ private static bool IsCodeOnlyLine(string line) =>
private static bool IsRegionLine(string line) =>
_regionRegex.IsMatch(line);

private List<Snippet> GetSnippetsInDirectory(string baseDirectory)
private async Task<List<Snippet>> GetSnippetsInDirectoryAsync(string baseDirectory)
{
var list = new List<Snippet>();
foreach (var file in Directory.GetFiles(baseDirectory, "*.cs", SearchOption.AllDirectories))
{
try
{
var text = await File.ReadAllTextAsync(file);
if (!text.Contains($"#region {_snippetPrefix}"))
{
continue;
}

var syntaxTree = CSharpSyntaxTree.ParseText(
File.ReadAllText(file),
text,
new CSharpParseOptions(LanguageVersion.Preview, preprocessorSymbols: _snippetPreprocessorSymbols),
path: file);

Expand Down
7 changes: 4 additions & 3 deletions eng/SnippetGenerator/MarkdownProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace SnippetGenerator
{
Expand All @@ -12,14 +13,14 @@ public class MarkdownProcessor
private static readonly Regex _snippetRegex = new Regex("```\\s*?C#[ ]*?(?<name>[\\w:]+).*?```",
RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.Singleline | RegexOptions.IgnoreCase);

public static string Process(string markdown, Func<string, string> snippetProvider)
public static async Task<string> ProcessAsync(string markdown, Func<string, ValueTask<string>> snippetProvider)
{
return _snippetRegex.Replace(markdown, match =>
return await _snippetRegex.ReplaceAsync(markdown, async match =>
{
var matchGroup = match.Groups["name"];
if (matchGroup.Success)
{
return string.Format(_snippetFormat, matchGroup.Value, Environment.NewLine, snippetProvider(matchGroup.Value));
return string.Format(_snippetFormat, matchGroup.Value, Environment.NewLine, await snippetProvider(matchGroup.Value));
}

return match.Value;
Expand Down
13 changes: 10 additions & 3 deletions eng/SnippetGenerator/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
Expand All @@ -16,16 +17,22 @@ public class Program
[Option(ShortName = "b")]
public string BasePath { get; set; }

public void OnExecuteAsync()
public async Task OnExecuteAsync()
{
var baseDirectory = new DirectoryInfo(BasePath).Name;
if (baseDirectory.Equals("sdk"))
{
Parallel.ForEach(Directory.GetDirectories(BasePath), sdkDir => new DirectoryProcessor(sdkDir).Process());
var tasks = new List<Task>();
foreach (var sdkDir in Directory.GetDirectories(BasePath))
{
tasks.Add(new DirectoryProcessor(sdkDir).ProcessAsync());
}

await Task.WhenAll(tasks);
}
else
{
new DirectoryProcessor(BasePath).Process();
await new DirectoryProcessor(BasePath).ProcessAsync();
}
}

Expand Down
30 changes: 30 additions & 0 deletions eng/SnippetGenerator/RegexAsyncExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace SnippetGenerator
{
internal static class RegexAsyncExtensions
{
public static async ValueTask<string> ReplaceAsync(this Regex regex, string input, Func<Match, ValueTask<string>> replacementFn)
{
var sb = new StringBuilder();
var lastIndex = 0;

foreach (Match match in regex.Matches(input))
{
sb.Append(input, lastIndex, match.Index - lastIndex)
.Append(await replacementFn(match).ConfigureAwait(false));

lastIndex = match.Index + match.Length;
}

sb.Append(input, lastIndex, input.Length - lastIndex);
return sb.ToString();
}
}
}
2 changes: 1 addition & 1 deletion eng/SnippetGenerator/SnippetGenerator.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.1</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
Expand Down
2 changes: 1 addition & 1 deletion eng/scripts/Update-Snippets.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ if ($ServiceDirectory -and ($ServiceDirectory -ne "*")) {
$root += '/' + $ServiceDirectory
}

Resolve-Path "$root" | %{ dotnet run -p $generatorProject -b "$_" }
Resolve-Path "$root" | %{ dotnet run -p $generatorProject -b "$_" -c Release }