コード例 #1
0
        public override async Task <RazorDiagnosticsResponse?> TranslateAsync(
            RazorLanguageKind languageKind,
            Uri razorDocumentUri,
            Diagnostic[] diagnostics,
            CancellationToken cancellationToken)
        {
            if (!_documentManager.TryGetDocument(razorDocumentUri, out var documentSnapshot))
            {
                return(new RazorDiagnosticsResponse()
                {
                    Diagnostics = Array.Empty <Diagnostic>(),
                });
            }

            var diagnosticsParams = new RazorDiagnosticsParams()
            {
                Kind             = languageKind,
                RazorDocumentUri = razorDocumentUri,
                Diagnostics      = diagnostics
            };

            var response = await _requestInvoker.ReinvokeRequestOnServerAsync <RazorDiagnosticsParams, RazorDiagnosticsResponse>(
                documentSnapshot.Snapshot.TextBuffer,
                LanguageServerConstants.RazorTranslateDiagnosticsEndpoint,
                RazorLSPConstants.RazorLanguageServerName,
                diagnosticsParams,
                cancellationToken).ConfigureAwait(false);

            if (!ReinvocationResponseHelper.TryExtractResultOrLog(response, _logger, RazorLSPConstants.RazorLanguageServerName, out var result))
            {
                return(null);
            }

            return(result);
        }
コード例 #2
0
        public override async Task <RazorDiagnosticsResponse> TranslateAsync(
            RazorLanguageKind languageKind,
            Uri razorDocumentUri,
            Diagnostic[] diagnostics,
            CancellationToken cancellationToken)
        {
            if (razorDocumentUri is null)
            {
                throw new ArgumentNullException(nameof(razorDocumentUri));
            }

            if (diagnostics is null)
            {
                throw new ArgumentNullException(nameof(diagnostics));
            }

            var diagnosticsParams = new RazorDiagnosticsParams()
            {
                Kind             = languageKind,
                RazorDocumentUri = razorDocumentUri,
                Diagnostics      = diagnostics
            };

            var diagnosticResponse = await _requestInvoker.ReinvokeRequestOnServerAsync <RazorDiagnosticsParams, RazorDiagnosticsResponse>(
                LanguageServerConstants.RazorTranslateDiagnosticsEndpoint,
                RazorLSPConstants.RazorLSPContentTypeName,
                diagnosticsParams,
                cancellationToken).ConfigureAwait(false);

            return(diagnosticResponse);
        }
コード例 #3
0
        public async Task <CompletionItem> HandleRequestAsync(CompletionItem request, ClientCapabilities clientCapabilities, CancellationToken cancellationToken)
        {
            if (request?.Data == null)
            {
                return(request);
            }

            CompletionResolveData resolveData;

            if (request.Data is CompletionResolveData data)
            {
                resolveData = data;
            }
            else
            {
                resolveData = ((JToken)request.Data).ToObject <CompletionResolveData>();
            }

            // Set the original resolve data back so the language server deserializes it correctly.
            request.Data = resolveData.OriginalData;

            if (resolveData.LanguageServerKind != LanguageServerKind.CSharp)
            {
                // We currently only want to resolve C# completion items.
                return(request);
            }

            var result = await _requestInvoker.ReinvokeRequestOnServerAsync <CompletionItem, CompletionItem>(
                Methods.TextDocumentCompletionResolveName,
                resolveData.LanguageServerKind,
                request,
                cancellationToken).ConfigureAwait(false);

            return(result);
        }
コード例 #4
0
        public async override Task <RazorMapToDocumentRangesResponse> MapToDocumentRangesAsync(RazorLanguageKind languageKind, Uri razorDocumentUri, Range[] projectedRanges, CancellationToken cancellationToken)
        {
            if (razorDocumentUri is null)
            {
                throw new ArgumentNullException(nameof(razorDocumentUri));
            }

            if (projectedRanges is null)
            {
                throw new ArgumentNullException(nameof(projectedRanges));
            }

            var mapToDocumentRangeParams = new RazorMapToDocumentRangesParams()
            {
                Kind             = languageKind,
                RazorDocumentUri = razorDocumentUri,
                ProjectedRanges  = projectedRanges
            };

            var documentMappingResponse = await _requestInvoker.ReinvokeRequestOnServerAsync <RazorMapToDocumentRangesParams, RazorMapToDocumentRangesResponse>(
                LanguageServerConstants.RazorMapToDocumentRangesEndpoint,
                LanguageServerKind.Razor,
                mapToDocumentRangeParams,
                cancellationToken).ConfigureAwait(false);

            return(documentMappingResponse);
        }
コード例 #5
0
        public async Task <SignatureHelp> HandleRequestAsync(TextDocumentPositionParams request, ClientCapabilities clientCapabilities, CancellationToken cancellationToken)
        {
            if (request is null)
            {
                throw new ArgumentNullException(nameof(request));
            }

            if (clientCapabilities is null)
            {
                throw new ArgumentNullException(nameof(clientCapabilities));
            }

            _logger.LogInformation($"Starting request for {request.TextDocument.Uri}.");

            if (!_documentManager.TryGetDocument(request.TextDocument.Uri, out var documentSnapshot))
            {
                _logger.LogWarning($"Failed to find document {request.TextDocument.Uri}.");
                return(null);
            }

            var projectionResult = await _projectionProvider.GetProjectionAsync(
                documentSnapshot,
                request.Position,
                cancellationToken).ConfigureAwait(false);

            if (projectionResult == null)
            {
                return(null);
            }

            cancellationToken.ThrowIfCancellationRequested();

            var textDocumentPositionParams = new TextDocumentPositionParams()
            {
                Position     = projectionResult.Position,
                TextDocument = new TextDocumentIdentifier()
                {
                    Uri = projectionResult.Uri
                },
            };

            _logger.LogInformation($"Requesting signature help for {projectionResult.Uri}.");

            var signatureHelp = await _requestInvoker.ReinvokeRequestOnServerAsync <TextDocumentPositionParams, SignatureHelp>(
                Methods.TextDocumentSignatureHelpName,
                projectionResult.LanguageKind.ToContainedLanguageContentType(),
                textDocumentPositionParams,
                cancellationToken).ConfigureAwait(false);

            if (signatureHelp is null)
            {
                _logger.LogInformation("Returning no result.");
            }
            else
            {
                _logger.LogInformation("Returning result.");
            }

            return(signatureHelp);
        }
コード例 #6
0
        public async Task <DocumentOnTypeRenameResponseItem> HandleRequestAsync(DocumentOnTypeRenameParams request, ClientCapabilities clientCapabilities, CancellationToken cancellationToken)
        {
            if (request is null)
            {
                throw new ArgumentNullException(nameof(request));
            }

            cancellationToken.ThrowIfCancellationRequested();

            if (!_documentManager.TryGetDocument(request.TextDocument.Uri, out var documentSnapshot))
            {
                return(null);
            }

            var projectionResult = await _projectionProvider.GetProjectionAsync(documentSnapshot, request.Position, cancellationToken).ConfigureAwait(false);

            if (projectionResult is null || projectionResult.LanguageKind != RazorLanguageKind.Html)
            {
                return(null);
            }

            var onTypeRenameParams = new DocumentOnTypeRenameParams()
            {
                Position     = projectionResult.Position,
                TextDocument = new TextDocumentIdentifier()
                {
                    Uri = projectionResult.Uri
                }
            };

            var contentType = projectionResult.LanguageKind.ToContainedLanguageContentType();
            var response    = await _requestInvoker.ReinvokeRequestOnServerAsync <DocumentOnTypeRenameParams, DocumentOnTypeRenameResponseItem>(
                MSLSPMethods.OnTypeRenameName,
                contentType,
                onTypeRenameParams,
                cancellationToken).ConfigureAwait(false);

            if (response is null)
            {
                return(null);
            }

            var mappingResult = await _documentMappingProvider.MapToDocumentRangesAsync(
                projectionResult.LanguageKind,
                request.TextDocument.Uri,
                response.Ranges,
                cancellationToken).ConfigureAwait(false);

            if (mappingResult is null ||
                (_documentManager.TryGetDocument(request.TextDocument.Uri, out var mappedDocumentSnapshot) &&
                 mappingResult.HostDocumentVersion != mappedDocumentSnapshot.Version))
            {
                // Couldn't remap the range or the document changed in the meantime. Discard this result.
                return(null);
            }

            response.Ranges = mappingResult.Ranges;
            return(response);
        }
