Exemplo n.º 1
0
        public async Task <TemplateResult> RunTemplateAsync(string content,
                                                            dynamic templateModel)
        {
            var razorEngine = RazorEngine.Create((builder) =>
            {
                RazorExtensions.Register(builder);
            });

            // Don't care about the RazorProject as we already have the content of the .cshtml file
            // and don't need to deal with imports.
            var razorProject        = RazorProject.Create(Directory.GetCurrentDirectory());
            var razorTemplateEngine = new RazorTemplateEngine(razorEngine, razorProject);

            var imports = new RazorSourceDocument[]
            {
                RazorSourceDocument.Create(@"
@using System
@using System.Threading.Tasks
", fileName: null)
            };

            var razorDocument    = RazorCodeDocument.Create(RazorSourceDocument.Create(content, "Template"), imports);
            var generatorResults = razorTemplateEngine.GenerateCode(razorDocument);

            if (generatorResults.Diagnostics.Any())
            {
                var messages = generatorResults.Diagnostics.Select(d => d.GetMessage());
                return(new TemplateResult()
                {
                    GeneratedText = string.Empty,
                    ProcessingException = new TemplateProcessingException(messages, generatorResults.GeneratedCode)
                });
            }
            var templateResult = _compilationService.Compile(generatorResults.GeneratedCode);

            if (templateResult.Messages.Any())
            {
                return(new TemplateResult()
                {
                    GeneratedText = string.Empty,
                    ProcessingException = new TemplateProcessingException(templateResult.Messages, generatorResults.GeneratedCode)
                });
            }

            var compiledObject = Activator.CreateInstance(templateResult.CompiledType);
            var razorTemplate  = compiledObject as RazorTemplateBase;

            string result = String.Empty;

            if (razorTemplate != null)
            {
                razorTemplate.Model = templateModel;
                //ToDo: If there are errors executing the code, they are missed here.
                result = await razorTemplate.ExecuteTemplate();
            }

            return(new TemplateResult()
            {
                GeneratedText = result,
                ProcessingException = null
            });
        }
        /// <summary>
        /// Generate a complete C# compilation unit containing a partial class
        /// with the given name, body contents, and the namespace and all
        /// usings from the existing code document.
        /// </summary>
        /// <param name="className">Name of the resultant partial class.</param>
        /// <param name="contents">Class body contents.</param>
        /// <param name="razorCodeDocument">Existing code document we're extracting from.</param>
        /// <returns></returns>
        private static string GenerateCodeBehindClass(string className, string contents, RazorCodeDocument razorCodeDocument)
        {
            var namespaceNode = (NamespaceDeclarationIntermediateNode)razorCodeDocument
                                .GetDocumentIntermediateNode()
                                .FindDescendantNodes <IntermediateNode>()
                                .FirstOrDefault(n => n is NamespaceDeclarationIntermediateNode);

            var mock   = (ClassDeclarationSyntax)CSharpSyntaxFactory.ParseMemberDeclaration($"class Class {contents}");
            var @class = CSharpSyntaxFactory
                         .ClassDeclaration(className)
                         .AddModifiers(CSharpSyntaxFactory.Token(CSharpSyntaxKind.PublicKeyword), CSharpSyntaxFactory.Token(CSharpSyntaxKind.PartialKeyword))
                         .AddMembers(mock.Members.ToArray());

            var @namespace = CSharpSyntaxFactory
                             .NamespaceDeclaration(CSharpSyntaxFactory.ParseName(namespaceNode.Content))
                             .AddMembers(@class);

            var usings = FindUsings(razorCodeDocument)
                         .Select(u => CSharpSyntaxFactory.UsingDirective(CSharpSyntaxFactory.ParseName(u)))
                         .ToArray();
            var compilationUnit = CSharpSyntaxFactory
                                  .CompilationUnit()
                                  .AddUsings(usings)
                                  .AddMembers(@namespace);

            return(compilationUnit.NormalizeWhitespace().ToFullString());
        }
Exemplo n.º 3
0
    protected void AssertCSharpDocumentMatchesBaseline(RazorCodeDocument codeDocument)
    {
        var document = codeDocument.GetCSharpDocument();

        // Normalize newlines to match those in the baseline.
        var actualCode = document.GeneratedCode.Replace("\r", "").Replace("\n", "\r\n");

        var baselineFilePath            = GetBaselineFilePath(codeDocument, ".codegen.cs");
        var baselineDiagnosticsFilePath = GetBaselineFilePath(codeDocument, ".diagnostics.txt");
        var baselineMappingsFilePath    = GetBaselineFilePath(codeDocument, ".mappings.txt");

        var serializedMappings = SourceMappingsSerializer.Serialize(document, codeDocument.Source);

        if (GenerateBaselines)
        {
            var baselineFullPath = Path.Combine(TestProjectRoot, baselineFilePath);
            Directory.CreateDirectory(Path.GetDirectoryName(baselineFullPath));
            WriteBaseline(actualCode, baselineFullPath);

            var baselineDiagnosticsFullPath = Path.Combine(TestProjectRoot, baselineDiagnosticsFilePath);
            var lines = document.Diagnostics.Select(RazorDiagnosticSerializer.Serialize).ToArray();
            if (lines.Any())
            {
                WriteBaseline(lines, baselineDiagnosticsFullPath);
            }
            else if (File.Exists(baselineDiagnosticsFullPath))
            {
                File.Delete(baselineDiagnosticsFullPath);
            }

            var baselineMappingsFullPath = Path.Combine(TestProjectRoot, baselineMappingsFilePath);
            var text = SourceMappingsSerializer.Serialize(document, codeDocument.Source);
            if (!string.IsNullOrEmpty(text))
            {
                WriteBaseline(text, baselineMappingsFullPath);
            }
            else if (File.Exists(baselineMappingsFullPath))
            {
                File.Delete(baselineMappingsFullPath);
            }

            return;
        }

        var codegenFile = TestFile.Create(baselineFilePath, GetType().Assembly);

        if (!codegenFile.Exists())
        {
            throw new XunitException($"The resource {baselineFilePath} was not found.");
        }

        var baseline = codegenFile.ReadAllText();

        Assert.Equal(baseline, actualCode);

        var baselineDiagnostics = string.Empty;
        var diagnosticsFile     = TestFile.Create(baselineDiagnosticsFilePath, GetType().Assembly);

        if (diagnosticsFile.Exists())
        {
            baselineDiagnostics = diagnosticsFile.ReadAllText();
        }

        var actualDiagnostics = string.Concat(document.Diagnostics.Select(d => RazorDiagnosticSerializer.Serialize(d) + "\r\n"));

        Assert.Equal(baselineDiagnostics, actualDiagnostics);

        var baselineMappings = string.Empty;
        var mappingsFile     = TestFile.Create(baselineMappingsFilePath, GetType().Assembly);

        if (mappingsFile.Exists())
        {
            baselineMappings = mappingsFile.ReadAllText();
        }

        var actualMappings = SourceMappingsSerializer.Serialize(document, codeDocument.Source);

        actualMappings = actualMappings.Replace("\r", "").Replace("\n", "\r\n");
        Assert.Equal(baselineMappings, actualMappings);

        AssertLinePragmas(codeDocument);
    }
Exemplo n.º 4
0
        /// <inheritdoc />
        protected override void OnDocumentStructureCreated(
            RazorCodeDocument codeDocument,
            NamespaceDeclarationIntermediateNode @namespace,
            ClassDeclarationIntermediateNode @class,
            MethodDeclarationIntermediateNode method)
        {
            if (!codeDocument.TryComputeNamespaceAndClass(out var computedNamespace, 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}";
            }

            if (MangleClassNames)
            {
                computedClass = ComponentMetadata.MangleClassName(computedClass);
            }

            @namespace.Content = computedNamespace;
            @class.ClassName   = computedClass;
            @class.Modifiers.Clear();
            @class.Modifiers.Add("public");

            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 documentNode        = codeDocument.GetDocumentIntermediateNode();
                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,
                    });
                }

                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 = "builder",
                    TypeName      = ComponentsApi.RenderTreeBuilder.FullTypeName,
                });
            }
        }
