コード例 #1
0
        /// <summary>
        /// Parses the template specified by <paramref name="codeDocument"/>.
        /// </summary>
        /// <param name="codeDocument">The <see cref="RazorProjectItem"/>.</param>
        /// <returns>The <see cref="RazorCSharpDocument"/>.</returns>
        public virtual RazorCSharpDocument GenerateCode(RazorCodeDocument codeDocument)
        {
            if (codeDocument == null)
            {
                throw new ArgumentNullException(nameof(codeDocument));
            }

            Engine.Process(codeDocument);
            return(codeDocument.GetCSharpDocument());
        }
コード例 #2
0
        private 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 = Path.GetRandomFileName();

            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 => MetadataReference.CreateFromFile(ass.Location))
                .ToList(),
                new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));

            MemoryStream memoryStream = new MemoryStream();

            EmitResult emitResult = compilation.Emit(memoryStream);

            if (!emitResult.Success)
            {
                List <Diagnostic> errors = emitResult.Diagnostics.ToList();

                RazorEngineCompilationException exception = new RazorEngineCompilationException($"Unable to compile template: {errors?.FirstOrDefault()}");
                exception.Errors = errors;

                throw exception;
            }

            memoryStream.Position = 0;

            return(memoryStream);
        }
コード例 #3
0
        private TemplateFactoryResult CompilationError(RazorCodeDocument document, CompilationResult result)
        {
            var csdocs = document.GetCSharpDocument();

            return(TemplateFactoryResult.Error(new StringValues(new string[]
            {
                document.Source.FilePath + ".error",
                string.Join(
                    Environment.NewLine,
                    result.Messages),
                csdocs.GeneratedCode
            }).ToString()));
        }
コード例 #4
0
        private TemplateFactoryResult GenerateError(RazorCodeDocument document)
        {
            var csdocs = document.GetCSharpDocument();

            return(TemplateFactoryResult.Error(new StringValues(new string[]
            {
                document.Source.FilePath + ".error",
                string.Join(
                    Environment.NewLine,
                    csdocs.Diagnostics.Select(d => d.GetMessage())),
                csdocs.GeneratedCode
            }).ToString()));
        }
        // Internal for testing only
        internal static bool TryGetMinimalCSharpRange(RazorCodeDocument codeDocument, Range razorRange, [NotNullWhen(true)] out Range?csharpRange)
        {
            SourceSpan?minGeneratedSpan = null;
            SourceSpan?maxGeneratedSpan = null;

            var sourceText = codeDocument.GetSourceText();
            var textSpan   = razorRange.AsTextSpan(sourceText);
            var csharpDoc  = codeDocument.GetCSharpDocument();

            // We want to find the min and max C# source mapping that corresponds with our Razor range.
            foreach (var mapping in csharpDoc.SourceMappings)
            {
                var mappedTextSpan = mapping.OriginalSpan.AsTextSpan();

                if (textSpan.OverlapsWith(mappedTextSpan))
                {
                    if (minGeneratedSpan is null || mapping.GeneratedSpan.AbsoluteIndex < minGeneratedSpan.Value.AbsoluteIndex)
                    {
                        minGeneratedSpan = mapping.GeneratedSpan;
                    }

                    var mappingEndIndex = mapping.GeneratedSpan.AbsoluteIndex + mapping.GeneratedSpan.Length;
                    if (maxGeneratedSpan is null || mappingEndIndex > maxGeneratedSpan.Value.AbsoluteIndex + maxGeneratedSpan.Value.Length)
                    {
                        maxGeneratedSpan = mapping.GeneratedSpan;
                    }
                }
            }

            // Create a new projected range based on our calculated min/max source spans.
            if (minGeneratedSpan is not null && maxGeneratedSpan is not null)
            {
                var csharpSourceText = codeDocument.GetCSharpSourceText();
                var startRange       = minGeneratedSpan.Value.AsTextSpan().AsRange(csharpSourceText);
                var endRange         = maxGeneratedSpan.Value.AsTextSpan().AsRange(csharpSourceText);

                csharpRange = new Range {
                    Start = startRange.Start, End = endRange.End
                };
                Debug.Assert(csharpRange.Start <= csharpRange.End, "Range.Start should not be larger than Range.End");

                return(true);
            }

            csharpRange = null;
            return(false);
        }