コード例 #7
0
        public async Task <DocumentOnAutoInsertResponseItem> HandleRequestAsync(DocumentOnAutoInsertParams request, ClientCapabilities clientCapabilities, CancellationToken cancellationToken)
        {
            if (!AllowedTriggerCharacters.Contains(request.Character, StringComparer.Ordinal))
            {
                // We haven't built support for this character yet.
                return(null);
            }

            if (!_documentManager.TryGetDocument(request.TextDocument.Uri, out var documentSnapshot))
            {
                return(null);
            }

            var projectionResult = await _projectionProvider.GetProjectionAsync(documentSnapshot, request.Position, cancellationToken).ConfigureAwait(false);

            if (projectionResult == null || projectionResult.LanguageKind != RazorLanguageKind.Html)
            {
                return(null);
            }

            var formattingParams = new DocumentOnAutoInsertParams()
            {
                Character    = request.Character,
                Options      = request.Options,
                Position     = projectionResult.Position,
                TextDocument = new TextDocumentIdentifier()
                {
                    Uri = projectionResult.Uri
                }
            };

            var contentType = projectionResult.LanguageKind.ToContainedLanguageContentType();
            var response    = await _requestInvoker.ReinvokeRequestOnServerAsync <DocumentOnAutoInsertParams, DocumentOnAutoInsertResponseItem>(
                MSLSPMethods.OnAutoInsertName,
                contentType,
                formattingParams,
                cancellationToken).ConfigureAwait(false);

            if (response == null)
            {
                return(null);
            }

            var remappedEdit = await _documentMappingProvider.RemapTextEditsAsync(projectionResult.Uri, new[] { response.TextEdit }, cancellationToken).ConfigureAwait(false);

            if (!remappedEdit.Any())
            {
                return(null);
            }
            var remappedResponse = new DocumentOnAutoInsertResponseItem()
            {
                TextEdit       = remappedEdit.Single(),
                TextEditFormat = response.TextEditFormat,
            };

            return(remappedResponse);
        }
コード例 #8
0
        public async Task <CompletionItem?> HandleRequestAsync(CompletionItem request, ClientCapabilities clientCapabilities, CancellationToken cancellationToken)
        {
            if (request.Data is null)
            {
                _logger.LogInformation("Received no completion resolve data.");
                return(request);
            }

            _logger.LogInformation("Starting request to resolve completion.");

            var resolveData = request.Data is CompletionResolveData data ? data : ((JToken)request.Data).ToObject <CompletionResolveData>();

            if (resolveData is null)
            {
                _logger.LogInformation("CompletionResolveData failed to serialize.");
                return(request);
            }

            // Set the original resolve data back so the language server deserializes it correctly.
            request.Data = resolveData.OriginalData;

            if (!_completionRequestContextCache.TryGet(resolveData.ResultId, out var requestContext))
            {
                _logger.LogInformation("Could not find the associated request context.");
                return(request);
            }

            if (!_documentManager.TryGetDocument(requestContext.HostDocumentUri, out var documentSnapshot))
            {
                _logger.LogError("Could not find the associated host document for completion resolve: {0}.", requestContext.HostDocumentUri);
                return(request);
            }

            var serverKind         = requestContext.LanguageServerKind;
            var languageServerName = serverKind.ToLanguageServerName();
            var textBuffer         = serverKind.GetTextBuffer(documentSnapshot);
            var response           = await _requestInvoker.ReinvokeRequestOnServerAsync <CompletionItem, CompletionItem>(
                textBuffer,
                Methods.TextDocumentCompletionResolveName,
                languageServerName,
                request,
                cancellationToken).ConfigureAwait(false);

            if (!ReinvocationResponseHelper.TryExtractResultOrLog(response, _logger, RazorLSPConstants.RazorCSharpLanguageServerName, out var result))
            {
                return(request);
            }

            _logger.LogInformation("Received result, post-processing.");

            var postProcessedResult = await PostProcessCompletionItemAsync(request, result, requestContext, documentSnapshot, cancellationToken).ConfigureAwait(false);

            _logger.LogInformation("Returning resolved completion.");
            return(postProcessedResult);
        }
コード例 #9
0
        public async Task <WorkspaceEdit> HandleRequestAsync(RenameParams request, ClientCapabilities clientCapabilities, CancellationToken cancellationToken)
        {
            if (request is null)
            {
                throw new ArgumentNullException(nameof(request));
            }

            if (clientCapabilities is null)
            {
                throw new ArgumentNullException(nameof(clientCapabilities));
            }

            if (!_documentManager.TryGetDocument(request.TextDocument.Uri, out var documentSnapshot))
            {
                return(null);
            }

            var projectionResult = await _projectionProvider.GetProjectionAsync(documentSnapshot, request.Position, cancellationToken).ConfigureAwait(false);

            if (projectionResult == null)
            {
                // We only support C# renames for now.
                return(null);
            }

            cancellationToken.ThrowIfCancellationRequested();

            var renameParams = new RenameParams()
            {
                Position     = projectionResult.Position,
                NewName      = request.NewName,
                TextDocument = new TextDocumentIdentifier()
                {
                    Uri = projectionResult.Uri
                }
            };

            var result = await _requestInvoker.ReinvokeRequestOnServerAsync <RenameParams, WorkspaceEdit>(
                Methods.TextDocumentRenameName,
                projectionResult.LanguageKind.ToContainedLanguageContentType(),
                renameParams,
                cancellationToken).ConfigureAwait(false);

            if (result == null)
            {
                return(null);
            }

            cancellationToken.ThrowIfCancellationRequested();

            var remappedResult = await _documentMappingProvider.RemapWorkspaceEditAsync(result, cancellationToken).ConfigureAwait(false);

            return(remappedResult);
        }
コード例 #10
0
        public async Task <Location[]> HandleRequestAsync(TextDocumentPositionParams request, ClientCapabilities clientCapabilities, CancellationToken cancellationToken)
        {
            if (request is null)
            {
                throw new ArgumentNullException(nameof(request));
            }

            if (clientCapabilities is null)
            {
                throw new ArgumentNullException(nameof(clientCapabilities));
            }

            if (!_documentManager.TryGetDocument(request.TextDocument.Uri, out var documentSnapshot))
            {
                return(null);
            }

            var projectionResult = await _projectionProvider.GetProjectionAsync(documentSnapshot, request.Position, cancellationToken).ConfigureAwait(false);

            if (projectionResult == null || projectionResult.LanguageKind != RazorLanguageKind.CSharp)
            {
                return(null);
            }

            cancellationToken.ThrowIfCancellationRequested();

            var serverKind = LanguageServerKind.CSharp;
            var textDocumentPositionParams = new TextDocumentPositionParams()
            {
                Position     = projectionResult.Position,
                TextDocument = new TextDocumentIdentifier()
                {
                    Uri = projectionResult.Uri
                }
            };

            var result = await _requestInvoker.ReinvokeRequestOnServerAsync <TextDocumentPositionParams, Location[]>(
                Methods.TextDocumentImplementationName,
                serverKind,
                textDocumentPositionParams,
                cancellationToken).ConfigureAwait(false);

            if (result == null || result.Length == 0)
            {
                return(result);
            }

            cancellationToken.ThrowIfCancellationRequested();

            var remappedLocations = await _documentMappingProvider.RemapLocationsAsync(result, cancellationToken).ConfigureAwait(false);

            return(remappedLocations);
        }
コード例 #11
0
        public async override Task <RazorMapToDocumentRangesResponse?> MapToDocumentRangesAsync(
            RazorLanguageKind languageKind,
            Uri razorDocumentUri,
            Range[] projectedRanges,
            LanguageServerMappingBehavior mappingBehavior,
            CancellationToken cancellationToken)
        {
            if (razorDocumentUri is null)
            {
                throw new ArgumentNullException(nameof(razorDocumentUri));
            }

            if (projectedRanges is null)
            {
                throw new ArgumentNullException(nameof(projectedRanges));
            }

            var mapToDocumentRangeParams = new RazorMapToDocumentRangesParams()
            {
                Kind             = languageKind,
                RazorDocumentUri = razorDocumentUri,
                ProjectedRanges  = projectedRanges,
                MappingBehavior  = mappingBehavior,
            };

            if (!_lazyDocumentManager.Value.TryGetDocument(razorDocumentUri, out var documentSnapshot))
            {
                return(null);
            }

            var documentMappingResponse = await _requestInvoker.ReinvokeRequestOnServerAsync <RazorMapToDocumentRangesParams, RazorMapToDocumentRangesResponse>(
                documentSnapshot.Snapshot.TextBuffer,
                LanguageServerConstants.RazorMapToDocumentRangesEndpoint,
                RazorLSPConstants.RazorLanguageServerName,
                CheckRazorRangeMappingCapability,
                mapToDocumentRangeParams,
                cancellationToken).ConfigureAwait(false);

            return(documentMappingResponse?.Response);
        }