Exemplo n.º 5
0
        public override HoverModel GetHoverInfo(RazorCodeDocument codeDocument, SourceSpan location)
        {
            if (codeDocument is null)
            {
                throw new ArgumentNullException(nameof(codeDocument));
            }

            var syntaxTree = codeDocument.GetSyntaxTree();
            var change     = new SourceChange(location, "");
            var owner      = syntaxTree.Root.LocateOwner(change);

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

            var parent   = owner.Parent;
            var position = new Position(location.LineIndex, location.CharacterIndex);
            var tagHelperDocumentContext = codeDocument.GetTagHelperContext();

            var ancestors = owner.Ancestors();

            var(parentTag, parentIsTagHelper) = _tagHelperFactsService.GetNearestAncestorTagInfo(ancestors);

            if (_htmlFactsService.TryGetElementInfo(parent, out var containingTagNameToken, out var attributes) &&
                containingTagNameToken.Span.IntersectsWith(location.AbsoluteIndex))
            {
                // Hovering over HTML tag name
                var stringifiedAttributes = _tagHelperFactsService.StringifyAttributes(attributes);
                var binding = _tagHelperFactsService.GetTagHelperBinding(
                    tagHelperDocumentContext,
                    containingTagNameToken.Content,
                    stringifiedAttributes,
                    parentTag: parentTag,
                    parentIsTagHelper: parentIsTagHelper);

                if (binding is null)
                {
                    // No matching tagHelpers, it's just HTML
                    return(null);
                }
                else
                {
                    Debug.Assert(binding.Descriptors.Count() > 0);

                    var range = GetRangeFromSyntaxNode(containingTagNameToken, codeDocument);

                    var result = ElementInfoToHover(binding.Descriptors, range);
                    return(result);
                }
            }

            if (_htmlFactsService.TryGetAttributeInfo(parent, out containingTagNameToken, out var selectedAttributeName, out attributes) &&
                attributes.Span.IntersectsWith(location.AbsoluteIndex))
            {
                // Hovering over HTML attribute name
                var stringifiedAttributes = _tagHelperFactsService.StringifyAttributes(attributes);


                var binding = _tagHelperFactsService.GetTagHelperBinding(
                    tagHelperDocumentContext,
                    containingTagNameToken.Content,
                    stringifiedAttributes,
                    parentTag: parentTag,
                    parentIsTagHelper: parentIsTagHelper);

                if (binding is null)
                {
                    // No matching TagHelpers, it's just HTML
                    return(null);
                }
                else
                {
                    Debug.Assert(binding.Descriptors.Count() > 0);
                    var tagHelperAttributes = _tagHelperFactsService.GetBoundTagHelperAttributes(tagHelperDocumentContext, selectedAttributeName, binding);

                    var attribute           = attributes.Single(a => a.Span.IntersectsWith(location.AbsoluteIndex));
                    var range               = GetRangeFromSyntaxNode(attribute, codeDocument);
                    var attributeHoverModel = AttributeInfoToHover(tagHelperAttributes, range);

                    return(attributeHoverModel);
                }
            }

            return(null);
        }
