protected override void ExecuteCore(RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode) { var @class = documentNode.FindPrimaryClass(); if (@class == null) { return; } var directiveNodes = new List <IntermediateNodeReference>(); directiveNodes.AddRange(documentNode.FindDirectiveReferences(FunctionsDirective.Directive)); if (FileKinds.IsComponent(codeDocument.GetFileKind())) { directiveNodes.AddRange(documentNode.FindDirectiveReferences(ComponentCodeDirective.Directive)); } // Now we have all the directive nodes, we want to add them to the end of the class node in document order. var orderedDirectives = directiveNodes.OrderBy(n => n.Node.Source?.AbsoluteIndex); foreach (var directiveReference in orderedDirectives) { var node = directiveReference.Node; for (var i = 0; i < node.Children.Count; i++) { @class.Children.Add(node.Children[i]); } // We don't want to keep the original directive node around anymore. // Otherwise this can cause unintended side effects in the subsequent passes. directiveReference.Remove(); } }
protected override void ExecuteCore(RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode) { if (FileKinds.IsComponent(codeDocument.GetFileKind())) { // Hot reload does not apply to components. return; } var @namespace = documentNode.FindPrimaryNamespace(); var @class = documentNode.FindPrimaryClass(); var classIndex = @namespace.Children.IndexOf(@class); if (classIndex == -1) { return; } var identifierFeature = Engine.Features.OfType <IMetadataIdentifierFeature>().First(); var identifier = identifierFeature.GetIdentifier(codeDocument, codeDocument.Source); var metadataAttributeNode = new CreateNewOnMetadataUpdateAttributeIntermediateNode(); // Metadata attributes need to be inserted right before the class declaration. @namespace.Children.Insert(classIndex, metadataAttributeNode); // [global:Microsoft.AspNetCore.Razor.Hosting.RazorCompiledItemMetadataAttribute("Identifier", "/Views/Home/Index.cshtml")] @namespace.Children.Insert(classIndex, new RazorCompiledItemMetadataAttributeIntermediateNode { Key = "Identifier", Value = identifier, }); }
public RazorSyntaxTree Execute(RazorCodeDocument codeDocument, RazorSyntaxTree syntaxTree) { if (FileKinds.IsComponent(codeDocument.GetFileKind())) { // Nothing to do here. return(syntaxTree); } var sectionVerifier = new NestedSectionVerifier(syntaxTree); return(sectionVerifier.Verify()); }
private static DocumentResolver CreateDocumentResolver(string documentPath, RazorCodeDocument codeDocument) { var sourceTextChars = new char[codeDocument.Source.Length]; codeDocument.Source.CopyTo(0, sourceTextChars, 0, codeDocument.Source.Length); var sourceText = SourceText.From(new string(sourceTextChars)); var documentSnapshot = Mock.Of <DocumentSnapshot>(document => document.GetGeneratedOutputAsync() == Task.FromResult(codeDocument) && document.FileKind == codeDocument.GetFileKind() && document.GetTextAsync() == Task.FromResult(sourceText), MockBehavior.Strict); var documentResolver = new Mock <DocumentResolver>(MockBehavior.Strict); documentResolver.Setup(resolver => resolver.TryResolveDocument(documentPath, out documentSnapshot)) .Returns(true); return(documentResolver.Object); }
protected override void ExecuteCore(RazorCodeDocument codeDocument) { var syntaxTree = codeDocument.GetSyntaxTree(); ThrowForMissingDocumentDependency(syntaxTree); var descriptors = codeDocument.GetTagHelpers(); if (descriptors == null) { var feature = Engine.GetFeature <ITagHelperFeature>(); if (feature == null) { // No feature, nothing to do. return; } descriptors = feature.GetDescriptors(); } var parserOptions = codeDocument.GetParserOptions(); // We need to find directives in all of the *imports* as well as in the main razor file // // The imports come logically before the main razor file and are in the order they // should be processed. DirectiveVisitor visitor; if (FileKinds.IsComponent(codeDocument.GetFileKind()) && (parserOptions == null || parserOptions.FeatureFlags.AllowComponentFileKind)) { codeDocument.TryComputeNamespace(fallbackToRootNamespace: true, out var currentNamespace); visitor = new ComponentDirectiveVisitor(codeDocument.Source.FilePath, descriptors, currentNamespace); } else { visitor = new TagHelperDirectiveVisitor(descriptors); } var imports = codeDocument.GetImportSyntaxTrees(); if (imports != null) { for (var i = 0; i < imports.Count; i++) { var import = imports[i]; visitor.Visit(import); } } visitor.Visit(syntaxTree); // This will always be null for a component document. var tagHelperPrefix = visitor.TagHelperPrefix; descriptors = visitor.Matches.ToArray(); var context = TagHelperDocumentContext.Create(tagHelperPrefix, descriptors); codeDocument.SetTagHelperContext(context); if (descriptors.Count == 0) { // No descriptors, no-op. return; } var rewrittenSyntaxTree = TagHelperParseTreeRewriter.Rewrite(syntaxTree, tagHelperPrefix, descriptors); codeDocument.SetSyntaxTree(rewrittenSyntaxTree); }
/// <inheritdoc /> protected override void OnDocumentStructureCreated( RazorCodeDocument codeDocument, NamespaceDeclarationIntermediateNode @namespace, ClassDeclarationIntermediateNode @class, MethodDeclarationIntermediateNode method) { if (!codeDocument.TryComputeNamespace(fallbackToRootNamespace: true, out var computedNamespace) || !TryComputeClassName(codeDocument, out var computedClass)) { // If we can't compute a nice namespace (no relative path) then just generate something // mangled. computedNamespace = FallbackRootNamespace; var checksum = Checksum.BytesToString(codeDocument.Source.GetChecksum()); computedClass = $"AspNetCore_{checksum}"; } var documentNode = codeDocument.GetDocumentIntermediateNode(); if (char.IsLower(computedClass, 0)) { // We don't allow component names to start with a lowercase character. documentNode.Diagnostics.Add( ComponentDiagnosticFactory.Create_ComponentNamesCannotStartWithLowerCase(computedClass, documentNode.Source)); } if (MangleClassNames) { computedClass = ComponentMetadata.MangleClassName(computedClass); } @namespace.Content = computedNamespace; @class.ClassName = computedClass; @class.Modifiers.Clear(); @class.Modifiers.Add("public"); @class.Modifiers.Add("partial"); if (FileKinds.IsComponentImport(codeDocument.GetFileKind())) { // We don't want component imports to be considered as real component. // But we still want to generate code for it so we can get diagnostics. @class.BaseType = typeof(object).FullName; method.ReturnType = "void"; method.MethodName = "Execute"; method.Modifiers.Clear(); method.Modifiers.Add("protected"); method.Parameters.Clear(); } else { @class.BaseType = ComponentsApi.ComponentBase.FullTypeName; var typeParamReferences = documentNode.FindDirectiveReferences(ComponentTypeParamDirective.Directive); for (var i = 0; i < typeParamReferences.Count; i++) { var typeParamNode = (DirectiveIntermediateNode)typeParamReferences[i].Node; if (typeParamNode.HasDiagnostics) { continue; } @class.TypeParameters.Add(new TypeParameter() { ParameterName = typeParamNode.Tokens.First().Content, Constraints = typeParamNode.Tokens.Skip(1).FirstOrDefault()?.Content }); } method.ReturnType = "void"; method.MethodName = ComponentsApi.ComponentBase.BuildRenderTree; method.Modifiers.Clear(); method.Modifiers.Add("protected"); method.Modifiers.Add("override"); method.Parameters.Clear(); method.Parameters.Add(new MethodParameter() { ParameterName = ComponentsApi.RenderTreeBuilder.BuilderParameter, TypeName = ComponentsApi.RenderTreeBuilder.FullTypeName, }); } }
protected override bool IsMatch(RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode) { return(FileKinds.IsComponent(codeDocument.GetFileKind())); }
private (RazorLanguageKind, TextEdit[]) GetFormattedEdits(RazorCodeDocument codeDocument, string expected, int positionBeforeTriggerChar) { var mappingService = new DefaultRazorDocumentMappingService(); var languageKind = mappingService.GetLanguageKind(codeDocument, positionBeforeTriggerChar); var expectedText = SourceText.From(expected); var(expectedCodeDocument, _) = CreateCodeDocumentAndSnapshot(expectedText, codeDocument.Source.FilePath, fileKind: codeDocument.GetFileKind()); var edits = Array.Empty <TextEdit>(); if (languageKind == RazorLanguageKind.CSharp) { var beforeCSharpText = SourceText.From(codeDocument.GetCSharpDocument().GeneratedCode); var afterCSharpText = SourceText.From(expectedCodeDocument.GetCSharpDocument().GeneratedCode); edits = SourceTextDiffer.GetMinimalTextChanges(beforeCSharpText, afterCSharpText, lineDiffOnly: false).Select(c => c.AsTextEdit(beforeCSharpText)).ToArray(); } else if (languageKind == RazorLanguageKind.Html) { var beforeHtmlText = SourceText.From(codeDocument.GetHtmlDocument().GeneratedHtml); var afterHtmlText = SourceText.From(expectedCodeDocument.GetHtmlDocument().GeneratedHtml); edits = SourceTextDiffer.GetMinimalTextChanges(beforeHtmlText, afterHtmlText, lineDiffOnly: false).Select(c => c.AsTextEdit(beforeHtmlText)).ToArray(); } return(languageKind, edits); }
protected override void ExecuteCore(RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode) { if (codeDocument == null) { throw new ArgumentNullException(nameof(codeDocument)); } if (documentNode == null) { throw new ArgumentNullException(nameof(documentNode)); } var @namespace = documentNode.FindPrimaryNamespace(); var @class = documentNode.FindPrimaryClass(); if (@namespace == null || @class == null) { return; } var directives = documentNode.FindDirectiveReferences(ComponentPageDirective.Directive); if (directives.Count == 0) { return; } // We don't allow @page directives in imports for (var i = 0; i < directives.Count; i++) { var directive = directives[i]; if (FileKinds.IsComponentImport(codeDocument.GetFileKind()) || directive.Node.IsImported()) { directive.Node.Diagnostics.Add(ComponentDiagnosticFactory.CreatePageDirective_CannotBeImported(directive.Node.Source.Value)); } } // Insert the attributes 'on-top' of the class declaration, since classes don't directly support attributes. var index = 0; for (; index < @namespace.Children.Count; index++) { if (object.ReferenceEquals(@class, @namespace.Children[index])) { break; } } for (var i = 0; i < directives.Count; i++) { var pageDirective = (DirectiveIntermediateNode)directives[i].Node; // The parser also adds errors for invalid syntax, we just need to not crash. var routeToken = pageDirective.Tokens.FirstOrDefault(); if (routeToken != null && routeToken.Content.Length >= 3 && routeToken.Content[0] == '\"' && routeToken.Content[1] == '/' && routeToken.Content[routeToken.Content.Length - 1] == '\"') { var template = new StringSegment(routeToken.Content, 1, routeToken.Content.Length - 2); @namespace.Children.Insert(index++, new RouteAttributeExtensionNode(template)); } else { pageDirective.Diagnostics.Add(ComponentDiagnosticFactory.CreatePageDirective_MustSpecifyRoute(pageDirective.Source)); } } }