Skip to content

Commit

Permalink
Merge pull request #38 from sharwell/opt-mem
Browse files Browse the repository at this point in the history
Eliminate the Solution references in TypeTarget and MemberTarget
  • Loading branch information
sharwell authored Oct 10, 2017
2 parents 2e7d6ab + 5ed4263 commit abf7919
Show file tree
Hide file tree
Showing 7 changed files with 201 additions and 51 deletions.
86 changes: 86 additions & 0 deletions Tvl.VisualStudio.InheritanceMargin.CSharp/AbstractTarget.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved.
// Licensed under the MIT License. See LICENSE.txt in the project root for license information.

namespace Tvl.VisualStudio.InheritanceMargin.CSharp
{
using System.Collections.Generic;
using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Text;

internal abstract class AbstractTarget : IInheritanceTarget
{
private readonly SourceTextContainer _textContainer;
private readonly ProjectId _projectId;
private readonly string _displayName;
private readonly KeyValuePair<SymbolKind, string>[] _symbolPath;

protected AbstractTarget(SourceTextContainer textContainer, Project currentProject, Solution solution, ISymbol symbol)
{
Project project = solution.GetProject(symbol.ContainingAssembly) ?? currentProject;

_textContainer = textContainer;
_projectId = project.Id;
_displayName = symbol.ToString();

List<KeyValuePair<SymbolKind, string>> symbolPath = new List<KeyValuePair<SymbolKind, string>>();
for (ISymbol currentSymbol = symbol.OriginalDefinition; currentSymbol != null; currentSymbol = currentSymbol.ContainingSymbol)
{
ImmutableArray<IParameterSymbol> parameters = default(ImmutableArray<IParameterSymbol>);
ImmutableArray<ITypeParameterSymbol> typeParameters = default(ImmutableArray<ITypeParameterSymbol>);
switch (currentSymbol.Kind)
{
case SymbolKind.Method:
parameters = ((IMethodSymbol)currentSymbol).Parameters;
typeParameters = ((IMethodSymbol)currentSymbol).TypeParameters;
break;

case SymbolKind.Property:
parameters = ((IPropertySymbol)currentSymbol).Parameters;
break;

default:
break;
}

if (!parameters.IsDefaultOrEmpty)
{
for (int i = parameters.Length - 1; i >= 0; i--)
{
symbolPath.Add(new KeyValuePair<SymbolKind, string>(SymbolKind.Parameter, parameters[i].ToString()));
}
}

if (currentSymbol.Kind == SymbolKind.Namespace && ((INamespaceSymbol)currentSymbol).IsGlobalNamespace)
break;

if (currentSymbol.Kind == SymbolKind.NetModule || currentSymbol.Kind == SymbolKind.Assembly)
break;

string metadataName = currentSymbol.MetadataName;
if (currentSymbol.Kind == SymbolKind.Method && !typeParameters.IsDefaultOrEmpty)
metadataName = metadataName + '`' + typeParameters.Length;

symbolPath.Add(new KeyValuePair<SymbolKind, string>(currentSymbol.Kind, metadataName));
}

symbolPath.Reverse();
_symbolPath = symbolPath.ToArray();
}

/// <inheritdoc/>
public string DisplayName
{
get
{
return _displayName;
}
}

/// <inheritdoc/>
public void NavigateTo()
{
CSharpInheritanceAnalyzer.NavigateToSymbol(_textContainer, _projectId, _symbolPath);
}
}
}
109 changes: 106 additions & 3 deletions Tvl.VisualStudio.InheritanceMargin.CSharp/CSharpInheritanceAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ public override string Name
}
}