コード例 #6
0
            static IEnumerable<ITagSpan<SourceMappingTag>> GetTagsWorker(RazorCodeDocument codeDocument, ITextSnapshot snapshot)
            {
                var csharpDocument = codeDocument.GetCSharpDocument();
                foreach (var mapping in csharpDocument.SourceMappings)
                {
                    var generatedText = GetGeneratedCodeSnippet(csharpDocument.GeneratedCode, mapping.GeneratedSpan.AbsoluteIndex);

                    var position = Math.Min(mapping.OriginalSpan.AbsoluteIndex, snapshot.Length);
                    var point = new SnapshotPoint(snapshot, position);
                    var tag = new SourceMappingTag(isStart: true, generatedText);
                    var span = new SnapshotSpan(point, 0);
                    yield return new TagSpan<SourceMappingTag>(span, tag);

                    position = Math.Min(mapping.OriginalSpan.AbsoluteIndex + mapping.OriginalSpan.Length, snapshot.Length);
                    point = new SnapshotPoint(snapshot, position);
                    tag = new SourceMappingTag(isStart: false);
                    span = new SnapshotSpan(point, 0);
                    yield return new TagSpan<SourceMappingTag>(span, tag);
                }
コード例 #7
0
        public static SourceText GetCSharpSourceText(this RazorCodeDocument document)
        {
            if (document == null)
            {
                throw new ArgumentNullException(nameof(document));
            }

            var sourceTextObj = document.Items[CSharpSourceTextKey];

            if (sourceTextObj == null)
            {
                var csharpDocument = document.GetCSharpDocument();
                var sourceText     = SourceText.From(csharpDocument.GeneratedCode);
                document.Items[CSharpSourceTextKey] = sourceText;

                return(sourceText);
            }

            return((SourceText)sourceTextObj);
        }
コード例 #8
0
        private static async Task <TextEdit[]> GetFormattedCSharpEditsAsync(
            RazorCodeDocument codeDocument,
            char typedChar,
            int position,
            bool insertSpaces,
            int tabSize)
        {
            var generatedCode    = codeDocument.GetCSharpDocument().GeneratedCode;
            var csharpSourceText = SourceText.From(generatedCode);
            var document         = GenerateRoslynCSharpDocument(csharpSourceText);
            var documentOptions  = await GetDocumentOptionsAsync(document, insertSpaces, tabSize);

            var formattingChanges = await RazorCSharpFormattingInteractionService.GetFormattingChangesAsync(
                document, typedChar, position, documentOptions, CancellationToken.None).ConfigureAwait(false);

            var textEdits = formattingChanges.Select(change => change.AsTextEdit(csharpSourceText)).ToArray();

            return(textEdits);

            Document GenerateRoslynCSharpDocument(SourceText csharpSourceText)
            {
                var workspace = TestWorkspace.Create();
                var project   = workspace.CurrentSolution.AddProject("TestProject", "TestAssembly", LanguageNames.CSharp);
                var document  = project.AddDocument("TestDocument", csharpSourceText);

                return(document);
            }

            async Task <DocumentOptionSet> GetDocumentOptionsAsync(Document document, bool insertSpaces, int tabSize)
            {
                var documentOptions = await document.GetOptionsAsync().ConfigureAwait(false);

                documentOptions = documentOptions.WithChangedOption(CodeAnalysis.Formatting.FormattingOptions.TabSize, tabSize)
                                  .WithChangedOption(CodeAnalysis.Formatting.FormattingOptions.IndentationSize, tabSize)
                                  .WithChangedOption(CodeAnalysis.Formatting.FormattingOptions.UseTabs, !insertSpaces);
                return(documentOptions);
            }
        }
コード例 #9
0
        public bool TrySetOutput(
            DefaultDocumentSnapshot document,
            RazorCodeDocument codeDocument,
            VersionStamp inputVersion,
            VersionStamp outputCSharpVersion,
            VersionStamp outputHtmlVersion)
        {
            lock (_setOutputLock)
            {
                if (_inputVersion.HasValue &&
                    _inputVersion.Value != inputVersion &&
                    _inputVersion == _inputVersion.Value.GetNewerVersion(inputVersion))
                {
                    // Latest document is newer than the provided document.
                    return(false);
                }

                if (!document.TryGetText(out var source))
                {
                    Debug.Fail("The text should have already been evaluated.");
                    return(false);
                }

                _source              = source;
                _inputVersion        = inputVersion;
                _outputCSharpVersion = outputCSharpVersion;
                _outputHtmlVersion   = outputHtmlVersion;
                _outputCSharp        = codeDocument.GetCSharpDocument();
                _outputHtml          = codeDocument.GetHtmlDocument();
                _latestDocument      = document;
                var csharpSourceText = codeDocument.GetCSharpSourceText();
                _csharpTextContainer.SetText(csharpSourceText);
                var htmlSourceText = codeDocument.GetHtmlSourceText();
                _htmlTextContainer.SetText(htmlSourceText);

                return(true);
            }
        }
コード例 #10
0
        private void AssertDocumentCompiles(
            RazorCodeDocument document,
            IEnumerable <MetadataReference> compilationReferences,
            IEnumerable <string> expectedErrors = null)
        {
            var cSharp = document.GetCSharpDocument().GeneratedCode;

            var syntaxTree  = CSharpSyntaxTree.ParseText(cSharp);
            var options     = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary);
            var compilation = CSharpCompilation.Create("CodeGenerationTestAssembly", new[] { syntaxTree }, compilationReferences, options);

            var diagnostics = compilation.GetDiagnostics();

            var errors = diagnostics.Where(d => d.Severity >= DiagnosticSeverity.Warning);

            if (expectedErrors == null)
            {
                Assert.Empty(errors.Select(e => e.GetMessage()));
            }
            else
            {
                Assert.Equal(expectedErrors, errors.Select(e => e.GetMessage()));
            }
        }
        public override bool TryMapFromProjectedDocumentRange(RazorCodeDocument codeDocument, Range projectedRange, out Range originalRange)
        {
            if (codeDocument is null)
            {
                throw new ArgumentNullException(nameof(codeDocument));
            }

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

            originalRange = default;

            var csharpSourceText = SourceText.From(codeDocument.GetCSharpDocument().GeneratedCode);
            var range            = projectedRange;
            var startIndex       = range.Start.GetAbsoluteIndex(csharpSourceText);

            if (!TryMapFromProjectedDocumentPosition(codeDocument, startIndex, out var hostDocumentStart, out var _))
            {
                return(false);
            }

            var endIndex = range.End.GetAbsoluteIndex(csharpSourceText);

            if (!TryMapFromProjectedDocumentPosition(codeDocument, endIndex, out var hostDocumentEnd, out var _))
            {
                return(false);
            }

            originalRange = new Range(
                hostDocumentStart,
                hostDocumentEnd);

            return(true);
        }
