private bool ParseSingleBlock(LinkedListStream <IBaseToken> stream)
        {
            // @NOTE(final) Single block = auto-brief

            IBaseToken    blockToken  = stream.Peek();
            DoxygenEntity blockEntity = new DoxygenEntity(DoxygenEntityKind.BlockSingle, blockToken);

            PushEntity(blockEntity);
            stream.Next();

            IBaseToken endToken   = null;
            IBaseToken briefToken = DoxygenTokenPool.Make(DoxygenTokenKind.Command, blockToken.Range, true);

            DoxygenEntity briefEntity = new DoxygenEntity(DoxygenEntityKind.Brief, briefToken);
            DoxygenNode   briefNode   = PushEntity(briefEntity);

            while (!stream.IsEOF)
            {
                IBaseToken   token     = stream.Peek();
                DoxygenToken doxyToken = token as DoxygenToken;
                if (doxyToken != null && doxyToken.Kind == DoxygenTokenKind.DoxyBlockEnd)
                {
                    endToken = token;
                    stream.Next();
                    break;
                }
                if (!ParseBlockContent(stream, briefNode))
                {
                    break;
                }
                Debug.Assert(stream.CurrentValue != token);
            }

            Pop(); // Pop brief

            Pop(); // Pop block

            if (endToken != null)
            {
                blockEntity.EndRange = endToken.Range;
            }

            return(true);
        }
        private DoxygenNode PushEntity(DoxygenEntity newEntity)
        {
            if (newEntity.Kind == DoxygenEntityKind.BlockSingle || newEntity.Kind == DoxygenEntityKind.BlockMulti)
            {
                if (Top != null)
                {
                    Debug.Assert(Top.Entity.Kind == DoxygenEntityKind.Group);
                    Pop();
                }
                DoxygenNode blockNode = new DoxygenNode(Top, newEntity);
                Push(blockNode);
                return(blockNode);
            }

            DoxygenNode itemNode = new DoxygenNode(Top, newEntity);

            Push(itemNode);
            return(itemNode);
        }
        private void ParseText(LinkedListStream <IBaseToken> stream, IBaseNode contentNode)
        {
            var nextToken = stream.Peek <DoxygenToken>();

            Debug.Assert(nextToken.Kind == DoxygenTokenKind.TextStart);
            TextPosition textStart = nextToken.Position;
            TextPosition textEnd   = textStart;

            stream.Next();
            while (!stream.IsEOF)
            {
                var t = stream.Peek <DoxygenToken>();
                if (t == null)
                {
                    break;
                }
                if (t.Kind == DoxygenTokenKind.TextEnd)
                {
                    textEnd = t.Position;
                    stream.Next();
                    break;
                }
                stream.Next();
            }
            if (contentNode != null)
            {
                int    textLen = textEnd.Index - textStart.Index;
                string text    = Source.Substring(textStart.Index, textLen).Trim();
                if (text.Length > 0)
                {
                    var textNode = new DoxygenNode(contentNode, new DoxygenEntity(DoxygenEntityKind.Text, new TextRange(textStart, textLen)));
                    textNode.Entity.Value    = text;
                    textNode.Entity.EndRange = new TextRange(textEnd, 0);
                    contentNode.AddChild(textNode);
                }
            }
        }
        private bool ParseCommand(LinkedListStream <IBaseToken> stream, IBaseNode contentRoot)
        {
            // @NOTE(final): This must always return true, due to the fact that the stream is advanced at least once
            DoxygenToken commandToken = stream.Peek <DoxygenToken>();

            Debug.Assert(commandToken != null && commandToken.Kind == DoxygenTokenKind.Command);

            string commandName = commandToken.Value.Substring(1);

            stream.Next();

            string typeName = "Command";

            var rule = DoxygenSyntax.GetCommandRule(commandName);

            if (rule != null)
            {
                if (rule.Kind == DoxygenSyntax.CommandKind.EndCommandBlock)
                {
                    var t = Top;
                    if (t == null)
                    {
                        AddError(commandToken.Position, $"Unterminated starting command block in command '{commandName}'", typeName, commandName);
                        return(false);
                    }
                    if (t.Entity.Kind != DoxygenEntityKind.BlockCommand)
                    {
                        AddError(commandToken.Position, $"Expect starting command block, but found '{t.Entity.Kind}' in command '{commandName}'", typeName, commandName);
                        return(false);
                    }
                    Pop();
                }

                // Paragraph or section command starts or command block starts -> Close previous paragraph or sectioning command
                if (rule.Kind == DoxygenSyntax.CommandKind.Paragraph ||
                    rule.Kind == DoxygenSyntax.CommandKind.Section ||
                    rule.Kind == DoxygenSyntax.CommandKind.StartCommandBlock)
                {
                    var t = Top;
                    if (t != null)
                    {
                        if (t.Entity.Kind == DoxygenEntityKind.Paragraph ||
                            t.Entity.Kind == DoxygenEntityKind.Section ||
                            t.Entity.Kind == DoxygenEntityKind.SubSection ||
                            t.Entity.Kind == DoxygenEntityKind.SubSubSection)
                        {
                            Pop();
                        }
                    }
                }

                DoxygenEntity commandEntity = null;
                IEntityBaseNode <DoxygenEntity> commandNode = null;
                if (rule.EntityKind != DoxygenEntityKind.None)
                {
                    commandEntity    = new DoxygenEntity(rule.EntityKind, commandToken.Range);
                    commandEntity.Id = commandName;
                    commandNode      = new DoxygenNode(Top, commandEntity);
                    if (rule.IsPush)
                    {
                        Push(commandNode);
                    }
                    else
                    {
                        Add(commandNode);
                    }
                }

                foreach (var arg in rule.Args)
                {
                    DoxygenToken argToken = stream.Peek <DoxygenToken>();
                    if (argToken == null)
                    {
                        break;
                    }
                    DoxygenTokenKind expectedTokenKind = DoxygenSyntax.ArgumentToTokenKindMap.ContainsKey(arg.Kind) ? DoxygenSyntax.ArgumentToTokenKindMap[arg.Kind] : DoxygenTokenKind.Invalid;
                    if (expectedTokenKind == DoxygenTokenKind.Invalid)
                    {
                        break;
                    }
                    if (expectedTokenKind != argToken.Kind)
                    {
                        AddError(argToken.Position, $"Expect argument token '{expectedTokenKind}', but got '{argToken.Kind}'", typeName, commandName);
                        break;
                    }
                    if (commandNode != null)
                    {
                        string paramName  = arg.Name;
                        string paramValue = argToken.Value;
                        commandNode.Entity.AddParameter(argToken, paramName, paramValue);
                    }
                    stream.Next();
                }

                if (commandEntity != null)
                {
                    // Get name and text parameter (Some commands, have different names and text parameters, so there is a variable list of strings)
                    var nameParam = commandEntity.FindParameterByName("name", "id");
                    var textParam = commandEntity.FindParameterByName("text", "title", "caption");
                    if (nameParam == null || string.IsNullOrWhiteSpace(nameParam.Value))
                    {
                        if (rule.Kind == DoxygenSyntax.CommandKind.Section)
                        {
                            if (!"mainpage".Equals(commandName))
                            {
                                AddError(commandToken.Position, $"Missing identifier mapping for command '{commandName}'", typeName, commandName);
                            }
                        }
                    }

                    if (nameParam != null && !string.IsNullOrWhiteSpace(nameParam.Value))
                    {
                        string symbolName = nameParam.Value;
                        Debug.Assert(commandNode != null);
                        if (rule.Kind == DoxygenSyntax.CommandKind.Section)
                        {
                            SourceSymbolKind kind = SourceSymbolKind.DoxygenSection;
                            if ("page".Equals(commandName) || "mainpage".Equals(commandName))
                            {
                                kind = SourceSymbolKind.DoxygenPage;
                            }
                            SymbolCache.AddSource(Tag, symbolName, new SourceSymbol(kind, nameParam.Token.Range, commandNode));
                        }
                        else if ("ref".Equals(commandName) || "refitem".Equals(commandName))
                        {
                            string       referenceValue = nameParam.Value;
                            TextPosition startPos       = new TextPosition(0, nameParam.Token.Position.Line, nameParam.Token.Position.Column);
                            using (TextStream referenceTextStream = new BasicTextStream(referenceValue, startPos, referenceValue.Length))
                            {
                                ReferenceSymbolKind referenceTarget = ReferenceSymbolKind.Any;
                                while (!referenceTextStream.IsEOF)
                                {
                                    char first  = referenceTextStream.Peek();
                                    char second = referenceTextStream.Peek(1);
                                    char third  = referenceTextStream.Peek(2);
                                    if (SyntaxUtils.IsIdentStart(first))
                                    {
                                        referenceTextStream.StartLexeme();
                                        while (!referenceTextStream.IsEOF)
                                        {
                                            if (!SyntaxUtils.IsIdentPart(referenceTextStream.Peek()))
                                            {
                                                break;
                                            }
                                            referenceTextStream.AdvanceColumn();
                                        }
                                        var    refRange          = referenceTextStream.LexemeRange;
                                        string singleRereference = referenceTextStream.GetSourceText(refRange.Index, refRange.Length);
                                        if (referenceTextStream.Peek() == '(')
                                        {
                                            referenceTarget = ReferenceSymbolKind.CppFunction;
                                            referenceTextStream.AdvanceColumn();
                                            while (!referenceTextStream.IsEOF)
                                            {
                                                if (referenceTextStream.Peek() == ')')
                                                {
                                                    break;
                                                }
                                                referenceTextStream.AdvanceColumn();
                                            }
                                        }
                                        var symbolRange = new TextRange(new TextPosition(nameParam.Token.Position.Index + refRange.Position.Index, refRange.Position.Line, refRange.Position.Column), refRange.Length);
                                        SymbolCache.AddReference(Tag, singleRereference, new ReferenceSymbol(referenceTarget, symbolRange, commandNode));
                                    }
                                    else if (first == '#' || first == '.')
                                    {
                                        referenceTarget = ReferenceSymbolKind.CppMember;
                                        referenceTextStream.AdvanceColumn();
                                    }
                                    else if (first == ':' || second == ':')
                                    {
                                        referenceTarget = ReferenceSymbolKind.CppMember;
                                        referenceTextStream.AdvanceColumns(2);
                                    }
                                    else
                                    {
                                        break;
                                    }
                                }
                            }
                        }
                        else if ("subpage".Equals(commandName))
                        {
                            SymbolCache.AddReference(Tag, symbolName, new ReferenceSymbol(ReferenceSymbolKind.DoxygenPage, nameParam.Token.Range, commandNode));
                        }
                    }
                }
                ParseBlockContent(stream, commandNode);
            }
            else
            {
                AddError(commandToken.Position, $"No parse rule for command '{commandName}' found", "Command", commandName);
            }
            return(true);
        }