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 }