コード例 #12
0
        private async Task <ICollection <RazorToCSharpModel> > ConvertRazorToCSharp(ICollection <ProjectFile> codeFiles)
        {
            // The first phase won't include any metadata references for component discovery. This mirrors what the build does.
            Console.WriteLine($"ConvertRazorToCSharp: {string.Join("\r\n", codeFiles.Select(x => x.Name))}");
            var projectEngine = CreateRazorProjectEngine(Array.Empty <MetadataReference>());
            var declarations  = new List <RazorToCSharpModel>();

            foreach (var codeFile in codeFiles.Where(x => x.Name.EndsWith(".razor")))
            {
                RazorProjectItem projectItem = CreateRazorProjectItem(codeFile.Name, codeFile.Content);

                RazorCodeDocument   codeDocument   = projectEngine.ProcessDeclarationOnly(projectItem);
                RazorCSharpDocument cSharpDocument = codeDocument.GetCSharpDocument();

                declarations.Add(new RazorToCSharpModel
                {
                    ProjectItem = projectItem,
                    Code        = cSharpDocument.GeneratedCode,
                    Diagnostics = cSharpDocument.Diagnostics.Select(diagnostic => new CustomDiag(diagnostic)),
                });
            }
            //ToDo Likely Solution
            foreach (var codeFile in codeFiles.Where(x => x.Name.EndsWith(".cs")))
            {
                declarations.Add(new RazorToCSharpModel
                {
                    Code        = codeFile.Content,
                    Diagnostics = new List <CustomDiag>()
                });
            }
            // Get initial core assembly
            var tempAssembly = GetCodeAssembly(declarations);

            if (tempAssembly.Diagnostics.Any(d => d.Severity == DiagnosticSeverity.Error))
            {
                return(new[] { new RazorToCSharpModel {
                                   Diagnostics = tempAssembly.Diagnostics
                               } });
            }
            // add new assemblies to project refs
            var references = new List <MetadataReference>(_baseCompilation.References)
            {
                tempAssembly.Compilation.ToMetadataReference()
            };

            projectEngine = this.CreateRazorProjectEngine(references);
            var results = new List <RazorToCSharpModel>();

            // Iterates through c# code docs and converts to members of generated Razor project
            foreach (var declaration in declarations.Where(x => x.ProjectItem != null))
            {
                var codeDocument   = projectEngine.Process(declaration.ProjectItem);
                var cSharpDocument = codeDocument.GetCSharpDocument();

                results.Add(new RazorToCSharpModel
                {
                    ProjectItem = declaration.ProjectItem,
                    Code        = cSharpDocument.GeneratedCode,
                    Diagnostics = cSharpDocument.Diagnostics.Select(x => new CustomDiag(x))
                });
            }

            foreach (var declaration in declarations.Where(x => x.ProjectItem == null))
            {
                results.Add(new RazorToCSharpModel
                {
                    Code        = declaration.Code,
                    Diagnostics = new List <CustomDiag>()
                });
            }
            return(results);
        }
