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.MARKDOWN_LINK) {
042                    PsiBuilder.Marker linkStart = builder.mark();
043                    builder.advanceLexer();
044                    linkStart.done(KDocElementTypes.KDOC_LINK);
045                }
046                else if (builder.getTokenType() == KDocTokens.END) {
047                    if (currentSectionMarker != null) {
048                        currentSectionMarker.done(KDocElementTypes.KDOC_SECTION);
049                        currentSectionMarker = null;
050                    }
051                    builder.advanceLexer();
052                }
053                else {
054                    builder.advanceLexer();
055                }
056            }
057    
058            if (currentSectionMarker != null) {
059                currentSectionMarker.done(KDocElementTypes.KDOC_SECTION);
060            }
061            rootMarker.done(root);
062            return builder.getTreeBuilt();
063        }
064    
065        private static PsiBuilder.Marker parseTag(PsiBuilder builder, PsiBuilder.Marker currentSectionMarker) {
066            String tagName = builder.getTokenText();
067            KDocKnownTag knownTag = KDocKnownTag.findByTagName(tagName);
068            if (knownTag != null && knownTag.isSectionStart()) {
069                currentSectionMarker.done(KDocElementTypes.KDOC_SECTION);
070                currentSectionMarker = builder.mark();
071            }
072            PsiBuilder.Marker tagStart = builder.mark();
073            builder.advanceLexer();
074    
075            if (knownTag != null && knownTag.isReferenceRequired() && builder.getTokenType() == KDocTokens.TEXT_OR_LINK) {
076                PsiBuilder.Marker referenceMarker = builder.mark();
077                PsiBuilder.Marker nameMarker = builder.mark();
078                builder.advanceLexer();
079                nameMarker.done(KDocElementTypes.KDOC_NAME);
080                referenceMarker.done(KDocElementTypes.KDOC_LINK);
081            }
082    
083            while (!builder.eof() && !isAtEndOfTag(builder)) {
084                if (builder.getTokenType() == KDocTokens.MARKDOWN_LINK) {
085                    PsiBuilder.Marker linkStart = builder.mark();
086                    builder.advanceLexer();
087                    linkStart.done(KDocElementTypes.KDOC_LINK);
088                }
089                else {
090                    builder.advanceLexer();
091                }
092            }
093            tagStart.done(KDocElementTypes.KDOC_TAG);
094            return currentSectionMarker;
095        }
096    
097        private static boolean isAtEndOfTag(PsiBuilder builder) {
098            if (builder.getTokenType() == KDocTokens.END) {
099                return true;
100            }
101            if (builder.getTokenType() == KDocTokens.LEADING_ASTERISK) {
102                int lookAheadCount = 1;
103                if (builder.lookAhead(1) == KDocTokens.TEXT) {
104                    lookAheadCount++;
105                }
106                if (builder.lookAhead(lookAheadCount) == KDocTokens.TAG_NAME) {
107                    return true;
108                }
109            }
110            return false;
111        }
112    }