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);
        }
        // Internal for testing
        internal bool TriggerAppliesToProjection(CompletionContext context, RazorLanguageKind languageKind)
        {
            if (languageKind == RazorLanguageKind.Razor)
            {
                // We don't handle any type of triggers in Razor pieces of the document
                return(false);
            }

            if (context.TriggerKind != CompletionTriggerKind.TriggerCharacter)
            {
                // Not a trigger character completion, allow it.
                return(true);
            }

            if (!AllTriggerCharacters.Contains(context.TriggerCharacter))
            {
                // This is an auto-invoked completion from the VS LSP platform. Completions are automatically invoked upon typing identifiers
                // and are represented as CompletionTriggerKind.TriggerCharacter and have a trigger character that we have not registered for.
                return(true);
            }

            if (IsApplicableTriggerCharacter(context.TriggerCharacter, languageKind))
            {
                // Trigger character is associated with the langauge at the current cursor position
                return(true);
            }

            // We were triggered but the trigger character doesn't make sense for the current cursor position. Bail.
            return(false);
        }
예제 #4
0
        private CompletionContext RewriteContext(CompletionContext context, RazorLanguageKind languageKind)
        {
            if (context.TriggerKind != CompletionTriggerKind.TriggerCharacter)
            {
                // Non-triggered based completion, the existing context is valid;
                return(context);
            }

            if (languageKind == RazorLanguageKind.CSharp && CSharpTriggerCharacters.Contains(context.TriggerCharacter))
            {
                // C# trigger character for C# content
                return(context);
            }

            if (languageKind == RazorLanguageKind.Html && HtmlTriggerCharacters.Contains(context.TriggerCharacter))
            {
                // HTML trigger character for HTML content
                return(context);
            }

            // Trigger character not associated with the current langauge. Transform the context into an invoked context.

            var rewrittenContext = new CompletionContext()
            {
                TriggerKind = CompletionTriggerKind.Invoked
            };

            return(rewrittenContext);
        }
예제 #5
0
        public override async Task <TextEdit[]> ApplyFormattedEditsAsync(
            DocumentUri uri,
            DocumentSnapshot documentSnapshot,
            RazorLanguageKind kind,
            TextEdit[] formattedEdits,
            FormattingOptions options,
            CancellationToken cancellationToken,
            bool bypassValidationPasses = false)
        {
            if (kind == RazorLanguageKind.Html)
            {
                // We don't support formatting HTML edits yet.
                return(formattedEdits);
            }

            var codeDocument = await documentSnapshot.GetGeneratedOutputAsync();

            using var context = FormattingContext.Create(uri, documentSnapshot, codeDocument, options, isFormatOnType: true);
            var result = new FormattingResult(formattedEdits, kind);

            foreach (var pass in _formattingPasses)
            {
                if (pass.IsValidationPass && bypassValidationPasses)
                {
                    continue;
                }

                cancellationToken.ThrowIfCancellationRequested();
                result = await pass.ExecuteAsync(context, result, cancellationToken);
            }

            return(result.Edits);
        }
예제 #6
0
 public abstract Task <TextEdit[]> ApplyFormattedEditsAsync(
     DocumentUri uri,
     DocumentSnapshot documentSnapshot,
     RazorLanguageKind kind,
     TextEdit[] formattedEdits,
     FormattingOptions options,
     CancellationToken cancellationToken);
예제 #7
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,
            };

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

            return(documentMappingResponse);
        }