コード例 #13
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);
        }
コード例 #14
0
        private bool TryMapFromProjectedDocumentRangeInclusive(RazorCodeDocument codeDocument, Range projectedRange, out Range originalRange)
        {
            originalRange = default;

            var csharpDoc            = codeDocument.GetCSharpDocument();
            var csharpSourceText     = SourceText.From(csharpDoc.GeneratedCode);
            var projectedRangeAsSpan = projectedRange.AsTextSpan(csharpSourceText);
            var range               = projectedRange;
            var startIndex          = projectedRangeAsSpan.Start;
            var startMappedDirectly = TryMapFromProjectedDocumentPosition(codeDocument, startIndex, out var hostDocumentStart, out _);

            var endIndex          = projectedRangeAsSpan.End;
            var endMappedDirectly = TryMapFromProjectedDocumentPosition(codeDocument, endIndex, out var hostDocumentEnd, out _);

            if (startMappedDirectly && endMappedDirectly)
            {
                // We strictly mapped the start/end of the projected range.
                originalRange = new Range(hostDocumentStart, hostDocumentEnd);
                return(true);
            }

            List <SourceMapping> candidateMappings;

            if (startMappedDirectly)
            {
                // Start of projected range intersects with a mapping
                candidateMappings = csharpDoc.SourceMappings.Where(mapping => IntersectsWith(startIndex, mapping.GeneratedSpan)).ToList();
            }
            else if (endMappedDirectly)
            {
                // End of projected range intersects with a mapping
                candidateMappings = csharpDoc.SourceMappings.Where(mapping => IntersectsWith(endIndex, mapping.GeneratedSpan)).ToList();
            }
            else
            {
                // Our range does not intersect with any mapping; we should see if it overlaps generated locations
                candidateMappings = csharpDoc.SourceMappings.Where(mapping => Overlaps(projectedRangeAsSpan, mapping.GeneratedSpan)).ToList();
            }

            if (candidateMappings.Count == 1)
            {
                // We're intersecting or overlapping a single mapping, lets choose that.

                var mapping = candidateMappings[0];
                originalRange = ConvertMapping(codeDocument.Source, mapping);
                return(true);
            }
            else
            {
                // More then 1 or exactly 0 intersecting/overlapping mappings
                return(false);
            }

            bool Overlaps(TextSpan projectedRangeAsSpan, SourceSpan span)
            {
                var overlapStart = Math.Max(projectedRangeAsSpan.Start, span.AbsoluteIndex);
                var overlapEnd   = Math.Min(projectedRangeAsSpan.End, span.AbsoluteIndex + span.Length);

                return(overlapStart < overlapEnd);
            }

            bool IntersectsWith(int position, SourceSpan span)
            {
                return(unchecked ((uint)(position - span.AbsoluteIndex) <= (uint)span.Length));
            }
コード例 #15
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);
        }