コード例 #12
0
        public async Task <CompletionItem> HandleRequestAsync(CompletionItem request, ClientCapabilities clientCapabilities, CancellationToken cancellationToken)
        {
            if (request?.Data == null)
            {
                _logger.LogInformation("Received no completion resolve data.");
                return(request);
            }

            _logger.LogInformation("Starting request to resolve completion.");

            CompletionResolveData resolveData;

            if (request.Data is CompletionResolveData data)
            {
                resolveData = data;
            }
            else
            {
                resolveData = ((JToken)request.Data).ToObject <CompletionResolveData>();
            }

            // Set the original resolve data back so the language server deserializes it correctly.
            request.Data = resolveData.OriginalData;

            if (!_completionRequestContextCache.TryGet(resolveData.ResultId, out var requestContext))
            {
                _logger.LogInformation("Could not find the associated request context.");
                return(request);
            }

            var serverContentType = requestContext.LanguageServerKind.ToContentType();
            var result            = await _requestInvoker.ReinvokeRequestOnServerAsync <CompletionItem, CompletionItem>(
                Methods.TextDocumentCompletionResolveName,
                serverContentType,
                request,
                cancellationToken).ConfigureAwait(false);

            _logger.LogInformation("Received result, post-processing.");

            result = await PostProcessCompletionItemAsync(request, result, requestContext, cancellationToken).ConfigureAwait(false);

            _logger.LogInformation("Returning resolved completion.");
            return(result);
        }
コード例 #13
0
        public async Task <SignatureHelp> HandleRequestAsync(TextDocumentPositionParams request, ClientCapabilities clientCapabilities, CancellationToken cancellationToken)
        {
            if (request is null)
            {
                throw new ArgumentNullException(nameof(request));
            }

            if (clientCapabilities is null)
            {
                throw new ArgumentNullException(nameof(clientCapabilities));
            }

            if (!_documentManager.TryGetDocument(request.TextDocument.Uri, out var documentSnapshot))
            {
                return(null);
            }

            var projectionResult = await _projectionProvider.GetProjectionAsync(documentSnapshot, request.Position, cancellationToken).ConfigureAwait(false);

            if (projectionResult == null || projectionResult.LanguageKind != RazorLanguageKind.CSharp)
            {
                return(null);
            }

            cancellationToken.ThrowIfCancellationRequested();

            var textDocumentPositionParams = new TextDocumentPositionParams()
            {
                Position     = projectionResult.Position,
                TextDocument = new TextDocumentIdentifier()
                {
                    Uri = projectionResult.Uri
                },
            };

            var signatureHelp = await _requestInvoker.ReinvokeRequestOnServerAsync <TextDocumentPositionParams, SignatureHelp>(
                Methods.TextDocumentSignatureHelpName,
                RazorLSPConstants.CSharpContentTypeName,
                textDocumentPositionParams,
                cancellationToken).ConfigureAwait(false);

            return(signatureHelp);
        }
コード例 #14
0
        public async Task <DocumentOnTypeRenameResponseItem> HandleRequestAsync(DocumentOnTypeRenameParams request, ClientCapabilities clientCapabilities, CancellationToken cancellationToken)
        {
            if (request is null)
            {
                throw new ArgumentNullException(nameof(request));
            }

            cancellationToken.ThrowIfCancellationRequested();

            _logger.LogInformation($"Starting request for {request.TextDocument.Uri}.");

            if (!_documentManager.TryGetDocument(request.TextDocument.Uri, out var documentSnapshot))
            {
                _logger.LogWarning($"Failed to find document {request.TextDocument.Uri}.");
                return(null);
            }

            var projectionResult = await _projectionProvider.GetProjectionAsync(
                documentSnapshot,
                request.Position,
                cancellationToken).ConfigureAwait(false);

            if (projectionResult is null)
            {
                return(null);
            }
            else if (projectionResult.LanguageKind != RazorLanguageKind.Html)
            {
                _logger.LogInformation($"Unsupported language kind {projectionResult.LanguageKind:G}.");
                return(null);
            }

            var onTypeRenameParams = new DocumentOnTypeRenameParams()
            {
                Position     = projectionResult.Position,
                TextDocument = new TextDocumentIdentifier()
                {
                    Uri = projectionResult.Uri
                }
            };

            _logger.LogInformation($"Requesting OnTypeRename for {projectionResult.Uri}.");

            var contentType = projectionResult.LanguageKind.ToContainedLanguageContentType();
            var response    = await _requestInvoker.ReinvokeRequestOnServerAsync <DocumentOnTypeRenameParams, DocumentOnTypeRenameResponseItem>(
                MSLSPMethods.OnTypeRenameName,
                contentType,
                onTypeRenameParams,
                cancellationToken).ConfigureAwait(false);

            if (response is null)
            {
                _logger.LogInformation("Received no results.");
                return(null);
            }

            _logger.LogInformation($"Received response, remapping.");

            var mappingResult = await _documentMappingProvider.MapToDocumentRangesAsync(
                projectionResult.LanguageKind,
                request.TextDocument.Uri,
                response.Ranges,
                cancellationToken).ConfigureAwait(false);

            if (mappingResult is null ||
                (_documentManager.TryGetDocument(request.TextDocument.Uri, out var mappedDocumentSnapshot) &&
                 mappingResult.HostDocumentVersion != mappedDocumentSnapshot.Version))
            {
                // Couldn't remap the range or the document changed in the meantime. Discard this result.
                _logger.LogInformation($"Mapping failed. Versions: {documentSnapshot.Version} -> {mappingResult?.HostDocumentVersion}.");
                return(null);
            }

            response.Ranges = mappingResult.Ranges;
            _logger.LogInformation("Returned remapped result.");
            return(response);
        }
コード例 #15
0
        private async Task <ProjectionResult?> GetProjectionCoreAsync(LSPDocumentSnapshot documentSnapshot, Position position, bool rejectOnNewerParallelRequest, CancellationToken cancellationToken)
        {
            if (documentSnapshot is null)
            {
                throw new ArgumentNullException(nameof(documentSnapshot));
            }

            if (position is null)
            {
                throw new ArgumentNullException(nameof(position));
            }

            // We initialize the logger here instead of the constructor as the projection provider is constructed
            // *before* the language server. Thus, the log hub has yet to be initialized, thus we would be unable to
            // create the logger at that time.
            await InitializeLogHubAsync(cancellationToken).ConfigureAwait(false);

            var languageQueryParams = new RazorLanguageQueryParams()
            {
                Position = position,
                Uri      = documentSnapshot.Uri
            };

            var response = await _requestInvoker.ReinvokeRequestOnServerAsync <RazorLanguageQueryParams, RazorLanguageQueryResponse>(
                documentSnapshot.Snapshot.TextBuffer,
                LanguageServerConstants.RazorLanguageQueryEndpoint,
                RazorLSPConstants.RazorLanguageServerName,
                CheckRazorLanguageQueryCapability,
                languageQueryParams,
                cancellationToken).ConfigureAwait(false);

            var languageResponse = response?.Response;

            if (languageResponse is null)
            {
                _logHubLogger?.LogInformation("The language server is still being spun up. Could not resolve the projection.");
                return(null);
            }

            VirtualDocumentSnapshot virtualDocument;

            if (languageResponse.Kind == RazorLanguageKind.CSharp &&
                documentSnapshot.TryGetVirtualDocument <CSharpVirtualDocumentSnapshot>(out var csharpDoc))
            {
                virtualDocument = csharpDoc;
            }
            else if (languageResponse.Kind == RazorLanguageKind.Html &&
                     documentSnapshot.TryGetVirtualDocument <HtmlVirtualDocumentSnapshot>(out var htmlDoc))
            {
                virtualDocument = htmlDoc;
            }
            else
            {
                _logHubLogger?.LogInformation($"Could not find projection for {languageResponse.Kind:G}.");
                return(null);
            }

            if (languageResponse.HostDocumentVersion is null)
            {
                // There should always be a document version attached to an open document.
                // Log it and move on as if it was synchronized.
                var message = $"Could not find a document version associated with the document '{documentSnapshot.Uri}'";
                _activityLogger.LogVerbose(message);
                _logHubLogger?.LogWarning(message);
            }
            else
            {
                var synchronized = await _documentSynchronizer.TrySynchronizeVirtualDocumentAsync(documentSnapshot.Version, virtualDocument, rejectOnNewerParallelRequest, cancellationToken).ConfigureAwait(false);

                if (!synchronized)
                {
                    _logHubLogger?.LogInformation("Could not synchronize.");
                    return(null);
                }
            }

            var result = new ProjectionResult()
            {
                Uri                 = virtualDocument.Uri,
                Position            = languageResponse.Position,
                PositionIndex       = languageResponse.PositionIndex,
                LanguageKind        = languageResponse.Kind,
                HostDocumentVersion = languageResponse.HostDocumentVersion
            };

            return(result);
        }
