protected override 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.Trivia(SyntaxFactory.EndRegionDirectiveTrivia(true)), SyntaxFactory.CarriageReturnLineFeed, SyntaxFactory.CarriageReturnLineFeed }); return(document.WithSyntaxRoot(newRoot)); }
private static async Task <Document> AddAssemblyInfoRegionAsync(Document document, ISymbol symbol, CancellationToken cancellationToken) { var assemblyInfo = MetadataAsSourceHelpers.GetAssemblyInfo(symbol.ContainingAssembly); var compilation = await document.Project.GetRequiredCompilationAsync(cancellationToken).ConfigureAwait(false); var assemblyPath = MetadataAsSourceHelpers.GetAssemblyDisplay(compilation, symbol.ContainingAssembly); var regionTrivia = SyntaxFactory.RegionDirectiveTrivia(true) .WithTrailingTrivia(new[] { SyntaxFactory.Space, SyntaxFactory.PreprocessingMessage(assemblyInfo) }); var oldRoot = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var newRoot = oldRoot.WithLeadingTrivia(new[] { SyntaxFactory.Trivia(regionTrivia), SyntaxFactory.CarriageReturnLineFeed, SyntaxFactory.Comment("// " + assemblyPath), SyntaxFactory.CarriageReturnLineFeed, SyntaxFactory.Comment($"// Decompiled with ICSharpCode.Decompiler {s_decompilerVersion.FileVersion}"), SyntaxFactory.CarriageReturnLineFeed, SyntaxFactory.Trivia(SyntaxFactory.EndRegionDirectiveTrivia(true)), SyntaxFactory.CarriageReturnLineFeed, SyntaxFactory.CarriageReturnLineFeed }); return(document.WithSyntaxRoot(newRoot)); }
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 // reuse that workspace. Otherwise, we have to go spin up a temporary project to do the binding. if (_openedDocumentIds.TryGetValue(fileInfo, out var openDocumentId)) { // Awesome, it's already open. Let's try to grab a document for it var document = _workspace.CurrentSolution.GetDocument(openDocumentId); return(await MetadataAsSourceHelpers.GetLocationInGeneratedSourceAsync(symbolId, document, cancellationToken).ConfigureAwait(false)); } // Annoying case: the file is still on disk. Only real option here is to spin up a fake project to go and bind in. var temporaryProjectInfoAndDocumentId = fileInfo.GetProjectInfoAndDocumentId(_workspace, loadFileFromDisk: true); var temporaryDocument = _workspace.CurrentSolution.AddProject(temporaryProjectInfoAndDocumentId.Item1) .GetDocument(temporaryProjectInfoAndDocumentId.Item2); return(await MetadataAsSourceHelpers.GetLocationInGeneratedSourceAsync(symbolId, temporaryDocument, cancellationToken).ConfigureAwait(false)); }
internal static DefinitionItem CreateMetadataDefinition( ImmutableArray <string> tags, ImmutableArray <TaggedText> displayParts, ImmutableArray <TaggedText> nameDisplayParts, Solution solution, ISymbol symbol, ImmutableDictionary <string, string>?properties = null, bool displayIfNoReferences = true) { properties ??= ImmutableDictionary <string, string> .Empty; var symbolKey = symbol.GetSymbolKey().ToString(); var projectId = solution.GetOriginatingProjectId(symbol); Contract.ThrowIfNull(projectId); properties = properties.Add(MetadataSymbolKey, symbolKey) .Add(MetadataSymbolOriginatingProjectIdGuid, projectId.Id.ToString()) .Add(MetadataSymbolOriginatingProjectIdDebugName, projectId.DebugName ?? ""); // Find the highest level containing type to show as the "file name". For metadata locations // that come from embedded source or SourceLink this could be wrong, as there is no reason // to assume a type is defined in a filename that matches, but its _way_ too expensive // to try to find the right answer. For metadata-as-source locations though, it will be the same // as the synthesized filename, so will make sense in the majority of cases. var containingTypeName = MetadataAsSourceHelpers.GetTopLevelContainingNamedType(symbol).Name; properties = properties.Add(AbstractReferenceFinder.ContainingTypeInfoPropertyName, containingTypeName); var originationParts = GetOriginationParts(symbol); return(new DefaultDefinitionItem( tags, displayParts, nameDisplayParts, originationParts, sourceSpans: ImmutableArray <DocumentSpan> .Empty, properties: properties, displayableProperties: ImmutableDictionary <string, string> .Empty, displayIfNoReferences: displayIfNoReferences)); }
public async Task <MetadataAsSourceFile> GetGeneratedFileAsync(Project project, ISymbol symbol, bool allowDecompilation, CancellationToken cancellationToken = default) { if (project == null) { throw new ArgumentNullException(nameof(project)); } if (symbol == null) { throw new ArgumentNullException(nameof(symbol)); } if (symbol.Kind == SymbolKind.Namespace) { throw new ArgumentException(EditorFeaturesResources.symbol_cannot_be_a_namespace, nameof(symbol)); } symbol = symbol.GetOriginalUnreducedDefinition(); MetadataAsSourceGeneratedFileInfo fileInfo; Location navigateLocation = null; var topLevelNamedType = MetadataAsSourceHelpers.GetTopLevelContainingNamedType(symbol); var symbolId = SymbolKey.Create(symbol, cancellationToken); using (await _gate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false)) { InitializeWorkspace(project); var infoKey = await GetUniqueDocumentKey(project, topLevelNamedType, cancellationToken).ConfigureAwait(false); fileInfo = _keyToInformation.GetOrAdd(infoKey, _ => new MetadataAsSourceGeneratedFileInfo(GetRootPathWithGuid_NoLock(), project, topLevelNamedType)); _generatedFilenameToInformation[fileInfo.TemporaryFilePath] = fileInfo; if (!File.Exists(fileInfo.TemporaryFilePath)) { // We need to generate this. First, we'll need a temporary project to do the generation into. We // avoid loading the actual file from disk since it doesn't exist yet. var temporaryProjectInfoAndDocumentId = fileInfo.GetProjectInfoAndDocumentId(_workspace, loadFileFromDisk: false); var temporaryDocument = _workspace.CurrentSolution.AddProject(temporaryProjectInfoAndDocumentId.Item1) .GetDocument(temporaryProjectInfoAndDocumentId.Item2); var useDecompiler = allowDecompilation; if (useDecompiler) { try { temporaryDocument = await DecompileSymbolAsync(temporaryDocument, symbol, cancellationToken).ConfigureAwait(false); } catch (Exception e) when(FatalError.ReportWithoutCrashUnlessCanceled(e)) { useDecompiler = false; } } if (!useDecompiler) { var sourceFromMetadataService = temporaryDocument.Project.LanguageServices.GetService <IMetadataAsSourceService>(); temporaryDocument = await sourceFromMetadataService.AddSourceToAsync(temporaryDocument, symbol, cancellationToken).ConfigureAwait(false); } // We have the content, so write it out to disk var text = await temporaryDocument.GetTextAsync(cancellationToken).ConfigureAwait(false); // Create the directory. It's possible a parallel deletion is happening in another process, so we may have // to retry this a few times. var directoryToCreate = Path.GetDirectoryName(fileInfo.TemporaryFilePath); while (!Directory.Exists(directoryToCreate)) { try { Directory.CreateDirectory(directoryToCreate); } catch (DirectoryNotFoundException) { } catch (UnauthorizedAccessException) { } } using (var textWriter = new StreamWriter(fileInfo.TemporaryFilePath, append: false, encoding: fileInfo.Encoding)) { text.Write(textWriter); } // Mark read-only new FileInfo(fileInfo.TemporaryFilePath).IsReadOnly = true; // Locate the target in the thing we just created navigateLocation = await MetadataAsSourceHelpers.GetLocationInGeneratedSourceAsync(symbolId, temporaryDocument, cancellationToken).ConfigureAwait(false); } // If we don't have a location yet, then that means we're re-using an existing file. In this case, we'll want to relocate the symbol. if (navigateLocation == null) { navigateLocation = await RelocateSymbol_NoLock(fileInfo, symbolId, cancellationToken).ConfigureAwait(false); } } var documentName = string.Format( "{0} [{1}]", topLevelNamedType.Name, EditorFeaturesResources.from_metadata); var documentTooltip = topLevelNamedType.ToDisplayString(new SymbolDisplayFormat(typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces)); return(new MetadataAsSourceFile(fileInfo.TemporaryFilePath, navigateLocation, documentName, documentTooltip)); }
public static Task <Location> GetLocationInGeneratedSourceAsync(ISymbol symbol, Document generatedDocument, CancellationToken cancellationToken) { var symbolKey = SymbolKey.Create(symbol, cancellationToken); return(MetadataAsSourceHelpers.GetLocationInGeneratedSourceAsync(symbolKey, generatedDocument, cancellationToken)); }
public static string GetAssemblyDisplay(Compilation compilation, IAssemblySymbol assemblySymbol) => MetadataAsSourceHelpers.GetAssemblyDisplay(compilation, assemblySymbol);
public static string GetAssemblyInfo(IAssemblySymbol assemblySymbol) => MetadataAsSourceHelpers.GetAssemblyInfo(assemblySymbol);