Example #1
0
        private void AddComponentAccessFromTag(RazorCodeActionContext context, MarkupStartTagSyntax startTag, List <RazorCodeAction> container)
        {
            var matching = FindMatchingTagHelpers(context, startTag);

            // For all the matches, add options for add @using and fully qualify
            foreach (var tagHelperPair in matching.Values)
            {
                if (tagHelperPair._fullyQualified is null)
                {
                    continue;
                }

                var fullyQualifiedName = tagHelperPair._short.Name;

                // Insert @using
                if (AddUsingsCodeActionProviderHelper.TryCreateAddUsingResolutionParams(fullyQualifiedName, context.Request.TextDocument.Uri, out var @namespace, out var resolutionParams))
                {
                    var addUsingCodeAction = RazorCodeActionFactory.CreateAddComponentUsing(@namespace, resolutionParams);
                    container.Add(addUsingCodeAction);
                }

                // Fully qualify
                var renameTagWorkspaceEdit   = CreateRenameTagEdit(context, startTag, fullyQualifiedName);
                var fullyQualifiedCodeAction = RazorCodeActionFactory.CreateFullyQualifyComponent(fullyQualifiedName, renameTagWorkspaceEdit);
                container.Add(fullyQualifiedCodeAction);
            }
        }
Example #2
0
        private void AddCreateComponentFromTag(RazorCodeActionContext context, MarkupStartTagSyntax startTag, List <RazorCodeAction> container)
        {
            if (context is null)
            {
                return;
            }

            if (!context.SupportsFileCreation)
            {
                return;
            }

            var path = context.Request.TextDocument.Uri.GetAbsoluteOrUNCPath();

            path = _filePathNormalizer.Normalize(path);
            var newComponentPath = Path.Combine(Path.GetDirectoryName(path), $"{startTag.Name.Content}.razor");

            if (File.Exists(newComponentPath))
            {
                return;
            }

            var actionParams = new CreateComponentCodeActionParams
            {
                Uri  = context.Request.TextDocument.Uri,
                Path = newComponentPath,
            };

            var resolutionParams = new RazorCodeActionResolutionParams
            {
                Action   = LanguageServerConstants.CodeActions.CreateComponentFromTag,
                Language = LanguageServerConstants.CodeActions.Languages.Razor,
                Data     = actionParams,
            };

            var codeAction = RazorCodeActionFactory.CreateComponentFromTag(resolutionParams);

            container.Add(codeAction);
        }
        private static IEnumerable <RazorCodeAction> ProcessCodeActionsVSCode(
            RazorCodeActionContext context,
            IEnumerable <RazorCodeAction> codeActions)
        {
            var diagnostics = context.Request.Context.Diagnostics.Where(diagnostic =>
                                                                        diagnostic.Severity == DiagnosticSeverity.Error &&
                                                                        diagnostic.Code?.IsString == true &&
                                                                        s_supportedDiagnostics.Any(d => diagnostic.Code.Value.String.Equals(d, StringComparison.OrdinalIgnoreCase)));

            if (diagnostics is null || !diagnostics.Any())
            {
                return(Array.Empty <RazorCodeAction>());
            }

            var typeAccessibilityCodeActions = new List <RazorCodeAction>();

            foreach (var diagnostic in diagnostics)
            {
                // Corner case handling for diagnostics which (momentarily) linger after
                // @code block is cleared out
                if (diagnostic.Range.End.Line > context.SourceText.Lines.Count ||
                    diagnostic.Range.End.Character > context.SourceText.Lines[diagnostic.Range.End.Line].End)
                {
                    continue;
                }

                var diagnosticSpan = diagnostic.Range.AsTextSpan(context.SourceText);

                // Based on how we compute `Range.AsTextSpan` it's possible to have a span
                // which goes beyond the end of the source text. Something likely changed
                // between the capturing of the diagnostic (by the platform) and the retrieval of the
                // document snapshot / source text. In such a case, we skip processing of the diagnostic.
                if (diagnosticSpan.End > context.SourceText.Length)
                {
                    continue;
                }

                foreach (var codeAction in codeActions)
                {
                    if (!codeAction.Name.Equals(LanguageServerConstants.CodeActions.CodeActionFromVSCode, StringComparison.Ordinal))
                    {
                        continue;
                    }

                    var associatedValue = context.SourceText.GetSubTextString(diagnosticSpan);

                    var fqn = string.Empty;

                    // When there's only one FQN suggestion, code action title is of the form:
                    // `System.Net.Dns`
                    if (!codeAction.Title.Any(c => char.IsWhiteSpace(c)) &&
                        codeAction.Title.EndsWith(associatedValue, StringComparison.OrdinalIgnoreCase))
                    {
                        fqn = codeAction.Title;
                    }
                    // When there are multiple FQN suggestions, the code action title is of the form:
                    // `Fully qualify 'Dns' -> System.Net.Dns`
                    else
                    {
                        var expectedCodeActionPrefix = $"Fully qualify '{associatedValue}' -> ";
                        if (codeAction.Title.StartsWith(expectedCodeActionPrefix, StringComparison.OrdinalIgnoreCase))
                        {
                            fqn = codeAction.Title.Substring(expectedCodeActionPrefix.Length);
                        }
                    }

                    if (string.IsNullOrEmpty(fqn))
                    {
                        continue;
                    }

                    var fqnCodeAction = CreateFQNCodeAction(context, diagnostic, codeAction, fqn);
                    typeAccessibilityCodeActions.Add(fqnCodeAction);

                    if (AddUsingsCodeActionProviderHelper.TryCreateAddUsingResolutionParams(fqn, context.Request.TextDocument.Uri, out var @namespace, out var resolutionParams))
                    {
                        var addUsingCodeAction = RazorCodeActionFactory.CreateAddComponentUsing(@namespace, resolutionParams);
                        typeAccessibilityCodeActions.Add(addUsingCodeAction);
                    }
                }
            }

            return(typeAccessibilityCodeActions);
        }
