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 }