Skip to content

Commit

Permalink
Add NetCore Support
Browse files Browse the repository at this point in the history
  • Loading branch information
efreykongcn committed Feb 23, 2022
1 parent 78fb0c2 commit c062712
Show file tree
Hide file tree
Showing 16 changed files with 807 additions and 46 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -214,3 +214,4 @@ pip-log.txt

#Mr Developer
.mr.developer.cfg
/Ref12.Tests/Fixtures/TestBed
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Road map

- [x] Support VS2022
- [ ] Support .NetCore project
- [x] Support .NetCore project
- [ ] Open source code in Visual Studio code editor


Expand All @@ -10,6 +10,9 @@
These are the changes to each version that has been released
on the official Visual Studio extension gallery.

## 5.1.0
-[x] Add .NetCore support. Ref12 extension is enabled in both .NetFramework and .NetCore project now.

## 5.0.0

- [x] Fixed Fix Ref12 fails to load reference URL SLaks#38.
Expand Down
38 changes: 21 additions & 17 deletions Ref12.Shared/Commands/GoToDefinitionInterceptor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Diagnostics;
using System.Linq;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.TextManager.Interop;
Expand All @@ -27,23 +28,26 @@ public GoToDefinitionInterceptor(IEnumerable<IReferenceSourceProvider> reference
static ISymbolResolver CreateRoslynResolver() { return new RoslynSymbolResolver(); }

protected override bool Execute(VSConstants.VSStd97CmdID commandId, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut) {
ISymbolResolver resolver = null;
SnapshotPoint? caretPoint = TextView.GetCaretPoint(s => resolvers.TryGetValue(s.ContentType.TypeName, out resolver));
if (caretPoint == null)
return false;

var symbol = resolver.GetSymbolAt(doc.FilePath, caretPoint.Value);
if (symbol == null || symbol.HasLocalSource)
return false;

var target = references.FirstOrDefault(r => r.AvailableAssemblies.Contains(symbol.AssemblyName));
if (target == null)
return false;

Debug.WriteLine("Ref12: Navigating to IndexID " + symbol.IndexId);

target.Navigate(symbol);
return true;
return ThreadHelper.JoinableTaskFactory.Run(async () =>
{
ISymbolResolver resolver = null;
SnapshotPoint? caretPoint = TextView.GetCaretPoint(s => resolvers.TryGetValue(s.ContentType.TypeName, out resolver));
if (caretPoint == null)
return false;

var symbol = await resolver.GetSymbolAtAsync(doc.FilePath, caretPoint.Value);
if (symbol == null || symbol.HasLocalSource)
return false;

var target = references.Where(r => r.Supports(symbol.TargetFramework)).FirstOrDefault(r => r.AvailableAssemblies.Contains(symbol.AssemblyName));
if (target == null)
return false;

Debug.WriteLine("Ref12: Navigating to IndexID " + symbol.IndexId);

target.Navigate(symbol);
return true;
});
}

protected override bool IsEnabled() {
Expand Down
20 changes: 20 additions & 0 deletions Ref12.Shared/Extensions.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
using System;
using System.Globalization;
using System.Linq;
using ICSharpCode.Decompiler.Metadata;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.OLE.Interop;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.Text.Projection;
using SLaks.Ref12.Services;

namespace SLaks.Ref12 {
public static class Extensions {
Expand All @@ -32,5 +34,23 @@ private static bool IsSourceBuffer(IProjectionBufferBase top, ITextBuffer bottom
var c = commandId.GetType().GUID;
ErrorHandler.ThrowOnFailure(target.Exec(ref c, Convert.ToUInt32(commandId, CultureInfo.InvariantCulture), execOptions, inHandle, outHandle));
}

public static IAssemblyResolver GetAssemblyResolver(this PEFile file, bool loadOnDemand = true)
{
return GetLoadedAssembly(file).GetAssemblyResolver(loadOnDemand);
}
public static LoadedAssembly GetLoadedAssembly(this PEFile file)
{
if (file == null)
throw new ArgumentNullException(nameof(file));
LoadedAssembly loadedAssembly;
lock (LoadedAssembly.loadedAssemblies)
{
if (!LoadedAssembly.loadedAssemblies.TryGetValue(file, out loadedAssembly))
throw new ArgumentException("The specified file is not associated with a LoadedAssembly!");
}
return loadedAssembly;
}

}
}
2 changes: 2 additions & 0 deletions Ref12.Shared/Ref12.Shared.projitems
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@
<Compile Include="$(MSBuildThisFileDirectory)Extensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Ref12Package.cs" />
<Compile Include="$(MSBuildThisFileDirectory)RoslynAssemblyRedirector.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Services\AssemblyFileFinder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Services\ILogger.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Services\IndexIdTranslator.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Services\ISymbolResolver.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Services\LoadedAssembly.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Services\ReferenceSourceProvider.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Services\RoslynSymbolResolver.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Services\VsLogger.cs" />
Expand Down
145 changes: 145 additions & 0 deletions Ref12.Shared/Services/AssemblyFileFinder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
using System;
using System.Linq;
using System.Text.RegularExpressions;
using ICSharpCode.Decompiler.Metadata;

namespace SLaks.Ref12.Services
{
public class AssemblyFileFinder
{
public static string FindAssemblyFile(Mono.Cecil.AssemblyDefinition assemblyDefinition, string assemblyFile)
{
string tfi = DetectTargetFrameworkId(assemblyDefinition, assemblyFile);
UniversalAssemblyResolver assemblyResolver;
if (IsReferenceAssembly(assemblyDefinition, assemblyFile))
{
assemblyResolver = new UniversalAssemblyResolver(null, throwOnError: false, tfi);
}
else
{
assemblyResolver = new UniversalAssemblyResolver(assemblyFile, throwOnError: false, tfi);
}

return assemblyResolver.FindAssemblyFile(AssemblyNameReference.Parse(assemblyDefinition.Name.FullName));
}

static readonly string RefPathPattern = @"NuGetFallbackFolder[/\\][^/\\]+[/\\][^/\\]+[/\\]ref[/\\]";

public static bool IsReferenceAssembly(Mono.Cecil.AssemblyDefinition assemblyDef, string assemblyFile)
{
if (assemblyDef.CustomAttributes.Any(ca => ca.AttributeType.FullName == "System.Runtime.CompilerServices.ReferenceAssemblyAttribute"))
return true;

// Try to detect reference assembly through specific path pattern
var refPathMatch = Regex.Match(assemblyFile, RefPathPattern, RegexOptions.IgnoreCase | RegexOptions.Compiled);
return refPathMatch.Success;
}

static readonly string DetectTargetFrameworkIdRefPathPattern =
@"(Reference Assemblies[/\\]Microsoft[/\\]Framework[/\\](?<1>.NETFramework)[/\\]v(?<2>[^/\\]+)[/\\])" +
@"|((NuGetFallbackFolder|packs|.nuget[/\\]packages)[/\\](?<1>[^/\\]+)\\(?<2>[^/\\]+)([/\\].*)?[/\\]ref[/\\])";

public static TargetFramework DetectTargetFramework(Mono.Cecil.AssemblyDefinition assembly, string assemblyPath = null)
{
var targetFrameworkId = DetectTargetFrameworkId(assembly, assemblyPath);
string[] tokens = targetFrameworkId.Split(',');
TargetFrameworkIdentifier identifier;

switch (tokens[0].Trim().ToUpperInvariant())
{
case ".NETCOREAPP":
identifier = TargetFrameworkIdentifier.NETCoreApp;
break;
case ".NETSTANDARD":
identifier = TargetFrameworkIdentifier.NETStandard;
break;
case "SILVERLIGHT":
identifier = TargetFrameworkIdentifier.Silverlight;
break;
default:
identifier = TargetFrameworkIdentifier.NETFramework;
break;
}

Version version = null;

for (int i = 1; i < tokens.Length; i++)
{
var pair = tokens[i].Trim().Split('=');

if (pair.Length != 2)
continue;

switch (pair[0].Trim().ToUpperInvariant())
{
case "VERSION":
var versionString = pair[1].TrimStart('v', ' ', '\t');
if (identifier == TargetFrameworkIdentifier.NETCoreApp ||
identifier == TargetFrameworkIdentifier.NETStandard)
{
if (versionString.Length == 3)
versionString += ".0";
}
if (!Version.TryParse(versionString, out version))
version = null;
break;
}
}

return new TargetFramework(identifier, version ?? new Version(0, 0, 0, 0));
}
public static string DetectTargetFrameworkId(Mono.Cecil.AssemblyDefinition assembly, string assemblyPath = null)
{
if (assembly == null)
throw new ArgumentNullException(nameof(assembly));

const string TargetFrameworkAttributeName = "System.Runtime.Versioning.TargetFrameworkAttribute";

foreach (var attribute in assembly.CustomAttributes)
{
if (attribute.AttributeType.FullName != TargetFrameworkAttributeName)
continue;
if (attribute.HasConstructorArguments)
{
if (attribute.ConstructorArguments[0].Value is string value)
return value;
}
}

// Optionally try to detect target version through assembly path as a fallback (use case: reference assemblies)
if (assemblyPath != null)
{
/*
* Detected path patterns (examples):
*
* - .NETFramework -> C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.6.1\mscorlib.dll
* - .NETCore -> C:\Program Files\dotnet\sdk\NuGetFallbackFolder\microsoft.netcore.app\2.1.0\ref\netcoreapp2.1\System.Console.dll
* -> C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\3.0.0\ref\netcoreapp3.0\System.Runtime.Extensions.dll
* - .NETStandard -> C:\Program Files\dotnet\sdk\NuGetFallbackFolder\netstandard.library\2.0.3\build\netstandard2.0\ref\netstandard.dll
*/
var pathMatch = Regex.Match(assemblyPath, DetectTargetFrameworkIdRefPathPattern,
RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.ExplicitCapture);
if (pathMatch.Success)
{
var type = pathMatch.Groups[1].Value;
var version = pathMatch.Groups[2].Value;

if (type == ".NETFramework")
{
return $".NETFramework,Version=v{version}";
}
else if (type.ToLower().Contains("netcore"))
{
return $".NETCoreApp,Version=v{version}";
}
else if (type.ToLower().Contains("netstandard"))
{
return $".NETStandard,Version=v{version}";
}
}
}

return string.Empty;
}
}
}
24 changes: 19 additions & 5 deletions Ref12.Shared/Services/ISymbolResolver.cs
Original file line number Diff line number Diff line change
@@ -1,24 +1,38 @@
using System.IO;
using System;
using System.IO;
using ICSharpCode.Decompiler.Metadata;
using Microsoft.VisualStudio.Text;

namespace SLaks.Ref12.Services {
public interface ISymbolResolver {
SymbolInfo GetSymbolAt(string sourceFileName, SnapshotPoint point);
System.Threading.Tasks.Task<SymbolInfo> GetSymbolAtAsync(string sourceFileName, SnapshotPoint point);
}
public class SymbolInfo {
public SymbolInfo(string indexId, bool isLocal, string assemblyPath) : this(indexId, isLocal, assemblyPath, Path.GetFileNameWithoutExtension(assemblyPath)) { }
public SymbolInfo(string indexId, bool isLocal, string assemblyPath, string assemblyName) {
public SymbolInfo(TargetFramework targetFramework, string indexId, bool isLocal, string assemblyPath) : this(targetFramework, indexId, isLocal, assemblyPath, Path.GetFileNameWithoutExtension(assemblyPath)) { }
public SymbolInfo(TargetFramework targetFramework, string indexId, bool isLocal, string assemblyPath, string assemblyName) {
this.TargetFramework = targetFramework;
this.IndexId = indexId;
this.AssemblyPath = assemblyPath;
this.AssemblyName = assemblyName;
this.HasLocalSource = isLocal;
}

public TargetFramework TargetFramework { get; }
public string IndexId { get; private set; }
public string AssemblyPath { get; private set; }
public string AssemblyName { get; private set; }

///<summary>Indicates whether this symbol is defined in the current solution.</summary>
public bool HasLocalSource { get; private set; }
}

public sealed class TargetFramework
{
public TargetFramework(TargetFrameworkIdentifier targetFrameworkIdentifier, Version version)
{
this.Identifier = targetFrameworkIdentifier;
this.Version = version;
}
public TargetFrameworkIdentifier Identifier { get; }
public Version Version { get; }
}
}
Loading

0 comments on commit c062712

Please sign in to comment.