From b0d274623689a1b2221a25a97583052d840cde16 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 7 Dec 2022 17:54:51 -0800 Subject: [PATCH 01/17] Simplify lsif generation concepts --- .../Lsif/Generator/CompilerInvocation.cs | 12 +++++++----- src/Features/Lsif/Generator/Generator.cs | 19 +++++++++++++------ src/Features/Lsif/Generator/Program.cs | 18 ++++++++++-------- 3 files changed, 30 insertions(+), 19 deletions(-) diff --git a/src/Features/Lsif/Generator/CompilerInvocation.cs b/src/Features/Lsif/Generator/CompilerInvocation.cs index 0ad226660d5c8..05c3644e09cac 100644 --- a/src/Features/Lsif/Generator/CompilerInvocation.cs +++ b/src/Features/Lsif/Generator/CompilerInvocation.cs @@ -33,14 +33,14 @@ public CompilerInvocation(Compilation compilation, LanguageServices languageServ Options = options; } - public static async Task CreateFromJsonAsync(string jsonContents) + public static async Task CreateFromJsonAsync(string jsonContents) { var invocationInfo = JsonConvert.DeserializeObject(jsonContents); Assumes.Present(invocationInfo); return await CreateFromInvocationInfoAsync(invocationInfo); } - public static async Task CreateFromInvocationInfoAsync(CompilerInvocationInfo invocationInfo) + public static async Task CreateFromInvocationInfoAsync(CompilerInvocationInfo invocationInfo) { // We will use a Workspace to simplify the creation of the compilation, but will be careful not to return the Workspace instance from this class. // We will still provide the language services which are used by the generator itself, but we don't tie it to a Workspace object so we can @@ -113,10 +113,12 @@ public static async Task CreateFromInvocationInfoAsync(Compi hostObjectType: null); var solution = workspace.CurrentSolution.AddProject(projectInfo); - var compilation = await solution.GetRequiredProject(projectId).GetRequiredCompilationAsync(CancellationToken.None); - var options = GeneratorOptions.Default; + //var compilation = await solution.GetRequiredProject(projectId).GetRequiredCompilationAsync(CancellationToken.None); + //var options = GeneratorOptions.Default; - return new CompilerInvocation(compilation, languageServices, invocationInfo.ProjectFilePath, options); + //return new CompilerInvocation(compilation, languageServices, invocationInfo.ProjectFilePath, options); + + return solution.GetRequiredProject(projectId); // Local methods: DocumentInfo CreateDocumentInfo(string unmappedPath) diff --git a/src/Features/Lsif/Generator/Generator.cs b/src/Features/Lsif/Generator/Generator.cs index 6b0baad0e2ac4..4635f32a56010 100644 --- a/src/Features/Lsif/Generator/Generator.cs +++ b/src/Features/Lsif/Generator/Generator.cs @@ -71,8 +71,12 @@ public static Generator CreateAndWriteCapabilitiesVertex(ILsifJsonWriter lsifJso return generator; } - public async Task GenerateForCompilationAsync(Compilation compilation, string projectPath, LanguageServices languageServices, GeneratorOptions options) + public async Task GenerateForProjectAsync(Project project, GeneratorOptions options) { + var compilation = await project.GetRequiredCompilationAsync(CancellationToken.None); + var projectPath = project.FilePath; + Contract.ThrowIfNull(projectPath); + var projectVertex = new Graph.LsifProject( kind: GetLanguageKind(compilation.Language), new Uri(projectPath), @@ -104,11 +108,11 @@ public async Task GenerateForCompilationAsync(Compilation compilation, string pr }; var tasks = new List(); - foreach (var syntaxTree in compilation.SyntaxTrees) + foreach (var document in project.Documents) { tasks.Add(Task.Run(async () => { - var semanticModel = compilation.GetSemanticModel(syntaxTree); + var semanticModel = document.GetSemanticModelAsync(); // We generate the document contents into an in-memory copy, and then write that out at once at the end. This // allows us to collect everything and avoid a lot of fine-grained contention on the write to the single @@ -117,11 +121,13 @@ public async Task GenerateForCompilationAsync(Compilation compilation, string pr // are allowed and might flush other unrelated stuff at the same time, but there's no harm -- the "causality" ordering // is preserved. var documentWriter = new BatchingLsifJsonWriter(_lsifJsonWriter); - var documentId = await GenerateForDocumentAsync(semanticModel, languageServices, options, topLevelSymbolsResultSetTracker, documentWriter, _idFactory); + var documentId = await GenerateForDocumentAsync(document, options, topLevelSymbolsResultSetTracker, documentWriter, _idFactory); topLevelSymbolsWriter.FlushToUnderlyingAndEmpty(); documentWriter.FlushToUnderlyingAndEmpty(); documentIds.Add(documentId); + + GC.KeepAlive(semanticModel); })); } @@ -144,13 +150,14 @@ public async Task GenerateForCompilationAsync(Compilation compilation, string pr /// leak outside a file. /// private static async Task> GenerateForDocumentAsync( - SemanticModel semanticModel, - LanguageServices languageServices, + Document document, GeneratorOptions options, IResultSetTracker topLevelSymbolsResultSetTracker, ILsifJsonWriter lsifJsonWriter, IdFactory idFactory) { + var semanticModel = await document.GetRequiredSemanticModelAsync(CancellationToken.None); + var languageServices = document.Project.Services; var syntaxTree = semanticModel.SyntaxTree; var sourceText = semanticModel.SyntaxTree.GetText(); var syntaxFactsService = languageServices.GetRequiredService(); diff --git a/src/Features/Lsif/Generator/Program.cs b/src/Features/Lsif/Generator/Program.cs index 49759ff226a4c..b22e3596f1456 100644 --- a/src/Features/Lsif/Generator/Program.cs +++ b/src/Features/Lsif/Generator/Program.cs @@ -12,11 +12,13 @@ using System.Linq; using System.Runtime.CompilerServices; using System.Text; +using System.Threading; using System.Threading.Tasks; using Microsoft.Build.Locator; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.Writing; using Microsoft.CodeAnalysis.MSBuild; +using Microsoft.CodeAnalysis.Shared.Extensions; using CompilerInvocationsReader = Microsoft.Build.Logging.StructuredLogger.CompilerInvocationsReader; namespace Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator @@ -172,12 +174,12 @@ private static async Task GenerateWithMSBuildWorkspaceAsync( if (project.SupportsCompilation && project.FilePath != null) { var compilationCreationStopwatch = Stopwatch.StartNew(); - var compilation = (await project.GetCompilationAsync())!; + var compilation = await project.GetRequiredCompilationAsync(CancellationToken.None); await logFile.WriteLineAsync($"Fetch of compilation for {project.FilePath} completed in {compilationCreationStopwatch.Elapsed.ToDisplayString()}."); var generationForProjectStopwatch = Stopwatch.StartNew(); - await lsifGenerator.GenerateForCompilationAsync(compilation, project.FilePath, project.Services, options); + await lsifGenerator.GenerateForProjectAsync(project, options); generationForProjectStopwatch.Stop(); totalTimeInGenerationPhase += generationForProjectStopwatch.Elapsed; @@ -195,14 +197,14 @@ private static async Task GenerateFromCompilerInvocationAsync(FileInfo compilerI await logFile.WriteLineAsync($"Processing compiler invocation from {compilerInvocationFile.FullName}..."); var compilerInvocationLoadStopwatch = Stopwatch.StartNew(); - var compilerInvocation = await CompilerInvocation.CreateFromJsonAsync(File.ReadAllText(compilerInvocationFile.FullName)); + var project = await CompilerInvocation.CreateFromJsonAsync(File.ReadAllText(compilerInvocationFile.FullName)); await logFile.WriteLineAsync($"Load of the project completed in {compilerInvocationLoadStopwatch.Elapsed.ToDisplayString()}."); var generationStopwatch = Stopwatch.StartNew(); var lsifGenerator = Generator.CreateAndWriteCapabilitiesVertex(lsifWriter); - await lsifGenerator.GenerateForCompilationAsync(compilerInvocation.Compilation, compilerInvocation.ProjectFilePath, compilerInvocation.LanguageServices, compilerInvocation.Options); - await logFile.WriteLineAsync($"Generation for {compilerInvocation.ProjectFilePath} completed in {generationStopwatch.Elapsed.ToDisplayString()}."); + await lsifGenerator.GenerateForProjectAsync(project, GeneratorOptions.Default); + await logFile.WriteLineAsync($"Generation for {project.FilePath} completed in {generationStopwatch.Elapsed.ToDisplayString()}."); } // This method can't be loaded until we've registered MSBuild with MSBuildLocator, as otherwise we might load a type prematurely. @@ -226,11 +228,11 @@ private static async Task GenerateFromBinaryLogAsync(FileInfo binLog, ILsifJsonW Tool = msbuildInvocation.Language == Microsoft.Build.Logging.StructuredLogger.CompilerInvocation.CSharp ? "csc" : "vbc" }; - var compilerInvocation = await CompilerInvocation.CreateFromInvocationInfoAsync(invocationInfo); + var project = await CompilerInvocation.CreateFromInvocationInfoAsync(invocationInfo); var generationStopwatch = Stopwatch.StartNew(); - await lsifGenerator.GenerateForCompilationAsync(compilerInvocation.Compilation, compilerInvocation.ProjectFilePath, compilerInvocation.LanguageServices, compilerInvocation.Options); - await logFile.WriteLineAsync($"Generation for {compilerInvocation.ProjectFilePath} completed in {generationStopwatch.Elapsed.ToDisplayString()}."); + await lsifGenerator.GenerateForProjectAsync(project, GeneratorOptions.Default); + await logFile.WriteLineAsync($"Generation for {project.FilePath} completed in {generationStopwatch.Elapsed.ToDisplayString()}."); } } } From edea17ff3ce32651a7fa61100158d5f3711fea25 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 7 Dec 2022 18:00:30 -0800 Subject: [PATCH 02/17] Share code for folding --- .../BlockStructureServiceWithProviders.cs | 9 ----- .../FoldingRanges/FoldingRangesHandler.cs | 34 ++++++------------- src/Features/Lsif/Generator/Generator.cs | 3 +- 3 files changed, 13 insertions(+), 33 deletions(-) diff --git a/src/Features/Core/Portable/Structure/BlockStructureServiceWithProviders.cs b/src/Features/Core/Portable/Structure/BlockStructureServiceWithProviders.cs index 884334c2b2a67..7d0547a108f51 100644 --- a/src/Features/Core/Portable/Structure/BlockStructureServiceWithProviders.cs +++ b/src/Features/Core/Portable/Structure/BlockStructureServiceWithProviders.cs @@ -55,15 +55,6 @@ public override async Task GetBlockStructureAsync( return GetBlockStructure(context, _providers); } - public BlockStructure GetBlockStructure( - SyntaxTree syntaxTree, - in BlockStructureOptions options, - CancellationToken cancellationToken) - { - var context = CreateContext(syntaxTree, options, cancellationToken); - return GetBlockStructure(context, _providers); - } - private static BlockStructureContext CreateContext( SyntaxTree syntaxTree, in BlockStructureOptions options, diff --git a/src/Features/LanguageServer/Protocol/Handler/FoldingRanges/FoldingRangesHandler.cs b/src/Features/LanguageServer/Protocol/Handler/FoldingRanges/FoldingRangesHandler.cs index 6c8a52aabff07..ae49a36b9c07b 100644 --- a/src/Features/LanguageServer/Protocol/Handler/FoldingRanges/FoldingRangesHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/FoldingRanges/FoldingRangesHandler.cs @@ -10,6 +10,7 @@ using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Structure; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.LanguageServer.Protocol; @@ -40,37 +41,24 @@ public FoldingRangesHandler(IGlobalOptionService globalOptions) if (document is null) return null; - var blockStructureService = document.Project.Services.GetService(); - if (blockStructureService == null) - { - return Array.Empty(); - } - var options = _globalOptions.GetBlockStructureOptions(document.Project); - var blockStructure = await blockStructureService.GetBlockStructureAsync(document, options, cancellationToken).ConfigureAwait(false); - if (blockStructure == null) - { - return Array.Empty(); - } - - var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); - return GetFoldingRanges(blockStructure, text); + return await GetFoldingRangesAsync(document, options, cancellationToken).ConfigureAwait(false); } - public static FoldingRange[] GetFoldingRanges( - SyntaxTree syntaxTree, - LanguageServices languageServices, - in BlockStructureOptions options, + /// + /// Used here and by lsif generator. + /// + public static async Task GetFoldingRangesAsync( + Document document, + BlockStructureOptions options, CancellationToken cancellationToken) { - var blockStructureService = (BlockStructureServiceWithProviders)languageServices.GetRequiredService(); - var blockStructure = blockStructureService.GetBlockStructure(syntaxTree, options, cancellationToken); + var blockStructureService = document.GetRequiredLanguageService(); + var blockStructure = await blockStructureService.GetBlockStructureAsync(document, options, cancellationToken).ConfigureAwait(false); if (blockStructure == null) - { return Array.Empty(); - } - var text = syntaxTree.GetText(cancellationToken); + var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); return GetFoldingRanges(blockStructure, text); } diff --git a/src/Features/Lsif/Generator/Generator.cs b/src/Features/Lsif/Generator/Generator.cs index 4635f32a56010..bc366a2c3bdd2 100644 --- a/src/Features/Lsif/Generator/Generator.cs +++ b/src/Features/Lsif/Generator/Generator.cs @@ -333,7 +333,8 @@ void MarkImplementationOfSymbol(ISymbol baseMember) lsifJsonWriter.Write(Edge.Create("contains", documentVertex.GetId(), rangeVertices, idFactory)); // Write the folding ranges for the document. - var foldingRanges = FoldingRangesHandler.GetFoldingRanges(syntaxTree, languageServices, options.BlockStructureOptions, CancellationToken.None); + var foldingRanges = await FoldingRangesHandler.GetFoldingRangesAsync( + document, options.BlockStructureOptions, CancellationToken.None).ConfigureAwait(false); var foldingRangeResult = new FoldingRangeResult(foldingRanges, idFactory); lsifJsonWriter.Write(foldingRangeResult); lsifJsonWriter.Write(Edge.Create(Methods.TextDocumentFoldingRangeName, documentVertex.GetId(), foldingRangeResult.GetId(), idFactory)); From 7b7f434ad66b5797c193811ea783a45a609a2040 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 7 Dec 2022 18:12:12 -0800 Subject: [PATCH 03/17] Extract different features into helper methods --- src/Features/Lsif/Generator/Generator.cs | 92 ++++++++++++++++-------- 1 file changed, 61 insertions(+), 31 deletions(-) diff --git a/src/Features/Lsif/Generator/Generator.cs b/src/Features/Lsif/Generator/Generator.cs index bc366a2c3bdd2..d14042af248d9 100644 --- a/src/Features/Lsif/Generator/Generator.cs +++ b/src/Features/Lsif/Generator/Generator.cs @@ -157,36 +157,52 @@ public async Task GenerateForProjectAsync(Project project, GeneratorOptions opti IdFactory idFactory) { var semanticModel = await document.GetRequiredSemanticModelAsync(CancellationToken.None); - var languageServices = document.Project.Services; - var syntaxTree = semanticModel.SyntaxTree; - var sourceText = semanticModel.SyntaxTree.GetText(); - var syntaxFactsService = languageServices.GetRequiredService(); - var semanticFactsService = languageServices.GetRequiredService(); - string? contentBase64Encoded = null; + var (uri, contentBase64Encoded) = await GetUriAndContentAsync(document).ConfigureAwait(false); - var uri = syntaxTree.FilePath; + var documentVertex = new Graph.LsifDocument(new Uri(uri, UriKind.RelativeOrAbsolute), GetLanguageKind(semanticModel.Language), contentBase64Encoded, idFactory); + lsifJsonWriter.Write(documentVertex); + lsifJsonWriter.Write(new Event(Event.EventKind.Begin, documentVertex.GetId(), idFactory)); - // TODO: move to checking the enum member mentioned in https://github.com/dotnet/roslyn/issues/49326 when that - // is implemented. In the mean time, we'll use a heuristic of the path being a relative path as a way to indicate - // this is a source generated file. - if (!PathUtilities.IsAbsolute(syntaxTree.FilePath)) - { - var text = semanticModel.SyntaxTree.GetText(); + await GenerateDocumentSymbolsAsync(document, documentVertex, options, topLevelSymbolsResultSetTracker, lsifJsonWriter, idFactory).ConfigureAwait(false); + await GenerateDocumentFoldingRangesAsync(document, documentVertex, options, lsifJsonWriter, idFactory).ConfigureAwait(false); - // We always use UTF-8 encoding when writing out file contents, as that's expected by LSIF implementations. - // TODO: when we move to .NET Core, is there a way to reduce allocations here? - contentBase64Encoded = Convert.ToBase64String(Encoding.UTF8.GetBytes(text.ToString())); + lsifJsonWriter.Write(new Event(Event.EventKind.End, documentVertex.GetId(), idFactory)); + GC.KeepAlive(semanticModel); - // There is a triple slash here, so the "host" portion of the URI is empty, similar to - // how file URIs work. - uri = "source-generated:///" + syntaxTree.FilePath.Replace('\\', '/'); - } + return documentVertex.GetId(); + } - var documentVertex = new Graph.LsifDocument(new Uri(uri, UriKind.RelativeOrAbsolute), GetLanguageKind(semanticModel.Language), contentBase64Encoded, idFactory); + private static async Task GenerateDocumentFoldingRangesAsync( + Document document, + LsifDocument documentVertex, + GeneratorOptions options, + ILsifJsonWriter lsifJsonWriter, + IdFactory idFactory) + { + var foldingRanges = await FoldingRangesHandler.GetFoldingRangesAsync( + document, options.BlockStructureOptions, CancellationToken.None).ConfigureAwait(false); + var foldingRangeResult = new FoldingRangeResult(foldingRanges, idFactory); + lsifJsonWriter.Write(foldingRangeResult); + lsifJsonWriter.Write(Edge.Create(Methods.TextDocumentFoldingRangeName, documentVertex.GetId(), foldingRangeResult.GetId(), idFactory)); + } - lsifJsonWriter.Write(documentVertex); - lsifJsonWriter.Write(new Event(Event.EventKind.Begin, documentVertex.GetId(), idFactory)); + private static async Task GenerateDocumentSymbolsAsync( + Document document, + LsifDocument documentVertex, + GeneratorOptions options, + IResultSetTracker topLevelSymbolsResultSetTracker, + ILsifJsonWriter lsifJsonWriter, + IdFactory idFactory) + { + var languageServices = document.Project.Services; + + var semanticModel = await document.GetRequiredSemanticModelAsync(CancellationToken.None); + + var syntaxTree = semanticModel.SyntaxTree; + var sourceText = semanticModel.SyntaxTree.GetText(); + var syntaxFactsService = languageServices.GetRequiredService(); + var semanticFactsService = languageServices.GetRequiredService(); // As we are processing this file, we are going to encounter symbols that have a shared resultSet with other documents like types // or methods. We're also going to encounter locals that never leave this document. We don't want those locals being held by @@ -331,16 +347,30 @@ void MarkImplementationOfSymbol(ISymbol baseMember) } lsifJsonWriter.Write(Edge.Create("contains", documentVertex.GetId(), rangeVertices, idFactory)); + } - // Write the folding ranges for the document. - var foldingRanges = await FoldingRangesHandler.GetFoldingRangesAsync( - document, options.BlockStructureOptions, CancellationToken.None).ConfigureAwait(false); - var foldingRangeResult = new FoldingRangeResult(foldingRanges, idFactory); - lsifJsonWriter.Write(foldingRangeResult); - lsifJsonWriter.Write(Edge.Create(Methods.TextDocumentFoldingRangeName, documentVertex.GetId(), foldingRangeResult.GetId(), idFactory)); + private static async Task<(string uri, string? contentBase64Encoded)> GetUriAndContentAsync(Document document) + { + string? contentBase64Encoded = null; + var uri = document.FilePath; - lsifJsonWriter.Write(new Event(Event.EventKind.End, documentVertex.GetId(), idFactory)); - return documentVertex.GetId(); + // TODO: move to checking the enum member mentioned in https://github.com/dotnet/roslyn/issues/49326 when that + // is implemented. In the mean time, we'll use a heuristic of the path being a relative path as a way to indicate + // this is a source generated file. + if (!PathUtilities.IsAbsolute(uri)) + { + var text = await document.GetTextAsync().ConfigureAwait(false); + + // We always use UTF-8 encoding when writing out file contents, as that's expected by LSIF implementations. + // TODO: when we move to .NET Core, is there a way to reduce allocations here? + contentBase64Encoded = Convert.ToBase64String(Encoding.UTF8.GetBytes(text.ToString())); + + // There is a triple slash here, so the "host" portion of the URI is empty, similar to + // how file URIs work. + uri = "source-generated:///" + uri.Replace('\\', '/'); + } + + return (uri, contentBase64Encoded); } private static bool IncludeSymbolInReferences(ISymbol symbol) From 0f4ff61846e4ea2760fb29781de9f9e55546dda2 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 7 Dec 2022 18:18:50 -0800 Subject: [PATCH 04/17] Move Hover onto Documents --- .../Protocol/Handler/Hover/HoverHandler.cs | 47 +++++++------------ .../Hover/ILspHoverResultCreationService.cs | 11 ++--- src/Features/Lsif/Generator/Generator.cs | 2 +- 3 files changed, 24 insertions(+), 36 deletions(-) diff --git a/src/Features/LanguageServer/Protocol/Handler/Hover/HoverHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Hover/HoverHandler.cs index 96acd1253bf72..a059d257138a1 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Hover/HoverHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Hover/HoverHandler.cs @@ -12,6 +12,7 @@ using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.QuickInfo; +using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.LanguageServer.Protocol; @@ -46,52 +47,40 @@ public HoverHandler(IGlobalOptionService globalOptions) var clientCapabilities = context.GetRequiredClientCapabilities(); var position = await document.GetPositionFromLinePositionAsync(ProtocolConversions.PositionToLinePosition(request.Position), cancellationToken).ConfigureAwait(false); - var quickInfoService = document.Project.Services.GetRequiredService(); var options = _globalOptions.GetSymbolDescriptionOptions(document.Project.Language); - var info = await quickInfoService.GetQuickInfoAsync(document, position, options, cancellationToken).ConfigureAwait(false); - if (info == null) - return null; + return await GetHoverAsync(document, position, options, clientCapabilities, cancellationToken).ConfigureAwait(false); - var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); - return await GetHoverAsync( - document.Project.Solution.Services, info, text, document.Project.Language, - document, clientCapabilities, cancellationToken).ConfigureAwait(false); + //var quickInfoService = document.Project.Services.GetRequiredService(); + // + //var info = await quickInfoService.GetQuickInfoAsync(document, position, options, cancellationToken).ConfigureAwait(false); + //if (info == null) + // return null; + + //var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); + //return await GetHoverAsync( + // document.Project.Solution.Services, info, text, document.Project.Language, + // document, clientCapabilities, cancellationToken).ConfigureAwait(false); } internal static async Task GetHoverAsync( - SemanticModel semanticModel, + Document document, int position, SymbolDescriptionOptions options, - LanguageServices languageServices, ClientCapabilities clientCapabilities, CancellationToken cancellationToken) { - Debug.Assert(semanticModel.Language is LanguageNames.CSharp or LanguageNames.VisualBasic); + var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); // Get the quick info service to compute quick info. // This code path is only invoked for C# and VB, so we can directly cast to QuickInfoServiceWithProviders. - var quickInfoService = (QuickInfoServiceWithProviders)languageServices.GetRequiredService(); - var info = await quickInfoService.GetQuickInfoAsync(semanticModel, position, options, cancellationToken).ConfigureAwait(false); + var quickInfoService = document.GetRequiredLanguageService(); + var info = await quickInfoService.GetQuickInfoAsync(document, position, options, cancellationToken).ConfigureAwait(false); if (info == null) return null; var text = await semanticModel.SyntaxTree.GetTextAsync(cancellationToken).ConfigureAwait(false); - return await GetHoverAsync( - languageServices.SolutionServices, info, text, semanticModel.Language, document: null, clientCapabilities, cancellationToken).ConfigureAwait(false); - } - - private static async Task GetHoverAsync( - SolutionServices solutionServices, - QuickInfoItem info, - SourceText text, - string language, - Document? document, - ClientCapabilities? clientCapabilities, - CancellationToken cancellationToken) - { - var hoverService = solutionServices.GetRequiredService(); - return await hoverService.CreateHoverAsync( - text, language, info, document, clientCapabilities, cancellationToken).ConfigureAwait(false); + var hoverService = document.Project.Solution.Services.GetRequiredService(); + return await hoverService.CreateHoverAsync(document, info, clientCapabilities, cancellationToken).ConfigureAwait(false); } } } diff --git a/src/Features/LanguageServer/Protocol/Handler/Hover/ILspHoverResultCreationService.cs b/src/Features/LanguageServer/Protocol/Handler/Hover/ILspHoverResultCreationService.cs index 9001aac425101..4a2aaf905caee 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Hover/ILspHoverResultCreationService.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Hover/ILspHoverResultCreationService.cs @@ -11,7 +11,6 @@ using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.QuickInfo; -using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.Handler @@ -19,7 +18,7 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler internal interface ILspHoverResultCreationService : IWorkspaceService { Task CreateHoverAsync( - SourceText text, string language, QuickInfoItem info, Document? document, ClientCapabilities? clientCapabilities, CancellationToken cancellationToken); + Document document, QuickInfoItem info, ClientCapabilities clientCapabilities, CancellationToken cancellationToken); } [ExportWorkspaceService(typeof(ILspHoverResultCreationService)), Shared] @@ -31,10 +30,7 @@ public DefaultLspHoverResultCreationService() { } - public Task CreateHoverAsync(SourceText text, string language, QuickInfoItem info, Document? document, ClientCapabilities? clientCapabilities, CancellationToken cancellationToken) - => Task.FromResult(CreateDefaultHover(text, language, info, clientCapabilities)); - - public static Hover CreateDefaultHover(SourceText text, string language, QuickInfoItem info, ClientCapabilities? clientCapabilities) + public async Task CreateHoverAsync(Document document, QuickInfoItem info, ClientCapabilities clientCapabilities, CancellationToken cancellationToken) { var clientSupportsMarkdown = clientCapabilities?.TextDocument?.Hover?.ContentFormat.Contains(MarkupKind.Markdown) == true; @@ -43,6 +39,9 @@ public static Hover CreateDefaultHover(SourceText text, string language, QuickIn .SelectMany(section => section.TaggedParts.Add(new TaggedText(TextTags.LineBreak, Environment.NewLine))) .ToImmutableArray(); + var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); + var language = document.Project.Language; + return new Hover { Range = ProtocolConversions.TextSpanToRange(info.Span, text), diff --git a/src/Features/Lsif/Generator/Generator.cs b/src/Features/Lsif/Generator/Generator.cs index d14042af248d9..7f0a97758491a 100644 --- a/src/Features/Lsif/Generator/Generator.cs +++ b/src/Features/Lsif/Generator/Generator.cs @@ -335,7 +335,7 @@ void MarkImplementationOfSymbol(ISymbol baseMember) if (symbolResultsTracker.ResultSetNeedsInformationalEdgeAdded(symbolForLinkedResultSet, Methods.TextDocumentHoverName)) { var hover = await HoverHandler.GetHoverAsync( - semanticModel, syntaxToken.SpanStart, options.SymbolDescriptionOptions, languageServices, LspClientCapabilities, CancellationToken.None); + document, syntaxToken.SpanStart, options.SymbolDescriptionOptions, LspClientCapabilities, CancellationToken.None); if (hover != null) { var hoverResult = new HoverResult(hover, idFactory); From 5915f19ab9ae819f330035fd894dcb4e49fdae4b Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 7 Dec 2022 18:21:36 -0800 Subject: [PATCH 05/17] Share more --- .../Core/LanguageServer/EditorHoverCreationService.cs | 7 +++++-- .../Handler/Hover/ILspHoverResultCreationService.cs | 5 ++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/EditorFeatures/Core/LanguageServer/EditorHoverCreationService.cs b/src/EditorFeatures/Core/LanguageServer/EditorHoverCreationService.cs index f679aa667e98e..077205f1e5253 100644 --- a/src/EditorFeatures/Core/LanguageServer/EditorHoverCreationService.cs +++ b/src/EditorFeatures/Core/LanguageServer/EditorHoverCreationService.cs @@ -31,12 +31,15 @@ public EditorLspHoverResultCreationService(IGlobalOptionService optionService) } public async Task CreateHoverAsync( - SourceText text, string language, QuickInfoItem info, Document? document, ClientCapabilities? clientCapabilities, CancellationToken cancellationToken) + Document document, QuickInfoItem info, ClientCapabilities clientCapabilities, CancellationToken cancellationToken) { var supportsVSExtensions = clientCapabilities.HasVisualStudioLspCapability(); if (!supportsVSExtensions) - return DefaultLspHoverResultCreationService.CreateDefaultHover(text, language, info, clientCapabilities); + return await DefaultLspHoverResultCreationService.CreateDefaultHoverAsync(document, info, clientCapabilities, cancellationToken).ConfigureAwait(false); + + var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); + var language = document.Project.Language; var classificationOptions = _optionService.GetClassificationOptions(language); diff --git a/src/Features/LanguageServer/Protocol/Handler/Hover/ILspHoverResultCreationService.cs b/src/Features/LanguageServer/Protocol/Handler/Hover/ILspHoverResultCreationService.cs index 4a2aaf905caee..30f513b083506 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Hover/ILspHoverResultCreationService.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Hover/ILspHoverResultCreationService.cs @@ -30,7 +30,10 @@ public DefaultLspHoverResultCreationService() { } - public async Task CreateHoverAsync(Document document, QuickInfoItem info, ClientCapabilities clientCapabilities, CancellationToken cancellationToken) + public Task CreateHoverAsync(Document document, QuickInfoItem info, ClientCapabilities clientCapabilities, CancellationToken cancellationToken) + => CreateDefaultHoverAsync(document, info, clientCapabilities, cancellationToken); + + public static async Task CreateDefaultHoverAsync(Document document, QuickInfoItem info, ClientCapabilities clientCapabilities, CancellationToken cancellationToken) { var clientSupportsMarkdown = clientCapabilities?.TextDocument?.Hover?.ContentFormat.Contains(MarkupKind.Markdown) == true; From bc76983922b8a66f7c82bc613a294da5245755ec Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 7 Dec 2022 18:22:20 -0800 Subject: [PATCH 06/17] Cleanup --- .../Core/LanguageServer/EditorHoverCreationService.cs | 2 -- .../Protocol/Handler/Hover/HoverHandler.cs | 11 ----------- 2 files changed, 13 deletions(-) diff --git a/src/EditorFeatures/Core/LanguageServer/EditorHoverCreationService.cs b/src/EditorFeatures/Core/LanguageServer/EditorHoverCreationService.cs index 077205f1e5253..424d7a2b75ea4 100644 --- a/src/EditorFeatures/Core/LanguageServer/EditorHoverCreationService.cs +++ b/src/EditorFeatures/Core/LanguageServer/EditorHoverCreationService.cs @@ -12,9 +12,7 @@ using Microsoft.CodeAnalysis.LanguageServer.Handler; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.QuickInfo; -using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.LanguageServer.Protocol; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.LanguageServer { diff --git a/src/Features/LanguageServer/Protocol/Handler/Hover/HoverHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Hover/HoverHandler.cs index a059d257138a1..c75e015ef7437 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Hover/HoverHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Hover/HoverHandler.cs @@ -49,17 +49,6 @@ public HoverHandler(IGlobalOptionService globalOptions) var position = await document.GetPositionFromLinePositionAsync(ProtocolConversions.PositionToLinePosition(request.Position), cancellationToken).ConfigureAwait(false); var options = _globalOptions.GetSymbolDescriptionOptions(document.Project.Language); return await GetHoverAsync(document, position, options, clientCapabilities, cancellationToken).ConfigureAwait(false); - - //var quickInfoService = document.Project.Services.GetRequiredService(); - // - //var info = await quickInfoService.GetQuickInfoAsync(document, position, options, cancellationToken).ConfigureAwait(false); - //if (info == null) - // return null; - - //var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); - //return await GetHoverAsync( - // document.Project.Solution.Services, info, text, document.Project.Language, - // document, clientCapabilities, cancellationToken).ConfigureAwait(false); } internal static async Task GetHoverAsync( From 9bddf8bf7c2abe32c17a6f1c483a4dc684349c27 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 7 Dec 2022 18:29:19 -0800 Subject: [PATCH 07/17] Update tests --- .../GeneratorTest/CompilerInvocationTests.vb | 51 +++++++++++-------- .../GeneratorTest/Utilities/TestLsifOutput.vb | 2 +- 2 files changed, 30 insertions(+), 23 deletions(-) diff --git a/src/Features/Lsif/GeneratorTest/CompilerInvocationTests.vb b/src/Features/Lsif/GeneratorTest/CompilerInvocationTests.vb index 64246e04f7061..2d5af6872c5e6 100644 --- a/src/Features/Lsif/GeneratorTest/CompilerInvocationTests.vb +++ b/src/Features/Lsif/GeneratorTest/CompilerInvocationTests.vb @@ -12,7 +12,7 @@ Namespace Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.UnitTests ' PortableExecutableReference.CreateFromFile implicitly reads the file so the file must exist. Dim referencePath = GetType(Object).Assembly.Location - Dim compilerInvocation = Await Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.CompilerInvocation.CreateFromJsonAsync(" + Dim project = Await Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.CompilerInvocation.CreateFromJsonAsync(" { ""tool"": ""csc"", ""arguments"": ""/noconfig /nowarn:1701,1702 /fullpaths /define:DEBUG /reference:" + referencePath.Replace("\", "\\") + " Z:\\SourceFile.cs /target:library /out:Z:\\Output.dll"", @@ -20,15 +20,16 @@ Namespace Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.UnitTests ""sourceRootPath"": ""Z:\\"" }") - Assert.Equal(LanguageNames.CSharp, compilerInvocation.Compilation.Language) - Assert.Equal("Z:\Project.csproj", compilerInvocation.ProjectFilePath) - Assert.Equal(OutputKind.DynamicallyLinkedLibrary, compilerInvocation.Compilation.Options.OutputKind) + Assert.Equal(LanguageNames.CSharp, project.Language) + Assert.Equal("Z:\Project.csproj", project.FilePath) + Dim compilation = Await project.GetCompilationAsync() + Assert.Equal(OutputKind.DynamicallyLinkedLibrary, compilation.Options.OutputKind) - Dim syntaxTree = Assert.Single(compilerInvocation.Compilation.SyntaxTrees) + Dim syntaxTree = Assert.Single(compilation.SyntaxTrees) Assert.Equal("Z:\SourceFile.cs", syntaxTree.FilePath) Assert.Equal("DEBUG", Assert.Single(syntaxTree.Options.PreprocessorSymbolNames)) - Dim metadataReference = Assert.Single(compilerInvocation.Compilation.References) + Dim metadataReference = Assert.Single(compilation.References) Assert.Equal(referencePath, DirectCast(metadataReference, PortableExecutableReference).FilePath) End Function @@ -38,7 +39,7 @@ Namespace Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.UnitTests ' PortableExecutableReference.CreateFromFile implicitly reads the file so the file must exist. Dim referencePath = GetType(Object).Assembly.Location - Dim compilerInvocation = Await Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.CompilerInvocation.CreateFromJsonAsync(" + Dim project = Await Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.CompilerInvocation.CreateFromJsonAsync(" { ""tool"": ""vbc"", ""arguments"": ""/noconfig /nowarn:1701,1702 /fullpaths /define:DEBUG /reference:" + referencePath.Replace("\", "\\") + " Z:\\SourceFile.vb /target:library /out:Z:\\Output.dll"", @@ -46,15 +47,16 @@ Namespace Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.UnitTests ""sourceRootPath"": ""Z:\\"" }") - Assert.Equal(LanguageNames.VisualBasic, compilerInvocation.Compilation.Language) - Assert.Equal("Z:\Project.vbproj", compilerInvocation.ProjectFilePath) - Assert.Equal(OutputKind.DynamicallyLinkedLibrary, compilerInvocation.Compilation.Options.OutputKind) + Assert.Equal(LanguageNames.VisualBasic, project.Language) + Assert.Equal("Z:\Project.vbproj", project.FilePath) + Dim compilation = Await project.GetCompilationAsync() + Assert.Equal(OutputKind.DynamicallyLinkedLibrary, compilation.Options.OutputKind) - Dim syntaxTree = Assert.Single(compilerInvocation.Compilation.SyntaxTrees) + Dim syntaxTree = Assert.Single(compilation.SyntaxTrees) Assert.Equal("Z:\SourceFile.vb", syntaxTree.FilePath) Assert.Contains("DEBUG", syntaxTree.Options.PreprocessorSymbolNames) - Dim metadataReference = Assert.Single(compilerInvocation.Compilation.References) + Dim metadataReference = Assert.Single(compilation.References) Assert.Equal(referencePath, DirectCast(metadataReference, PortableExecutableReference).FilePath) End Function @@ -62,7 +64,7 @@ Namespace Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.UnitTests Public Async Function TestSourceFilePathMappingWithDriveLetters( from As String, [to] As String) As Task - Dim compilerInvocation = Await Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.CompilerInvocation.CreateFromJsonAsync(" + Dim project = Await Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.CompilerInvocation.CreateFromJsonAsync(" { ""tool"": ""csc"", ""arguments"": ""/noconfig /nowarn:1701,1702 /fullpaths /define:DEBUG F:\\SourceFile.cs /target:library /out:F:\\Output.dll"", @@ -75,14 +77,15 @@ Namespace Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.UnitTests }] }") - Dim syntaxTree = Assert.Single(compilerInvocation.Compilation.SyntaxTrees) + Dim compilation = Await project.GetCompilationAsync() + Dim syntaxTree = Assert.Single(compilation.SyntaxTrees) Assert.Equal("T:\SourceFile.cs", syntaxTree.FilePath) End Function Public Async Function TestSourceFilePathMappingWithSubdirectoriesWithoutTrailingSlashes() As Task - Dim compilerInvocation = Await Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.CompilerInvocation.CreateFromJsonAsync(" + Dim project = Await Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.CompilerInvocation.CreateFromJsonAsync(" { ""tool"": ""csc"", ""arguments"": ""/noconfig /nowarn:1701,1702 /fullpaths /define:DEBUG F:\\Directory\\SourceFile.cs /target:library /out:F:\\Output.dll"", @@ -95,14 +98,15 @@ Namespace Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.UnitTests }] }") - Dim syntaxTree = Assert.Single(compilerInvocation.Compilation.SyntaxTrees) + Dim compilation = Await project.GetCompilationAsync() + Dim syntaxTree = Assert.Single(compilation.SyntaxTrees) Assert.Equal("T:\Directory\SourceFile.cs", syntaxTree.FilePath) End Function Public Async Function TestSourceFilePathMappingWithSubdirectoriesWithDoubleSlashesInFilePath() As Task - Dim compilerInvocation = Await Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.CompilerInvocation.CreateFromJsonAsync(" + Dim project = Await Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.CompilerInvocation.CreateFromJsonAsync(" { ""tool"": ""csc"", ""arguments"": ""/noconfig /nowarn:1701,1702 /fullpaths /define:DEBUG F:\\Directory\\\\SourceFile.cs /target:library /out:F:\\Output.dll"", @@ -115,7 +119,8 @@ Namespace Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.UnitTests }] }") - Dim syntaxTree = Assert.Single(compilerInvocation.Compilation.SyntaxTrees) + Dim compilation = Await Project.GetCompilationAsync() + Dim syntaxTree = Assert.Single(compilation.SyntaxTrees) Assert.Equal("T:\Directory\SourceFile.cs", syntaxTree.FilePath) End Function @@ -133,7 +138,7 @@ Namespace Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.UnitTests ruleSet.WriteAllText(RuleSetContents) ' We will test that if we redirect the ruleset to the temporary file that we wrote that the values are still read. - Dim compilerInvocation = Await Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.CompilerInvocation.CreateFromJsonAsync(" + Dim project = Await Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.CompilerInvocation.CreateFromJsonAsync(" { ""tool"": ""csc"", ""arguments"": ""/noconfig /nowarn:1701,1702 /fullpaths /define:DEBUG /ruleset:F:\\Ruleset.ruleset /out:Output.dll"", @@ -146,7 +151,8 @@ Namespace Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.UnitTests }] }") - Assert.Equal(ReportDiagnostic.Warn, compilerInvocation.Compilation.Options.SpecificDiagnosticOptions("CA1001")) + Dim compilation = Await project.GetCompilationAsync() + Assert.Equal(ReportDiagnostic.Warn, compilation.Options.SpecificDiagnosticOptions("CA1001")) End Using End Function @@ -154,7 +160,7 @@ Namespace Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.UnitTests Public Async Function TestSourceGeneratorOutputIncludedInCompilation() As Task Dim sourceGeneratorLocation = GetType(TestSourceGenerator.HelloWorldGenerator).Assembly.Location - Dim compilerInvocation = Await Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.CompilerInvocation.CreateFromJsonAsync(" + Dim project = Await Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.CompilerInvocation.CreateFromJsonAsync(" { ""tool"": ""csc"", ""arguments"": ""/noconfig /analyzer:\""" + sourceGeneratorLocation.Replace("\", "\\") + "\"" /out:Output.dll"", @@ -162,7 +168,8 @@ Namespace Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.UnitTests ""sourceRootPath"": ""F:\\"" }") - Dim generatedTrees = compilerInvocation.Compilation.SyntaxTrees + Dim compilation = Await project.GetCompilationAsync() + Dim generatedTrees = compilation.SyntaxTrees Assert.Single(generatedTrees, Function(t) t.FilePath.EndsWith(TestSourceGenerator.HelloWorldGenerator.GeneratedEnglishClassName + ".cs")) Assert.Single(generatedTrees, Function(t) t.FilePath.EndsWith(TestSourceGenerator.HelloWorldGenerator.GeneratedSpanishClassName + ".cs")) diff --git a/src/Features/Lsif/GeneratorTest/Utilities/TestLsifOutput.vb b/src/Features/Lsif/GeneratorTest/Utilities/TestLsifOutput.vb index 221a30c89f792..97cb52a158596 100644 --- a/src/Features/Lsif/GeneratorTest/Utilities/TestLsifOutput.vb +++ b/src/Features/Lsif/GeneratorTest/Utilities/TestLsifOutput.vb @@ -52,7 +52,7 @@ Namespace Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.UnitTests.U ' Assert we don't have any errors to prevent any typos in the tests Assert.Empty(compilation.GetDiagnostics().Where(Function(d) d.Severity = DiagnosticSeverity.Error)) - Await lsifGenerator.GenerateForCompilationAsync(compilation, project.FilePath, project.Services, GeneratorOptions.Default) + Await lsifGenerator.GenerateForProjectAsync(project, GeneratorOptions.Default) Next End Function From 24a46c438c442ea9e217c01b1d2d83aebc696df1 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 7 Dec 2022 18:30:44 -0800 Subject: [PATCH 08/17] Remove comments --- src/Features/Lsif/Generator/CompilerInvocation.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/Features/Lsif/Generator/CompilerInvocation.cs b/src/Features/Lsif/Generator/CompilerInvocation.cs index 05c3644e09cac..b6b8aafadc8f4 100644 --- a/src/Features/Lsif/Generator/CompilerInvocation.cs +++ b/src/Features/Lsif/Generator/CompilerInvocation.cs @@ -113,11 +113,6 @@ public static async Task CreateFromInvocationInfoAsync(CompilerInvocati hostObjectType: null); var solution = workspace.CurrentSolution.AddProject(projectInfo); - //var compilation = await solution.GetRequiredProject(projectId).GetRequiredCompilationAsync(CancellationToken.None); - //var options = GeneratorOptions.Default; - - //return new CompilerInvocation(compilation, languageServices, invocationInfo.ProjectFilePath, options); - return solution.GetRequiredProject(projectId); // Local methods: From b947a42169d1564f41746a7c55880c9e929930b3 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 8 Dec 2022 08:55:42 -0800 Subject: [PATCH 09/17] Simplify --- .../LanguageServer/Protocol/Handler/Hover/HoverHandler.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Features/LanguageServer/Protocol/Handler/Hover/HoverHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Hover/HoverHandler.cs index c75e015ef7437..5d2cea40d19fe 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Hover/HoverHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Hover/HoverHandler.cs @@ -4,7 +4,6 @@ using System; using System.Composition; -using System.Diagnostics; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Host; @@ -13,7 +12,6 @@ using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.QuickInfo; using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.Handler @@ -67,7 +65,6 @@ public HoverHandler(IGlobalOptionService globalOptions) if (info == null) return null; - var text = await semanticModel.SyntaxTree.GetTextAsync(cancellationToken).ConfigureAwait(false); var hoverService = document.Project.Solution.Services.GetRequiredService(); return await hoverService.CreateHoverAsync(document, info, clientCapabilities, cancellationToken).ConfigureAwait(false); } From 510324d1a24371172df4b9cca71abdbd1774daaf Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 8 Dec 2022 09:01:10 -0800 Subject: [PATCH 10/17] Fix --- src/Features/Lsif/Generator/Generator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Features/Lsif/Generator/Generator.cs b/src/Features/Lsif/Generator/Generator.cs index 7f0a97758491a..55fba55abaf51 100644 --- a/src/Features/Lsif/Generator/Generator.cs +++ b/src/Features/Lsif/Generator/Generator.cs @@ -108,7 +108,7 @@ public async Task GenerateForProjectAsync(Project project, GeneratorOptions opti }; var tasks = new List(); - foreach (var document in project.Documents) + foreach (var document in await project.GetAllRegularAndSourceGeneratedDocumentsAsync().ConfigureAwait(false)) { tasks.Add(Task.Run(async () => { @@ -352,7 +352,7 @@ void MarkImplementationOfSymbol(ISymbol baseMember) private static async Task<(string uri, string? contentBase64Encoded)> GetUriAndContentAsync(Document document) { string? contentBase64Encoded = null; - var uri = document.FilePath; + var uri = document.FilePath ?? ""; // TODO: move to checking the enum member mentioned in https://github.com/dotnet/roslyn/issues/49326 when that // is implemented. In the mean time, we'll use a heuristic of the path being a relative path as a way to indicate From 1622af751a1fe8977c9a27e84afde7c4dbce4f2e Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Thu, 8 Dec 2022 09:24:52 -0800 Subject: [PATCH 11/17] Update src/Features/LanguageServer/Protocol/Handler/Hover/HoverHandler.cs --- .../LanguageServer/Protocol/Handler/Hover/HoverHandler.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Features/LanguageServer/Protocol/Handler/Hover/HoverHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Hover/HoverHandler.cs index 5d2cea40d19fe..72e0ff3d8044a 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Hover/HoverHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Hover/HoverHandler.cs @@ -56,8 +56,6 @@ public HoverHandler(IGlobalOptionService globalOptions) ClientCapabilities clientCapabilities, CancellationToken cancellationToken) { - var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); - // Get the quick info service to compute quick info. // This code path is only invoked for C# and VB, so we can directly cast to QuickInfoServiceWithProviders. var quickInfoService = document.GetRequiredLanguageService(); From c62b0a373a183ba75212e500398047fa967ab850 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Fri, 9 Dec 2022 16:15:21 -0800 Subject: [PATCH 12/17] Update src/Features/Lsif/Generator/Generator.cs Co-authored-by: Jason Malinowski --- src/Features/Lsif/Generator/Generator.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Features/Lsif/Generator/Generator.cs b/src/Features/Lsif/Generator/Generator.cs index 55fba55abaf51..1ea43a670ef44 100644 --- a/src/Features/Lsif/Generator/Generator.cs +++ b/src/Features/Lsif/Generator/Generator.cs @@ -354,10 +354,7 @@ void MarkImplementationOfSymbol(ISymbol baseMember) string? contentBase64Encoded = null; var uri = document.FilePath ?? ""; - // TODO: move to checking the enum member mentioned in https://github.com/dotnet/roslyn/issues/49326 when that - // is implemented. In the mean time, we'll use a heuristic of the path being a relative path as a way to indicate - // this is a source generated file. - if (!PathUtilities.IsAbsolute(uri)) + if (document is SourceGeneratedDocument) { var text = await document.GetTextAsync().ConfigureAwait(false); From a23ccd4e85f6f1c2d39e7e9edcac2f3224c93251 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 9 Dec 2022 16:38:17 -0800 Subject: [PATCH 13/17] make into static class --- src/Features/Lsif/Generator/CompilerInvocation.cs | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/src/Features/Lsif/Generator/CompilerInvocation.cs b/src/Features/Lsif/Generator/CompilerInvocation.cs index b6b8aafadc8f4..38d8653728670 100644 --- a/src/Features/Lsif/Generator/CompilerInvocation.cs +++ b/src/Features/Lsif/Generator/CompilerInvocation.cs @@ -18,21 +18,8 @@ namespace Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator { - internal class CompilerInvocation + internal static class CompilerInvocation { - public Compilation Compilation { get; } - public LanguageServices LanguageServices { get; } - public string ProjectFilePath { get; } - public GeneratorOptions Options { get; } - - public CompilerInvocation(Compilation compilation, LanguageServices languageServices, string projectFilePath, GeneratorOptions options) - { - Compilation = compilation; - LanguageServices = languageServices; - ProjectFilePath = projectFilePath; - Options = options; - } - public static async Task CreateFromJsonAsync(string jsonContents) { var invocationInfo = JsonConvert.DeserializeObject(jsonContents); From f4fe235a17913c414aaa573c92e11af21dab7102 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 9 Dec 2022 16:39:37 -0800 Subject: [PATCH 14/17] Simplify --- src/Features/Lsif/Generator/Generator.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Features/Lsif/Generator/Generator.cs b/src/Features/Lsif/Generator/Generator.cs index 1ea43a670ef44..771d758213aba 100644 --- a/src/Features/Lsif/Generator/Generator.cs +++ b/src/Features/Lsif/Generator/Generator.cs @@ -112,8 +112,6 @@ public async Task GenerateForProjectAsync(Project project, GeneratorOptions opti { tasks.Add(Task.Run(async () => { - var semanticModel = document.GetSemanticModelAsync(); - // We generate the document contents into an in-memory copy, and then write that out at once at the end. This // allows us to collect everything and avoid a lot of fine-grained contention on the write to the single // LSIF file. Because of the rule that vertices must be written before they're used by an edge, we'll flush any top- @@ -126,8 +124,6 @@ public async Task GenerateForProjectAsync(Project project, GeneratorOptions opti documentWriter.FlushToUnderlyingAndEmpty(); documentIds.Add(documentId); - - GC.KeepAlive(semanticModel); })); } @@ -156,6 +152,8 @@ public async Task GenerateForProjectAsync(Project project, GeneratorOptions opti ILsifJsonWriter lsifJsonWriter, IdFactory idFactory) { + // Create and keep the semantic model alive for this document. That way all work/services we kick off that + // use this document can benefit from that single shared model. var semanticModel = await document.GetRequiredSemanticModelAsync(CancellationToken.None); var (uri, contentBase64Encoded) = await GetUriAndContentAsync(document).ConfigureAwait(false); @@ -168,6 +166,7 @@ public async Task GenerateForProjectAsync(Project project, GeneratorOptions opti await GenerateDocumentFoldingRangesAsync(document, documentVertex, options, lsifJsonWriter, idFactory).ConfigureAwait(false); lsifJsonWriter.Write(new Event(Event.EventKind.End, documentVertex.GetId(), idFactory)); + GC.KeepAlive(semanticModel); return documentVertex.GetId(); From 0db148205ee8fcb37523d6fe394fc6300c5cea78 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 9 Dec 2022 16:40:08 -0800 Subject: [PATCH 15/17] Rename --- src/Features/Lsif/Generator/Generator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Features/Lsif/Generator/Generator.cs b/src/Features/Lsif/Generator/Generator.cs index 771d758213aba..6ed9e485f9975 100644 --- a/src/Features/Lsif/Generator/Generator.cs +++ b/src/Features/Lsif/Generator/Generator.cs @@ -162,7 +162,7 @@ public async Task GenerateForProjectAsync(Project project, GeneratorOptions opti lsifJsonWriter.Write(documentVertex); lsifJsonWriter.Write(new Event(Event.EventKind.Begin, documentVertex.GetId(), idFactory)); - await GenerateDocumentSymbolsAsync(document, documentVertex, options, topLevelSymbolsResultSetTracker, lsifJsonWriter, idFactory).ConfigureAwait(false); + await GenerateDocumentRangesAndLinks(document, documentVertex, options, topLevelSymbolsResultSetTracker, lsifJsonWriter, idFactory).ConfigureAwait(false); await GenerateDocumentFoldingRangesAsync(document, documentVertex, options, lsifJsonWriter, idFactory).ConfigureAwait(false); lsifJsonWriter.Write(new Event(Event.EventKind.End, documentVertex.GetId(), idFactory)); @@ -186,7 +186,7 @@ private static async Task GenerateDocumentFoldingRangesAsync( lsifJsonWriter.Write(Edge.Create(Methods.TextDocumentFoldingRangeName, documentVertex.GetId(), foldingRangeResult.GetId(), idFactory)); } - private static async Task GenerateDocumentSymbolsAsync( + private static async Task GenerateDocumentRangesAndLinks( Document document, LsifDocument documentVertex, GeneratorOptions options, From 12e92a492e06078aa3d80bee17fab68b9b3adc96 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 9 Dec 2022 16:43:21 -0800 Subject: [PATCH 16/17] pull up --- src/Features/Lsif/Generator/Generator.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Features/Lsif/Generator/Generator.cs b/src/Features/Lsif/Generator/Generator.cs index 6ed9e485f9975..28589e6f14bfd 100644 --- a/src/Features/Lsif/Generator/Generator.cs +++ b/src/Features/Lsif/Generator/Generator.cs @@ -162,9 +162,13 @@ public async Task GenerateForProjectAsync(Project project, GeneratorOptions opti lsifJsonWriter.Write(documentVertex); lsifJsonWriter.Write(new Event(Event.EventKind.Begin, documentVertex.GetId(), idFactory)); - await GenerateDocumentRangesAndLinks(document, documentVertex, options, topLevelSymbolsResultSetTracker, lsifJsonWriter, idFactory).ConfigureAwait(false); + // We will walk the file token-by-token, making a range for each one and then attaching information for it + var rangeVertices = new List>(); + + await GenerateDocumentRangesAndLinks(document, documentVertex, options, topLevelSymbolsResultSetTracker, lsifJsonWriter, idFactory, rangeVertices).ConfigureAwait(false); await GenerateDocumentFoldingRangesAsync(document, documentVertex, options, lsifJsonWriter, idFactory).ConfigureAwait(false); + lsifJsonWriter.Write(Edge.Create("contains", documentVertex.GetId(), rangeVertices, idFactory)); lsifJsonWriter.Write(new Event(Event.EventKind.End, documentVertex.GetId(), idFactory)); GC.KeepAlive(semanticModel); @@ -192,7 +196,8 @@ private static async Task GenerateDocumentRangesAndLinks( GeneratorOptions options, IResultSetTracker topLevelSymbolsResultSetTracker, ILsifJsonWriter lsifJsonWriter, - IdFactory idFactory) + IdFactory idFactory, + List> rangeVertices) { var languageServices = document.Project.Services; @@ -228,9 +233,6 @@ SymbolKind.RangeVariable or } }); - // We will walk the file token-by-token, making a range for each one and then attaching information for it - var rangeVertices = new List>(); - foreach (var syntaxToken in syntaxTree.GetRoot().DescendantTokens(descendIntoTrivia: true)) { // We'll only create the Range vertex once it's needed, but any number of bits of code might create it first, @@ -344,8 +346,6 @@ void MarkImplementationOfSymbol(ISymbol baseMember) } } } - - lsifJsonWriter.Write(Edge.Create("contains", documentVertex.GetId(), rangeVertices, idFactory)); } private static async Task<(string uri, string? contentBase64Encoded)> GetUriAndContentAsync(Document document) From 1d11efd05eb257fc4549aaef26ee14b69b69be2b Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 9 Dec 2022 17:11:24 -0800 Subject: [PATCH 17/17] Reorder --- src/Features/Lsif/Generator/Generator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Features/Lsif/Generator/Generator.cs b/src/Features/Lsif/Generator/Generator.cs index 28589e6f14bfd..4053e77e18316 100644 --- a/src/Features/Lsif/Generator/Generator.cs +++ b/src/Features/Lsif/Generator/Generator.cs @@ -164,11 +164,11 @@ public async Task GenerateForProjectAsync(Project project, GeneratorOptions opti // We will walk the file token-by-token, making a range for each one and then attaching information for it var rangeVertices = new List>(); - await GenerateDocumentRangesAndLinks(document, documentVertex, options, topLevelSymbolsResultSetTracker, lsifJsonWriter, idFactory, rangeVertices).ConfigureAwait(false); + lsifJsonWriter.Write(Edge.Create("contains", documentVertex.GetId(), rangeVertices, idFactory)); + await GenerateDocumentFoldingRangesAsync(document, documentVertex, options, lsifJsonWriter, idFactory).ConfigureAwait(false); - lsifJsonWriter.Write(Edge.Create("contains", documentVertex.GetId(), rangeVertices, idFactory)); lsifJsonWriter.Write(new Event(Event.EventKind.End, documentVertex.GetId(), idFactory)); GC.KeepAlive(semanticModel);