public static void NavigateToSymbol(SourceTextContainer textContainer, ISymbol symbol, Project project)
public static void NavigateToSymbol(SourceTextContainer textContainer, ProjectId projectId, KeyValuePair<SymbolKind, string>[] symbolPath)
{
Workspace workspace;
if (!Workspace.TryGetWorkspace(textContainer, out workspace))
Expand All @@ -255,9 +255,112 @@ public static void NavigateToSymbol(SourceTextContainer textContainer, ISymbol s
if (visualStudioWorkspace == null)
return;

var project = visualStudioWorkspace.CurrentSolution.GetProject(projectId);
if (project == null)
return;

var compilation = project.GetCompilationAsync(CancellationToken.None).GetAwaiter().GetResult();
if (compilation == null)
return;

ImmutableArray<ISymbol> currentSymbols = ImmutableArray.Create<ISymbol>(compilation.GlobalNamespace);
int firstParameterIndex = symbolPath.Length;
for (int i = 0; i < symbolPath.Length; i++)
{
bool complete = false;
var pair = symbolPath[i];
switch (pair.Key)
{
case SymbolKind.Namespace:
// The current symbols must be namespaces
currentSymbols = currentSymbols.SelectMany(currentSymbol => ((INamespaceSymbol)currentSymbol).GetNamespaceMembers().Where(ns => ns.Name == pair.Value)).Select(ns => (ISymbol)ns).ToImmutableArray();
continue;

case SymbolKind.NamedType:
// The current symbol must be a namespaces or types
GetNameAndArity(pair.Value, out string typeName, out int arity);
currentSymbols = currentSymbols.SelectMany(currentSymbol => ((INamespaceOrTypeSymbol)currentSymbol).GetTypeMembers(typeName, arity)).Select(sym => (ISymbol)sym).ToImmutableArray();
continue;

case SymbolKind.Property:
case SymbolKind.Event:
currentSymbols = currentSymbols.SelectMany(currentSymbol => ((INamedTypeSymbol)currentSymbol).GetMembers().Where(sym => sym.Kind == pair.Key && sym.MetadataName == pair.Value)).ToImmutableArray();
firstParameterIndex = i + 1;
complete = true;
break;

case SymbolKind.Method:
GetNameAndArity(pair.Value, out string memberName, out arity);
currentSymbols = currentSymbols.SelectMany(currentSymbol => ((INamedTypeSymbol)currentSymbol).GetMembers().Where(sym => sym.MetadataName == memberName && ((IMethodSymbol)sym).Arity == arity && sym.Kind == pair.Key)).ToImmutableArray();
firstParameterIndex = i + 1;
complete = true;
break;

default:
return;
}

if (complete)
break;
}

if (firstParameterIndex < symbolPath.Length)
{
string[] parameters = symbolPath.Skip(firstParameterIndex).Select(pair => pair.Value).ToArray();
Func<ISymbol, bool> matchesSignature =
sym =>
{
ImmutableArray<IParameterSymbol> parameterSymbols;
switch (sym.Kind)
{
case SymbolKind.Property:
parameterSymbols = ((IPropertySymbol)sym).Parameters;
break;
case SymbolKind.Method:
parameterSymbols = ((IMethodSymbol)sym).Parameters;
break;
default:
return false;
}
if (parameterSymbols.Length != parameters.Length)
return false;
for (int i = 0; i < parameters.Length; i++)
{
if (parameterSymbols[i].ToString() != parameters[i])
return false;
}
return true;
};

currentSymbols = currentSymbols.Where(matchesSignature).ToImmutableArray();
}

ISymbol symbol = currentSymbols.FirstOrDefault();
if (symbol == null)
return;

visualStudioWorkspace.TryGoToDefinition(symbol, project, CancellationToken.None);
}

private static void GetNameAndArity(string metadataName, out string typeName, out int arity)
{
int backtick = metadataName.IndexOf('`');
if (backtick == -1)
{
typeName = metadataName;
arity = 0;
return;
}

arity = int.Parse(metadataName.Substring(backtick + 1));
typeName = metadataName.Substring(0, backtick);
}

private static async Task<IEnumerable<INamedTypeSymbol>> GetDerivedInterfacesFromImmediatelyDerivedAsync(
INamedTypeSymbol type,
Solution solution,
Expand Down Expand Up @@ -388,7 +491,7 @@ protected override void ReParseImpl()

InheritanceGlyph tag = typeSymbol.TypeKind == TypeKind.Interface ? InheritanceGlyph.HasImplementations : InheritanceGlyph.Overridden;

var targets = derivedTypes.Select(i => new TypeTarget(textContainer, i, solution));
var targets = derivedTypes.Select(i => new TypeTarget(textContainer, project, solution, i));
tags.Add(new TagSpan<IInheritanceTag>(span, _tagFactory.CreateTag(tag, builder.ToString().TrimEnd(), targets)));
}

Expand Down Expand Up @@ -537,7 +640,7 @@ protected override void ReParseImpl()
members.AddRange(implementingMethods);
members.AddRange(overridingMethods);

var targets = members.Select(i => new MemberTarget(textContainer, i, solution));
var targets = members.Select(i => new MemberTarget(textContainer, project, solution, i));
tags.Add(new TagSpan<IInheritanceTag>(span, _tagFactory.CreateTag(tag, builder.ToString().TrimEnd(), targets)));
}
}
Expand Down
27 changes: 3 additions & 24 deletions Tvl.VisualStudio.InheritanceMargin.CSharp/MemberTarget.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,32 +6,11 @@ namespace Tvl.VisualStudio.InheritanceMargin.CSharp
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Text;