Exemplo n.º 6
0
        protected virtual MemoryStream CreateAndCompileToStream(string templateSource, RazorEngineCompilationOptions options)
        {
            templateSource = this.WriteDirectives(templateSource, options);

            RazorProjectEngine engine = RazorProjectEngine.Create(
                RazorConfiguration.Default,
                RazorProjectFileSystem.Create(@"."),
                (builder) =>
            {
                builder.SetNamespace(options.TemplateNamespace);
            });

            string fileName = string.IsNullOrWhiteSpace(options.TemplateFilename) ? Path.GetRandomFileName() : options.TemplateFilename;

            RazorSourceDocument document = RazorSourceDocument.Create(templateSource, fileName);

            RazorCodeDocument codeDocument = engine.Process(
                document,
                null,
                new List <RazorSourceDocument>(),
                new List <TagHelperDescriptor>());

            RazorCSharpDocument razorCSharpDocument = codeDocument.GetCSharpDocument();

            SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(razorCSharpDocument.GeneratedCode);

            CSharpCompilation compilation = CSharpCompilation.Create(
                fileName,
                new[]
            {
                syntaxTree
            },
                options.ReferencedAssemblies
                .Select(ass =>
            {
#if NETSTANDARD2_0
                return(MetadataReference.CreateFromFile(ass.Location));
#else
                unsafe
                {
                    ass.TryGetRawMetadata(out byte *blob, out int length);
                    ModuleMetadata moduleMetadata                 = ModuleMetadata.CreateFromMetadata((IntPtr)blob, length);
                    AssemblyMetadata assemblyMetadata             = AssemblyMetadata.Create(moduleMetadata);
                    PortableExecutableReference metadataReference = assemblyMetadata.GetReference();

                    return(metadataReference);
                }
#endif
            })
                .Concat(options.MetadataReferences)
                .ToList(),
                new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));

            MemoryStream memoryStream = new MemoryStream();

            EmitResult emitResult = compilation.Emit(memoryStream);

            if (!emitResult.Success)
            {
                RazorEngineCompilationException exception = new RazorEngineCompilationException()
                {
                    Errors        = emitResult.Diagnostics.ToList(),
                    GeneratedCode = razorCSharpDocument.GeneratedCode
                };

                throw exception;
            }

            memoryStream.Position = 0;

            return(memoryStream);
        }
Exemplo n.º 7
0
 protected override bool IsMatch(RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode)
 {
     return(FileKinds.IsComponent(codeDocument.GetFileKind()));
 }
        protected override void ExecuteCore(
            RazorCodeDocument codeDocument,
            DocumentIntermediateNode documentNode)
        {
            if (!IsComponentDocument(documentNode))
            {
                return;
            }

            if (documentNode.Options.DesignTime)
            {
                // Nothing to do during design time.
                return;
            }

            var findVisitor = new FindHtmlTreeVisitor();

            findVisitor.Visit(documentNode);

            var trees          = findVisitor.Trees;
            var rewriteVisitor = new RewriteVisitor(trees);

            while (trees.Count > 0)
            {
                // Walk backwards since we did a postorder traversal.
                var reference = trees[trees.Count - 1];

                // Forcibly remove a node to prevent infinite loops.
                trees.RemoveAt(trees.Count - 1);

                // We want to fold together siblings where possible. To do this, first we find
                // the index of the node we're looking at now - then we need to walk backwards
                // and identify a set of contiguous nodes we can merge.
                var start = reference.Parent.Children.Count - 1;
                for (; start >= 0; start--)
                {
                    if (ReferenceEquals(reference.Node, reference.Parent.Children[start]))
                    {
                        break;
                    }
                }

                // This is the current node. Check if the left sibling is always a candidate
                // for rewriting. Due to the order we processed the nodes, we know that the
                // left sibling is next in the list to process if it's a candidate.
                var end = start;
                while (start - 1 >= 0)
                {
                    var candidate = reference.Parent.Children[start - 1];
                    if (trees.Count == 0 || !ReferenceEquals(trees[trees.Count - 1].Node, candidate))
                    {
                        // This means the we're out of nodes, or the left sibling is not in the list.
                        break;
                    }

                    // This means that the left sibling is valid to merge.
                    start--;

                    // Remove this since we're combining it.
                    trees.RemoveAt(trees.Count - 1);
                }

                // As a degenerate case, don't bother rewriting an single HtmlContent node
                // It doesn't add any value.
                if (end - start == 0 && reference.Node is HtmlContentIntermediateNode)
                {
                    continue;
                }

                // Now we know the range of nodes to rewrite (end is inclusive)
                var length = end + 1 - start;
                while (length > 0)
                {
                    // Keep using start since we're removing nodes.
                    var node = reference.Parent.Children[start];
                    reference.Parent.Children.RemoveAt(start);

                    rewriteVisitor.Visit(node);

                    length--;
                }

                reference.Parent.Children.Insert(start, new MarkupBlockIntermediateNode()
                {
                    Content = rewriteVisitor.Builder.ToString(),
                });

                rewriteVisitor.Builder.Clear();
            }
        }
 private TagHelperSemanticRangeVisitor(RazorCodeDocument razorCodeDocument, Range?range)
 {
     _semanticRanges    = new List <SemanticRange>();
     _razorCodeDocument = razorCodeDocument;
     _range             = range;
 }
