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                builder.advanceLexer();
078                referenceMarker.done(KDocElementTypes.KDOC_LINK);
079            }
080    
081            while (!builder.eof() && !isAtEndOfTag(builder)) {
082                if (builder.getTokenType() == KDocTokens.MARKDOWN_LINK) {
083                    PsiBuilder.Marker linkStart = builder.mark();
084                    builder.advanceLexer();
085                    linkStart.done(KDocElementTypes.KDOC_LINK);
086                }
087                else {
088                    builder.advanceLexer();
089                }
090            }
091            tagStart.done(KDocElementTypes.KDOC_TAG);
092            return currentSectionMarker;
093        }
094    
095        private static boolean isAtEndOfTag(PsiBuilder builder) {
096            if (builder.getTokenType() == KDocTokens.END) {
097                return true;
098            }
099            if (builder.getTokenType() == KDocTokens.LEADING_ASTERISK) {
100                int lookAheadCount = 1;
101                if (builder.lookAhead(1) == KDocTokens.TEXT) {
102                    lookAheadCount++;
103                }
104                if (builder.lookAhead(lookAheadCount) == KDocTokens.TAG_NAME) {
105                    return true;
106                }
107            }
108            return false;
109        }
110    }