コード例 #16
0
            private async Task <(RazorCodeDocument, VersionStamp, 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 configurationVersion         = project.State.ConfigurationVersion;
                var projectWorkspaceStateVersion = project.State.ProjectWorkspaceStateVersion;
                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(configurationVersion) == configurationVersion)
                {
                    inputVersion = configurationVersion;
                }

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

                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 olderCSharpOutputVersion  = default(VersionStamp);
                var olderHtmlOutputVersion    = default(VersionStamp);

                if (_older?.TaskUnsafeReference != null &&
                    _older.TaskUnsafeReference.TryGetTarget(out var taskUnsafe))
                {
                    (olderOutput, olderInputVersion, olderCSharpOutputVersion, olderHtmlOutputVersion) = await taskUnsafe.ConfigureAwait(false);

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

                // OK we have to generate the code.
                var importSources = new List <RazorSourceDocument>();
                var projectEngine = project.GetProjectEngine();

                foreach (var item in imports)
                {
                    var importProjectItem = item.FilePath == null ? null : projectEngine.FileSystem.GetItem(item.FilePath, item.FileKind);
                    var sourceDocument    = await GetRazorSourceDocumentAsync(item.Document, importProjectItem).ConfigureAwait(false);

                    importSources.Add(sourceDocument);
                }

                var projectItem    = document.FilePath == null ? null : projectEngine.FileSystem.GetItem(document.FilePath, document.FileKind);
                var documentSource = await GetRazorSourceDocumentAsync(document, projectItem).ConfigureAwait(false);


                var codeDocument   = projectEngine.ProcessDesignTime(documentSource, fileKind: document.FileKind, importSources, project.TagHelpers);
                var csharpDocument = codeDocument.GetCSharpDocument();
                var htmlDocument   = codeDocument.GetHtmlDocument();

                // 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#/HTML here (not the other artifacts).
                // This is the reason why we have three versions associated with the document.
                //
                // The INPUT version is related the .cshtml files and tag helpers
                // The CSHARPOUTPUT version is related to the generated C#
                // The HTMLOUTPUT version is related to the generated HTML
                //
                // Examples:
                //
                // A change to a tag helper not used by this document - 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 outputCSharpVersion = inputVersion;
                var outputHtmlVersion   = inputVersion;

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

                    if (string.Equals(
                            olderOutput.GetHtmlDocument().GeneratedHtml,
                            htmlDocument.GeneratedHtml,
                            StringComparison.Ordinal))
                    {
                        outputHtmlVersion = olderHtmlOutputVersion;
                    }
                }

                if (document is DefaultDocumentSnapshot defaultDocument)
                {
                    defaultDocument.State.HostDocument.GeneratedDocumentContainer.SetOutput(
                        defaultDocument,
                        csharpDocument,
                        htmlDocument,
                        inputVersion,
                        outputCSharpVersion,
                        outputHtmlVersion);
                }

                return(codeDocument, inputVersion, outputCSharpVersion, outputHtmlVersion);
            }