Exemplo n.º 10
0
    private DocumentIntermediateNode Lower(RazorCodeDocument codeDocument)
    {
        var projectEngine = CreateProjectEngine();

        return(Lower(codeDocument, projectEngine));
    }
Exemplo n.º 11
0
 public CompiledCSharpCode(Compilation baseCompilation, RazorCodeDocument codeDocument)
 {
     BaseCompilation = baseCompilation;
     CodeDocument    = codeDocument;
 }
Exemplo n.º 12
0
        static RazorCodeDocument CreateCodeDocument(string rootDir, string sourceFileName)
        {
            string        detectedLayout    = null;
            string        detectedTagName   = null;
            string        detectedModel     = null;
            bool          detectedPage      = false;
            List <string> detectedPageGroup = null;

            using (var ms = new MemoryStream())
            {
                using (var sw = new StreamWriter(ms))
                {
                    var filesToIncludeInOrder = ViewImportsFromRootToSameDir(rootDir, sourceFileName)
                                                .Concat(new[] { sourceFileName });
                    var    sourceLines      = filesToIncludeInOrder.SelectMany(file => File.ReadAllLines(file)).ToList();
                    string lastInheritsLine = null;
                    var    usingNamespaces  = new List <string>();
                    foreach (var line in sourceLines)
                    {
                        const string inheritsLinePrefix = "@inherits ";
                        if (line.StartsWith(inheritsLinePrefix))
                        {
                            lastInheritsLine = line.Substring(inheritsLinePrefix.Length).Replace("<TModel>", string.Empty);
                            continue;
                        }

                        const string layoutLinePrefix = "@layout ";
                        if (line.StartsWith(layoutLinePrefix))
                        {
                            detectedLayout = line.Substring(layoutLinePrefix.Length);
                            continue;
                        }

                        const string usingLinePrefix = "@using ";
                        if (line.StartsWith(usingLinePrefix))
                        {
                            usingNamespaces.Add(line.Substring(usingLinePrefix.Length));
                            continue;
                        }

                        const string modelLinePrefix = "@model ";
                        if (line.StartsWith(modelLinePrefix))
                        {
                            detectedModel = line.Substring(modelLinePrefix.Length);
                            continue;
                        }

                        // The @page directive is only used to generate the page models.
                        // Currently, blazor assumes that all razor components are pages.
                        const string pageLinePrefix = "@page";
                        if (line.StartsWith(pageLinePrefix))
                        {
                            detectedPage = true;
                            var parameters = new Regex("^\\s*{\\s*([^}]+)}\\s*");
                            var matches    = parameters.Matches(line.Substring(pageLinePrefix.Length));
                            if (matches.Count > 0)
                            {
                                detectedPageGroup = new List <string>();
                                foreach (Match match in matches)
                                {
                                    var collection = match.Groups;
                                    detectedPageGroup.Add(collection[0].Value);
                                }
                            }
                            continue;
                        }

                        var tagNameRegex = new Regex("^\\s*\\@TagName\\(\\s*\\\"([^\\\"]+)\\\"\\s*\\)");
                        var tagNameMatch = tagNameRegex.Match(line);
                        if (tagNameMatch.Success)
                        {
                            detectedTagName = tagNameMatch.Groups[1].Value;
                            continue;
                        }

                        sw.WriteLine(line);
                    }
                    sw.Flush();
                    ms.Position = 0;

                    var sourceDoc = RazorSourceDocument.ReadFrom(ms, sourceFileName);
                    var codeDoc   = RazorCodeDocument.Create(sourceDoc);
                    codeDoc.Items["DetectedLayout"]      = detectedLayout;
                    codeDoc.Items["DetectedTagName"]     = detectedTagName;
                    codeDoc.Items["DetectedModel"]       = detectedModel;
                    codeDoc.Items["DetectedBaseClass"]   = lastInheritsLine;
                    codeDoc.Items["UsingNamespaces"]     = usingNamespaces;
                    codeDoc.Items["DetectedPage"]        = detectedPage;
                    codeDoc.Items["DetectedPageMatches"] = detectedPageGroup;

                    return(codeDoc);
                }
            }
        }