예제 #8
0
        protected override async Task <WorkspaceEdit?> TryGetRazorWorkspaceEditAsync(RazorLanguageKind languageKind, UriPresentationParams request, CancellationToken cancellationToken)
        {
            if (languageKind is not RazorLanguageKind.Html)
            {
                // We don't do anything for HTML
                return(null);
            }

            if (request.Uris is null || request.Uris.Length == 0)
            {
                _logger.LogInformation($"No URIs were included in the request?");
                return(null);
            }

            // We only want to handle requests for a single .razor file, but when there are files nested under a .razor
            // file (for example, Goo.razor.css, Goo.razor.cs etc.) then we'll get all of those files as well, when the user
            // thinks they're just dragging the parent one, so we have to be a little bit clever with the filter here
            var razorFileUri = request.Uris.Last();
            var fileName     = Path.GetFileName(razorFileUri.GetAbsoluteOrUNCPath());

            if (!fileName.EndsWith(".razor", FilePathComparison.Instance))
            {
                _logger.LogInformation("Last file in the drop was not a single razor file URI.");
                return(null);
            }

            if (request.Uris.Any(uri => !Path.GetFileName(uri.GetAbsoluteOrUNCPath()).StartsWith(fileName, FilePathComparison.Instance)))
            {
                _logger.LogInformation("One or more URIs were not a child file of the main .razor file.");
                return(null);
            }

            var componentTagText = await TryGetComponentTagAsync(razorFileUri, cancellationToken).ConfigureAwait(false);

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

            return(new WorkspaceEdit
            {
                DocumentChanges = new TextDocumentEdit[]
                {
                    new TextDocumentEdit
                    {
                        TextDocument = new()
                        {
                            Uri = request.TextDocument.Uri
                        },
                        Edits = new[]
                        {
                            new TextEdit
                            {
                                NewText = componentTagText,
                                Range = request.Range
                            }
                        }
                    }
                }
            });
예제 #9
0
 public abstract Task <TextEdit[]> ApplyFormattedEditsAsync(
     DocumentUri uri,
     DocumentSnapshot documentSnapshot,
     RazorLanguageKind kind,
     TextEdit[] formattedEdits,
     FormattingOptions options,
     CancellationToken cancellationToken,
     bool bypassValidationPasses = false,
     bool collapseEdits          = false);
 public static string ToContainedLanguageServerName(this RazorLanguageKind razorLanguageKind)
 {
     return(razorLanguageKind switch
     {
         RazorLanguageKind.CSharp => RazorLSPConstants.RazorCSharpLanguageServerName,
         RazorLanguageKind.Html => RazorLSPConstants.HtmlLanguageServerName,
         RazorLanguageKind.Razor => RazorLSPConstants.RazorLanguageServerName,
         _ => throw new NotImplementedException("A RazorLanguageKind did not have a corresponding ClientName"),
     });
 public abstract Task <TextEdit[]> FormatOnTypeAsync(
     DocumentUri uri,
     DocumentSnapshot documentSnapshot,
     RazorLanguageKind kind,
     TextEdit[] formattedEdits,
     FormattingOptions options,
     int hostDocumentIndex,
     char triggerCharacter,
     CancellationToken cancellationToken);
예제 #12
0
        public override async Task <TextEdit[]> ApplyFormattedEditsAsync(
            DocumentUri uri,
            DocumentSnapshot documentSnapshot,
            RazorLanguageKind kind,
            TextEdit[] formattedEdits,
            FormattingOptions options,
            CancellationToken cancellationToken,
            bool bypassValidationPasses = false,
            bool collapseEdits          = false)
        {
            if (kind == RazorLanguageKind.Html)
            {
                // We don't support formatting HTML edits yet.
                return(formattedEdits);
            }

            // If we only received a single edit, let's always return a single edit back.
            // Otherwise, merge only if explicitly asked.
            collapseEdits |= formattedEdits.Length == 1;

            var codeDocument = await documentSnapshot.GetGeneratedOutputAsync();

            using var context = FormattingContext.Create(uri, documentSnapshot, codeDocument, options, _workspaceFactory, isFormatOnType: true);
            var result = new FormattingResult(formattedEdits, kind);

            foreach (var pass in _formattingPasses)
            {
                if (pass.IsValidationPass && bypassValidationPasses)
                {
                    continue;
                }

                cancellationToken.ThrowIfCancellationRequested();
                result = await pass.ExecuteAsync(context, result, cancellationToken);
            }

            var originalText = context.SourceText;
            var edits        = result.Edits;

            if (collapseEdits)
            {
                var collapsedEdit = MergeEdits(result.Edits, originalText);
                edits = new[] { collapsedEdit };
            }

            // Make sure the edits actually change something, or its not worth responding
            var textChanges = edits.Select(e => e.AsTextChange(originalText));
            var changedText = originalText.WithChanges(textChanges);

            if (changedText.ContentEquals(originalText))
            {
                return(Array.Empty <TextEdit>());
            }

            return(edits);
        }
예제 #13
0
        public FormattingResult(TextEdit[] edits, RazorLanguageKind kind = RazorLanguageKind.Razor)
        {
            if (edits is null)
            {
                throw new ArgumentNullException(nameof(edits));
            }

            Edits = edits;
            Kind  = kind;
        }
예제 #14
0
 public override Task <RazorMapToDocumentRangesResponse> MapToDocumentRangesAsync(
     RazorLanguageKind languageKind,
     Uri razorDocumentUri,
     Range[] projectedRanges,
     LanguageServerMappingBehavior mappingBehavior,
     CancellationToken cancellationToken)
 {
     _mappings.TryGetValue(projectedRanges[0], out var response);
     return(Task.FromResult(response));
 }
예제 #15
0
        private static bool IsApplicableTriggerCharacter(string triggerCharacter, RazorLanguageKind languageKind)
        {
            if (languageKind == RazorLanguageKind.CSharp)
            {
                return(CSharpTriggerCharacters.Contains(triggerCharacter));
            }
            else if (languageKind == RazorLanguageKind.Html)
            {
                return(HtmlTriggerCharacters.Contains(triggerCharacter));
            }

            // Unknown trigger character.
            return(false);
        }
예제 #16
0
        public override async Task <TextEdit[]> ApplyFormattedEditsAsync(
            DocumentUri uri,
            DocumentSnapshot documentSnapshot,
            RazorLanguageKind kind,
            TextEdit[] formattedEdits,
            FormattingOptions options,
            CancellationToken cancellationToken,
            bool bypassValidationPasses = false,
            bool collapseEdits          = false)
        {
            if (kind == RazorLanguageKind.Html)
            {
                // We don't support formatting HTML edits yet.
                return(formattedEdits);
            }

            // If we only received a single edit, let's always return a single edit back.
            // Otherwise, merge only if explicitly asked.
            collapseEdits |= formattedEdits.Length == 1;

            var codeDocument = await documentSnapshot.GetGeneratedOutputAsync();

            using var context = FormattingContext.Create(uri, documentSnapshot, codeDocument, options, isFormatOnType: true);
            var result = new FormattingResult(formattedEdits, kind);

            foreach (var pass in _formattingPasses)
            {
                if (pass.IsValidationPass && bypassValidationPasses)
                {
                    continue;
                }

                cancellationToken.ThrowIfCancellationRequested();
                result = await pass.ExecuteAsync(context, result, cancellationToken);
            }

            var edits = result.Edits;

            if (collapseEdits)
            {
                var collapsedEdit = MergeEdits(result.Edits, context.SourceText);
                edits = new[] { collapsedEdit };
            }

            return(edits);
        }
예제 #17
0
        private static bool IsApplicableTriggerCharacter(string triggerCharacter, RazorLanguageKind languageKind)
        {
            if (RazorTriggerCharacters.Contains(triggerCharacter))
            {
                // Razor trigger characters always transition into either C# or HTML, always note as "applicable".
                return(true);
            }
            else if (languageKind == RazorLanguageKind.CSharp)
            {
                return(CSharpTriggerCharacters.Contains(triggerCharacter));
            }
            else if (languageKind == RazorLanguageKind.Html)
            {
                return(HtmlTriggerCharacters.Contains(triggerCharacter));
            }

            // Unknown trigger character.
            return(false);
        }
        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);
        }
