/// <inheritdoc /> protected override void OnDocumentStructureCreated( RazorCodeDocument codeDocument, NamespaceDeclarationIntermediateNode @namespace, ClassDeclarationIntermediateNode @class, MethodDeclarationIntermediateNode method) { if (!TryComputeNamespaceAndClass( codeDocument.Source.FilePath, codeDocument.Source.RelativePath, out var computedNamespace, out var computedClass)) { // If we can't compute a nice namespace (no relative path) then just generate something // mangled. computedNamespace = BaseNamespace; computedClass = CSharpIdentifier.GetClassNameFromPath(codeDocument.Source.FilePath) ?? "__BlazorComponent"; } if (MangleClassNames) { computedClass = "__" + computedClass; } @namespace.Content = computedNamespace; @class.BaseType = BlazorApi.BlazorComponent.FullTypeName; @class.ClassName = computedClass; @class.Modifiers.Clear(); @class.Modifiers.Add("public"); method.ReturnType = "void"; method.MethodName = BlazorApi.BlazorComponent.BuildRenderTree; method.Modifiers.Clear(); method.Modifiers.Add("protected"); method.Modifiers.Add("override"); method.Parameters.Clear(); method.Parameters.Add(new MethodParameter() { ParameterName = "builder", TypeName = BlazorApi.RenderTreeBuilder.FullTypeName, }); // We need to call the 'base' method as the first statement. var callBase = new CSharpCodeIntermediateNode(); callBase.Annotations.Add(BuildRenderTreeBaseCallAnnotation, true); callBase.Children.Add(new IntermediateToken { Kind = TokenKind.CSharp, Content = $"base.{BlazorApi.BlazorComponent.BuildRenderTree}(builder);" }); method.Children.Insert(0, callBase); }
// In general documents will have a relative path (relative to the project root). // We can only really compute a nice class/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. private bool TryComputeNamespaceAndClass(string filePath, string relativePath, out string @namespace, out string @class) { if (filePath == null || relativePath == null || filePath.Length <= relativePath.Length) { @namespace = null; @class = null; return(false); } // Try and infer a namespace from the project directory. We don't yet have the ability to pass // the namespace through from the project. var trimLength = relativePath.Length + (relativePath.StartsWith("/") ? 0 : 1); var baseDirectory = filePath.Substring(0, filePath.Length - trimLength); var lastSlash = baseDirectory.LastIndexOfAny(PathSeparators); var baseNamespace = lastSlash == -1 ? baseDirectory : baseDirectory.Substring(lastSlash + 1); if (string.IsNullOrEmpty(baseNamespace)) { @namespace = null; @class = null; return(false); } var builder = new StringBuilder(); // Sanitize the base namespace, but leave the dots. var segments = baseNamespace.Split(NamespaceSeparators, StringSplitOptions.RemoveEmptyEntries); builder.Append(CSharpIdentifier.SanitizeClassName(segments[0])); for (var i = 1; i < segments.Length; i++) { builder.Append('.'); builder.Append(CSharpIdentifier.SanitizeClassName(segments[i])); } segments = relativePath.Split(PathSeparators, StringSplitOptions.RemoveEmptyEntries); // Skip the last segment because it's the FileName. for (var i = 0; i < segments.Length - 1; i++) { builder.Append('.'); builder.Append(CSharpIdentifier.SanitizeClassName(segments[i])); } @namespace = builder.ToString(); @class = CSharpIdentifier.SanitizeClassName(Path.GetFileNameWithoutExtension(relativePath)); return(true); }