Exemplo n.º 13
0
        private static void AddIComponentRazorViewFactoryImplementation(ClassDeclarationIRNode classNode, RazorCodeDocument codeDoc)
        {
            // The Activator.CreateInstance feature that I added to the DNA runtime is very basic and doesn't
            // actually invoke the default constructor of the type being created. It just allocates memory for
            // the instance and returns it, without having run any constructor. This could be confusing if you
            // put constructor logic (such as field initializers) in your Razor page, given that we instantiate
            // it using Activator.CreateInstance.
            // As a workaround (without actually adding constructor support to Activator.CreateInstance, which
            // would be nontrivial), the Razor views privately implement an interface IComponentRazorViewFactory
            // that can return new instances of their own type. We can then just call this with normal .NET code.
            // This means we allocate memory for two instances of the view even though we're only using one,
            // but it's not going to matter much as the first instance will just be released to GC immediately.
            if (classNode.Interfaces == null)
            {
                classNode.Interfaces = new List <string>();
            }
            classNode.Interfaces.Add(typeof(IRazorComponentFactory).FullName);
            var methodStatement = new CSharpStatementIRNode {
                Parent = classNode, Source = null
            };

            classNode.Children.Add(methodStatement);

            var    model    = codeDoc.Items["DetectedModel"];
            string content  = "";
            string isPage   = "";
            string addItems = "";

            if (model != null)
            {
                content = $@"Model = new {model}();";
            }
            if ((bool)codeDoc.Items["DetectedPage"])
            {
                isPage += "classNode.IsPage = true;";
            }

            if ((codeDoc.Items["DetectedPageMatches"]) != null)
            {
                var list = (List <string>)codeDoc.Items["DetectedPageMatches"];
                addItems += "List<string> list = new List<string>();";
                foreach (var item in list)
                {
                    addItems += $"\r\n list.Add(\"{item}\");";
                }
                addItems += "\r\n Items = list;";
            }
            var razorToken = new RazorIRToken
            {
                Kind    = RazorIRToken.TokenKind.CSharp,
                Parent  = classNode,
                Content = $@"
                    {typeof(RazorComponent).FullName} {typeof(IRazorComponentFactory).FullName}.{nameof(IRazorComponentFactory.Instantiate)}()
                    {{
                        {content} 
                        var classNode = new {classNode.Name}();
                        {isPage}
                        {addItems}
                        return classNode;
                    }}"
            };

            methodStatement.Children.Add(razorToken);
        }
Exemplo n.º 14
0
 protected override bool IsMatch(RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode)
 {
     return(ShouldMatch);
 }
Exemplo n.º 15
0
        private static DocumentIntermediateNode CreateIRDocument(RazorProjectEngine projectEngine, RazorCodeDocument codeDocument)
        {
            for (var i = 0; i < projectEngine.Phases.Count; i++)
            {
                var phase = projectEngine.Phases[i];
                phase.Execute(codeDocument);

                if (phase is IRazorIntermediateNodeLoweringPhase)
                {
                    break;
                }
            }

            return(codeDocument.GetDocumentIntermediateNode());
        }
Exemplo n.º 16
0
        protected override void ExecuteCore(RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode)
        {
            if (documentNode.Options == null || documentNode.Options.SuppressMetadataAttributes)
            {
                // Metadata attributes are turned off (or options not populated), nothing to do.
                return;
            }

            // We need to be able to compute the data we need for the [RazorCompiledItem] attribute - that includes
            // a full type name, and a document kind, and optionally an identifier.
            //
            // If we can't use [RazorCompiledItem] then we don't care about the rest of the attributes.
            var @namespace = documentNode.FindPrimaryNamespace();

            if (@namespace == null || string.IsNullOrEmpty(@namespace.Content))
            {
                // No namespace node or it's incomplete. Skip.
                return;
            }

            var @class = documentNode.FindPrimaryClass();

            if (@class == null || string.IsNullOrEmpty(@class.ClassName))
            {
                // No class node or it's incomplete. Skip.
                return;
            }

            if (documentNode.DocumentKind == null)
            {
                // No document kind. Skip.
                return;
            }

            var identifier = codeDocument.GetIdentifier();

            if (identifier == null)
            {
                // No identifier. Skip
                return;
            }

            // [RazorCompiledItem] is an [assembly: ... ] attribute, so it needs to be applied at the global scope.
            documentNode.Children.Insert(0, new RazorCompiledItemAttributeIntermediateNode()
            {
                TypeName   = @namespace.Content + "." + @class.ClassName,
                Kind       = documentNode.DocumentKind,
                Identifier = identifier,
            });

            // Now we need to add a [RazorSourceChecksum] for the source and for each import
            // these are class attributes, so we need to find the insertion point to put them
            // right before the class.
            var insert = (int?)null;

            for (var j = 0; j < @namespace.Children.Count; j++)
            {
                if (object.ReferenceEquals(@namespace.Children[j], @class))
                {
                    insert = j;
                    break;
                }
            }

            if (insert == null)
            {
                // Can't find a place to put the attributes, just bail.
                return;
            }

            // Checksum of the main source
            AddChecksum(codeDocument.Source.GetChecksum(), codeDocument.Source.GetChecksumAlgorithm(), identifier);

            // Now process the checksums of the imports
            //
            // It's possible that the counts of these won't match, just process as many as we can.
            var importIdentifiers = codeDocument.GetImportIdentifiers() ?? Array.Empty <string>();

            for (var i = 0; i < codeDocument.Imports.Count && i < importIdentifiers.Count; i++)
            {
                var import = codeDocument.Imports[i];
                AddChecksum(import.GetChecksum(), import.GetChecksumAlgorithm(), importIdentifiers[i]);
            }

            void AddChecksum(byte[] checksum, string checksumAlgorithm, string id)
            {
                if (checksum == null || checksum.Length == 0 || checksumAlgorithm == null || id == null)
                {
                    // Don't generate anything unless we have all of the required information.
                    return;
                }

                // Checksum of the main source
                @namespace.Children.Insert((int)insert++, new RazorSourceChecksumAttributeIntermediateNode()
                {
                    Checksum          = checksum,
                    ChecksumAlgorithm = checksumAlgorithm,
                    Identifier        = id,
                });
            }
        }
Exemplo n.º 17
0
    private RazorCodeDocument CreateDocument(string content)
    {
        var source = RazorSourceDocument.Create(content, "test.cshtml");

        return(RazorCodeDocument.Create(source));
    }
Exemplo n.º 18
0
 public abstract RazorCSharpDocument WriteDocument(RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode);
