Skip to content

Commit

Permalink
Highlight attribute values from AttributeData
Browse files Browse the repository at this point in the history
  • Loading branch information
Rekkonnect committed Aug 6, 2024
1 parent d94a2d9 commit 32c4b61
Show file tree
Hide file tree
Showing 8 changed files with 385 additions and 127 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ private void DestroyChildrenFromSecondLevel()
if (parentNode is null)
return current;

if (current.NodeLine.AnalysisNodeKind != targetKind)
if (MatchesNodeKind(current.NodeLine.AnalysisNodeKind, targetKind))
{
goto next;
}
Expand All @@ -182,6 +182,18 @@ private void DestroyChildrenFromSecondLevel()
}
}

private static bool MatchesNodeKind(AnalysisNodeKind value, AnalysisNodeKind target)
{
if (target is AnalysisNodeKind.Symbol)
{
return value
is AnalysisNodeKind.Attribute
or AnalysisNodeKind.Symbol;
}

return value == target;
}

protected override void OnPointerExited(PointerEventArgs e)
{
base.OnPointerExited(e);
Expand Down Expand Up @@ -408,7 +420,7 @@ private async Task AwaitExpansion(AnalysisTreeListNode node)
if (current is null)
return null;

if (current.NodeLine.AnalysisNodeKind == TargetAnalysisNodeKind)
if (MatchesNodeKind(current.NodeLine.AnalysisNodeKind, TargetAnalysisNodeKind))
{
return current;
}
Expand Down Expand Up @@ -459,7 +471,7 @@ private async Task AwaitExpansion(AnalysisTreeListNode node)
.Where(s =>
{
var nodeLine = s.NodeLine;
return nodeLine.AnalysisNodeKind == TargetAnalysisNodeKind
return MatchesNodeKind(nodeLine.AnalysisNodeKind, TargetAnalysisNodeKind)
&& nodeLine.DisplaySpan.Contains(span);
})
.ToArray();
Expand Down
176 changes: 176 additions & 0 deletions Syndiesis/Core/AttributeDataViewModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.VisualBasic;
using System.Collections.Immutable;
using System.Threading;

namespace Syndiesis.Core;

public sealed class AttributeDataViewModel
{
public required AttributeData AttributeData { get; init; }
public required ImmutableArray<LinkedAttributeArgument> ConstructorArguments { get; init; }
public required ImmutableArray<LinkedAttributeArgument> NamedArguments { get; init; }

public ImmutableArray<LinkedAttributeArgument> AllArguments
{
get
{
return
[
.. ConstructorArguments,
.. NamedArguments,
];
}
}

private AttributeDataViewModel() { }

public static AttributeDataViewModel? Create(
AttributeData data,
CancellationToken cancellationToken = default)
{
var syntax = data.ApplicationSyntaxReference?.GetSyntax(cancellationToken);
if (syntax is null)
return null;

if (data.HasNoArguments())
return Empty(data);

switch (syntax)
{
case CSharpSyntaxNode csAttribute:
return CreateCSharpModel(data, csAttribute);
case VisualBasicSyntaxNode vbAttribute:
return CreateVisualBasicModel(data, vbAttribute);
default:
return null;
}
}

private static AttributeDataViewModel Empty(AttributeData data)
{
return new()
{
AttributeData = data,
ConstructorArguments = [],
NamedArguments = [],
};
}

private static AttributeDataViewModel? CreateCSharpModel(
AttributeData data, CSharpSyntaxNode csAttribute)
{
var attribute = csAttribute as Microsoft.CodeAnalysis.CSharp.Syntax.AttributeSyntax;
if (attribute is null)
return null;

var argumentList = attribute.ArgumentList;
if (argumentList is null)
return Empty(data);

var arguments = argumentList.Arguments;
if (arguments is [])
return Empty(data);

return ConstructFromArguments(data, arguments);
}

private static AttributeDataViewModel? CreateVisualBasicModel(
AttributeData data, VisualBasicSyntaxNode vbAttribute)
{
var attribute = vbAttribute as Microsoft.CodeAnalysis.VisualBasic.Syntax.AttributeSyntax;
if (attribute is null)
return null;

var argumentList = attribute.ArgumentList;
if (argumentList is null)
return Empty(data);

var arguments = argumentList.Arguments;
if (arguments is [])
return Empty(data);

return ConstructFromArguments(data, arguments);
}

private static AttributeDataViewModel ConstructFromArguments<TSyntax>(
AttributeData data, SeparatedSyntaxList<TSyntax> arguments)
where TSyntax : SyntaxNode
{
ImmutableArray<LinkedAttributeArgument> regularArguments = [];
ImmutableArray<LinkedAttributeArgument> namedArguments = [];

if (data.ConstructorArguments is not [] and var constructorArgumentsData)
{
var regularArgumentsBuilder = ImmutableArray.CreateBuilder<LinkedAttributeArgument>(
constructorArgumentsData.Length);

// We always have a constructor if we have constructor arguments
// We expect a potential breaking API change that returns a null
// constructor with a non-empty argument list, in which case we show the param order
var constructor = data.AttributeConstructor;
var constructorParameters = constructor?.Parameters;
var mappingKind =
constructorParameters is not null
? AttributeArgumentNameMappingKind.Parameter
: AttributeArgumentNameMappingKind.ParameterIndex;
for (int i = 0; i < constructorArgumentsData.Length; i++)
{
var argumentSyntax = arguments[i];
var value = constructorArgumentsData[i];
var parameter = constructorParameters?[i];
var displayName = parameter?.Name ?? i.ToString();
regularArgumentsBuilder.Add(new(
argumentSyntax,
displayName,
value,
mappingKind));
}
regularArguments = regularArgumentsBuilder.ToImmutable();
}

if (data.NamedArguments is not [] and var namedArgumentsData)
{
var namedArgumentsBuilder = ImmutableArray.CreateBuilder<LinkedAttributeArgument>(
namedArgumentsData.Length);

int offset = data.ConstructorArguments.Length;

for (int i = 0; i < namedArgumentsData.Length; i++)
{
var argumentSyntax = arguments[i + offset];
var kvp = namedArgumentsData[i];
var name = kvp.Key;
var value = kvp.Value;
namedArgumentsBuilder.Add(new(
argumentSyntax,
name,
value,
AttributeArgumentNameMappingKind.Named));
}
namedArguments = namedArgumentsBuilder.ToImmutable();
}

return new()
{
AttributeData = data,
ConstructorArguments = regularArguments,
NamedArguments = namedArguments,
};
}

public sealed record class LinkedAttributeArgument(
SyntaxNode ArgumentSyntax,
string Name,
TypedConstant Value,
AttributeArgumentNameMappingKind MappingKind)
;

public enum AttributeArgumentNameMappingKind
{
Parameter,
ParameterIndex,
Named,
}
}
17 changes: 11 additions & 6 deletions Syndiesis/Core/AttributeTree.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Microsoft.CodeAnalysis;
using Garyon.Functions;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Operations;
using Microsoft.CodeAnalysis.VisualBasic;
Expand Down Expand Up @@ -121,17 +122,21 @@ ImmutableArray<AttributeDataView> DataViews(
ImmutableArray<AttributeData> data)
{
return data.Select(FromAttribute)
.ToImmutableArray();
.Where(Predicates.NotNull)
.ToImmutableArray()!;
}

AttributeDataView FromAttribute(AttributeData data)
AttributeDataView? FromAttribute(AttributeData data)
{
var model = AttributeDataViewModel.Create(data);
if (model is null)
return null;
var syntax = data.ApplicationSyntaxReference?.GetSyntax(cancellationToken);
if (syntax is null)
return new(data, null);
return new(model, null);

var operation = semanticModel.GetOperation(syntax, cancellationToken);
return new AttributeDataView(data, operation as IAttributeOperation);
return new AttributeDataView(model, operation as IAttributeOperation);
}
}

Expand Down Expand Up @@ -220,5 +225,5 @@ public sealed record SymbolContainer(
ISymbol Symbol, ImmutableArray<AttributeDataView> Attributes);

public sealed record AttributeDataView(
AttributeData Data, IAttributeOperation? AttributeOperation);
AttributeDataViewModel Data, IAttributeOperation? AttributeOperation);
}
Loading

0 comments on commit 32c4b61

Please sign in to comment.