コード例 #17
0
        protected void AssertSourceMappingsMatchBaseline(RazorCodeDocument codeDocument)
        {
            if (FileName == null)
            {
                var message = $"{nameof(AssertSourceMappingsMatchBaseline)} should only be called from an integration test ({nameof(FileName)} is null).";
                throw new InvalidOperationException(message);
            }

            var csharpDocument = codeDocument.GetCSharpDocument();

            Assert.NotNull(csharpDocument);

            var baselineFileName   = Path.ChangeExtension(FileName, ".mappings.txt");
            var serializedMappings = SourceMappingsSerializer.Serialize(csharpDocument, codeDocument.Source);

            if (GenerateBaselines)
            {
                var baselineFullPath = Path.Combine(TestProjectRoot, baselineFileName);
                File.WriteAllText(baselineFullPath, serializedMappings);
                return;
            }

            var testFile = TestFile.Create(baselineFileName, GetType().GetTypeInfo().Assembly);

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

            var baseline = testFile.ReadAllText();

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

            Assert.Equal(baseline, actualBaseline);

            var syntaxTree = codeDocument.GetSyntaxTree();
            var visitor    = new CodeSpanVisitor();

            visitor.Visit(syntaxTree.Root);

            var charBuffer = new char[codeDocument.Source.Length];

            codeDocument.Source.CopyTo(0, charBuffer, 0, codeDocument.Source.Length);
            var sourceContent = new string(charBuffer);

            var spans = visitor.CodeSpans;

            for (var i = 0; i < spans.Count; i++)
            {
                var span       = spans[i];
                var sourceSpan = span.GetSourceSpan(codeDocument.Source);
                if (sourceSpan == null)
                {
                    // Not in the main file, skip.
                    continue;
                }

                var expectedSpan = sourceContent.Substring(sourceSpan.AbsoluteIndex, sourceSpan.Length);

                // See #2593
                if (string.IsNullOrWhiteSpace(expectedSpan))
                {
                    // For now we don't verify whitespace inside of a directive. We know that directives cheat
                    // with how they bound whitespace/C#/markup to make completion work.
                    if (span.FirstAncestorOrSelf <RazorDirectiveSyntax>() != null)
                    {
                        continue;
                    }
                }

                // See #2594
                if (string.Equals("@", expectedSpan))
                {
                    // For now we don't verify an escaped transition. In some cases one of the @ tokens in @@foo
                    // will be mapped as C# but will not be present in the output buffer because it's not actually C#.
                    continue;
                }

                var found = false;
                for (var j = 0; j < csharpDocument.SourceMappings.Count; j++)
                {
                    var mapping = csharpDocument.SourceMappings[j];
                    if (mapping.OriginalSpan == sourceSpan)
                    {
                        var actualSpan = csharpDocument.GeneratedCode.Substring(
                            mapping.GeneratedSpan.AbsoluteIndex,
                            mapping.GeneratedSpan.Length);

                        if (!string.Equals(expectedSpan, actualSpan, StringComparison.Ordinal))
                        {
                            throw new XunitException(
                                      $"Found the span {sourceSpan} in the output mappings but it contains " +
                                      $"'{EscapeWhitespace(actualSpan)}' instead of '{EscapeWhitespace(expectedSpan)}'.");
                        }

                        found = true;
                        break;
                    }
                }

                if (!found)
                {
                    throw new XunitException(
                              $"Could not find the span {sourceSpan} - containing '{EscapeWhitespace(expectedSpan)}' " +
                              $"in the output.");
                }
            }
        }