예제 #19
0
        // Internal for testing
        internal bool TriggerAppliesToProjection(CompletionContext context, RazorLanguageKind languageKind)
        {
            if (languageKind == RazorLanguageKind.Razor)
            {
                // We don't handle any type of triggers in Razor pieces of the document
                return(false);
            }

            if (context.TriggerKind != CompletionTriggerKind.TriggerCharacter)
            {
                // Not a trigger character completion, allow it.
                return(true);
            }

            if (!AllTriggerCharacters.Contains(context.TriggerCharacter))
            {
                // This is an auto-invoked completion from the VS LSP platform. Completions are automatically invoked upon typing identifiers
                // and are represented as CompletionTriggerKind.TriggerCharacter and have a trigger character that we have not registered for.
                return(true);
            }

            if (context.TriggerCharacter == "(")
            {
                // This is a special case.
                // We added `(` as a trigger character to workaround problems with Razor explicit expressions.
                // Example - https://github.com/dotnet/aspnetcore/issues/21154
                // But we don't really want to show a completion box every time a `(` is typed.
                return(false);
            }

            if (IsApplicableTriggerCharacter(context.TriggerCharacter, languageKind))
            {
                // Trigger character is associated with the langauge at the current cursor position
                return(true);
            }

            // We were triggered but the trigger character doesn't make sense for the current cursor position. Bail.
            return(false);
        }
 public abstract Task <RazorMapToDocumentRangesResponse> MapToDocumentRangesAsync(RazorLanguageKind languageKind, Uri razorDocumentUri, Range[] projectedRanges, LanguageServerMappingBehavior mappingBehavior, CancellationToken cancellationToken);
        private LSPDocumentMappingProvider GetDocumentMappingProvider(Range expectedRange, int expectedVersion, RazorLanguageKind languageKind)
        {
            var remappingResult = new RazorMapToDocumentRangesResponse()
            {
                Ranges = new[] { expectedRange },
                HostDocumentVersion = expectedVersion
            };
            var documentMappingProvider = new Mock <LSPDocumentMappingProvider>(MockBehavior.Strict);

            documentMappingProvider.Setup(d => d.MapToDocumentRangesAsync(languageKind, Uri, It.IsAny <Range[]>(), It.IsAny <CancellationToken>())).
            Returns(Task.FromResult(remappingResult));

            return(documentMappingProvider.Object);
        }
 // TODO; HTML filtering blocked on https://dev.azure.com/devdiv/DevDiv/_workitems/edit/1257401
 static bool CanDiagnosticBeFiltered(RazorLanguageKind kind, Diagnostic d)
 {
     return(kind == RazorLanguageKind.CSharp ?
            CanCSharpDiagnosticBeFiltered(d) :
            CanHTMLDiagnosticBeFiltered(d));
 }
