/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LanguageServerWorkspaceFactory.cs
122 строки
7 KB
Jason Malinowski
Switch to just using disposal as a way to cancel file watching
04 авг 2026, 23:51
04 авг 2026, 23:51
e6b7a7c
Код
Авторство
О чём код?
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System.Collections.Immutable; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageServer.Services; using Microsoft.CodeAnalysis.ProjectSystem; using Microsoft.CodeAnalysis.Workspaces.AnalyzerRedirecting; using Microsoft.CodeAnalysis.Workspaces.ProjectSystem; using Microsoft.CommonLanguageServerProtocol.Framework; using Microsoft.Extensions.Logging; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.LanguageServer.HostWorkspace; /// <summary> /// Owns the host and miscellaneous-files <see cref="LanguageServerWorkspace"/> instances and their /// <see cref="ProjectSystemProjectFactory"/> objects for a single LSP server. Created once per /// <see cref="LspServices"/> instance by <see cref="LanguageServerWorkspaceFactoryServiceFactory"/> and /// disposed when the LSP server shuts down. /// </summary> internal sealed class LanguageServerWorkspaceFactory : ILspService, IHostWorkspaceProvider, IDisposable { private readonly ILogger _logger; private readonly ImmutableArray<string> _solutionLevelAnalyzerPaths; public LanguageServerWorkspaceFactory( HostServicesProvider hostServicesProvider, ILspServices lspServices, ExtensionAssemblyManager extensionManager, IEnumerable<IAnalyzerAssemblyRedirector> assemblyRedirectors, ILoggerFactory loggerFactory) { _logger = loggerFactory.CreateLogger(nameof(LanguageServerWorkspaceFactory)); // Before we can create the workspace, let's figure out the solution-level analyzers; we'll pull in analyzers from our own binaries // as well as anything coming from extensions. _solutionLevelAnalyzerPaths = new DirectoryInfo(AppContext.BaseDirectory).GetFiles("*.dll") .Where(f => f.Name.StartsWith("Microsoft.CodeAnalysis.", StringComparison.Ordinal) && !f.Name.Contains("LanguageServer", StringComparison.Ordinal)) .Select(f => f.FullName) .Concat(extensionManager.ExtensionAssemblyPaths) .ToImmutableArray(); // Create the workspace and set analyzer references for it var workspace = new LanguageServerWorkspace(hostServicesProvider.HostServices, WorkspaceKind.Host); var hostAnalyzerLoaderProvider = workspace.Services.GetRequiredService<IAnalyzerAssemblyLoaderProvider>(); var analyzerReferences = CreateSolutionLevelAnalyzerReferences(hostAnalyzerLoaderProvider); workspace.SetCurrentSolution(s => s.WithAnalyzerReferences(analyzerReferences), WorkspaceChangeKind.SolutionChanged); var fileChangeWatcher = lspServices.GetRequiredService<IFileChangeWatcher>(); HostProjectFactory = new ProjectSystemProjectFactory( workspace, fileChangeWatcher, static (_, _) => Task.CompletedTask, _ => { }); workspace.ProjectSystemProjectFactory = HostProjectFactory; // https://github.com/dotnet/roslyn/issues/78560: Move this workspace creation to 'FileBasedProgramsWorkspaceProviderFactory'. // 'CreateSolutionLevelAnalyzerReferences' needs to be broken out into its own service for us to be able to move this. var miscellaneousFilesWorkspace = new LanguageServerWorkspace(hostServicesProvider.HostServices, WorkspaceKind.MiscellaneousFiles); Contract.ThrowIfFalse( object.ReferenceEquals(hostAnalyzerLoaderProvider, miscellaneousFilesWorkspace.Services.GetRequiredService<IAnalyzerAssemblyLoaderProvider>()), "Expected same AnalyzerLoaderProvider to be used for both host and misc workspaces."); miscellaneousFilesWorkspace.SetCurrentSolution(s => s.WithAnalyzerReferences(analyzerReferences), WorkspaceChangeKind.SolutionChanged); MiscellaneousFilesWorkspaceProjectFactory = new ProjectSystemProjectFactory( miscellaneousFilesWorkspace, fileChangeWatcher, static (_, _) => Task.CompletedTask, _ => { }); miscellaneousFilesWorkspace.ProjectSystemProjectFactory = MiscellaneousFilesWorkspaceProjectFactory; // Register this server's Host and miscellaneous-files workspaces directly with its own registration // service, rather than relying on the process-wide event listener (which, in the standalone server, only // tracks the shared MetadataAsSource workspace). This keeps these per-server workspaces visible only to // this server so concurrent daemon-mode servers stay isolated from one another. var workspaceRegistrationService = lspServices.GetRequiredService<LspWorkspaceRegistrationService>(); workspaceRegistrationService.Register(workspace); workspaceRegistrationService.Register(miscellaneousFilesWorkspace); ProjectSystemHostInfo = new ProjectSystemHostInfo( AnalyzerAssemblyRedirectors: [.. assemblyRedirectors]); } public Workspace HostWorkspace => HostProjectFactory.Workspace; public ProjectSystemProjectFactory HostProjectFactory { get; } public ProjectSystemProjectFactory MiscellaneousFilesWorkspaceProjectFactory { get; } public ProjectSystemHostInfo ProjectSystemHostInfo { get; } Workspace IHostWorkspaceProvider.Workspace => HostWorkspace; public void Dispose() { HostProjectFactory.Dispose(); MiscellaneousFilesWorkspaceProjectFactory.Dispose(); } private ImmutableArray<AnalyzerFileReference> CreateSolutionLevelAnalyzerReferences(IAnalyzerAssemblyLoaderProvider loaderProvider) { // Load all analyzers into a fresh shadow copied load context. In the future, if we want to support reloading // of solution-level analyzer references, we should just need to listen for changes to those analyzer paths and // then call back into this method to update the solution accordingly. var analyzerLoader = loaderProvider.CreateNewShadowCopyLoader(); var references = _solutionLevelAnalyzerPaths .AsParallel() .Where(analyzerPath => { if (File.Exists(analyzerPath)) return true; _logger.LogWarning($"Solution-level analyzer at {analyzerPath} could not be found."); return false; }) .Select(analyzerPath => { var reference = new AnalyzerFileReference(analyzerPath, analyzerLoader); _logger.LogDebug($"Solution-level analyzer at {analyzerPath} added to workspace."); return reference; }) .ToImmutableArray(); return references; } }