コード例 #18
0
        protected void AssertLinePragmas(RazorCodeDocument codeDocument, bool designTime)
        {
            if (FileName == null)
            {
                var message = $"{nameof(AssertSourceMappingsMatchBaseline)} should only be called from an integration test. ({nameof(FileName)} is null).";
                throw new InvalidOperationException(message);
            }

            var csharpDocument = codeDocument.GetCSharpDocument();

            Assert.NotNull(csharpDocument);
            var linePragmas = csharpDocument.LinePragmas;

            designTime = false;
            if (designTime)
            {
                var sourceMappings = csharpDocument.SourceMappings;
                foreach (var sourceMapping in sourceMappings)
                {
                    var foundMatchingPragma = false;
                    foreach (var linePragma in linePragmas)
                    {
                        if (sourceMapping.OriginalSpan.LineIndex >= linePragma.StartLineIndex &&
                            sourceMapping.OriginalSpan.LineIndex <= linePragma.EndLineIndex)
                        {
                            // Found a match.
                            foundMatchingPragma = true;
                            break;
                        }
                    }

                    Assert.True(foundMatchingPragma, $"No line pragma found for code at line {sourceMapping.OriginalSpan.LineIndex + 1}.");
                }
            }
            else
            {
                var syntaxTree   = codeDocument.GetSyntaxTree();
                var sourceBuffer = new char[syntaxTree.Source.Length];
                syntaxTree.Source.CopyTo(0, sourceBuffer, 0, syntaxTree.Source.Length);
                var sourceContent   = new string(sourceBuffer);
                var classifiedSpans = syntaxTree.GetClassifiedSpans();
                foreach (var classifiedSpan in classifiedSpans)
                {
                    var content = sourceContent.Substring(classifiedSpan.Span.AbsoluteIndex, classifiedSpan.Span.Length);
                    if (!string.IsNullOrWhiteSpace(content) &&
                        classifiedSpan.BlockKind != BlockKindInternal.Directive &&
                        classifiedSpan.SpanKind == SpanKindInternal.Code)
                    {
                        var foundMatchingPragma = false;
                        foreach (var linePragma in linePragmas)
                        {
                            if (classifiedSpan.Span.LineIndex >= linePragma.StartLineIndex &&
                                classifiedSpan.Span.LineIndex <= linePragma.EndLineIndex)
                            {
                                // Found a match.
                                foundMatchingPragma = true;
                                break;
                            }
                        }

                        Assert.True(foundMatchingPragma, $"No line pragma found for code '{content}' at line {classifiedSpan.Span.LineIndex + 1}.");
                    }
                }
            }
        }
コード例 #19
0
        private 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 = Path.GetRandomFileName();

            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);
        }
コード例 #20
0
        private MemoryStream CreateAndCompileToStream(string templateSource, params Assembly[] linkedAssemblies)
        {
            RazorProjectEngine engine = RazorProjectEngine.Create(
                RazorConfiguration.Default,
                RazorProjectFileSystem.Create(@"."),
                (builder) =>
            {
                builder.SetNamespace("TemplateNamespace");
            });

            string fileName = Path.GetRandomFileName();

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

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

            RazorCSharpDocument razorCSharpDocument = codeDocument.GetCSharpDocument();

            List <PortableExecutableReference> portableExecutableReferences = new List <PortableExecutableReference>
            {
                MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
                MetadataReference.CreateFromFile(Assembly.Load(new AssemblyName("Microsoft.CSharp")).Location),
                MetadataReference.CreateFromFile(typeof(RazorEngineTemplateBase).Assembly.Location),
                MetadataReference.CreateFromFile(typeof(ExpandoObject).Assembly.Location),
                MetadataReference.CreateFromFile(Assembly.Load(new AssemblyName("netstandard")).Location),
                MetadataReference.CreateFromFile(Assembly.Load(new AssemblyName("System.Runtime")).Location),
            };

            foreach (Assembly assembly in linkedAssemblies)
            {
                portableExecutableReferences.Add(MetadataReference.CreateFromFile(assembly.Location));
            }

            CSharpCompilation compilation = CSharpCompilation.Create(
                fileName,
                new[]
            {
                CSharpSyntaxTree.ParseText(razorCSharpDocument.GeneratedCode)
            },
                portableExecutableReferences,
                new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));

            MemoryStream memoryStream = new MemoryStream();

            EmitResult emitResult = compilation.Emit(memoryStream);

            if (!emitResult.Success)
            {
                List <Diagnostic> errors = emitResult.Diagnostics.ToList();

                RazorEngineCompilationException exception = new RazorEngineCompilationException("Unable to compile template: " + errors.FirstOrDefault()?.ToString());
                exception.Errors = errors;

                throw exception;
            }

            memoryStream.Position = 0;
            return(memoryStream);
        }