Exemplo n.º 19
0
        private static Type CreateGeneratorType(Type _)
        {
            var name = typeof(T).FullName;

            var metadataReferences = GetMetadataReferences();

            RazorSourceDocument document;

            using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream($"{name}.razor"))
            {
                if (stream == null)
                {
                    throw new Exception($"Could not find embedded resource {name}.razor");
                }
                using (var reader = new StreamReader(stream))
                {
                    document = RazorSourceDocument.Create(reader.ReadToEnd(), $"{name}.razor");
                }
            }

#pragma warning disable CS0618 //Create and Register are marked as obsolete but there aren't alternative available
            var engine = RazorEngine.Create(b =>
            {
                FunctionsDirective.Register(b);
                InheritsDirective.Register(b);
#pragma warning restore CS0618
            });
            RazorCodeDocument codeDocument = RazorCodeDocument.Create(document);
            engine.Process(codeDocument);
            string code;
            using (var srcFileWriter = new StringWriter())
            {
                code = codeDocument.GetCSharpDocument().GeneratedCode;
            }

            SourceText sourceText = SourceText.From(code, Encoding.UTF8);
            SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(sourceText, CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.Latest));
            CSharpCompilationOptions compilationOptions = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)
                                                          .WithSpecificDiagnosticOptions(new Dictionary <string, ReportDiagnostic> {
                // Binding redirects
                ["CS1701"] = ReportDiagnostic.Suppress,
                ["CS1702"] = ReportDiagnostic.Suppress,
                ["CS1705"] = ReportDiagnostic.Suppress,
                ["CS8019"] = ReportDiagnostic.Suppress
            })
                                                          .WithUsings("System");
            CSharpCompilation compilation = CSharpCompilation.Create(typeof(T).FullName, new List <SyntaxTree> {
                syntaxTree
            }, metadataReferences, compilationOptions);

            Assembly    assembly;
            EmitOptions emitOptions = new EmitOptions(debugInformationFormat: DebugInformationFormat.PortablePdb);
            using (MemoryStream assemblyStream = new MemoryStream())
            {
                using (MemoryStream pdbStream = new MemoryStream())
                {
                    var emitResult = compilation.Emit(
                        assemblyStream,
                        pdbStream,
                        options: emitOptions);

                    if (!emitResult.Success)
                    {
                        throw new Exception("Compilation error: " + string.Join("; ", emitResult.Diagnostics.Select(d => d.ToString())));
                    }

                    assemblyStream.Seek(0, SeekOrigin.Begin);
                    pdbStream.Seek(0, SeekOrigin.Begin);

                    assembly = Assembly.Load(assemblyStream.ToArray(), pdbStream.ToArray());
                }
            }

            var generatorType = assembly.GetTypes().Where(type => type.IsSubclassOf(typeof(T))).Single();
            return(generatorType);
        }
        public DefaultCodeRenderingContext(
            CodeWriter codeWriter,
            IntermediateNodeWriter nodeWriter,
            RazorCodeDocument codeDocument,
            DocumentIntermediateNode documentNode,
            RazorCodeGenerationOptions options)
        {
            if (codeWriter == null)
            {
                throw new ArgumentNullException(nameof(codeWriter));
            }

            if (nodeWriter == null)
            {
                throw new ArgumentNullException(nameof(nodeWriter));
            }

            if (codeDocument == null)
            {
                throw new ArgumentNullException(nameof(codeDocument));
            }

            if (documentNode == null)
            {
                throw new ArgumentNullException(nameof(documentNode));
            }

            if (options == null)
            {
                throw new ArgumentNullException(nameof(options));
            }

            CodeWriter    = codeWriter;
            _codeDocument = codeDocument;
            _documentNode = documentNode;
            Options       = options;

            _ancestors     = new Stack <IntermediateNode>();
            Diagnostics    = new RazorDiagnosticCollection();
            Items          = new ItemCollection();
            SourceMappings = new List <SourceMapping>();
            LinePragmas    = new List <LinePragma>();

            var diagnostics = _documentNode.GetAllDiagnostics();

            for (var i = 0; i < diagnostics.Count; i++)
            {
                Diagnostics.Add(diagnostics[i]);
            }

            var newLineString = codeDocument.Items[NewLineString];

            if (newLineString != null)
            {
                // Set new line character to a specific string regardless of platform, for testing purposes.
                codeWriter.NewLine = (string)newLineString;
            }

            Items[NewLineString]     = codeDocument.Items[NewLineString];
            Items[SuppressUniqueIds] = codeDocument.Items[SuppressUniqueIds];

            _scopes = new List <ScopeInternal>();
            _scopes.Add(new ScopeInternal(nodeWriter));
        }
Exemplo n.º 21
0
 protected override CodeTarget CreateTarget(RazorCodeDocument codeDocument, RazorCodeGenerationOptions options)
 {
     return(new ComponentCodeTarget(options, TargetExtensions));
 }
Exemplo n.º 22
0
    protected override void ExecuteCore(RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode)
    {
        var visitor = new Visitor();

        visitor.Visit(documentNode);
    }