コード例 #16
0
        public async Task <SumType <CompletionItem[], CompletionList>?> HandleRequestAsync(CompletionParams request, ClientCapabilities clientCapabilities, CancellationToken cancellationToken)
        {
            if (request is null)
            {
                throw new ArgumentNullException(nameof(request));
            }

            if (clientCapabilities is null)
            {
                throw new ArgumentNullException(nameof(clientCapabilities));
            }

            if (!_documentManager.TryGetDocument(request.TextDocument.Uri, out var documentSnapshot))
            {
                return(null);
            }

            var projectionResult = await _projectionProvider.GetProjectionAsync(documentSnapshot, request.Position, cancellationToken).ConfigureAwait(false);

            if (projectionResult == null)
            {
                return(null);
            }

            var serverKind = projectionResult.LanguageKind == RazorLanguageKind.CSharp ? LanguageServerKind.CSharp : LanguageServerKind.Html;

            var(succeeded, result) = await TryGetProvisionalCompletionsAsync(request, documentSnapshot, projectionResult, cancellationToken).ConfigureAwait(false);

            if (succeeded)
            {
                // This means the user has just typed a dot after some identifier such as (cursor is pipe): "DateTime.| "
                // In this case Razor interprets after the dot as Html and before it as C#.
                // We use this criteria to provide a better completion experience for what we call provisional changes.
            }
            else if (!TriggerAppliesToProjection(request.Context, projectionResult.LanguageKind))
            {
                return(null);
            }
            else
            {
                var completionContext = RewriteContext(request.Context, projectionResult.LanguageKind);

                // This is a valid non-provisional completion request.
                var completionParams = new CompletionParams()
                {
                    Context      = completionContext,
                    Position     = projectionResult.Position,
                    TextDocument = new TextDocumentIdentifier()
                    {
                        Uri = projectionResult.Uri
                    }
                };

                result = await _requestInvoker.ReinvokeRequestOnServerAsync <CompletionParams, SumType <CompletionItem[], CompletionList>?>(
                    Methods.TextDocumentCompletionName,
                    serverKind.ToContentType(),
                    completionParams,
                    cancellationToken).ConfigureAwait(false);
            }

            if (TryConvertToCompletionList(result, out var completionList))
            {
                var wordExtent = documentSnapshot.Snapshot.GetWordExtent(request.Position.Line, request.Position.Character, _textStructureNavigator);

                if (serverKind == LanguageServerKind.CSharp)
                {
                    completionList = PostProcessCSharpCompletionList(request, documentSnapshot, wordExtent, completionList);
                }

                completionList = TranslateTextEdits(request.Position, projectionResult.Position, wordExtent, completionList);

                var requestContext = new CompletionRequestContext(documentSnapshot.Uri, projectionResult.Uri, serverKind);
                var resultId       = _completionRequestContextCache.Set(requestContext);
                SetResolveData(resultId, completionList);
            }

            return(completionList);
コード例 #17
0
        public async Task <TextEdit[]> HandleRequestAsync(DocumentOnTypeFormattingParams request, ClientCapabilities clientCapabilities, CancellationToken cancellationToken)
        {
            if (!AllTriggerCharacters.Contains(request.Character, StringComparer.Ordinal))
            {
                // Unexpected trigger character.
                return(null);
            }

            if (!_documentManager.TryGetDocument(request.TextDocument.Uri, out var documentSnapshot))
            {
                return(null);
            }

            var triggerCharacterKind = await GetTriggerCharacterLanguageKindAsync(documentSnapshot, request.Position, request.Character, cancellationToken).ConfigureAwait(false);

            if (triggerCharacterKind == null || triggerCharacterKind != RazorLanguageKind.CSharp)
            {
                return(null);
            }

            if (!IsApplicableTriggerCharacter(request.Character, triggerCharacterKind.Value))
            {
                // We were triggered but the trigger character doesn't make sense for the current cursor position. Bail.
                return(null);
            }

            cancellationToken.ThrowIfCancellationRequested();

            var projectionResult = await _projectionProvider.GetProjectionAsync(documentSnapshot, request.Position, cancellationToken).ConfigureAwait(false);

            if (projectionResult == null)
            {
                return(null);
            }

            var formattingParams = new DocumentOnTypeFormattingParams()
            {
                Character    = request.Character,
                Options      = request.Options,
                Position     = projectionResult.Position,
                TextDocument = new TextDocumentIdentifier()
                {
                    Uri = projectionResult.Uri
                }
            };

            cancellationToken.ThrowIfCancellationRequested();

            var contentType = triggerCharacterKind.Value.ToContainedLanguageContentType();
            var response    = await _requestInvoker.ReinvokeRequestOnServerAsync <DocumentOnTypeFormattingParams, TextEdit[]>(
                Methods.TextDocumentOnTypeFormattingName,
                contentType,
                formattingParams,
                cancellationToken).ConfigureAwait(false);

            if (response == null)
            {
                return(null);
            }

            cancellationToken.ThrowIfCancellationRequested();

            var remappedEdits = await _documentMappingProvider.RemapFormattedTextEditsAsync(projectionResult.Uri, response, request.Options, cancellationToken).ConfigureAwait(false);

            return(remappedEdits);
        }
コード例 #18
0
        public async Task <SumType <CompletionItem[], CompletionList>?> HandleRequestAsync(CompletionParams request, ClientCapabilities clientCapabilities, CancellationToken cancellationToken)
        {
            if (request is null)
            {
                throw new ArgumentNullException(nameof(request));
            }

            if (clientCapabilities is null)
            {
                throw new ArgumentNullException(nameof(clientCapabilities));
            }

            _logger.LogInformation($"Starting request for {request.TextDocument.Uri}.");

            if (!_documentManager.TryGetDocument(request.TextDocument.Uri, out var documentSnapshot))
            {
                _logger.LogWarning($"Failed to find document {request.TextDocument.Uri}.");
                return(null);
            }

            if (!TryGetWordExtent(request, documentSnapshot, out var wordExtent))
            {
                return(null);
            }

            var projectionResult = await _projectionProvider.GetProjectionAsync(
                documentSnapshot,
                request.Position,
                cancellationToken).ConfigureAwait(false);

            if (projectionResult == null)
            {
                if (IsRazorCompilerBugWithCSharpKeywords(request, wordExtent))
                {
                    var csharpPolyfilledCompletionList = new CompletionList()
                    {
                        Items        = Array.Empty <CompletionItem>(),
                        IsIncomplete = true,
                    };
                    csharpPolyfilledCompletionList = IncludeCSharpKeywords(csharpPolyfilledCompletionList);
                    return(csharpPolyfilledCompletionList);
                }

                return(null);
            }

            var projectedPosition    = projectionResult.Position;
            var projectedDocumentUri = projectionResult.Uri;
            var serverKind           = projectionResult.LanguageKind == RazorLanguageKind.CSharp ? LanguageServerKind.CSharp : LanguageServerKind.Html;
            var languageServerName   = projectionResult.LanguageKind == RazorLanguageKind.CSharp ? RazorLSPConstants.RazorCSharpLanguageServerName : RazorLSPConstants.HtmlLanguageServerName;

            var(succeeded, result) = await TryGetProvisionalCompletionsAsync(request, documentSnapshot, projectionResult, cancellationToken).ConfigureAwait(false);

            if (succeeded)
            {
                // This means the user has just typed a dot after some identifier such as (cursor is pipe): "DateTime.| "
                // In this case Razor interprets after the dot as Html and before it as C#.
                // We use this criteria to provide a better completion experience for what we call provisional changes.
                serverKind = LanguageServerKind.CSharp;
                if (documentSnapshot.TryGetVirtualDocument <CSharpVirtualDocumentSnapshot>(out var csharpVirtualDocumentSnapshot))
                {
                    projectedDocumentUri = csharpVirtualDocumentSnapshot.Uri;
                }
                else
                {
                    _logger.LogError("Could not acquire C# virtual document snapshot after provisional completion.");
                }
            }
            else if (!TriggerAppliesToProjection(request.Context, projectionResult.LanguageKind))
            {
                _logger.LogInformation("Trigger does not apply to projection.");
                return(null);
            }
            else
            {
                // This is a valid non-provisional completion request.
                _logger.LogInformation("Searching for non-provisional completions, rewriting context.");

                var completionContext = RewriteContext(request.Context, projectionResult.LanguageKind);

                var completionParams = new CompletionParams()
                {
                    Context      = completionContext,
                    Position     = projectedPosition,
                    TextDocument = new TextDocumentIdentifier()
                    {
                        Uri = projectedDocumentUri
                    }
                };

                _logger.LogInformation($"Requesting non-provisional completions for {projectedDocumentUri}.");

                var textBuffer = serverKind.GetTextBuffer(documentSnapshot);
                var response   = await _requestInvoker.ReinvokeRequestOnServerAsync <CompletionParams, SumType <CompletionItem[], CompletionList>?>(
                    textBuffer,
                    Methods.TextDocumentCompletionName,
                    languageServerName,
                    completionParams,
                    cancellationToken).ConfigureAwait(false);

                if (!ReinvocationResponseHelper.TryExtractResultOrLog(response, _logger, languageServerName, out result))
                {
                    return(null);
                }

                _logger.LogInformation("Found non-provisional completion");
            }

            if (TryConvertToCompletionList(result, out var completionList))
            {
                if (serverKind == LanguageServerKind.CSharp)
                {
                    completionList = PostProcessCSharpCompletionList(request, documentSnapshot, wordExtent, completionList);
                }

                completionList = TranslateTextEdits(request.Position, projectedPosition, wordExtent, completionList);

                var requestContext = new CompletionRequestContext(documentSnapshot.Uri, projectedDocumentUri, serverKind);
                var resultId       = _completionRequestContextCache.Set(requestContext);
                SetResolveData(resultId, completionList);
            }

            if (completionList != null)
            {
                completionList = completionList is VSInternalCompletionList vsCompletionList
                    ? new OptimizedVSCompletionList(vsCompletionList)
                    : new OptimizedVSCompletionList(completionList);
            }

            _logger.LogInformation("Returning completion list.");
            return(completionList);
コード例 #19
0
        public async Task <Hover> HandleRequestAsync(TextDocumentPositionParams request, ClientCapabilities clientCapabilities, CancellationToken cancellationToken)
        {
            await _joinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);

            if (!_documentManager.TryGetDocument(request.TextDocument.Uri, out var documentSnapshot))
            {
                return(null);
            }

            // Switch to a background thread.
            await TaskScheduler.Default;

            var projectionResult = await _projectionProvider.GetProjectionAsync(documentSnapshot, request.Position, cancellationToken).ConfigureAwait(false);

            if (projectionResult == null)
            {
                return(null);
            }

            var serverKind = projectionResult.LanguageKind == RazorLanguageKind.CSharp ? LanguageServerKind.CSharp : LanguageServerKind.Html;

            var textDocumentPositionParams = new TextDocumentPositionParams()
            {
                Position     = projectionResult.Position,
                TextDocument = new TextDocumentIdentifier()
                {
                    Uri = projectionResult.Uri
                }
            };

            var result = await _requestInvoker.ReinvokeRequestOnServerAsync <TextDocumentPositionParams, Hover>(
                Methods.TextDocumentHoverName,
                serverKind,
                textDocumentPositionParams,
                cancellationToken).ConfigureAwait(false);

            if (result?.Range == null || result?.Contents == null)
            {
                return(null);
            }

            var mappingResult = await _documentMappingProvider.MapToDocumentRangeAsync(projectionResult.LanguageKind, request.TextDocument.Uri, result.Range, cancellationToken).ConfigureAwait(false);

            if (mappingResult == null)
            {
                // Couldn't remap the edits properly. Returning hover at initial request position.
                return(new Hover
                {
                    Contents = result.Contents,
                    Range = new Range()
                    {
                        Start = request.Position,
                        End = request.Position
                    }
                });
            }
            else if (mappingResult.HostDocumentVersion != documentSnapshot.Version)
            {
                // Discard hover if document has changed.
                return(null);
            }

            return(new Hover
            {
                Contents = result.Contents,
                Range = mappingResult.Range
            });
        }
コード例 #20
0
        public async Task <WorkspaceEdit?> HandleRequestAsync(RenameParams request, ClientCapabilities clientCapabilities, CancellationToken cancellationToken)
        {
            if (request is null)
            {
                throw new ArgumentNullException(nameof(request));
            }

            if (clientCapabilities is null)
            {
                throw new ArgumentNullException(nameof(clientCapabilities));
            }

            _logger.LogInformation($"Starting request for {request.TextDocument.Uri}.");

            if (!_documentManager.TryGetDocument(request.TextDocument.Uri, out var documentSnapshot))
            {
                _logger.LogWarning($"Failed to find document {request.TextDocument.Uri}.");
                return(null);
            }

            var projectionResult = await _projectionProvider.GetProjectionAsync(
                documentSnapshot,
                request.Position,
                cancellationToken).ConfigureAwait(false);

            if (projectionResult is null)
            {
                return(null);
            }

            cancellationToken.ThrowIfCancellationRequested();

            var renameParams = new RenameParams()
            {
                Position     = projectionResult.Position,
                NewName      = request.NewName,
                TextDocument = new TextDocumentIdentifier()
                {
                    Uri = projectionResult.Uri
                }
            };

            _logger.LogInformation($"Requesting rename for {projectionResult.Uri}.");

            var serverKind         = projectionResult.LanguageKind.ToLanguageServerKind();
            var textBuffer         = serverKind.GetTextBuffer(documentSnapshot);
            var languageServerName = serverKind.ToLanguageServerName();
            var response           = await _requestInvoker.ReinvokeRequestOnServerAsync <RenameParams, WorkspaceEdit>(
                textBuffer,
                Methods.TextDocumentRenameName,
                languageServerName,
                renameParams,
                cancellationToken).ConfigureAwait(false);

            if (!ReinvocationResponseHelper.TryExtractResultOrLog(response, _logger, languageServerName, out var result))
            {
                return(null);
            }

            _logger.LogInformation("Received result, remapping.");

            var remappedResult = await _documentMappingProvider.RemapWorkspaceEditAsync(result, cancellationToken).ConfigureAwait(false);

            _logger.LogInformation("Returned rename result.");
            return(remappedResult);
        }
コード例 #21
0
        // Internal for testing
        internal async override Task <VSReferenceItem[]> HandleRequestAsync(ReferenceParams request, ClientCapabilities clientCapabilities, string token, CancellationToken cancellationToken)
        {
            if (request is null)
            {
                throw new ArgumentNullException(nameof(request));
            }

            if (clientCapabilities is null)
            {
                throw new ArgumentNullException(nameof(clientCapabilities));
            }

            if (!_documentManager.TryGetDocument(request.TextDocument.Uri, out var documentSnapshot))
            {
                return(null);
            }

            var projectionResult = await _projectionProvider.GetProjectionAsync(documentSnapshot, request.Position, cancellationToken).ConfigureAwait(false);

            if (projectionResult == null)
            {
                return(null);
            }

            cancellationToken.ThrowIfCancellationRequested();

            var referenceParams = new SerializableReferenceParams()
            {
                Position     = projectionResult.Position,
                TextDocument = new TextDocumentIdentifier()
                {
                    Uri = projectionResult.Uri
                },
                Context            = request.Context,
                PartialResultToken = token // request.PartialResultToken
            };

            if (!_lspProgressListener.TryListenForProgress(
                    token,
                    onProgressNotifyAsync: (value, ct) => ProcessReferenceItemsAsync(value, request.PartialResultToken, ct),
                    WaitForProgressNotificationTimeout,
                    cancellationToken,
                    out var onCompleted))
            {
                return(null);
            }

            var result = await _requestInvoker.ReinvokeRequestOnServerAsync <SerializableReferenceParams, VSReferenceItem[]>(
                Methods.TextDocumentReferencesName,
                projectionResult.LanguageKind.ToContainedLanguageContentType(),
                referenceParams,
                cancellationToken).ConfigureAwait(false);

            if (result == null)
            {
                return(null);
            }

            cancellationToken.ThrowIfCancellationRequested();

            // We must not return till we have received the progress notifications
            // and reported the results via the PartialResultToken
            await onCompleted.ConfigureAwait(false);

            cancellationToken.ThrowIfCancellationRequested();

            // Results returned through Progress notification
            var remappedResults = await RemapReferenceItemsAsync(result, cancellationToken).ConfigureAwait(false);

            return(remappedResults);
        }
コード例 #22
0
        public async Task <SumType <CompletionItem[], CompletionList>?> HandleRequestAsync(CompletionParams request, ClientCapabilities clientCapabilities, CancellationToken cancellationToken)
        {
            if (request is null)
            {
                throw new ArgumentNullException(nameof(request));
            }

            if (clientCapabilities is null)
            {
                throw new ArgumentNullException(nameof(clientCapabilities));
            }

            if (!_documentManager.TryGetDocument(request.TextDocument.Uri, out var documentSnapshot))
            {
                return(null);
            }

            var projectionResult = await _projectionProvider.GetProjectionAsync(documentSnapshot, request.Position, cancellationToken).ConfigureAwait(false);

            if (projectionResult == null)
            {
                return(null);
            }

            var serverKind = projectionResult.LanguageKind == RazorLanguageKind.CSharp ? LanguageServerKind.CSharp : LanguageServerKind.Html;

            var(succeeded, result) = await TryGetProvisionalCompletionsAsync(request, documentSnapshot, projectionResult, cancellationToken).ConfigureAwait(false);

            if (succeeded)
            {
                // This means the user has just typed a dot after some identifier such as (cursor is pipe): "DateTime.| "
                // In this case Razor interprets after the dot as Html and before it as C#.
                // We use this criteria to provide a better completion experience for what we call provisional changes.
            }
            else if (!TriggerAppliesToProjection(request.Context, projectionResult.LanguageKind))
            {
                return(null);
            }
            else
            {
                // This is a valid non-provisional completion request.
                var completionParams = new CompletionParams()
                {
                    Context      = request.Context,
                    Position     = projectionResult.Position,
                    TextDocument = new TextDocumentIdentifier()
                    {
                        Uri = projectionResult.Uri
                    }
                };

                result = await _requestInvoker.ReinvokeRequestOnServerAsync <CompletionParams, SumType <CompletionItem[], CompletionList>?>(
                    Methods.TextDocumentCompletionName,
                    serverKind,
                    completionParams,
                    cancellationToken).ConfigureAwait(false);
            }

            if (result.HasValue)
            {
                // Set some context on the CompletionItem so the CompletionResolveHandler can handle it accordingly.
                result = SetResolveData(result.Value, serverKind);
            }

            return(result);
        }
コード例 #23
0
        public async Task <Hover?> HandleRequestAsync(TextDocumentPositionParams request, ClientCapabilities clientCapabilities, CancellationToken cancellationToken)
        {
            if (request is null)
            {
                throw new ArgumentNullException(nameof(request));
            }

            if (clientCapabilities is null)
            {
                throw new ArgumentNullException(nameof(clientCapabilities));
            }

            _logger.LogInformation($"Starting request for {request.TextDocument.Uri}.");

            if (!_documentManager.TryGetDocument(request.TextDocument.Uri, out var documentSnapshot))
            {
                _logger.LogWarning($"Failed to find document {request.TextDocument.Uri}.");
                return(null);
            }

            var projectionResult = await _projectionProvider.GetProjectionAsync(
                documentSnapshot,
                request.Position,
                cancellationToken).ConfigureAwait(false);

            if (projectionResult is null)
            {
                return(null);
            }

            var languageServerName = projectionResult.LanguageKind.ToContainedLanguageServerName();

            cancellationToken.ThrowIfCancellationRequested();

            var textDocumentPositionParams = new TextDocumentPositionParams()
            {
                Position     = projectionResult.Position,
                TextDocument = new TextDocumentIdentifier()
                {
                    Uri = projectionResult.Uri
                }
            };

            _logger.LogInformation($"Requesting hovers for {projectionResult.Uri}.");

            var serverKind = projectionResult.LanguageKind.ToLanguageServerKind();
            var textBuffer = serverKind.GetTextBuffer(documentSnapshot);
            var response   = await _requestInvoker.ReinvokeRequestOnServerAsync <TextDocumentPositionParams, Hover>(
                textBuffer,
                Methods.TextDocumentHoverName,
                languageServerName,
                textDocumentPositionParams,
                cancellationToken).ConfigureAwait(false);

            if (!ReinvocationResponseHelper.TryExtractResultOrLog(response, _logger, languageServerName, out var result))
            {
                return(null);
            }

            if (result.Range is null)
            {
                _logger.LogInformation("Received no results.");
                return(null);
            }

            _logger.LogInformation("Received result, remapping.");

            var mappingResult = await _documentMappingProvider.MapToDocumentRangesAsync(
                projectionResult.LanguageKind,
                request.TextDocument.Uri,
                new[] { result.Range },
                cancellationToken).ConfigureAwait(false);

            if (mappingResult is null || mappingResult.Ranges[0].IsUndefined())
            {
                // Couldn't remap the edits properly. Returning hover at initial request position.
                _logger.LogInformation("Mapping failed");
                return(CreateHover(result, new Range
                {
                    Start = request.Position,
                    End = request.Position
                }));
            }
コード例 #24
0
        // Internal for testing
        internal async override Task <VSInternalReferenceItem[]> HandleRequestAsync(ReferenceParams request, ClientCapabilities clientCapabilities, string token, CancellationToken cancellationToken)
        {
            if (request is null)
            {
                throw new ArgumentNullException(nameof(request));
            }

            if (clientCapabilities is null)
            {
                throw new ArgumentNullException(nameof(clientCapabilities));
            }

            _logger.LogInformation($"Starting request for {request.TextDocument.Uri}.");

            if (!_documentManager.TryGetDocument(request.TextDocument.Uri, out var documentSnapshot))
            {
                _logger.LogWarning($"Failed to find document {request.TextDocument.Uri}.");
                return(null);
            }

            var projectionResult = await _projectionProvider.GetProjectionAsync(
                documentSnapshot,
                request.Position,
                cancellationToken).ConfigureAwait(false);

            if (projectionResult == null)
            {
                return(null);
            }

            cancellationToken.ThrowIfCancellationRequested();

            var referenceParams = new SerializableReferenceParams()
            {
                Position     = projectionResult.Position,
                TextDocument = new TextDocumentIdentifier()
                {
                    Uri = projectionResult.Uri
                },
                Context            = request.Context,
                PartialResultToken = token // request.PartialResultToken
            };

            _logger.LogInformation("Attaching to progress listener.");

            if (!_lspProgressListener.TryListenForProgress(
                    token,
                    onProgressNotifyAsync: (value, ct) => ProcessReferenceItemsAsync(value, request.PartialResultToken, ct),
                    DelayAfterLastNotifyAsync,
                    cancellationToken,
                    out var onCompleted))
            {
                _logger.LogWarning("Failed to attach to progress listener.");
                return(null);
            }

            _logger.LogInformation($"Requesting references for {projectionResult.Uri}.");

            var response = await _requestInvoker.ReinvokeRequestOnServerAsync <SerializableReferenceParams, VSInternalReferenceItem[]>(
                Methods.TextDocumentReferencesName,
                projectionResult.LanguageKind.ToContainedLanguageServerName(),
                referenceParams,
                cancellationToken).ConfigureAwait(false);

            var result = response.Result;

            if (result is null)
            {
                _logger.LogInformation("Received no results from initial request.");
                return(null);
            }

            cancellationToken.ThrowIfCancellationRequested();

            _logger.LogInformation("Waiting on progress notifications.");

            // We must not return till we have received the progress notifications
            // and reported the results via the PartialResultToken
            await onCompleted.ConfigureAwait(false);

            cancellationToken.ThrowIfCancellationRequested();

            _logger.LogInformation("Finished waiting, remapping results.");

            // Results returned through Progress notification
            var remappedResults = await RemapReferenceItemsAsync(result, cancellationToken).ConfigureAwait(false);

            _logger.LogInformation($"Returning {remappedResults.Length} results.");

            return(remappedResults);

            // Local functions
            async Task DelayAfterLastNotifyAsync(CancellationToken cancellationToken)
            {
                using var combined = ImmediateNotificationTimeout.CombineWith(cancellationToken);

                try
                {
                    await Task.Delay(WaitForProgressNotificationTimeout, combined.Token).ConfigureAwait(false);
                }
                catch (TaskCanceledException) when(ImmediateNotificationTimeout.IsCancellationRequested)
                {
                    // The delay was requested to complete immediately
                }
            }
        }
コード例 #25
0
        public async Task <Hover> HandleRequestAsync(TextDocumentPositionParams request, ClientCapabilities clientCapabilities, CancellationToken cancellationToken)
        {
            if (request is null)
            {
                throw new ArgumentNullException(nameof(request));
            }

            if (clientCapabilities is null)
            {
                throw new ArgumentNullException(nameof(clientCapabilities));
            }

            if (!_documentManager.TryGetDocument(request.TextDocument.Uri, out var documentSnapshot))
            {
                return(null);
            }

            var projectionResult = await _projectionProvider.GetProjectionAsync(documentSnapshot, request.Position, cancellationToken).ConfigureAwait(false);

            if (projectionResult == null)
            {
                return(null);
            }

            var contentType = projectionResult.LanguageKind.ToContainedLanguageContentType();

            cancellationToken.ThrowIfCancellationRequested();

            var textDocumentPositionParams = new TextDocumentPositionParams()
            {
                Position     = projectionResult.Position,
                TextDocument = new TextDocumentIdentifier()
                {
                    Uri = projectionResult.Uri
                }
            };

            var result = await _requestInvoker.ReinvokeRequestOnServerAsync <TextDocumentPositionParams, Hover>(
                Methods.TextDocumentHoverName,
                contentType,
                textDocumentPositionParams,
                cancellationToken).ConfigureAwait(false);

            if (result?.Range == null || result?.Contents == null)
            {
                return(null);
            }

            var mappingResult = await _documentMappingProvider.MapToDocumentRangesAsync(projectionResult.LanguageKind, request.TextDocument.Uri, new[] { result.Range }, cancellationToken).ConfigureAwait(false);

            if (mappingResult == null || mappingResult.Ranges[0].IsUndefined())
            {
                // Couldn't remap the edits properly. Returning hover at initial request position.
                return(CreateHover(result, new Range
                {
                    Start = request.Position,
                    End = request.Position
                }));
            }
            else if (mappingResult.HostDocumentVersion != documentSnapshot.Version)
            {
                // Discard hover if document has changed.
                return(null);
            }

            return(CreateHover(result, mappingResult.Ranges[0]));
        }
コード例 #26
0
        public async Task <TextEdit[]> HandleRequestAsync(DocumentOnTypeFormattingParams request, ClientCapabilities clientCapabilities, CancellationToken cancellationToken)
        {
            if (!AllTriggerCharacters.Contains(request.Character, StringComparer.Ordinal))
            {
                // Unexpected trigger character.
                return(null);
            }

            _logger.LogInformation($"Starting request for {request.TextDocument.Uri}.");

            if (!_documentManager.TryGetDocument(request.TextDocument.Uri, out var documentSnapshot))
            {
                _logger.LogWarning($"Failed to find document {request.TextDocument.Uri}.");
                return(null);
            }

            var triggerCharacterKind = await GetTriggerCharacterLanguageKindAsync(documentSnapshot, request.Position, request.Character, cancellationToken).ConfigureAwait(false);

            if (triggerCharacterKind == null)
            {
                _logger.LogInformation($"Failed to identify trigger character language context.");
                return(null);
            }
            else if (triggerCharacterKind != RazorLanguageKind.CSharp)
            {
                _logger.LogInformation($"Unsupported trigger character language {triggerCharacterKind:G}.");
                return(null);
            }

            if (!IsApplicableTriggerCharacter(request.Character, triggerCharacterKind.Value))
            {
                // We were triggered but the trigger character doesn't make sense for the current cursor position. Bail.
                _logger.LogInformation("Unsupported trigger character location.");
                return(null);
            }

            cancellationToken.ThrowIfCancellationRequested();

            var projectionResult = await _projectionProvider.GetProjectionAsync(
                documentSnapshot,
                request.Position,
                cancellationToken).ConfigureAwait(false);

            if (projectionResult == null)
            {
                return(null);
            }

            var formattingParams = new DocumentOnTypeFormattingParams()
            {
                Character    = request.Character,
                Options      = request.Options,
                Position     = projectionResult.Position,
                TextDocument = new TextDocumentIdentifier()
                {
                    Uri = projectionResult.Uri
                }
            };

            cancellationToken.ThrowIfCancellationRequested();

            _logger.LogInformation($"Requesting formatting for {projectionResult.Uri}.");

            var contentType = triggerCharacterKind.Value.ToContainedLanguageContentType();
            var textEdits   = await _requestInvoker.ReinvokeRequestOnServerAsync <DocumentOnTypeFormattingParams, TextEdit[]>(
                Methods.TextDocumentOnTypeFormattingName,
                contentType,
                formattingParams,
                cancellationToken).ConfigureAwait(false);

            if (textEdits == null)
            {
                _logger.LogInformation("Received no results.");
                return(null);
            }

            _logger.LogInformation($"Received {textEdits.Length} results, remapping.");

            cancellationToken.ThrowIfCancellationRequested();

            var remappedTextEdits = await _documentMappingProvider.RemapFormattedTextEditsAsync(projectionResult.Uri, textEdits, request.Options, containsSnippet : false, cancellationToken).ConfigureAwait(false);

            _logger.LogInformation($"Returning {remappedTextEdits?.Length} text edits.");
            return(remappedTextEdits);
        }
コード例 #27
0
        public async Task <DocumentHighlight[]> HandleRequestAsync(DocumentHighlightParams request, ClientCapabilities clientCapabilities, CancellationToken cancellationToken)
        {
            if (request is null)
            {
                throw new ArgumentNullException(nameof(request));
            }

            if (clientCapabilities is null)
            {
                throw new ArgumentNullException(nameof(clientCapabilities));
            }

            _logger.LogInformation($"Starting request for {request.TextDocument.Uri}.");

            if (!_documentManager.TryGetDocument(request.TextDocument.Uri, out var documentSnapshot))
            {
                _logger.LogWarning($"Failed to find document {request.TextDocument.Uri}.");
                return(null);
            }

            var projectionResult = await _projectionProvider.GetProjectionAsync(
                documentSnapshot,
                request.Position,
                cancellationToken).ConfigureAwait(false);

            if (projectionResult == null)
            {
                return(null);
            }

            var serverKind = projectionResult.LanguageKind == RazorLanguageKind.CSharp ? LanguageServerKind.CSharp : LanguageServerKind.Html;

            cancellationToken.ThrowIfCancellationRequested();

            var documentHighlightParams = new DocumentHighlightParams()
            {
                Position     = projectionResult.Position,
                TextDocument = new TextDocumentIdentifier()
                {
                    Uri = projectionResult.Uri
                }
            };

            _logger.LogInformation($"Requesting highlights for {projectionResult.Uri} at ({projectionResult.Position?.Line}, {projectionResult.Position?.Character}).");

            var highlights = await _requestInvoker.ReinvokeRequestOnServerAsync <DocumentHighlightParams, DocumentHighlight[]>(
                Methods.TextDocumentDocumentHighlightName,
                serverKind.ToContentType(),
                documentHighlightParams,
                cancellationToken).ConfigureAwait(false);

            if (highlights == null || highlights.Length == 0)
            {
                _logger.LogInformation("Received no results.");
                return(highlights);
            }

            _logger.LogInformation($"Received {highlights.Length} results, remapping.");

            var remappedHighlights = new List <DocumentHighlight>();

            var rangesToMap   = highlights.Select(r => r.Range).ToArray();
            var mappingResult = await _documentMappingProvider.MapToDocumentRangesAsync(
                projectionResult.LanguageKind,
                request.TextDocument.Uri,
                rangesToMap,
                cancellationToken).ConfigureAwait(false);

            if (mappingResult == null || mappingResult.HostDocumentVersion != documentSnapshot.Version)
            {
                // Couldn't remap the range or the document changed in the meantime. Discard this highlight.
                _logger.LogInformation($"Mapping failed. Versions: {documentSnapshot.Version} -> {mappingResult?.HostDocumentVersion}.");
                return(Array.Empty <DocumentHighlight>());
            }

            for (var i = 0; i < highlights.Length; i++)
            {
                var highlight = highlights[i];
                var range     = mappingResult.Ranges[i];
                if (range.IsUndefined())
                {
                    // Couldn't remap the range correctly. Discard this range.
                    continue;
                }

                var remappedHighlight = new DocumentHighlight()
                {
                    Range = range,
                    Kind  = highlight.Kind
                };

                remappedHighlights.Add(remappedHighlight);
            }

            _logger.LogInformation($"Returning {remappedHighlights.Count} highlights.");
            return(remappedHighlights.ToArray());
        }
コード例 #28
0
        public override async Task <ProjectionResult> GetProjectionAsync(LSPDocumentSnapshot documentSnapshot, Position position, CancellationToken cancellationToken)
        {
            if (documentSnapshot is null)
            {
                throw new ArgumentNullException(nameof(documentSnapshot));
            }

            if (position is null)
            {
                throw new ArgumentNullException(nameof(position));
            }

            var languageQueryParams = new RazorLanguageQueryParams()
            {
                Position = new Position(position.Line, position.Character),
                Uri      = documentSnapshot.Uri
            };

            var languageResponse = await _requestInvoker.ReinvokeRequestOnServerAsync <RazorLanguageQueryParams, RazorLanguageQueryResponse>(
                LanguageServerConstants.RazorLanguageQueryEndpoint,
                LanguageServerKind.Razor,
                languageQueryParams,
                cancellationToken).ConfigureAwait(false);

            VirtualDocumentSnapshot virtualDocument;

            if (languageResponse.Kind == RazorLanguageKind.CSharp &&
                documentSnapshot.TryGetVirtualDocument <CSharpVirtualDocumentSnapshot>(out var csharpDoc))
            {
                virtualDocument = csharpDoc;
            }
            else if (languageResponse.Kind == RazorLanguageKind.Html &&
                     documentSnapshot.TryGetVirtualDocument <HtmlVirtualDocumentSnapshot>(out var htmlDoc))
            {
                virtualDocument = htmlDoc;
            }
            else
            {
                return(null);
            }

            if (languageResponse.HostDocumentVersion == UndefinedDocumentVersion)
            {
                // There should always be a document version attached to an open document.
                // Log it and move on as if it was synchronized.
                _logger.LogVerbose($"Could not find a document version associated with the document '{documentSnapshot.Uri}'");
            }
            else
            {
                var synchronized = await _documentSynchronizer.TrySynchronizeVirtualDocumentAsync(documentSnapshot.Version, virtualDocument, cancellationToken).ConfigureAwait(false);

                if (!synchronized)
                {
                    // Could not synchronize
                    return(null);
                }
            }

            var result = new ProjectionResult()
            {
                Uri                 = virtualDocument.Uri,
                Position            = languageResponse.Position,
                PositionIndex       = languageResponse.PositionIndex,
                LanguageKind        = languageResponse.Kind,
                HostDocumentVersion = languageResponse.HostDocumentVersion
            };

            return(result);
        }
コード例 #29
0
        public async Task <Location[]> HandleRequestAsync(TextDocumentPositionParams request, ClientCapabilities clientCapabilities, CancellationToken cancellationToken)
        {
            if (request is null)
            {
                throw new ArgumentNullException(nameof(request));
            }

            if (clientCapabilities is null)
            {
                throw new ArgumentNullException(nameof(clientCapabilities));
            }

            _logger.LogInformation($"Starting request for {request.TextDocument.Uri}.");

            if (!_documentManager.TryGetDocument(request.TextDocument.Uri, out var documentSnapshot))
            {
                _logger.LogWarning($"Failed to find document {request.TextDocument.Uri}.");
                return(null);
            }

            var projectionResult = await _projectionProvider.GetProjectionAsync(
                documentSnapshot,
                request.Position,
                cancellationToken).ConfigureAwait(false);

            if (projectionResult == null)
            {
                return(null);
            }

            cancellationToken.ThrowIfCancellationRequested();

            var textDocumentPositionParams = new TextDocumentPositionParams()
            {
                Position     = projectionResult.Position,
                TextDocument = new TextDocumentIdentifier()
                {
                    Uri = projectionResult.Uri
                }
            };

            var serverKind         = projectionResult.LanguageKind.ToLanguageServerKind();
            var languageServerName = serverKind.ToLanguageServerName();

            _logger.LogInformation($"Requesting {languageServerName} implementation for {projectionResult.Uri}.");

            var textBuffer = serverKind.GetTextBuffer(documentSnapshot);
            var response   = await _requestInvoker.ReinvokeRequestOnServerAsync <TextDocumentPositionParams, Location[]>(
                textBuffer,
                Methods.TextDocumentImplementationName,
                languageServerName,
                textDocumentPositionParams,
                cancellationToken).ConfigureAwait(false);

            if (!ReinvocationResponseHelper.TryExtractResultOrLog(response, _logger, languageServerName, out var locations))
            {
                return(null);
            }

            if (locations.Length == 0)
            {
                _logger.LogInformation("Received no results.");
                return(locations);
            }

            _logger.LogInformation($"Received {locations.Length} results, remapping.");

            cancellationToken.ThrowIfCancellationRequested();

            var remappedLocations = await _documentMappingProvider.RemapLocationsAsync(locations, cancellationToken).ConfigureAwait(false);

            _logger.LogInformation($"Returning {remappedLocations?.Length} locations.");
            return(remappedLocations);
        }
コード例 #30
0
        public async Task <DocumentOnAutoInsertResponseItem> HandleRequestAsync(DocumentOnAutoInsertParams request, ClientCapabilities clientCapabilities, CancellationToken cancellationToken)
        {
            if (request is null)
            {
                throw new ArgumentNullException(nameof(request));
            }

            if (!AllAllowedTriggerCharacters.Contains(request.Character, StringComparer.Ordinal))
            {
                // We haven't built support for this character yet.
                return(null);
            }

            _logger.LogInformation($"Starting request for {request.TextDocument.Uri}, with trigger character {request.Character}.");

            if (!_documentManager.TryGetDocument(request.TextDocument.Uri, out var documentSnapshot))
            {
                return(null);
            }

            var projectionResult = await _projectionProvider.GetProjectionAsync(documentSnapshot, request.Position, cancellationToken).ConfigureAwait(false);

            if (projectionResult == null)
            {
                _logger.LogWarning($"Failed to find document {request.TextDocument.Uri}.");
                return(null);
            }
            else if (projectionResult.LanguageKind == RazorLanguageKind.Razor)
            {
                _logger.LogInformation("OnAutoInsert not supported in Razor context.");
                return(null);
            }
            else if (projectionResult.LanguageKind == RazorLanguageKind.Html &&
                     !HTMLAllowedTriggerCharacters.Contains(request.Character, StringComparer.Ordinal))
            {
                _logger.LogInformation("Inapplicable HTML trigger char.");
                return(null);
            }
            else if (projectionResult.LanguageKind == RazorLanguageKind.CSharp &&
                     !CSharpAllowedTriggerCharacters.Contains(request.Character, StringComparer.Ordinal))
            {
                _logger.LogInformation("Inapplicable C# trigger char.");
                return(null);
            }

            var formattingParams = new DocumentOnAutoInsertParams()
            {
                Character    = request.Character,
                Options      = request.Options,
                Position     = projectionResult.Position,
                TextDocument = new TextDocumentIdentifier()
                {
                    Uri = projectionResult.Uri
                }
            };

            _logger.LogInformation($"Requesting auto-insert for {projectionResult.Uri}.");

            var contentType = projectionResult.LanguageKind.ToContainedLanguageContentType();
            var response    = await _requestInvoker.ReinvokeRequestOnServerAsync <DocumentOnAutoInsertParams, DocumentOnAutoInsertResponseItem>(
                MSLSPMethods.OnAutoInsertName,
                contentType,
                formattingParams,
                cancellationToken).ConfigureAwait(false);

            if (response == null)
            {
                _logger.LogInformation("Received no results.");
                return(null);
            }

            _logger.LogInformation("Received result, remapping.");

            var containsSnippet = response.TextEditFormat == InsertTextFormat.Snippet;
            var remappedEdits   = await _documentMappingProvider.RemapFormattedTextEditsAsync(
                projectionResult.Uri,
                new[] { response.TextEdit },
                request.Options,
                containsSnippet,
                cancellationToken).ConfigureAwait(false);

            if (!remappedEdits.Any())
            {
                _logger.LogInformation("No edits remain after remapping.");
                return(null);
            }

            var remappedEdit     = remappedEdits.Single();
            var remappedResponse = new DocumentOnAutoInsertResponseItem()
            {
                TextEdit       = remappedEdit,
                TextEditFormat = response.TextEditFormat,
            };

            _logger.LogInformation($"Returning edit.");
            return(remappedResponse);
        }