private static void BuildNodes()

in src/dotnet/APIView/APIViewWeb/Languages/CppLanguageService.cs [162:844]


        private static void BuildNodes(CodeFileTokensBuilder builder, List<NavigationItem> navigation, List<CodeDiagnostic> diagnostic, CppAstNode root, string packageNamespace)
        {
            //Mapping of each namespace to it's leaf namespace nodes
            //These leaf nodes are processed to generate and group them together
            //C++ ast has declarations under same namespace in multiple files so these needs to be grouped for better presentation
            var namespaceLeafMap = new Dictionary<string, List<CppAstNode>>();
            var types = new HashSet<string>();

            var namespaceNodes = root.inner.Where(n => n.kind == NamespaceDeclKind && n.name == RootNamespace);
            bool foundFilterNamespace = false;
            foreach (var node in namespaceNodes)
            {
                var namespacebldr = new StringBuilder();
                var leafNamespaceNode = node;
                var currentNode = node;
                //Iterate until leaf namespace node and generate full namespace
                while (currentNode?.kind == NamespaceDeclKind)
                {
                    if (namespacebldr.Length > 0)
                    {
                        namespacebldr.Append("::");
                    }
                    namespacebldr.Append(currentNode.name);
                    leafNamespaceNode = currentNode;
                    currentNode = currentNode.inner?.FirstOrDefault(n => n.kind == NamespaceDeclKind);
                    if (leafNamespaceNode.inner?.Any(n => n.kind != NamespaceDeclKind) == true)
                    {
                        var nameSpace = namespacebldr.ToString();
                        if (!foundFilterNamespace && nameSpace.StartsWith(packageNamespace))
                        {
                            foundFilterNamespace = true;
                        }

                        if (!namespaceLeafMap.ContainsKey(nameSpace))
                        {
                            namespaceLeafMap[nameSpace] = new List<CppAstNode>();
                        }
                        namespaceLeafMap[nameSpace].Add(leafNamespaceNode);
                    }
                }
            }

            foreach (var nameSpace in namespaceLeafMap.Keys)
            {
                // Filter namespace based on file name if any of the namespace matches file name pattern
                // If no namespace matches file name then allow all namespaces to be part of review to avoid mandating file name convention
                if ((!foundFilterNamespace || nameSpace.StartsWith(packageNamespace)) && !nameSpace.EndsWith(DetailsNamespacePostfix))
                {
                    ProcessNamespaceNode(nameSpace);
                    builder.NewLine();
                    builder.NewLine();
                }
            }

            void ProcessNamespaceNode(string nameSpace)
            {
                NavigationItem currentNamespace = new NavigationItem()
                {
                    NavigationId = nameSpace,
                    Text = nameSpace,
                    Tags = { { "TypeKind", "namespace" } }
                };
                List<NavigationItem> currentNamespaceMembers = new List<NavigationItem>();

                builder.Keyword("namespace");
                builder.Space();
                var namespaceTokens = nameSpace.Split("::");
                foreach (var token in namespaceTokens)
                {
                    builder.Text(token);
                    builder.Space();
                    builder.Punctuation("{");
                    builder.Space();
                }
                builder.NewLine();
                //Process all nodes in namespace
                foreach (var leafNamespaceNode in namespaceLeafMap[nameSpace])
                {
                    if (leafNamespaceNode.inner != null)
                    {
                        foreach (var member in leafNamespaceNode.inner)
                        {
                            // Name space has mix of details namespace and classes
                            // API View should skip those sub details namespaces also
                            if (member.kind == NamespaceDeclKind)
                                continue;

                            builder.IncrementIndent();
                            ProcessNode(member, currentNamespaceMembers, nameSpace);
                            builder.DecrementIndent();
                        }
                    }
                }

                currentNamespace.ChildItems = currentNamespaceMembers.ToArray();
                navigation.Add(currentNamespace);
                builder.NewLine();
                for (int i = 0; i < namespaceTokens.Length; i++)
                    builder.Punctuation("}");
                builder.NewLine();
            }

            NavigationItem BuildDeclaration(string name, string kind, string parentId = "")
            {
                string definitionId = name;
                if (!string.IsNullOrEmpty(parentId))
                {
                    definitionId = parentId + "::" + name;
                }

                builder.Append(new CodeFileToken()
                {
                    DefinitionId = definitionId,
                    Kind = CodeFileTokenKind.TypeName,
                    Value = name,
                });
                types.Add(name);
                return new NavigationItem()
                {
                    NavigationId = definitionId,
                    Text = name,
                    Tags = { { "TypeKind", kind } }
                };
            }

            void BuildMemberDeclaration(string containerName, string name, string id = "")
            {
                if (string.IsNullOrEmpty(id))
                {
                    id = name;
                }
                builder.Append(new CodeFileToken()
                {
                    DefinitionId = containerName + "." + id,
                    Kind = CodeFileTokenKind.MemberName,
                    Value = name,
                });
            }

            string GenerateUniqueMethodId(CppAstNode methodNode)
            {
                var bldr = new StringBuilder();
                bldr.Append(methodNode.name);

                if (methodNode.inner != null)
                {
                    foreach (var parameterNode in methodNode.inner)
                    {
                        if (parameterNode.kind == ParmVarDeclKind)
                        {
                            bldr.Append(":");
                            bldr.Append(parameterNode.type.Replace(" ", "_"));
                        }
                    }
                }

                bldr.Append("::");
                var type = string.IsNullOrEmpty(methodNode.type) ? "void" : methodNode.type;
                bldr.Append(type.Replace(" ", "_"));
                return bldr.ToString();
            }

            NavigationItem ProcessClassNode(CppAstNode node, string parentName, List<CppAstNode> templateParams = null)
            {
                NavigationItem navigationItem = null;

                builder.Keyword(node.tagUsed);
                builder.Space();
                string nodeName = node.name;
                if (templateParams != null)
                {
                    nodeName += "<";
                    bool first = true;
                    foreach (var paramNode in templateParams)
                    {
                        if (!first)
                        {
                            nodeName += ", ";
                        }
                        nodeName += paramNode.name;
                    }
                    nodeName += ">";
                }
                if (!string.IsNullOrEmpty(nodeName))
                {
                    navigationItem = BuildDeclaration(nodeName, node.tagUsed, parentName);
                    builder.Space();
                }

                var memberNavigations = new List<NavigationItem>();
                var parents = node.inner?.Where(n => parentTypes.Contains(n.kind));
                if (parents != null)
                {
                    bool first = true;
                    //Show inheritance details
                    foreach (var parent in parents)
                    {
                        if (first)
                        {
                            builder.Punctuation(":");
                            builder.Space();
                            first = false;
                        }
                        else
                        {
                            builder.Punctuation(",");
                            builder.Space();
                        }
                        builder.Keyword(parent.access);
                        builder.Space();
                        if (parent.name != null)
                        {
                            BuildType(builder, parent.name, types);
                        }
                    }
                    builder.Space();
                }

                builder.NewLine();
                builder.WriteIndent();
                builder.Punctuation("{");
                builder.NewLine();
                //Double indentation for members since access modifier is not parent for members
                builder.IncrementIndent();
                builder.IncrementIndent();
                bool hasFoundDefaultAccessMembers = false;
                var id = parentName + "::" + node.name;

                if (node.inner != null)
                {
                    bool isPrivateMember = false;
                    string currentAccessModifier = "";
                    foreach (var childNode in node.inner)
                    {
                        if (childNode.kind == CxxRecordDeclKind && childNode.name == node.name)
                            continue;

                        // add public or protected access specifier
                        if (childNode.kind == AccessSpecDeclKind)
                        {
                            //Skip all private members
                            isPrivateMember = (childNode.access == AccessModifierPrivate);
                            if (isPrivateMember)
                            {
                                continue;
                            }
                            currentAccessModifier = childNode.access;
                            builder.DecrementIndent();
                            builder.WriteIndent();
                            builder.Keyword(childNode.access);
                            builder.Punctuation(":");
                            builder.IncrementIndent();
                            builder.NewLine();
                        }
                        else if (!isPrivateMember && !parentTypes.Contains(childNode.kind))
                        {
                            if (string.IsNullOrEmpty(currentAccessModifier) && !hasFoundDefaultAccessMembers)
                            {
                                hasFoundDefaultAccessMembers = true;
                            }
                            ProcessNode(childNode, memberNavigations, id);
                        }
                    }
                }

                builder.DecrementIndent();
                builder.DecrementIndent();
                builder.WriteIndent();
                builder.Punctuation("}");
                builder.Punctuation(";");
                builder.NewLine();
                builder.NewLine();

                if (navigationItem != null)
                {
                    navigationItem.ChildItems = memberNavigations.ToArray();
                }

                if (node.tagUsed == "class")
                {
                    if (node.inner?.Any(n => n.isimplicit == true && n.kind == CxxConstructorDeclKind) == true)
                    {
                        diagnostic.Add(new CodeDiagnostic("", navigationItem.NavigationId, ImplicitConstrucorHintError, ""));
                    }

                    if (hasFoundDefaultAccessMembers)
                    {
                        diagnostic.Add(new CodeDiagnostic("", navigationItem.NavigationId, NonAccessModifierMemberError, ""));
                    }
                }

                return navigationItem;
            }

            NavigationItem ProcessEnumNode(CppAstNode node)
            {
                builder.Keyword("enum");
                builder.Space();
                var navigationItem = BuildDeclaration(node.name, "enum");
                builder.NewLine();
                builder.WriteIndent();
                builder.Punctuation("{");
                builder.NewLine();
                builder.IncrementIndent();

                if (node.inner != null)
                {
                    foreach (var parameterNode in node.inner)
                    {
                        if (parameterNode.kind == EnumConstantDeclKind)
                        {
                            builder.WriteIndent();
                            BuildMemberDeclaration("", parameterNode.name);
                            if (parameterNode.inner?.FirstOrDefault(n => n.kind == "ConstantExpr") is CppAstNode
                                exprNode)
                            {
                                builder.Space();
                                builder.Punctuation("=");
                                builder.Space();
                                BuildExpression(builder, exprNode);
                            }

                            builder.Punctuation(",");
                            builder.NewLine();
                        }
                    }
                }

                builder.DecrementIndent();
                builder.WriteIndent();
                builder.Punctuation("};");
                builder.NewLine();
                builder.NewLine();
                return navigationItem;
            }

            void ProcessFunctionDeclNode(CppAstNode node, string parentName)
            {
                if (node.isimplicit == true)
                {
                    builder.Keyword("implicit");
                    builder.Space();
                }

                if (node.isvirtual == true)
                {
                    builder.Keyword("virtual");
                    builder.Space();
                }

                if (node.inline == true)
                {
                    builder.Keyword("inline");
                    builder.Space();
                }

                if (!string.IsNullOrEmpty(node.storageClass))
                {
                    builder.Keyword(node.storageClass);
                    builder.Space();
                }

                if (node.type != null)
                {
                    BuildType(builder, node.type, types);
                    builder.Space();
                }

                BuildMemberDeclaration(parentName, node.name, GenerateUniqueMethodId(node));
                builder.Punctuation("(");
                bool first = true;
                if (node.inner != null)
                {
                    bool isMultiLineArgs = node.inner.Count > 1;
                    builder.IncrementIndent();
                    foreach (var parameterNode in node.inner)
                    {
                        if (parameterNode.kind == ParmVarDeclKind)
                        {
                            if (first)
                            {
                                first = false;
                            }
                            else
                            {
                                builder.Punctuation(",");
                                builder.Space();
                            }

                            if (isMultiLineArgs)
                            {
                                builder.NewLine();
                                builder.WriteIndent();
                            }
                            BuildType(builder, parameterNode.type, types);

                            if (!string.IsNullOrEmpty(parameterNode.name))
                            {
                                builder.Space();
                                builder.Text(parameterNode.name);
                            }
                        }
                    }
                    builder.DecrementIndent();
                }

                builder.Punctuation(")");
                //Add any postfix keywords if signature has any.
                // Few expamples are 'const noexcept' 
                if (!string.IsNullOrEmpty(node.keywords))
                {
                    foreach (var key in node.keywords.Split())
                    {
                        builder.Space();
                        builder.Keyword(key);
                    }
                }

                // If method is tagged as pure, delete, or default then it should be marked as "=<0|default|delete>"
                if (node.ispure == true)
                {
                    builder.Space();
                    builder.Punctuation("=");
                    builder.Space();
                    builder.Text("0");
                }
                else if (node.isdefault == true)
                {
                    builder.Space();
                    builder.Punctuation("=");
                    builder.Space();
                    builder.Keyword("default");
                }
                else if (node.isdelete == true)
                {
                    builder.Space();
                    builder.Punctuation("=");
                    builder.Space();
                    builder.Keyword("delete");
                }

                builder.Punctuation(";");
                builder.NewLine();
            }

            NavigationItem ProcessTemplateClassDeclNode(CppAstNode node, string parentName)
            {
                NavigationItem returnValue = null;
                builder.Keyword("template");
                builder.Space();

                if (node.inner != null)
                {
                    bool first = true;
                    builder.Punctuation("<");
                    List<CppAstNode> templateParams = new List<CppAstNode>();
                    foreach (var childnode in node.inner.Where(n => n.kind == TemplateTypeParmDeclKind))
                    {
                        if (!first)
                        {
                            builder.Punctuation(",");
                            builder.Space();
                        }

                        templateParams.Add(childnode);
                        BuildType(builder, childnode.name, types);
                    }
                    builder.Punctuation(">");
                    builder.NewLine();
                    builder.WriteIndent();
                    foreach (var childnode in node.inner.Where(n => n.kind == CxxRecordDeclKind))
                    {
                        returnValue = ProcessClassNode(childnode, parentName, templateParams);
                    }
                }
                return returnValue;
            }

            NavigationItem ProcessTemplateClassSpecializationDeclNode(CppAstNode node, string parentName)
            {
                NavigationItem returnValue = null;
                builder.Keyword("template");
                builder.Space();

                if (node.inner != null)
                {
                    builder.Punctuation("<>");
                    List<CppAstNode> templateParams = new List<CppAstNode>();
                    foreach (var childnode in node.inner.Where(n => n.kind == TemplateArgumentKind))
                    {
                        templateParams.Add(childnode);
                    }
                    builder.NewLine();
                    builder.WriteIndent();
                    foreach (var childnode in node.inner.Where(n => n.kind == CxxRecordDeclKind))
                    {
                        returnValue = ProcessClassNode(childnode, parentName, templateParams);
                    }
                }
                return returnValue;
            }


            void ProcessTemplateFuncDeclNode(CppAstNode node, string parentName)
            {
                builder.Keyword("template");
                builder.Space();

                if (node.inner != null)
                {
                    bool first = true;
                    builder.Punctuation("<");
                    foreach (var childnode in node.inner.Where(n => n.kind == TemplateTypeParmDeclKind))
                    {
                        if (!first)
                        {
                            builder.Punctuation(",");
                            builder.Space();
                        }
                        BuildType(builder, childnode.name, types);
                    }
                    builder.Punctuation(">");

                    builder.Space();
                    var methodNode = node.inner.FirstOrDefault(node => node.kind == CxxMethodDeclKind);
                    if (methodNode != null)
                    {
                        ProcessFunctionDeclNode(methodNode, parentName);
                    }
                }
            }

            void ProcessTypeAlias(CppAstNode node)
            {
                builder.Keyword("using");
                builder.Space();
                builder.Text(node.name);
                builder.Space();
                builder.Punctuation("=");
                builder.Space();
                BuildType(builder, node.type, types);
                builder.Punctuation(";");
                builder.NewLine();
            }

            void ProcessVarDecNode(CppAstNode node, string parentName)
            {
                if (node.constexpr == true)
                {
                    builder.Keyword("constexpr");
                    builder.Space();
                }
                if (!string.IsNullOrEmpty(node.storageClass))
                {
                    builder.Keyword(node.storageClass);
                    builder.Space();
                }

                //Remove left most const from type if it is constexpr
                string type = node.type;
                if (node.constexpr == true && type.StartsWith("const"))
                {
                    var regex = new Regex(Regex.Escape("const"));
                    type = regex.Replace(type, "", 1).Trim();
                }

                BuildType(builder, type, types);
                builder.Space();
                BuildMemberDeclaration(parentName, node.name);
                if (node.inner?.FirstOrDefault() is CppAstNode
                                exprNode)
                {
                    builder.Space();
                    builder.Punctuation("=");
                    builder.Space();
                    BuildExpression(builder, exprNode);
                }
                builder.Punctuation(";");
                builder.NewLine();
            }

            void ProcessNode(CppAstNode node, List<NavigationItem> navigationItems, string parentName)
            {
                NavigationItem currentNavItem = null;
                builder.WriteIndent();
                switch (node.kind)
                {
                    case CxxRecordDeclKind:
                        {
                            currentNavItem = ProcessClassNode(node, parentName);
                            builder.NewLine();
                            break;
                        }

                    case CxxConstructorDeclKind:
                    case CxxDestructorDeclKind:
                    case FunctionDeclKind:
                    case CxxMethodDeclKind:
                        {
                            ProcessFunctionDeclNode(node, parentName);
                            break;
                        }
                    case EnumDeclKind:
                        {
                            currentNavItem = ProcessEnumNode(node);
                            builder.NewLine();
                            break;
                        }

                    case FieldDeclKind:
                    case VarDeclKind:
                        {
                            ProcessVarDecNode(node, parentName);
                            break;
                        }

                    case TypeAliasDeclKind:
                        {
                            ProcessTypeAlias(node);
                            break;
                        }

                    case FunctionTemplateDeclKind:
                        {
                            ProcessTemplateFuncDeclNode(node, parentName);
                            break;
                        }
                    case ClassTemplateDeclKind:
                        {
                            currentNavItem = ProcessTemplateClassDeclNode(node, parentName);
                            break;
                        }
                    case ClassTemplateSpecializationDeclKind:
                        {
                            currentNavItem = ProcessTemplateClassSpecializationDeclNode(node, parentName);
                            break;
                        }
                    default:
                        builder.Text(node.ToString());
                        builder.NewLine();
                        break;
                }

                if (currentNavItem != null && navigationItems != null)
                {
                    navigationItems.Add(currentNavItem);
                }
            }

            void BuildType(CodeFileTokensBuilder builder, string type, HashSet<string> types)
            {
                foreach (Match typePartMatch in _typeTokenizer.Matches(type))
                {
                    var typePart = typePartMatch.ToString();
                    if (_keywords.Contains(typePart))
                    {
                        builder.Keyword(typePart);
                    }
                    else if (typePart.Contains("::"))
                    {
                        // Handle type usage before it's defintition
                        var typeNamespace = typePart.Substring(0, typePart.LastIndexOf("::"));
                        string typeValue = typePart;
                        string navigateToId = "";
                        if (types.Contains(typePart) || namespaceLeafMap.ContainsKey(typeNamespace))
                        {
                            typeValue = typePart.Substring(typePart.LastIndexOf("::") + 2);
                            navigateToId = typePart;
                        }
                        builder.Append(new CodeFileToken()
                        {
                            Kind = CodeFileTokenKind.TypeName,
                            NavigateToId = navigateToId,
                            Value = typeValue
                        });
                    }
                    else
                    {
                        builder.Text(typePart);
                    }
                }
            }
        }