예제 #23
0
 public override Task <RazorMapToDocumentRangesResponse> MapToDocumentRangesAsync(RazorLanguageKind languageKind, Uri razorDocumentUri, Range[] projectedRanges, CancellationToken cancellationToken)
 => MapToDocumentRangesAsync(languageKind, razorDocumentUri, projectedRanges, LanguageServerMappingBehavior.Strict, cancellationToken);
 public override Task <RazorMapToDocumentRangesResponse> MapToDocumentRangesAsync(RazorLanguageKind languageKind, Uri razorDocumentUri, LanguageServer.Protocol.Range[] projectedRanges, LanguageServerMappingBehavior mappingBehavior, CancellationToken cancellationToken) => throw new NotImplementedException();
 public abstract Task <RazorDiagnosticsResponse> TranslateAsync(
     RazorLanguageKind languageKind,
     Uri razorDocumentUri,
     Diagnostic[] diagnostics,
     CancellationToken cancellationToken);
예제 #26
0
 protected override Task <WorkspaceEdit?> TryGetRazorWorkspaceEditAsync(RazorLanguageKind languageKind, TextPresentationParams request, CancellationToken cancellationToken)
 {
     // We don't do anything special with text
     return(Task.FromResult <WorkspaceEdit?>(null));
 }
 public static string ToContainedLanguageContentType(this RazorLanguageKind razorLanguageKind) =>
 razorLanguageKind == RazorLanguageKind.CSharp ? RazorLSPConstants.CSharpContentTypeName : RazorLSPConstants.HtmlLSPDelegationContentTypeName;
        protected TextEdit[] RemapTextEdits(RazorCodeDocument codeDocument, TextEdit[] projectedTextEdits, RazorLanguageKind projectedKind)
        {
            if (codeDocument is null)
            {
                throw new ArgumentNullException(nameof(codeDocument));
            }

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

            if (projectedKind != RazorLanguageKind.CSharp)
            {
                // Non C# projections map directly to Razor. No need to remap.
                return(projectedTextEdits);
            }

            var edits = new List <TextEdit>();

            for (var i = 0; i < projectedTextEdits.Length; i++)
            {
                var projectedRange = projectedTextEdits[i].Range;
                if (codeDocument.IsUnsupported() ||
                    !_documentMappingService.TryMapFromProjectedDocumentRange(codeDocument, projectedRange, out var originalRange))
                {
                    // Can't map range. Discard this edit.
                    continue;
                }

                var edit = new TextEdit()
                {
                    Range   = originalRange,
                    NewText = projectedTextEdits[i].NewText
                };

                edits.Add(edit);
            }

            return(edits.ToArray());
        }
 public abstract Task <RazorMapToDocumentRangeResponse> MapToDocumentRangeAsync(RazorLanguageKind languageKind, Uri razorDocumentUri, Range projectedRange, CancellationToken cancellationToken);
        protected TextEdit[] RemapTextEdits(RazorCodeDocument codeDocument, TextEdit[] projectedTextEdits, RazorLanguageKind projectedKind)
        {
            if (codeDocument is null)
            {
                throw new ArgumentNullException(nameof(codeDocument));
            }

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

            if (projectedKind != RazorLanguageKind.CSharp)
            {
                // Non C# projections map directly to Razor. No need to remap.
                return(projectedTextEdits);
            }

            if (codeDocument.IsUnsupported())
            {
                return(Array.Empty <TextEdit>());
            }

            var edits = DocumentMappingService.GetProjectedDocumentEdits(codeDocument, projectedTextEdits);

            return(edits);
        }