Exemplo n.º 23
0
            private async Task <(RazorCodeDocument, VersionStamp, VersionStamp)> GetGeneratedOutputAndVersionCoreAsync(DefaultProjectSnapshot project, DocumentSnapshot document)
            {
                // We only need to produce the generated code if any of our inputs is newer than the
                // previously cached output.
                //
                // First find the versions that are the inputs:
                // - The project + computed state
                // - The imports
                // - This document
                //
                // All of these things are cached, so no work is wasted if we do need to generate the code.
                var computedStateVersion = await project.State.GetComputedStateVersionAsync(project).ConfigureAwait(false);

                var documentCollectionVersion = project.State.DocumentCollectionVersion;
                var imports = await GetImportsAsync(project, document).ConfigureAwait(false);

                var documentVersion = await document.GetTextVersionAsync().ConfigureAwait(false);

                // OK now that have the previous output and all of the versions, we can see if anything
                // has changed that would require regenerating the code.
                var inputVersion = documentVersion;

                if (inputVersion.GetNewerVersion(computedStateVersion) == computedStateVersion)
                {
                    inputVersion = computedStateVersion;
                }

                if (inputVersion.GetNewerVersion(documentCollectionVersion) == documentCollectionVersion)
                {
                    inputVersion = documentCollectionVersion;
                }

                for (var i = 0; i < imports.Count; i++)
                {
                    var importVersion = imports[i].Version;
                    if (inputVersion.GetNewerVersion(importVersion) == importVersion)
                    {
                        inputVersion = importVersion;
                    }
                }

                RazorCodeDocument olderOutput = null;
                var olderInputVersion         = default(VersionStamp);
                var olderOutputVersion        = default(VersionStamp);

                if (_older?.TaskUnsafe != null)
                {
                    (olderOutput, olderInputVersion, olderOutputVersion) = await _older.TaskUnsafe.ConfigureAwait(false);

                    if (inputVersion.GetNewerVersion(olderInputVersion) == olderInputVersion)
                    {
                        // Nothing has changed, we can use the cached result.
                        lock (_lock)
                        {
                            TaskUnsafe = _older.TaskUnsafe;
                            _older     = null;
                            return(olderOutput, olderInputVersion, olderOutputVersion);
                        }
                    }
                }

                // OK we have to generate the code.
                var tagHelpers = await project.GetTagHelpersAsync().ConfigureAwait(false);

                var importSources = new List <RazorSourceDocument>();

                foreach (var item in imports)
                {
                    var sourceDocument = await GetRazorSourceDocumentAsync(item.Document).ConfigureAwait(false);

                    importSources.Add(sourceDocument);
                }

                var documentSource = await GetRazorSourceDocumentAsync(document).ConfigureAwait(false);

                var projectEngine = project.GetProjectEngine();

                var codeDocument   = projectEngine.ProcessDesignTime(documentSource, fileKind: document.FileKind, importSources, tagHelpers);
                var csharpDocument = codeDocument.GetCSharpDocument();

                // OK now we've generated the code. Let's check if the output is actually different. This is
                // a valuable optimization for our use cases because lots of changes you could make require
                // us to run code generation, but don't change the result.
                //
                // Note that we're talking about the effect on the generated C# code here (not the other artifacts).
                // This is the reason why we have two versions associated with the output.
                //
                // The INPUT version is related the .cshtml files and tag helpers
                // The OUTPUT version is related to the generated C#.
                //
                // Examples:
                //
                // A change to a tag helper not used by this document - updates the INPUT version, but not
                // the OUTPUT version.
                //
                // A change in the HTML - updates the INPUT version, but not the OUTPUT version.
                //
                //
                // Razor IDE features should always retrieve the output and party on it regardless. Depending
                // on the use cases we may or may not need to synchronize the output.

                var outputVersion = inputVersion;

                if (olderOutput != null)
                {
                    if (string.Equals(
                            olderOutput.GetCSharpDocument().GeneratedCode,
                            csharpDocument.GeneratedCode,
                            StringComparison.Ordinal))
                    {
                        outputVersion = olderOutputVersion;
                    }
                }

                if (document is DefaultDocumentSnapshot defaultDocument)
                {
                    defaultDocument.State.HostDocument.GeneratedCodeContainer.SetOutput(
                        defaultDocument,
                        csharpDocument,
                        inputVersion,
                        outputVersion);
                }

                return(codeDocument, inputVersion, outputVersion);
            }
Exemplo n.º 24
0
        public static FormattingContext Create(
            DocumentUri uri,
            DocumentSnapshot originalSnapshot,
            RazorCodeDocument codeDocument,
            FormattingOptions options,
            Range range         = null,
            bool isFormatOnType = false)
        {
            if (uri is null)
            {
                throw new ArgumentNullException(nameof(uri));
            }

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

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

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

            var text = codeDocument.GetSourceText();

            range ??= TextSpan.FromBounds(0, text.Length).AsRange(text);

            var result = new FormattingContext()
            {
                Uri = uri,
                OriginalSnapshot = originalSnapshot,
                CodeDocument     = codeDocument,
                Range            = range,
                Options          = options,
                IsFormatOnType   = isFormatOnType
            };

            var source          = codeDocument.Source;
            var syntaxTree      = codeDocument.GetSyntaxTree();
            var formattingSpans = syntaxTree.GetFormattingSpans();
            var indentations    = new Dictionary <int, IndentationContext>();

            var total = 0;
            var previousIndentationLevel = 0;

            for (var i = 0; i < source.Lines.Count; i++)
            {
                // Get first non-whitespace character position
                var lineLength = source.Lines.GetLineLength(i);
                var nonWsChar  = 0;
                for (var j = 0; j < lineLength; j++)
                {
                    var ch = source[total + j];
                    if (!char.IsWhiteSpace(ch) && !ParserHelpers.IsNewLine(ch))
                    {
                        nonWsChar = j;
                        break;
                    }
                }

                // position now contains the first non-whitespace character or 0. Get the corresponding FormattingSpan.
                if (TryGetFormattingSpan(total + nonWsChar, formattingSpans, out var span))
                {
                    indentations[i] = new IndentationContext
                    {
                        Line                     = i,
                        IndentationLevel         = span.IndentationLevel,
                        RelativeIndentationLevel = span.IndentationLevel - previousIndentationLevel,
                        ExistingIndentation      = nonWsChar,
                        FirstSpan                = span,
                    };
                    previousIndentationLevel = span.IndentationLevel;
                }
                else
                {
                    // Couldn't find a corresponding FormattingSpan. Happens if it is a 0 length line.
                    // Let's create a 0 length span to represent this and default it to HTML.
                    var placeholderSpan = new FormattingSpan(
                        new Language.Syntax.TextSpan(total + nonWsChar, 0),
                        new Language.Syntax.TextSpan(total + nonWsChar, 0),
                        FormattingSpanKind.Markup,
                        FormattingBlockKind.Markup,
                        indentationLevel: 0,
                        isInClassBody: false);

                    indentations[i] = new IndentationContext
                    {
                        Line                     = i,
                        IndentationLevel         = 0,
                        RelativeIndentationLevel = previousIndentationLevel,
                        ExistingIndentation      = nonWsChar,
                        FirstSpan                = placeholderSpan,
                    };
                }

                total += lineLength;
            }

            result.Indentations = indentations;

            return(result);
        }