コード例 #21
0
        /// <summary>
        /// Getting the generated code through Microsoft.AspNetCore.Razor.Language
        /// </summary>
        /// <param name="dynamicTemplateNamespace"></param>
        /// <param name="templateSourceCode"></param>
        /// <param name="generatedCSharpClassName"></param>
        /// <param name="classBaseType"></param>
        /// <param name="templateFile"></param>
        /// <returns></returns>
        public static string GetGeneratedCode(string dynamicTemplateNamespace,
                                              string templateSourceCode,
                                              string generatedCSharpClassName,
                                              string classBaseType,
                                              string templateFile = null)
        {
            string systemPath = Directory.GetCurrentDirectory();
            string path       = null;

            if (string.IsNullOrWhiteSpace(templateFile))
            {
                path = systemPath;
            }
            else
            {
                path = Path.GetDirectoryName(templateFile);
            }
            RazorProjectFileSystem fs     = RazorProjectFileSystem.Create(path); // or '.'
            RazorProjectEngine     engine = RazorProjectEngine.Create(RazorConfiguration.Default, fs, (builder) =>
            {
                InheritsDirective.Register(builder);
                FunctionsDirective.Register(builder);
                SectionDirective.Register(builder);
                builder.ConfigureClass((document, @class) =>
                {
                    @class.ClassName = generatedCSharpClassName;
                });
                builder.SetNamespace(dynamicTemplateNamespace); // define a namespace for the Template class
                builder.SetBaseType(classBaseType);
                builder.AddDefaultImports("@using System",
                                          "@using System.Threading.Tasks",
                                          "@using System.Collections.Generic",
                                          "@using System.Linq",
                                          "@using System.Text",
                                          "@using RazorEngine",
                                          "@using RazorEngine.Templating");
            });
            string razorRelativePath;
            string randomRazorFileFullPath = null;

            if (string.IsNullOrEmpty(templateFile))
            {
                razorRelativePath       = Path.GetRandomFileName();
                randomRazorFileFullPath = Path.Combine(systemPath, razorRelativePath);
                File.AppendAllText(randomRazorFileFullPath, templateSourceCode ?? string.Empty, System.Text.Encoding.UTF8);
            }
            else
            {
                razorRelativePath = templateFile;
            }
            RazorProjectItem    item           = fs.GetItem(razorRelativePath);
            RazorCodeDocument   codeDocument   = engine.Process(item);
            RazorCSharpDocument csharpDocument = codeDocument.GetCSharpDocument();

            if (!string.IsNullOrEmpty(randomRazorFileFullPath))
            {
                try
                {
                    File.Delete(randomRazorFileFullPath);
                }
                catch (Exception) { }
            }
            if (csharpDocument.Diagnostics.Any())
            {
                var diagnostics = string.Join(Environment.NewLine, csharpDocument.Diagnostics);
                throw new InvalidOperationException($"One or more parse errors encountered. This will not prevent the generator from continuing: {Environment.NewLine}{diagnostics}.");
            }
            //Manual loading of assemblies to prevent DLLs from being lost when compiling classes
            AppDomain.CurrentDomain.Load(typeof(RazorCompiledItemAttribute).Assembly.FullName);
            //手动加载程序集,防止编译的类时找不到 DLL
            return(csharpDocument.GeneratedCode);
        }