internal sealed class MemberTarget : IInheritanceTarget
internal sealed class MemberTarget : AbstractTarget
{
private readonly SourceTextContainer _textContainer;
private readonly ISymbol _memberIdentifier;
private readonly Solution _solution;

public MemberTarget(SourceTextContainer textContainer, ISymbol memberIdentifier, Solution solution)
{
_textContainer = textContainer;
_memberIdentifier = memberIdentifier;
_solution = solution;
}

/// <inheritdoc/>
public string DisplayName
{
get
{
return _memberIdentifier.ToString();
}
}

/// <inheritdoc/>
public void NavigateTo()
public MemberTarget(SourceTextContainer textContainer, Project currentProject, Solution solution, ISymbol symbol)
: base(textContainer, currentProject, solution, symbol)
{
CSharpInheritanceAnalyzer.NavigateToSymbol(_textContainer, _memberIdentifier, _solution.GetProject(_memberIdentifier.ContainingAssembly));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Compile Include="AbstractTarget.cs" />
<Compile Include="BackgroundParser.cs" />
<Compile Include="CSharpInheritanceAnalyzer.cs" />
<Compile Include="MemberTarget.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,7 @@
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Compile Include="AbstractTarget.cs" />
<Compile Include="BackgroundParser.cs" />
<Compile Include="CSharpInheritanceAnalyzer.cs" />
<Compile Include="MemberTarget.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Compile Include="AbstractTarget.cs" />
<Compile Include="BackgroundParser.cs" />
<Compile Include="CSharpInheritanceAnalyzer.cs" />
<Compile Include="MemberTarget.cs" />
Expand Down
27 changes: 3 additions & 24 deletions Tvl.VisualStudio.InheritanceMargin.CSharp/TypeTarget.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,32 +6,11 @@ namespace Tvl.VisualStudio.InheritanceMargin.CSharp
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Text;

internal sealed class TypeTarget : IInheritanceTarget
internal sealed class TypeTarget : AbstractTarget
{
private readonly SourceTextContainer _textContainer;
private readonly ISymbol _typeIdentifier;
private readonly Solution _solution;

public TypeTarget(SourceTextContainer textContainer, ISymbol typeIdentifier, Solution solution)
{
_textContainer = textContainer;
_typeIdentifier = typeIdentifier;
_solution = solution;
}

/// <inheritdoc/>
public string DisplayName
{
get
{
return _typeIdentifier.ToString();
}
}

/// <inheritdoc/>
public void NavigateTo()
public TypeTarget(SourceTextContainer textContainer, Project currentProject, Solution solution, ISymbol symbol)
: base(textContainer, currentProject, solution, symbol)
{
CSharpInheritanceAnalyzer.NavigateToSymbol(_textContainer, _typeIdentifier, _solution.GetProject(_typeIdentifier.ContainingAssembly));
}
}
}

0 comments on commit abf7919

Please sign in to comment.