Skip to content

Commit

Permalink
Added property support, for example; localStorage.length.
Browse files Browse the repository at this point in the history
  • Loading branch information
IEvangelist committed Mar 8, 2022
1 parent 92bd380 commit c2e8289
Show file tree
Hide file tree
Showing 7 changed files with 109 additions and 35 deletions.
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,15 @@ public static partial class SynchronousLocalStorageExtensions
"window.localStorage.setItem",
key,
value.ToJson(options));

/// <summary>
/// Source generated extension method implementation of <c>window.localStorage.length</c>.
/// <a href="https://developer.mozilla.org/docs/Web/API/Storage/length"></a>
/// </summary>
public static double Length(
this IJSInProcessRuntime javaScript) =>
javaScript.Invoke<double>(
"eval", "window.localStorage.length");
}
```

Expand Down Expand Up @@ -228,6 +237,15 @@ public static partial class AsynchronousLocalStorageExtensions
"window.localStorage.setItem",
key,
value);

/// <summary>
/// Source generated extension method implementation of <c>window.localStorage.length</c>.
/// <a href="https://developer.mozilla.org/docs/Web/API/Storage/length"></a>
/// </summary>
public static ValueTask<double> LengthAsync(
this IJSRuntime javaScript) =>
javaScript.InvokeAsync<double>(
"eval", "window.localStorage.length");
}
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,5 @@ public static IServiceCollection AddInProcessJavaScript(
this IServiceCollection services) =>
services.AddScoped<IJSInProcessRuntime>(
serviceProvider =>
(IJSInProcessRuntime)serviceProvider.GetRequiredService<IJSRuntime>())
.AddScoped<IJSUnmarshalledRuntime>(
serviceProvider =>
(IJSUnmarshalledRuntime)serviceProvider.GetRequiredService<IJSRuntime>());
(IJSInProcessRuntime)serviceProvider.GetRequiredService<IJSRuntime>());
}
15 changes: 15 additions & 0 deletions src/Blazor.SourceGenerators/Builders/Indentation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright (c) David Pine. All rights reserved.
// Licensed under the MIT License.

internal readonly record struct Indentation(int Level)
{
private readonly int _spaces = 4;

internal Indentation Reset() => ResetTo(0);
internal Indentation ResetTo(int level) => this with { Level = level };
internal Indentation Increase(int extra = 0) => this with { Level = Level + 1 + extra };
internal Indentation Decrease(int extra = 0) => this with { Level = Level - 1 - extra };

public override string ToString() =>
new(' ', _spaces * Level);
}
68 changes: 57 additions & 11 deletions src/Blazor.SourceGenerators/CSharp/CSharpExtensionObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

namespace Blazor.SourceGenerators.CSharp;

internal sealed record CSharpExtensionObject(string RawTypeName)
internal sealed partial record CSharpExtensionObject(string RawTypeName)
{
private List<CSharpMethod>? _methods = null!;
private List<CSharpProperty>? _properties = null!;
Expand Down Expand Up @@ -46,6 +46,7 @@ internal string ToStaticPartialClassString(
indentation = indentation.Increase();
var methodLevel = indentation.Level;

// Add methods.
foreach (var (index, method) in (Methods ?? new List<CSharpMethod>()).Select())
{
var isVoid = method.RawReturnTypeName == "void";
Expand Down Expand Up @@ -246,6 +247,40 @@ internal string ToStaticPartialClassString(
}
}

// Add properties.
foreach (var (index, property) in (Properties ?? new List<CSharpProperty>()).Select())
{
if (index.IsFirst) builder.Append("\r\n");
if (property.IsIndexer) continue;

indentation = indentation.ResetTo(methodLevel);

var (suffix, extendingType) = options.IsWebAssembly ? ("", "IJSInProcessRuntime") : ("Async", "IJSRuntime");
var csharpMethodName = property.RawName.CapitalizeFirstLetter();
var javaScriptIndentifier = options.PathFromWindow is not null
? $"{options.PathFromWindow}.{property.RawName}"
: property.RawName;
var (returnType, bareType) = GetMethodTypes(property, options);
var genericTypeArgs = $"<{bareType}>";

AppendTripleSlashComments(builder, property, options, indentation);

builder.Append($"{indentation}public static {returnType} {csharpMethodName}{suffix}(\r\n");
indentation = indentation.Increase();

builder.Append($"{indentation}this {extendingType} javaScript) =>\r\n");
indentation = indentation.Increase();

builder.Append($"{indentation}javaScript.Invoke{suffix}{genericTypeArgs}(\r\n");
indentation = indentation.Increase();

builder.Append($"{indentation}\"eval\", \"{javaScriptIndentifier}\");\r\n");
if (!index.IsLast)
{
builder.Append("\r\n");
}
}

AppendClosingCurlyBrace(builder, indentation.Reset());

var staticPartialClassDefinition = builder.ToString();
Expand Down Expand Up @@ -347,6 +382,22 @@ static void AppendTripleSlashComments(
builder.Append($"{indent}/// </summary>\r\n");
}

static void AppendTripleSlashComments(
StringBuilder builder, CSharpProperty property, GeneratorOptions options, Indentation indentation)
{
var indent = indentation.ToString();
builder.Append($"{indent}/// <summary>\r\n");

var jsMethodName = property.RawName.LowerCaseFirstLetter();
var func = $"{options.PathFromWindow}.{jsMethodName}";

builder.Append($"{indent}/// Source generated extension method implementation of <c>{func}</c>.\r\n");
var rootUrl = "https://developer.mozilla.org/docs/Web/API";
var fullUrl = $"{rootUrl}/{options.TypeName}/{property.RawName.LowerCaseFirstLetter()}";
builder.Append($"{indent}/// <a href=\"{fullUrl}\"></a>\r\n");
builder.Append($"{indent}/// </summary>\r\n");
}

static (string ReturnType, string BareType) GetMethodTypes(
bool isGenericReturnType, bool isPrimitiveType, bool isVoid, CSharpMethod method, GeneratorOptions options)
{
Expand Down Expand Up @@ -386,16 +437,11 @@ static void AppendTripleSlashComments(
}
}

private readonly record struct Indentation(int Level)
static (string ReturnType, string BareType) GetMethodTypes(
CSharpProperty property, GeneratorOptions options)
{
private readonly int _spaces = 4;

internal Indentation Reset() => ResetTo(0);
internal Indentation ResetTo(int level) => this with { Level = level };
internal Indentation Increase(int extra = 0) => this with { Level = Level + 1 + extra };
internal Indentation Decrease(int extra = 0) => this with { Level = Level - 1 - extra };

public override string ToString() =>
new(' ', _spaces * Level);
return (
ReturnType: options.IsWebAssembly ? property.MappedTypeName : $"ValueTask<{property.MappedTypeName}>",
BareType: property.MappedTypeName);
}
}
2 changes: 2 additions & 0 deletions src/Blazor.SourceGenerators/CSharp/CSharpProperty.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,6 @@ internal record CSharpProperty(
bool IsReadonly = false) : CSharpType(RawName, RawTypeName, IsNullable)
{
public string MappedTypeName => TypeMap.PrimitiveTypes[RawTypeName];

public bool IsIndexer => RawName.StartsWith("[") && RawName.EndsWith("]");
}
28 changes: 12 additions & 16 deletions src/Blazor.SourceGenerators/JavaScript/JavaScriptMethod.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,19 @@

namespace Blazor.SourceGenerators.JavaScript;

/// <summary>
/// An object that represents a JavaScript method.
/// </summary>
/// <param name="JavaScriptApiMethodName">The exact name of the JavaScript API method.</param>
/// <param name="InvokableMethodName">
/// The invokable method name, when different than
/// the <paramref name="JavaScriptApiMethodName"/>
/// the method is not considered pure.</param>
/// <param name="ParameterDefinitions">The optional listing of method parameters.</param>
internal sealed record JavaScriptMethod(
// <summary>
// The exact name of the JavaScript API method.
// </summary>
string JavaScriptApiMethodName,

// <summary>
// The invokable method name, when different than
// the <paramref name="JavaScriptApiMethodName"/>
// the method is not considered pure.
// </summary>
string? InvokableMethodName = null,

// <summary>
// The optional listing of method parameters.
// </summary>
List<CSharpType>? ParameterDefinitions = null)
string JavaScriptApiMethodName,
string? InvokableMethodName = null,
List<CSharpType>? ParameterDefinitions = null)
{
/// <summary>
/// A "pure" JavaScript method is one that
Expand Down
8 changes: 4 additions & 4 deletions src/Blazor.SourceGenerators/JavaScriptInteropGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ internal sealed partial class JavaScriptInteropGenerator : ISourceGenerator
public void Initialize(GeneratorInitializationContext context)
{
#if DEBUG
if (!System.Diagnostics.Debugger.IsAttached)
{
System.Diagnostics.Debugger.Launch();
}
//if (!System.Diagnostics.Debugger.IsAttached)
//{
// System.Diagnostics.Debugger.Launch();
//}
#endif

// Register a syntax receiver that will be created for each generation pass
Expand Down

0 comments on commit c2e8289

Please sign in to comment.