未验证 提交 8411c173 编写于 作者: S Sam Harwell 提交者: GitHub

Merge pull request #29149 from siegfriedpammer/dev/siegfriedpammer/decompiler

Improved User Experience with "Navigate to decompiled sources"
// 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.IO;
using System.Linq;
using System.Reflection.PortableExecutable;
using ICSharpCode.Decompiler.Metadata;
using Microsoft.CodeAnalysis.Shared.Extensions;
namespace Microsoft.CodeAnalysis.Editor.CSharp.DecompiledSource
{
internal class AssemblyResolver : IAssemblyResolver
{
private readonly Compilation parentCompilation;
private static readonly Version zeroVersion = new Version(0, 0, 0, 0);
public AssemblyResolver(Compilation parentCompilation)
{
this.parentCompilation = parentCompilation;
}
public PEFile Resolve(IAssemblyReference name)
{
foreach (var assembly in parentCompilation.GetReferencedAssemblySymbols())
{
// First, find the correct IAssemblySymbol by name and PublicKeyToken.
if (assembly.Identity.Name != name.Name
|| !assembly.Identity.PublicKeyToken.SequenceEqual(name.PublicKeyToken ?? Array.Empty<byte>()))
{
continue;
}
// Normally we skip versions that do not match, except if the reference is "mscorlib" (see comments below)
// or if the name.Version is '0.0.0.0'. This is because we require the metadata of all transitive references
// and modules, to achieve best decompilation results.
// In the case of .NET Standard projects for example, the 'netstandard' reference contains no references
// with actual versions. All versions are '0.0.0.0', therefore we have to ignore those version numbers,
// and can just use the references provided by Roslyn instead.
if (assembly.Identity.Version != name.Version && name.Version != zeroVersion
&& !string.Equals("mscorlib", assembly.Identity.Name, StringComparison.OrdinalIgnoreCase))
{
// MSBuild treats mscorlib special for the purpose of assembly resolution/unification, where all
// versions of the assembly are considered equal. The same policy is adopted here.
continue;
}
// reference assemblies should be fine here, we only need the metadata of references.
var reference = parentCompilation.GetMetadataReference(assembly);
return new PEFile(reference.Display, PEStreamOptions.PrefetchMetadata);
}
// not found
return null;
}
public PEFile ResolveModule(PEFile mainModule, string moduleName)
{
// Primitive implementation to support multi-module assemblies
// where all modules are located next to the main module.
string baseDirectory = Path.GetDirectoryName(mainModule.FileName);
string moduleFileName = Path.Combine(baseDirectory, moduleName);
if (!File.Exists(moduleFileName))
{
return null;
}
return new PEFile(moduleFileName, PEStreamOptions.PrefetchMetadata);
}
}
}
// 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.Globalization;
using System.Linq;
using System.Reflection.PortableExecutable;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.CSharp;
using ICSharpCode.Decompiler.CSharp.Transforms;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.TypeSystem;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.DocumentationComments;
using Microsoft.CodeAnalysis.DocumentationComments;
using Microsoft.CodeAnalysis.ErrorReporting;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.MetadataAsSource;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Editor.CSharp.DecompiledSource
{
internal class CSharpDecompiledSourceService : IDecompiledSourceService
{
private readonly HostLanguageServices provider;
private static readonly FileVersionInfo decompilerVersion = FileVersionInfo.GetVersionInfo(typeof(CSharpDecompiler).Assembly.Location);
public CSharpDecompiledSourceService(HostLanguageServices provider)
{
this.provider = provider;
}
public async Task<Document> AddSourceToAsync(Document document, ISymbol symbol, CancellationToken cancellationToken)
{
// Get the name of the type the symbol is in
var containingOrThis = symbol.GetContainingTypeOrThis();
var fullName = GetFullReflectionName(containingOrThis);
var compilation = await document.Project.GetCompilationAsync(cancellationToken).ConfigureAwait(false);
string assemblyLocation = null;
var isReferenceAssembly = symbol.ContainingAssembly.GetAttributes().Any(attribute => attribute.AttributeClass.Name == nameof(ReferenceAssemblyAttribute)
&& attribute.AttributeClass.ToNameDisplayString() == typeof(ReferenceAssemblyAttribute).FullName);
if (isReferenceAssembly)
{
try
{
var fullAssemblyName = symbol.ContainingAssembly.Identity.GetDisplayName();
GlobalAssemblyCache.Instance.ResolvePartialName(fullAssemblyName, out assemblyLocation, preferredCulture: CultureInfo.CurrentCulture);
}
catch (Exception e) when (FatalError.ReportWithoutCrash(e))
{
}
}
if (assemblyLocation == null)
{
var reference = compilation.GetMetadataReference(symbol.ContainingAssembly);
assemblyLocation = (reference as PortableExecutableReference)?.FilePath;
if (assemblyLocation == null)
{
throw new NotSupportedException(EditorFeaturesResources.Cannot_navigate_to_the_symbol_under_the_caret);
}
}
// Decompile
document = PerformDecompilation(document, fullName, compilation, assemblyLocation);
document = await AddAssemblyInfoRegionAsync(document, symbol, cancellationToken);
// Convert XML doc comments to regular comments, just like MAS
var docCommentFormattingService = document.GetLanguageService<IDocumentationCommentFormattingService>();
document = await ConvertDocCommentsToRegularCommentsAsync(document, docCommentFormattingService, cancellationToken);
var node = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
// Apply formatting rules
document = await Formatter.FormatAsync(
document, SpecializedCollections.SingletonEnumerable(node.FullSpan),
options: null, Formatter.GetDefaultFormattingRules(document), cancellationToken).ConfigureAwait(false);
return document;
}
private Document PerformDecompilation(Document document, string fullName, Compilation compilation, string assemblyLocation)
{
// Load the assembly.
var file = new PEFile(assemblyLocation, PEStreamOptions.PrefetchEntireImage);
// Initialize a decompiler with default settings.
var decompiler = new CSharpDecompiler(file, new AssemblyResolver(compilation), new DecompilerSettings());
// Escape invalid identifiers to prevent Roslyn from failing to parse the generated code.
// (This happens for example, when there is compiler-generated code that is not yet recognized/transformed by the decompiler.)
decompiler.AstTransforms.Add(new EscapeInvalidIdentifiers());
var fullTypeName = new FullTypeName(fullName);
// Try to decompile; if an exception is thrown the caller will handle it
var text = decompiler.DecompileTypeAsString(fullTypeName);
return document.WithText(SourceText.From(text));
}
private async Task<Document> AddAssemblyInfoRegionAsync(Document document, ISymbol symbol, CancellationToken cancellationToken)
{
string assemblyInfo = MetadataAsSourceHelpers.GetAssemblyInfo(symbol.ContainingAssembly);
var compilation = await document.Project.GetCompilationAsync(cancellationToken).ConfigureAwait(false);
string assemblyPath = MetadataAsSourceHelpers.GetAssemblyDisplay(compilation, symbol.ContainingAssembly);
var regionTrivia = SyntaxFactory.RegionDirectiveTrivia(true)
.WithTrailingTrivia(new[] { SyntaxFactory.Space, SyntaxFactory.PreprocessingMessage(assemblyInfo) });
var oldRoot = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var newRoot = oldRoot.WithLeadingTrivia(new[]
{
SyntaxFactory.Trivia(regionTrivia),
SyntaxFactory.CarriageReturnLineFeed,
SyntaxFactory.Comment("// " + assemblyPath),
SyntaxFactory.CarriageReturnLineFeed,
SyntaxFactory.Comment($"// Decompiled with ICSharpCode.Decompiler {decompilerVersion.FileVersion}"),
SyntaxFactory.CarriageReturnLineFeed,
SyntaxFactory.Trivia(SyntaxFactory.EndRegionDirectiveTrivia(true)),
SyntaxFactory.CarriageReturnLineFeed,
SyntaxFactory.CarriageReturnLineFeed
});
return document.WithSyntaxRoot(newRoot);
}
private async Task<Document> ConvertDocCommentsToRegularCommentsAsync(Document document, IDocumentationCommentFormattingService docCommentFormattingService, CancellationToken cancellationToken)
{
var syntaxRoot = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var newSyntaxRoot = DocCommentConverter.ConvertToRegularComments(syntaxRoot, docCommentFormattingService, cancellationToken);
return document.WithSyntaxRoot(newSyntaxRoot);
}
private string GetFullReflectionName(INamedTypeSymbol containingType)
{
var stack = new Stack<string>();
stack.Push(containingType.MetadataName);
var ns = containingType.ContainingNamespace;
do
{
stack.Push(ns.Name);
ns = ns.ContainingNamespace;
}
while (ns != null && !ns.IsGlobalNamespace);
return string.Join(".", stack);
}
}
}
// 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.Composition;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Host.Mef;
namespace Microsoft.CodeAnalysis.Editor.CSharp.DecompiledSource
{
[ExportLanguageServiceFactory(typeof(IDecompiledSourceService), LanguageNames.CSharp), Shared]
internal partial class CSharpDecompiledSourceServiceFactory : ILanguageServiceFactory
{
public ILanguageService CreateLanguageService(HostLanguageServices provider)
{
return new CSharpDecompiledSourceService(provider);
}
}
}
......@@ -48,6 +48,7 @@
<ItemGroup>
<Reference Include="System.ComponentModel.Composition" />
<PackageReference Include="Microsoft.VisualStudio.Language.Intellisense" Version="$(MicrosoftVisualStudioLanguageIntellisenseVersion)" />
<PackageReference Include="ICSharpCode.Decompiler" Version="$(ICSharpCodeDecompilerVersion)" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="CSharpEditorResources.resx">
......
// 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.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Host;
namespace Microsoft.CodeAnalysis.Editor
{
internal interface IDecompiledSourceService : ILanguageService
{
/// <summary>
/// Generates formatted source code containing general information about the symbol's
/// containing assembly and the decompiled source code which the given ISymbol is or is a part of
/// into the given document
/// </summary>
/// <param name="document">The document to generate source into</param>
/// <param name="symbol">The symbol to generate source for</param>
/// <param name="cancellationToken">To cancel document operations</param>
/// <returns>The updated document</returns>
Task<Document> AddSourceToAsync(Document document, ISymbol symbol, CancellationToken cancellationToken);
}
}
......@@ -3,19 +3,11 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection.PortableExecutable;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.CSharp;
using ICSharpCode.Decompiler.CSharp.Transforms;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.TypeSystem;
using Microsoft.CodeAnalysis.ErrorReporting;
using Microsoft.CodeAnalysis.MetadataAsSource;
using Microsoft.CodeAnalysis.Shared.Extensions;
......@@ -128,7 +120,15 @@ public async Task<MetadataAsSourceFile> GetGeneratedFileAsync(Project project, I
{
try
{
temporaryDocument = await DecompileSymbolAsync(temporaryDocument, symbol, cancellationToken).ConfigureAwait(false);
var decompiledSourceService = temporaryDocument.GetLanguageService<IDecompiledSourceService>();
if (decompiledSourceService != null)
{
temporaryDocument = await decompiledSourceService.AddSourceToAsync(temporaryDocument, symbol, cancellationToken).ConfigureAwait(false);
}
else
{
useDecompiler = false;
}
}
catch (Exception e) when (FatalError.ReportWithoutCrashUnlessCanceled(e))
{
......@@ -191,136 +191,6 @@ public async Task<MetadataAsSourceFile> GetGeneratedFileAsync(Project project, I
return new MetadataAsSourceFile(fileInfo.TemporaryFilePath, navigateLocation, documentName, documentTooltip);
}
private async Task<Document> DecompileSymbolAsync(Document temporaryDocument, ISymbol symbol, CancellationToken cancellationToken)
{
// Get the name of the type the symbol is in
var containingOrThis = symbol.GetContainingTypeOrThis();
var fullName = GetFullReflectionName(containingOrThis);
var compilation = await temporaryDocument.Project.GetCompilationAsync(cancellationToken).ConfigureAwait(false);
string assemblyLocation = null;
var isReferenceAssembly = symbol.ContainingAssembly.GetAttributes().Any(attribute => attribute.AttributeClass.Name == nameof(ReferenceAssemblyAttribute)
&& attribute.AttributeClass.ToNameDisplayString() == typeof(ReferenceAssemblyAttribute).FullName);
if (isReferenceAssembly)
{
try
{
var fullAssemblyName = symbol.ContainingAssembly.Identity.GetDisplayName();
GlobalAssemblyCache.Instance.ResolvePartialName(fullAssemblyName, out assemblyLocation, preferredCulture: CultureInfo.CurrentCulture);
}
catch (Exception e) when (FatalError.ReportWithoutCrash(e))
{
}
}
if (assemblyLocation == null)
{
var reference = compilation.GetMetadataReference(symbol.ContainingAssembly);
assemblyLocation = (reference as PortableExecutableReference)?.FilePath;
if (assemblyLocation == null)
{
throw new NotSupportedException(EditorFeaturesResources.Cannot_navigate_to_the_symbol_under_the_caret);
}
}
// Load the assembly.
var pefile = new PEFile(assemblyLocation, PEStreamOptions.PrefetchEntireImage);
// Initialize a decompiler with default settings.
var settings = new DecompilerSettings(LanguageVersion.Latest);
var decompiler = new CSharpDecompiler(pefile, new RoslynAssemblyResolver(compilation), settings);
// Escape invalid identifiers to prevent Roslyn from failing to parse the generated code.
// (This happens for example, when there is compiler-generated code that is not yet recognized/transformed by the decompiler.)
decompiler.AstTransforms.Add(new EscapeInvalidIdentifiers());
var fullTypeName = new FullTypeName(fullName);
var decompilerVersion = FileVersionInfo.GetVersionInfo(typeof(CSharpDecompiler).Assembly.Location);
// Add header to match output of metadata-only view.
// (This also makes debugging easier, because you can see which assembly was decompiled inside VS.)
var header = $"#region {FeaturesResources.Assembly} {pefile.FullName}" + Environment.NewLine
+ $"// {assemblyLocation}" + Environment.NewLine
+ $"// Decompiled with ICSharpCode.Decompiler {decompilerVersion.FileVersion}" + Environment.NewLine
+ "#endregion" + Environment.NewLine;
// Try to decompile; if an exception is thrown the caller will handle it
var text = decompiler.DecompileTypeAsString(fullTypeName);
return temporaryDocument.WithText(SourceText.From(header + text));
}
private class RoslynAssemblyResolver : IAssemblyResolver
{
private readonly Compilation parentCompilation;
private static readonly Version zeroVersion = new Version(0, 0, 0, 0);
public RoslynAssemblyResolver(Compilation parentCompilation)
{
this.parentCompilation = parentCompilation;
}
public PEFile Resolve(IAssemblyReference name)
{
foreach (var assembly in parentCompilation.GetReferencedAssemblySymbols())
{
// First, find the correct IAssemblySymbol by name and PublicKeyToken.
if (assembly.Identity.Name != name.Name
|| !assembly.Identity.PublicKeyToken.SequenceEqual(name.PublicKeyToken ?? Array.Empty<byte>()))
{
continue;
}
// Normally we skip versions that do not match, except if the reference is "mscorlib" (see comments below)
// or if the name.Version is '0.0.0.0'. This is because we require the metadata of all transitive references
// and modules, to achieve best decompilation results.
// In the case of .NET Standard projects for example, the 'netstandard' reference contains no references
// with actual versions. All versions are '0.0.0.0', therefore we have to ignore those version numbers,
// and can just use the references provided by Roslyn instead.
if (assembly.Identity.Version != name.Version && name.Version != zeroVersion
&& !string.Equals("mscorlib", assembly.Identity.Name, StringComparison.OrdinalIgnoreCase))
{
// MSBuild treats mscorlib special for the purpose of assembly resolution/unification, where all
// versions of the assembly are considered equal. The same policy is adopted here.
continue;
}
// reference assemblies should be fine here, we only need the metadata of references.
var reference = parentCompilation.GetMetadataReference(assembly);
return new PEFile(reference.Display, PEStreamOptions.PrefetchMetadata);
}
// not found
return null;
}
public PEFile ResolveModule(PEFile mainModule, string moduleName)
{
// Primitive implementation to support multi-module assemblies
// where all modules are located next to the main module.
string baseDirectory = Path.GetDirectoryName(mainModule.FileName);
string moduleFileName = Path.Combine(baseDirectory, moduleName);
if (!File.Exists(moduleFileName))
return null;
return new PEFile(moduleFileName, PEStreamOptions.PrefetchMetadata);
}
}
private string GetFullReflectionName(INamedTypeSymbol containingType)
{
var stack = new Stack<string>();
stack.Push(containingType.MetadataName);
var ns = containingType.ContainingNamespace;
do
{
stack.Push(ns.Name);
ns = ns.ContainingNamespace;
}
while (ns != null && !ns.IsGlobalNamespace);
return string.Join(".", stack);
}
private async Task<Location> RelocateSymbol_NoLock(MetadataAsSourceGeneratedFileInfo fileInfo, SymbolKey symbolId, CancellationToken cancellationToken)
{
// We need to relocate the symbol in the already existing file. If the file is open, we can just
......
......@@ -35,7 +35,6 @@
<PackageReference Include="Microsoft.VisualStudio.Text.UI" Version="$(MicrosoftVisualStudioTextUIVersion)" />
<PackageReference Include="Microsoft.VisualStudio.Threading" Version="$(MicrosoftVisualStudioThreadingVersion)" />
<PackageReference Include="Microsoft.CodeAnalysis.Elfie" Version="$(MicrosoftCodeAnalysisElfieVersion)" />
<PackageReference Include="ICSharpCode.Decompiler" Version="$(ICSharpCodeDecompilerVersion)" />
</ItemGroup>
<ItemGroup>
<InternalsVisibleTo Include="InteractiveHost" />
......
// 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.Collections.Generic;
using System.Threading;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.DocumentationComments;
using Microsoft.CodeAnalysis.MetadataAsSource;
using Microsoft.CodeAnalysis.Shared.Utilities;
namespace Microsoft.CodeAnalysis.CSharp.DocumentationComments
{
internal class DocCommentConverter : CSharpSyntaxRewriter
{
private readonly IDocumentationCommentFormattingService _formattingService;
private readonly CancellationToken _cancellationToken;
public static SyntaxNode ConvertToRegularComments(SyntaxNode node, IDocumentationCommentFormattingService formattingService, CancellationToken cancellationToken)
{
var converter = new DocCommentConverter(formattingService, cancellationToken);
return converter.Visit(node);
}
private DocCommentConverter(IDocumentationCommentFormattingService formattingService, CancellationToken cancellationToken)
: base(visitIntoStructuredTrivia: false)
{
_formattingService = formattingService;
_cancellationToken = cancellationToken;
}
public override SyntaxNode Visit(SyntaxNode node)
{
_cancellationToken.ThrowIfCancellationRequested();
if (node == null)
{
return node;
}
// Process children first
node = base.Visit(node);
// Check the leading trivia for doc comments.
if (node.GetLeadingTrivia().Any(SyntaxKind.SingleLineDocumentationCommentTrivia))
{
var newLeadingTrivia = new List<SyntaxTrivia>();
foreach (var trivia in node.GetLeadingTrivia())
{
if (trivia.Kind() == SyntaxKind.SingleLineDocumentationCommentTrivia)
{
newLeadingTrivia.Add(SyntaxFactory.Comment("//"));
newLeadingTrivia.Add(SyntaxFactory.ElasticCarriageReturnLineFeed);
var structuredTrivia = (DocumentationCommentTriviaSyntax)trivia.GetStructure();
newLeadingTrivia.AddRange(ConvertDocCommentToRegularComment(structuredTrivia));
}
else
{
newLeadingTrivia.Add(trivia);
}
}
node = node.WithLeadingTrivia(newLeadingTrivia);
}
return node;
}
private IEnumerable<SyntaxTrivia> ConvertDocCommentToRegularComment(DocumentationCommentTriviaSyntax structuredTrivia)
{
var xmlFragment = DocumentationCommentUtilities.ExtractXMLFragment(structuredTrivia.ToFullString(), "///");
var docComment = DocumentationComment.FromXmlFragment(xmlFragment);
var commentLines = AbstractMetadataAsSourceService.DocCommentFormatter.Format(_formattingService, docComment);
foreach (var line in commentLines)
{
if (!string.IsNullOrWhiteSpace(line))
{
yield return SyntaxFactory.Comment("// " + line);
}
else
{
yield return SyntaxFactory.Comment("//");
}
yield return SyntaxFactory.ElasticCarriageReturnLineFeed;
}
}
}
}
......@@ -6,6 +6,7 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeGeneration;
using Microsoft.CodeAnalysis.CSharp.DocumentationComments;
using Microsoft.CodeAnalysis.CSharp.Simplification;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.Utilities;
......@@ -127,87 +128,5 @@ protected override bool IsNewLine(char c)
return SyntaxFacts.IsNewLine(c);
}
}
private class DocCommentConverter : CSharpSyntaxRewriter
{
private readonly IDocumentationCommentFormattingService _formattingService;
private readonly CancellationToken _cancellationToken;
public static SyntaxNode ConvertToRegularComments(SyntaxNode node, IDocumentationCommentFormattingService formattingService, CancellationToken cancellationToken)
{
var converter = new DocCommentConverter(formattingService, cancellationToken);
return converter.Visit(node);
}
private DocCommentConverter(IDocumentationCommentFormattingService formattingService, CancellationToken cancellationToken)
: base(visitIntoStructuredTrivia: false)
{
_formattingService = formattingService;
_cancellationToken = cancellationToken;
}
public override SyntaxNode Visit(SyntaxNode node)
{
_cancellationToken.ThrowIfCancellationRequested();
if (node == null)
{
return node;
}
// Process children first
node = base.Visit(node);
// Check the leading trivia for doc comments.
if (node.GetLeadingTrivia().Any(SyntaxKind.SingleLineDocumentationCommentTrivia))
{
var newLeadingTrivia = new List<SyntaxTrivia>();
foreach (var trivia in node.GetLeadingTrivia())
{
if (trivia.Kind() == SyntaxKind.SingleLineDocumentationCommentTrivia)
{
newLeadingTrivia.Add(SyntaxFactory.Comment("//"));
newLeadingTrivia.Add(SyntaxFactory.ElasticCarriageReturnLineFeed);
var structuredTrivia = (DocumentationCommentTriviaSyntax)trivia.GetStructure();
newLeadingTrivia.AddRange(ConvertDocCommentToRegularComment(structuredTrivia));
}
else
{
newLeadingTrivia.Add(trivia);
}
}
node = node.WithLeadingTrivia(newLeadingTrivia);
}
return node;
}
private IEnumerable<SyntaxTrivia> ConvertDocCommentToRegularComment(DocumentationCommentTriviaSyntax structuredTrivia)
{
var xmlFragment = DocumentationCommentUtilities.ExtractXMLFragment(structuredTrivia.ToFullString(), "///");
var docComment = DocumentationComment.FromXmlFragment(xmlFragment);
var commentLines = AbstractMetadataAsSourceService.DocCommentFormatter.Format(_formattingService, docComment);
foreach (var line in commentLines)
{
if (!string.IsNullOrWhiteSpace(line))
{
yield return SyntaxFactory.Comment("// " + line);
}
else
{
yield return SyntaxFactory.Comment("//");
}
yield return SyntaxFactory.ElasticCarriageReturnLineFeed;
}
}
}
}
}
......@@ -114,14 +114,20 @@ public bool TryNavigateToSymbol(ISymbol symbol, Project project, OptionSet optio
}
// Generate new source or retrieve existing source for the symbol in question
var allowDecompilation = project.Solution.Workspace.Options.GetOption(FeatureOnOffOptions.NavigateToDecompiledSources) && !symbol.IsFromSource();
if (allowDecompilation && !project.Solution.Workspace.Options.GetOption(FeatureOnOffOptions.AcceptedDecompilerDisclaimer))
var allowDecompilation = false;
// Check whether decompilation is supported for the project. We currently only support this for C# projects.
if (project.LanguageServices.GetService<IDecompiledSourceService>() != null)
{
var notificationService = project.Solution.Workspace.Services.GetService<INotificationService>();
allowDecompilation = notificationService.ConfirmMessageBox(ServicesVSResources.Decompiler_Legal_Notice_Message, ServicesVSResources.Decompiler_Legal_Notice_Title, NotificationSeverity.Warning);
if (allowDecompilation)
allowDecompilation = project.Solution.Workspace.Options.GetOption(FeatureOnOffOptions.NavigateToDecompiledSources) && !symbol.IsFromSource();
if (allowDecompilation && !project.Solution.Workspace.Options.GetOption(FeatureOnOffOptions.AcceptedDecompilerDisclaimer))
{
project.Solution.Workspace.Options = project.Solution.Workspace.Options.WithChangedOption(FeatureOnOffOptions.AcceptedDecompilerDisclaimer, true);
var notificationService = project.Solution.Workspace.Services.GetService<INotificationService>();
allowDecompilation = notificationService.ConfirmMessageBox(ServicesVSResources.Decompiler_Legal_Notice_Message, ServicesVSResources.Decompiler_Legal_Notice_Title, NotificationSeverity.Warning);
if (allowDecompilation)
{
project.Solution.Workspace.Options = project.Solution.Workspace.Options.WithChangedOption(FeatureOnOffOptions.AcceptedDecompilerDisclaimer, true);
}
}
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册