001    /*
002     * Copyright 2010-2015 JetBrains s.r.o.
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    
017    package org.jetbrains.kotlin.kdoc.parser;
018    
019    import com.intellij.lang.ASTNode;
020    import com.intellij.lang.PsiBuilder;
021    import com.intellij.lang.PsiParser;
022    import com.intellij.psi.tree.IElementType;
023    import org.jetbrains.annotations.NotNull;
024    import org.jetbrains.kotlin.kdoc.lexer.KDocTokens;
025    
026    public class KDocParser implements PsiParser {
027        @Override
028        @NotNull
029        public ASTNode parse(IElementType root, PsiBuilder builder) {
030            PsiBuilder.Marker rootMarker = builder.mark();
031            if (builder.getTokenType() == KDocTokens.START) {
032                builder.advanceLexer();
033            }
034            PsiBuilder.Marker currentSectionMarker = builder.mark();
035    
036            // todo: parse KDoc tags, markdown, etc...
037            while (!builder.eof()) {
038                if (builder.getTokenType() == KDocTokens.TAG_NAME) {
039                    currentSectionMarker = parseTag(builder, currentSectionMarker);
040                }
041                else if (builder.getTokenType() == KDocTokens.END) {
042                    if (currentSectionMarker != null) {
043                        currentSectionMarker.done(KDocElementTypes.KDOC_SECTION);
044                        currentSectionMarker = null;
045                    }
046                    builder.advanceLexer();
047                }
048                else {
049                    builder.advanceLexer();
050                }
051            }
052    
053            if (currentSectionMarker != null) {
054                currentSectionMarker.done(KDocElementTypes.KDOC_SECTION);
055            }
056            rootMarker.done(root);
057            return builder.getTreeBuilt();
058        }
059    
060        private static PsiBuilder.Marker parseTag(PsiBuilder builder, PsiBuilder.Marker currentSectionMarker) {
061            String tagName = builder.getTokenText();
062            KDocKnownTag knownTag = KDocKnownTag.findByTagName(tagName);
063            if (knownTag != null && knownTag.isSectionStart()) {
064                currentSectionMarker.done(KDocElementTypes.KDOC_SECTION);
065                currentSectionMarker = builder.mark();
066            }
067            PsiBuilder.Marker tagStart = builder.mark();
068            builder.advanceLexer();
069    
070            while (!builder.eof() && !isAtEndOfTag(builder)) {
071                builder.advanceLexer();
072            }
073            tagStart.done(KDocElementTypes.KDOC_TAG);
074            return currentSectionMarker;
075        }
076    
077        private static boolean isAtEndOfTag(PsiBuilder builder) {
078            if (builder.getTokenType() == KDocTokens.END) {
079                return true;
080            }
081            if (builder.getTokenType() == KDocTokens.LEADING_ASTERISK) {
082                int lookAheadCount = 1;
083                if (builder.lookAhead(1) == KDocTokens.TEXT) {
084                    lookAheadCount++;
085                }
086                if (builder.lookAhead(lookAheadCount) == KDocTokens.TAG_NAME) {
087                    return true;
088                }
089            }
090            return false;
091        }
092    }