public async override Task <RazorMapToDocumentRangeResponse> MapToDocumentRangeAsync(RazorLanguageKind languageKind, Uri razorDocumentUri, Range projectedRange, CancellationToken cancellationToken) { if (razorDocumentUri is null) { throw new ArgumentNullException(nameof(razorDocumentUri)); } if (projectedRange is null) { throw new ArgumentNullException(nameof(projectedRange)); } var mapToDocumentRangeParams = new RazorMapToDocumentRangeParams() { Kind = languageKind, RazorDocumentUri = razorDocumentUri, ProjectedRange = new Range() { Start = new Position(projectedRange.Start.Line, projectedRange.Start.Character), End = new Position(projectedRange.End.Line, projectedRange.End.Character) } }; var documentMappingResponse = await _requestInvoker.RequestServerAsync <RazorMapToDocumentRangeParams, RazorMapToDocumentRangeResponse>( LanguageServerConstants.RazorMapToDocumentRangeEndpoint, LanguageServerKind.Razor, mapToDocumentRangeParams, cancellationToken).ConfigureAwait(false); return(documentMappingResponse); }
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.RequestServerAsync <CompletionItem, CompletionItem>( Methods.TextDocumentCompletionResolveName, resolveData.LanguageServerKind, request, cancellationToken).ConfigureAwait(false); return(result); }
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 } }; Hover result; try { result = await _requestInvoker.RequestServerAsync <TextDocumentPositionParams, Hover>( Methods.TextDocumentHoverName, serverKind, textDocumentPositionParams, cancellationToken).ConfigureAwait(false); } catch { // Ensure we fail silently (Temporary till roslyn update is live) return(null); } 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 }); }
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.RequestServerAsync <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); }
public async Task <SumType <CompletionItem[], CompletionList>?> HandleRequestAsync(CompletionParams 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(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.RequestServerAsync <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); }
public async Task <TextEdit[]> HandleRequestAsync(DocumentOnTypeFormattingParams request, ClientCapabilities clientCapabilities, CancellationToken cancellationToken) { if (!AllowedTriggerCharacters.Contains(request.Character, StringComparer.Ordinal)) { // We haven't built support for this character yet. return(EmptyEdits); } await _joinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); if (!_documentManager.TryGetDocument(request.TextDocument.Uri, out var documentSnapshot)) { return(EmptyEdits); } await SwitchToBackgroundThread().ConfigureAwait(false); var projectionResult = await _projectionProvider.GetProjectionAsync(documentSnapshot, request.Position, cancellationToken).ConfigureAwait(false); if (projectionResult == null || projectionResult.LanguageKind != RazorLanguageKind.Html) { return(EmptyEdits); } if (request.Options.OtherOptions == null) { request.Options.OtherOptions = new Dictionary <string, object>(); } request.Options.OtherOptions[LanguageServerConstants.ExpectsCursorPlaceholderKey] = true; var formattingParams = new DocumentOnTypeFormattingParams() { Character = request.Character, Options = request.Options, Position = projectionResult.Position, TextDocument = new TextDocumentIdentifier() { Uri = projectionResult.Uri } }; var serverKind = projectionResult.LanguageKind == RazorLanguageKind.CSharp ? LanguageServerKind.CSharp : LanguageServerKind.Html; var edits = await _requestInvoker.RequestServerAsync <DocumentOnTypeFormattingParams, TextEdit[]>( Methods.TextDocumentOnTypeFormattingName, serverKind, formattingParams, cancellationToken).ConfigureAwait(false); if (edits == null) { return(EmptyEdits); } var mappedEdits = new List <TextEdit>(); foreach (var edit in edits) { if (edit.Range == null || edit.NewText == null) { // Sometimes the HTML language server returns invalid edits like these. We should just ignore those. continue; } var mappingResult = await _documentMappingProvider.MapToDocumentRangeAsync(projectionResult.LanguageKind, request.TextDocument.Uri, edit.Range, cancellationToken).ConfigureAwait(false); if (mappingResult == null || mappingResult.HostDocumentVersion != documentSnapshot.Version) { // Couldn't remap the edits properly. Discard this request. return(EmptyEdits); } var mappedEdit = new TextEdit() { NewText = edit.NewText, Range = mappingResult.Range }; mappedEdits.Add(mappedEdit); } if (mappedEdits.Count == 0) { return(EmptyEdits); } await _joinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); if (!_documentManager.TryGetDocument(request.TextDocument.Uri, out var newDocumentSnapshot) || newDocumentSnapshot.Version != documentSnapshot.Version) { // The document changed while were working on the background. Discard this request. return(EmptyEdits); } await _editorService.ApplyTextEditsAsync(documentSnapshot.Uri, documentSnapshot.Snapshot, mappedEdits).ConfigureAwait(false); // We would have already applied the edits and moved the cursor. Return empty. return(EmptyEdits); }