Example #4
0
        public override Task <IReadOnlyList <RazorCodeAction> > ProvideAsync(RazorCodeActionContext context, CancellationToken cancellationToken)
        {
            if (context is null)
            {
                return(s_emptyResult);
            }

            if (!context.SupportsFileCreation)
            {
                return(s_emptyResult);
            }

            if (!FileKinds.IsComponent(context.CodeDocument.GetFileKind()))
            {
                return(s_emptyResult);
            }

            var change     = new SourceChange(context.Location.AbsoluteIndex, length: 0, newText: string.Empty);
            var syntaxTree = context.CodeDocument.GetSyntaxTree();

            if (syntaxTree?.Root is null)
            {
                return(s_emptyResult);
            }

            var owner = syntaxTree.Root.LocateOwner(change);

            if (owner is null)
            {
                Debug.Fail("Owner should never be null.");
                return(s_emptyResult);
            }

            var node = owner.Ancestors().FirstOrDefault(n => n.Kind == SyntaxKind.RazorDirective);

            if (node is not RazorDirectiveSyntax directiveNode)
            {
                return(s_emptyResult);
            }

            // Make sure we've found a @code or @functions
            if (directiveNode.DirectiveDescriptor != ComponentCodeDirective.Directive &&
                directiveNode.DirectiveDescriptor != FunctionsDirective.Directive)
            {
                return(s_emptyResult);
            }

            // No code action if malformed
            if (directiveNode.GetDiagnostics().Any(d => d.Severity == RazorDiagnosticSeverity.Error))
            {
                return(s_emptyResult);
            }

            var csharpCodeBlockNode = directiveNode.Body.DescendantNodes().FirstOrDefault(n => n is CSharpCodeBlockSyntax);

            if (csharpCodeBlockNode is null)
            {
                return(s_emptyResult);
            }

            if (HasUnsupportedChildren(csharpCodeBlockNode))
            {
                return(s_emptyResult);
            }

            // Do not provide code action if the cursor is inside the code block
            if (context.Location.AbsoluteIndex > csharpCodeBlockNode.SpanStart)
            {
                return(s_emptyResult);
            }

            var actionParams = new ExtractToCodeBehindCodeActionParams()
            {
                Uri          = context.Request.TextDocument.Uri,
                ExtractStart = csharpCodeBlockNode.Span.Start,
                ExtractEnd   = csharpCodeBlockNode.Span.End,
                RemoveStart  = directiveNode.Span.Start,
                RemoveEnd    = directiveNode.Span.End
            };

            var resolutionParams = new RazorCodeActionResolutionParams()
            {
                Action   = LanguageServerConstants.CodeActions.ExtractToCodeBehindAction,
                Language = LanguageServerConstants.CodeActions.Languages.Razor,
                Data     = actionParams,
            };

            var codeAction  = RazorCodeActionFactory.CreateExtractToCodeBehind(resolutionParams);
            var codeActions = new List <RazorCodeAction> {
                codeAction
            };

            return(Task.FromResult(codeActions as IReadOnlyList <RazorCodeAction>));
        }