public RequestExecutionQueue( ILspLogger logger, LspWorkspaceRegistrationService lspWorkspaceRegistrationService, LspMiscellaneousFilesWorkspace?lspMiscellaneousFilesWorkspace, IGlobalOptionService globalOptions, ImmutableArray <string> supportedLanguages, string serverName, string serverTypeName) { _logger = logger; _globalOptions = globalOptions; _supportedLanguages = supportedLanguages; _serverName = serverName; _queue = new AsyncQueue <QueueItem>(); _cancelSource = new CancellationTokenSource(); // Pass the language client instance type name to the telemetry logger to ensure we can // differentiate between the different C# LSP servers that have the same client name. // We also don't use the language client's name property as it is a localized user facing string // which is difficult to write telemetry queries for. _requestTelemetryLogger = new RequestTelemetryLogger(serverTypeName); _lspWorkspaceManager = new LspWorkspaceManager(logger, lspMiscellaneousFilesWorkspace, lspWorkspaceRegistrationService, _requestTelemetryLogger); // Start the queue processing _ = ProcessQueueAsync(); }
public TestAccessor(LspWorkspaceManager manager) => _manager = manager;
public static RequestContext?Create( bool requiresLSPSolution, TextDocumentIdentifier?textDocument, string?clientName, ILspLogger logger, ClientCapabilities clientCapabilities, LspWorkspaceManager lspWorkspaceManager, IDocumentChangeTracker documentChangeTracker, ImmutableArray <string> supportedLanguages, IGlobalOptionService globalOptions) { // Retrieve the current LSP tracked text as of this request. // This is safe as all creation of request contexts cannot happen concurrently. var trackedDocuments = lspWorkspaceManager.GetTrackedLspText(); // If the handler doesn't need an LSP solution we do two important things: // 1. We don't bother building the LSP solution for perf reasons // 2. We explicitly don't give the handler a solution or document, even if we could // so they're not accidentally operating on stale solution state. if (!requiresLSPSolution) { return(new RequestContext(solution: null, logger, clientCapabilities, clientName, document: null, documentChangeTracker, trackedDocuments, supportedLanguages, globalOptions)); } // Go through each registered workspace, find the solution that contains the document that // this request is for, and then updates it based on the state of the world as we know it, based on the // text content in the document change tracker. Document?document = null; var workspaceSolution = lspWorkspaceManager.TryGetHostLspSolution(); if (textDocument is not null) { // we were given a request associated with a document. Find the corresponding roslyn // document for this. If we can't, we cannot proceed. document = lspWorkspaceManager.GetLspDocument(textDocument, clientName); if (document != null) { workspaceSolution = document.Project.Solution; } } if (workspaceSolution == null) { logger.TraceError("Could not find appropriate solution for operation"); return(null); } var context = new RequestContext( workspaceSolution, logger, clientCapabilities, clientName, document, documentChangeTracker, trackedDocuments, supportedLanguages, globalOptions); return(context); }
public static async Task <RequestContext?> CreateAsync( bool requiresLSPSolution, TextDocumentIdentifier?textDocument, WellKnownLspServerKinds serverKind, ILspLogger logger, ClientCapabilities clientCapabilities, LspWorkspaceManager lspWorkspaceManager, ILanguageServerNotificationManager notificationManager, IDocumentChangeTracker documentChangeTracker, ImmutableArray <string> supportedLanguages, IGlobalOptionService globalOptions, CancellationToken queueCancellationToken, CancellationToken requestCancellationToken) { // Retrieve the current LSP tracked text as of this request. // This is safe as all creation of request contexts cannot happen concurrently. var trackedDocuments = lspWorkspaceManager.GetTrackedLspText(); // If the handler doesn't need an LSP solution we do two important things: // 1. We don't bother building the LSP solution for perf reasons // 2. We explicitly don't give the handler a solution or document, even if we could // so they're not accidentally operating on stale solution state. if (!requiresLSPSolution) { return(new RequestContext( solution: null, logger: logger, clientCapabilities: clientCapabilities, serverKind: serverKind, document: null, documentChangeTracker: documentChangeTracker, trackedDocuments: trackedDocuments, supportedLanguages: supportedLanguages, globalOptions: globalOptions, notificationManager: notificationManager, queueCancellationToken: queueCancellationToken)); } Solution?workspaceSolution; Document?document = null; if (textDocument is not null) { // we were given a request associated with a document. Find the corresponding roslyn document for this. // There are certain cases where we may be asked for a document that does not exist (for example a document is removed) // For example, document pull diagnostics can ask us after removal to clear diagnostics for a document. document = await lspWorkspaceManager.GetLspDocumentAsync(textDocument, requestCancellationToken).ConfigureAwait(false); } workspaceSolution = document?.Project.Solution ?? await lspWorkspaceManager.TryGetHostLspSolutionAsync(requestCancellationToken).ConfigureAwait(false); if (workspaceSolution == null) { logger.TraceError("Could not find appropriate solution for operation"); return(null); } var context = new RequestContext( workspaceSolution, logger, clientCapabilities, serverKind, document, documentChangeTracker, trackedDocuments, supportedLanguages, globalOptions, notificationManager, queueCancellationToken); return(context); }
internal LanguageServerTarget( AbstractRequestDispatcherFactory requestDispatcherFactory, JsonRpc jsonRpc, ICapabilitiesProvider capabilitiesProvider, LspWorkspaceRegistrationService workspaceRegistrationService, LspMiscellaneousFilesWorkspace?lspMiscellaneousFilesWorkspace, IGlobalOptionService globalOptions, IAsynchronousOperationListenerProvider listenerProvider, ILspLogger logger, ImmutableArray <string> supportedLanguages, WellKnownLspServerKinds serverKind) { _requestDispatcher = requestDispatcherFactory.CreateRequestDispatcher(serverKind); _capabilitiesProvider = capabilitiesProvider; _logger = logger; _jsonRpc = jsonRpc; _jsonRpc.AddLocalRpcTarget(this); _jsonRpc.Disconnected += JsonRpc_Disconnected; _notificationManager = new LanguageServerNotificationManager(_jsonRpc); _listener = listenerProvider.GetListener(FeatureAttribute.LanguageServer); // Pass the language client instance type name to the telemetry logger to ensure we can // differentiate between the different C# LSP servers that have the same client name. // We also don't use the language client's name property as it is a localized user facing string // which is difficult to write telemetry queries for. _requestTelemetryLogger = new RequestTelemetryLogger(serverKind.ToTelemetryString()); _lspWorkspaceManager = new LspWorkspaceManager(logger, lspMiscellaneousFilesWorkspace, workspaceRegistrationService, _requestTelemetryLogger); _queue = new RequestExecutionQueue( logger, globalOptions, supportedLanguages, serverKind, _requestTelemetryLogger, _lspWorkspaceManager, _notificationManager); _queue.RequestServerShutdown += RequestExecutionQueue_Errored; var entryPointMethod = typeof(DelegatingEntryPoint).GetMethod(nameof(DelegatingEntryPoint.EntryPointAsync)); Contract.ThrowIfNull(entryPointMethod, $"{typeof(DelegatingEntryPoint).FullName} is missing method {nameof(DelegatingEntryPoint.EntryPointAsync)}"); foreach (var metadata in _requestDispatcher.GetRegisteredMethods()) { // Instead of concretely defining methods for each LSP method, we instead dynamically construct the // generic method info from the exported handler types. This allows us to define multiple handlers for // the same method but different type parameters. This is a key functionality to support TS external // access as we do not want to couple our LSP protocol version dll to theirs. // // We also do not use the StreamJsonRpc support for JToken as the rpc method parameters because we want // StreamJsonRpc to do the deserialization to handle streaming requests using IProgress<T>. var delegatingEntryPoint = new DelegatingEntryPoint(metadata.MethodName, this); var genericEntryPointMethod = entryPointMethod.MakeGenericMethod(metadata.RequestType, metadata.ResponseType); _jsonRpc.AddLocalRpcMethod(genericEntryPointMethod, delegatingEntryPoint, new JsonRpcMethodAttribute(metadata.MethodName) { UseSingleObjectParameterDeserialization = true }); } }