Skip to content

Commit

Permalink
Add quick info (Roslyn)
Browse files Browse the repository at this point in the history
  • Loading branch information
wtfsck committed Sep 18, 2016
1 parent 9d889a6 commit 263550c
Show file tree
Hide file tree
Showing 48 changed files with 3,413 additions and 28 deletions.
7 changes: 7 additions & 0 deletions dnSpy.sln
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dnSpy.Decompiler.ILSpy.Core
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dnSpy.Roslyn.Internal", "dnSpy\dnSpy.Roslyn.Internal\dnSpy.Roslyn.Internal.csproj", "{C5F25F3E-B8EF-4A8F-8BD6-5B9863A56FE2}"
EndProject
Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "dnSpy.Roslyn.VisualBasic.Internal", "dnSpy\dnSpy.Roslyn.VisualBasic.Internal\dnSpy.Roslyn.VisualBasic.Internal.vbproj", "{8B774E77-C956-4ABC-BBFE-8756CB4111C8}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -177,6 +179,10 @@ Global
{C5F25F3E-B8EF-4A8F-8BD6-5B9863A56FE2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C5F25F3E-B8EF-4A8F-8BD6-5B9863A56FE2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C5F25F3E-B8EF-4A8F-8BD6-5B9863A56FE2}.Release|Any CPU.Build.0 = Release|Any CPU
{8B774E77-C956-4ABC-BBFE-8756CB4111C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8B774E77-C956-4ABC-BBFE-8756CB4111C8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8B774E77-C956-4ABC-BBFE-8756CB4111C8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8B774E77-C956-4ABC-BBFE-8756CB4111C8}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -211,5 +217,6 @@ Global
{CB6F6372-9479-41E2-83A8-07854326BCA8} = {293F0A13-6653-4580-9116-1D255F4C4E69}
{CE9EC29D-8A2A-41D4-8F82-BF8B536ABC31} = {6C439ABB-7252-4F00-9A21-121C11E98895}
{C5F25F3E-B8EF-4A8F-8BD6-5B9863A56FE2} = {293F0A13-6653-4580-9116-1D255F4C4E69}
{8B774E77-C956-4ABC-BBFE-8756CB4111C8} = {293F0A13-6653-4580-9116-1D255F4C4E69}
EndGlobalSection
EndGlobal
5 changes: 5 additions & 0 deletions dnSpy/dnSpy.Contracts.DnSpy/Command/CommandConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -138,5 +138,10 @@ public static class CommandConstants {
/// Order of Roslyn signature help <see cref="ICommandTargetFilter"/>
/// </summary>
public const double CMDTARGETFILTER_ORDER_ROSLYN_SIGNATUREHELP = CMDTARGETFILTER_ORDER_ROSLYN_STATEMENTCOMPLETION - 1000;

/// <summary>
/// Order of Roslyn quick info <see cref="ICommandTargetFilter"/>
/// </summary>
public const double CMDTARGETFILTER_ORDER_ROSLYN_QUICKINFO = CMDTARGETFILTER_ORDER_ROSLYN_SIGNATUREHELP - 1000;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,10 @@ static class PredefinedDnSpyQuickInfoSourceProviders {
/// <see cref="IDocumentViewer"/>
/// </summary>
public const string DocumentViewer = "dnSpy-" + nameof(DocumentViewer);

/// <summary>
/// Roslyn languages (C# or Visual Basic)
/// </summary>
public const string Roslyn = "dnSpy-Roslyn";
}
}
187 changes: 187 additions & 0 deletions dnSpy/dnSpy.Roslyn.Internal/DocumentExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.ErrorReporting;
using Microsoft.CodeAnalysis.FindSymbols;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.SemanticModelWorkspaceService;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;