Exemplo n.º 25
0
 public void Process(RazorCodeDocument document)
 => _engine.Process(document);
        public override Task <TextEdit[]> FormatOnTypeAsync(Uri uri, RazorCodeDocument codeDocument, Position position, string character, FormattingOptions options)
        {
            if (uri is null)
            {
                throw new ArgumentNullException(nameof(uri));
            }

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

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

            if (string.IsNullOrEmpty(character))
            {
                throw new ArgumentNullException(nameof(character));
            }

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

            if (!_optionsMonitor.CurrentValue.AutoClosingTags || character != ">")
            {
                // We currently only support auto-closing tags our onType formatter.
                return(Task.FromResult(EmptyArray));
            }

            bool addCursorPlaceholder;

            if (options.TryGetValue(LanguageServerConstants.ExpectsCursorPlaceholderKey, out var value) && value.IsBool)
            {
                addCursorPlaceholder = value.Bool;
            }
            else
            {
                // Temporary:
                // no-op if cursor placeholder isn't supported. This means the request isn't coming from VS.
                // Can remove this once VSCode starts using this endpoint for auto closing <text> tags.
                return(Task.FromResult(EmptyArray));
            }

            var formattingContext = CreateFormattingContext(uri, codeDocument, new Range(position, position), options);

            if (IsAtTextTag(formattingContext, position))
            {
                // This is a text tag.
                var cursorPlaceholder = addCursorPlaceholder ? LanguageServerConstants.CursorPlaceholderString : string.Empty;
                var edit = new TextEdit()
                {
                    NewText = $"{cursorPlaceholder}</{SyntaxConstants.TextTagName}>",
                    Range   = new Range(position, position)
                };
                return(Task.FromResult(new[] { edit }));
            }

            return(Task.FromResult(EmptyArray));
        }
        private static FormattingContext CreateFormattingContext(Uri uri, RazorCodeDocument codedocument, Range range, FormattingOptions options)
        {
            var result = new FormattingContext()
            {
                Uri          = uri,
                CodeDocument = codedocument,
                Range        = range,
                Options      = options
            };

            var source          = codedocument.Source;
            var syntaxTree      = codedocument.GetSyntaxTree();
            var formattingSpans = syntaxTree.GetFormattingSpans();

            var total = 0;
            var previousIndentationLevel = 0;

            for (var i = 0; i < source.Lines.Count; i++)
            {
                // Get first non-whitespace character position
                var lineLength = source.Lines.GetLineLength(i);
                var nonWsChar  = 0;
                for (var j = 0; j < lineLength; j++)
                {
                    var ch = source[total + j];
                    if (!char.IsWhiteSpace(ch) && !ParserHelpers.IsNewLine(ch))
                    {
                        nonWsChar = j;
                        break;
                    }
                }

                // position now contains the first non-whitespace character or 0. Get the corresponding FormattingSpan.
                if (TryGetFormattingSpan(total + nonWsChar, formattingSpans, out var span))
                {
                    result.Indentations[i] = new IndentationContext
                    {
                        Line                     = i,
                        IndentationLevel         = span.IndentationLevel,
                        RelativeIndentationLevel = span.IndentationLevel - previousIndentationLevel,
                        ExistingIndentation      = nonWsChar,
                        FirstSpan                = span,
                    };
                    previousIndentationLevel = span.IndentationLevel;
                }
                else
                {
                    // Couldn't find a corresponding FormattingSpan.
                    result.Indentations[i] = new IndentationContext
                    {
                        Line                     = i,
                        IndentationLevel         = -1,
                        RelativeIndentationLevel = previousIndentationLevel,
                        ExistingIndentation      = nonWsChar,
                    };
                }

                total += lineLength;
            }

            return(result);
        }
Exemplo n.º 28
0
 public abstract bool TryGetFromDocument(TextDocument document, out RazorCodeDocument codeDocument);
 protected override bool IsMatch(RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode)
 {
     return(PageDirective.TryGetPageDirective(documentNode, out var pageDirective));
 }
 public abstract bool TryGetGeneratedOutput(out RazorCodeDocument result);