Example #1
0
        private static DocumentIntermediateNode Lower(RazorCodeDocument codeDocument, RazorEngine engine)
        {
            for (var i = 0; i < engine.Phases.Count; i++)
            {
                var phase = engine.Phases[i];
                phase.Execute(codeDocument);

                if (phase is IRazorDirectiveClassifierPhase)
                {
                    break;
                }
            }

            var documentNode = codeDocument.GetDocumentIntermediateNode();

            Assert.NotNull(documentNode);

            return(documentNode);
        }
        protected override void ExecuteCore(RazorCodeDocument codeDocument)
        {
            var documentNode = codeDocument.GetDocumentIntermediateNode();

            ThrowForMissingDocumentDependency(documentNode);

            var target = documentNode.Target;

            if (target == null)
            {
                var message = Resources.FormatDocumentMissingTarget(
                    documentNode.DocumentKind,
                    nameof(CodeTarget),
                    nameof(DocumentIntermediateNode.Target));
                throw new InvalidOperationException(message);
            }

            var writer         = new DefaultDocumentWriter(documentNode.Target, documentNode.Options);
            var cSharpDocument = writer.WriteDocument(codeDocument, documentNode);

            codeDocument.SetCSharpDocument(cSharpDocument);
        }
Example #3
0
        private DocumentIntermediateNode Lower(
            RazorCodeDocument codeDocument,
            Action <RazorProjectEngineBuilder> builder   = null,
            IEnumerable <TagHelperDescriptor> tagHelpers = null,
            bool designTime = false)
        {
            tagHelpers = tagHelpers ?? new TagHelperDescriptor[0];

            Action <RazorProjectEngineBuilder> configureEngine = b =>
            {
                builder?.Invoke(b);

                FunctionsDirective.Register(b);
                SectionDirective.Register(b);
                b.AddTagHelpers(tagHelpers);

                b.Features.Add(new DesignTimeOptionsFeature(designTime));
            };

            var projectEngine = RazorProjectEngine.Create(configureEngine);

            for (var i = 0; i < projectEngine.Phases.Count; i++)
            {
                var phase = projectEngine.Phases[i];
                phase.Execute(codeDocument);

                if (phase is IRazorIntermediateNodeLoweringPhase)
                {
                    break;
                }
            }

            var documentNode = codeDocument.GetDocumentIntermediateNode();

            Assert.NotNull(documentNode);

            return(documentNode);
        }
        // In general documents will have a relative path (relative to the project root).
        // We can only really compute a nice namespace when we know a relative path.
        //
        // However all kinds of thing are possible in tools. We shouldn't barf here if the document isn't
        // set up correctly.
        public static bool TryComputeNamespace(this RazorCodeDocument document, bool fallbackToRootNamespace, out string @namespace)
        {
            if (document == null)
            {
                throw new ArgumentNullException(nameof(document));
            }

            var filePath = document.Source.FilePath;

            if (filePath == null || document.Source.RelativePath == null || filePath.Length < document.Source.RelativePath.Length)
            {
                @namespace = null;
                return(false);
            }

            // If the document or it's imports contains a @namespace directive, we want to use that over the root namespace.
            var baseNamespace         = string.Empty;
            var appendSuffix          = true;
            var lastNamespaceContent  = string.Empty;
            var lastNamespaceLocation = SourceSpan.Undefined;
            var importSyntaxTrees     = document.GetImportSyntaxTrees();

            if (importSyntaxTrees != null)
            {
                // ImportSyntaxTrees is usually set. Just being defensive.
                foreach (var importSyntaxTree in importSyntaxTrees)
                {
                    if (importSyntaxTree != null && NamespaceVisitor.TryGetLastNamespaceDirective(importSyntaxTree, out var importNamespaceContent, out var importNamespaceLocation))
                    {
                        lastNamespaceContent  = importNamespaceContent;
                        lastNamespaceLocation = importNamespaceLocation;
                    }
                }
            }

            var syntaxTree = document.GetSyntaxTree();

            if (syntaxTree != null && NamespaceVisitor.TryGetLastNamespaceDirective(syntaxTree, out var namespaceContent, out var namespaceLocation))
            {
                lastNamespaceContent  = namespaceContent;
                lastNamespaceLocation = namespaceLocation;
            }

            StringSegment relativePath = document.Source.RelativePath;

            // If there are multiple @namespace directives in the heirarchy,
            // we want to pick the closest one to the current document.
            if (!string.IsNullOrEmpty(lastNamespaceContent))
            {
                baseNamespace = lastNamespaceContent;
                var directiveLocationDirectory = NormalizeDirectory(lastNamespaceLocation.FilePath);

                var sourceFilePath = new StringSegment(document.Source.FilePath);
                // We're specifically using OrdinalIgnoreCase here because Razor treats all paths as case-insensitive.
                if (!sourceFilePath.StartsWith(directiveLocationDirectory, StringComparison.OrdinalIgnoreCase) ||
                    sourceFilePath.Length <= directiveLocationDirectory.Length)
                {
                    // The most relevant directive is not from the directory hierarchy, can't compute a suffix.
                    appendSuffix = false;
                }
                else
                {
                    // We know that the document containing the namespace directive is in the current document's heirarchy.
                    // Let's compute the actual relative path that we'll use to compute the namespace suffix.
                    relativePath = sourceFilePath.Subsegment(directiveLocationDirectory.Length);
                }
            }
            else if (fallbackToRootNamespace)
            {
                var options = document.GetCodeGenerationOptions() ?? document.GetDocumentIntermediateNode()?.Options;
                baseNamespace = options?.RootNamespace;
                appendSuffix  = true;
            }

            if (string.IsNullOrEmpty(baseNamespace))
            {
                // There was no valid @namespace directive and we couldn't compute the RootNamespace.
                @namespace = null;
                return(false);
            }

            var builder = new StringBuilder();

            // Sanitize the base namespace, but leave the dots.
            var segments = new StringTokenizer(baseNamespace, NamespaceSeparators);
            var first    = true;

            foreach (var token in segments)
            {
                if (token.IsEmpty)
                {
                    continue;
                }

                if (first)
                {
                    first = false;
                }
                else
                {
                    builder.Append('.');
                }

                CSharpIdentifier.AppendSanitized(builder, token);
            }

            if (appendSuffix)
            {
                // If we get here, we already have a base namespace and the relative path that should be used as the namespace suffix.
                segments = new StringTokenizer(relativePath, PathSeparators);
                var previousLength = builder.Length;
                foreach (var token in segments)
                {
                    if (token.IsEmpty)
                    {
                        continue;
                    }

                    previousLength = builder.Length;

                    builder.Append('.');
                    CSharpIdentifier.AppendSanitized(builder, token);
                }

                // Trim the last segment because it's the FileName.
                builder.Length = previousLength;
            }

            @namespace = builder.ToString();

            return(true);

            // We want to normalize the path of the file containing the '@namespace' directive to just the containing
            // directory with a trailing separator.
            //
            // Not using Path.GetDirectoryName here because it doesn't meet these requirements, and we want to handle
            // both 'view engine' style paths and absolute paths.
            //
            // We also don't normalize the separators here. We expect that all documents are using a consistent style of path.
            //
            // If we can't normalize the path, we just return null so it will be ignored.
            StringSegment NormalizeDirectory(string path)
            {
                if (string.IsNullOrEmpty(path))
                {
                    return(default);