namespace dnSpy.Roslyn.Internal
{
internal static partial class DocumentExtensions
{
public static TLanguageService GetLanguageService<TLanguageService>(this Document document) where TLanguageService : class, ILanguageService
{
return document?.Project?.LanguageServices?.GetService<TLanguageService>();
}

public static bool IsOpen(this Document document)
{
var workspace = document.Project.Solution.Workspace as Workspace;
return workspace != null && workspace.IsDocumentOpen(document.Id);
}

/// <summary>
/// this will return either regular semantic model or speculative semantic based on context.
/// any feature that is involved in typing or run on UI thread should use this to take advantage of speculative semantic model
/// whenever possible automatically.
///
/// when using this API, semantic model should only be used to ask node inside of the given span.
/// otherwise, it might throw if semantic model returned by this API is a speculative semantic model.
///
/// also, symbols from the semantic model returned by this API might have out of date location information.
/// if exact location (not relative location) is needed from symbol, regular GetSemanticModel should be used.
/// </summary>
public static async Task<SemanticModel> GetSemanticModelForSpanAsync(this Document document, TextSpan span, CancellationToken cancellationToken)
{
try
{
var syntaxFactService = document.Project.LanguageServices.GetService<ISyntaxFactsService>();
var semanticModelService = document.Project.Solution.Workspace.Services.GetService<ISemanticModelService>();
if (semanticModelService == null || syntaxFactService == null)
{
return await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
}

var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var token = root.FindToken(span.Start);
if (token.Parent == null)
{
return await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
}

var node = token.Parent.AncestorsAndSelf().FirstOrDefault(a => a.FullSpan.Contains(span));
return await GetSemanticModelForNodeAsync(semanticModelService, syntaxFactService, document, node, span, cancellationToken).ConfigureAwait(false);
}
catch (Exception e) when (FatalError.ReportUnlessCanceled(e))
{
throw ExceptionUtilities.Unreachable;
}
}

/// <summary>
/// this will return either regular semantic model or speculative semantic based on context.
/// any feature that is involved in typing or run on UI thread should use this to take advantage of speculative semantic model
/// whenever possible automatically.
///
/// when using this API, semantic model should only be used to ask node inside of the given node except ones that belong to
/// member signature. otherwise, it might throw if semantic model returned by this API is a speculative semantic model.
///
/// also, symbols from the semantic model returned by this API might have out of date location information.
/// if exact location (not relative location) is needed from symbol, regular GetSemanticModel should be used.
/// </summary>
public static Task<SemanticModel> GetSemanticModelForNodeAsync(this Document document, SyntaxNode node, CancellationToken cancellationToken)
{
var syntaxFactService = document.Project.LanguageServices.GetService<ISyntaxFactsService>();
var semanticModelService = document.Project.Solution.Workspace.Services.GetService<ISemanticModelService>();
if (semanticModelService == null || syntaxFactService == null || node == null)
{
return document.GetSemanticModelAsync(cancellationToken);
}

return GetSemanticModelForNodeAsync(semanticModelService, syntaxFactService, document, node, node.FullSpan, cancellationToken);
}

private static Task<SemanticModel> GetSemanticModelForNodeAsync(
ISemanticModelService semanticModelService, ISyntaxFactsService syntaxFactService,
Document document, SyntaxNode node, TextSpan span, CancellationToken cancellationToken)
{
// check whether given span is a valid span to do speculative binding
var speculativeBindingSpan = syntaxFactService.GetMemberBodySpanForSpeculativeBinding(node);
if (!speculativeBindingSpan.Contains(span))
{
return document.GetSemanticModelAsync(cancellationToken);
}

return semanticModelService.GetSemanticModelForNodeAsync(document, node, cancellationToken);
}

#if DEBUG
public static async Task<bool> HasAnyErrorsAsync(this Document document, CancellationToken cancellationToken, List<string> ignoreErrorCode = null)
{
if (!document.SupportsSemanticModel)
{
return false;
}

var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
return semanticModel.GetDiagnostics(cancellationToken: cancellationToken).Any(diag => diag.Severity == DiagnosticSeverity.Error &&
(ignoreErrorCode == null || ignoreErrorCode.Count == 0 ? true : !ignoreErrorCode.Contains(diag.Id)));
}

/// <summary>
/// Debug only extension method to verify no errors were introduced by formatting, pretty listing and other related document altering service in error-free code.
/// </summary>
public static async Task VerifyNoErrorsAsync(this Document newDocument, string message, CancellationToken cancellationToken, List<string> ignoreErrorCodes = null)
{
bool newDocumentHasErrors = await newDocument.HasAnyErrorsAsync(cancellationToken, ignoreErrorCodes).ConfigureAwait(false);
Debug.Assert(!newDocumentHasErrors, message);
}
#endif

public static bool IsFromPrimaryBranch(this Document document)
{
return document.Project.Solution.BranchId == document.Project.Solution.Workspace.PrimaryBranchId;
}

public static async Task<bool> IsForkedDocumentWithSyntaxChangesAsync(this Document document, CancellationToken cancellationToken)
{
try
{
if (document.IsFromPrimaryBranch())
{
return false;
}

var currentSolution = document.Project.Solution.Workspace.CurrentSolution;
var currentDocument = currentSolution.GetDocument(document.Id);
if (currentDocument == null)
{
return true;
}

var documentVersion = await document.GetSyntaxVersionAsync(cancellationToken).ConfigureAwait(false);
var currentDocumentVersion = await currentDocument.GetSyntaxVersionAsync(cancellationToken).ConfigureAwait(false);
return !documentVersion.Equals(currentDocumentVersion);
}
catch (Exception e) when (FatalError.ReportUnlessCanceled(e))
{
throw ExceptionUtilities.Unreachable;
}
}

public static async Task<IDeclarationInfo> GetDeclarationInfoAsync(this Document document, CancellationToken cancellationToken)
{
return await SyntaxTreeInfo.GetDeclarationInfoAsync(document, cancellationToken).ConfigureAwait(false);
}

/// <summary>
/// Returns the semantic model for this document that may be produced from partial semantics. The semantic model
/// is only guaranteed to contain the syntax tree for <paramref name="document"/> and nothing else.
/// </summary>
public static async Task<SemanticModel> GetPartialSemanticModelAsync(this Document document, CancellationToken cancellationToken)
{
Compilation compilation;

if (document.Project.TryGetCompilation(out compilation))
{
// We already have a compilation, so at this point it's fastest to just get a SemanticModel
return await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
}
else
{
var frozenDocument = await document.WithFrozenPartialSemanticsAsync(cancellationToken).ConfigureAwait(false);
return await frozenDocument.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
}
}
}
}
Loading

0 comments on commit 263550c

Please sign in to comment.