mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-18 09:34:34 +07:00
GitOrigin-RevId: c41d2f58f48822002c8a5bbc795fd087ef2fdeb1
426 lines
17 KiB
Java
426 lines
17 KiB
Java
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
|
package com.intellij.lang.java.parser;
|
|
|
|
import com.intellij.core.JavaPsiBundle;
|
|
import com.intellij.java.syntax.lexer.JavaDocLexer;
|
|
import com.intellij.java.syntax.lexer.JavaLexer;
|
|
import com.intellij.lang.*;
|
|
import com.intellij.lang.impl.PsiBuilderAdapter;
|
|
import com.intellij.lang.java.JavaLanguage;
|
|
import com.intellij.openapi.project.Project;
|
|
import com.intellij.openapi.util.Key;
|
|
import com.intellij.openapi.util.NlsContexts;
|
|
import com.intellij.openapi.util.Pair;
|
|
import com.intellij.platform.syntax.lexer.Lexer;
|
|
import com.intellij.platform.syntax.lexer.TokenList;
|
|
import com.intellij.platform.syntax.parser.SyntaxTreeBuilder;
|
|
import com.intellij.platform.syntax.psi.*;
|
|
import com.intellij.pom.java.LanguageLevel;
|
|
import com.intellij.psi.JavaTokenType;
|
|
import com.intellij.psi.PsiElement;
|
|
import com.intellij.psi.PsiFile;
|
|
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
|
|
import com.intellij.psi.impl.source.WhiteSpaceAndCommentSetHolder;
|
|
import com.intellij.psi.impl.source.tree.TreeUtil;
|
|
import com.intellij.psi.tree.IElementType;
|
|
import com.intellij.psi.tree.TokenSet;
|
|
import com.intellij.util.indexing.IndexingDataKeys;
|
|
import org.jetbrains.annotations.ApiStatus;
|
|
import org.jetbrains.annotations.NotNull;
|
|
import org.jetbrains.annotations.Nullable;
|
|
import org.jetbrains.annotations.PropertyKey;
|
|
|
|
import java.util.function.Function;
|
|
import java.util.function.Predicate;
|
|
|
|
import static com.intellij.platform.syntax.lexer.TokenListUtil.tokenListLexer;
|
|
import static com.intellij.psi.impl.source.BasicJavaDocElementType.BASIC_DOC_COMMENT;
|
|
|
|
@ApiStatus.Experimental
|
|
public final class BasicJavaParserUtil {
|
|
private static final Key<LanguageLevel> LANG_LEVEL_KEY = Key.create("JavaParserUtil.LanguageLevel");
|
|
private static final Key<Boolean> DEEP_PARSE_BLOCKS_IN_STATEMENTS = Key.create("JavaParserUtil.ParserExtender");
|
|
|
|
private BasicJavaParserUtil() { }
|
|
|
|
/**
|
|
* @deprecated Use the new Java syntax library instead.
|
|
* See {@link com.intellij.java.syntax.parser.JavaParser}
|
|
*/
|
|
@Deprecated
|
|
public static void setLanguageLevel(final PsiBuilder builder, final LanguageLevel level) {
|
|
builder.putUserData(LANG_LEVEL_KEY, level);
|
|
}
|
|
|
|
/**
|
|
* @deprecated Use the new Java syntax library instead.
|
|
* See {@link com.intellij.java.syntax.parser.JavaParser}
|
|
*/
|
|
@Deprecated
|
|
public static @NotNull LanguageLevel getLanguageLevel(final PsiBuilder builder) {
|
|
final LanguageLevel level = builder.getUserData(LANG_LEVEL_KEY);
|
|
assert level != null : builder;
|
|
return level;
|
|
}
|
|
|
|
/**
|
|
* @deprecated Use the new Java syntax library instead.
|
|
* See {@link com.intellij.java.syntax.parser.JavaParser}
|
|
*/
|
|
@Deprecated
|
|
public static void setParseStatementCodeBlocksDeep(final PsiBuilder builder, final boolean deep) {
|
|
builder.putUserData(DEEP_PARSE_BLOCKS_IN_STATEMENTS, deep);
|
|
}
|
|
|
|
/**
|
|
* @deprecated Use the new Java syntax library instead.
|
|
* See {@link com.intellij.java.syntax.parser.JavaParser}
|
|
*/
|
|
@Deprecated
|
|
public static boolean isParseStatementCodeBlocksDeep(final PsiBuilder builder) {
|
|
return Boolean.TRUE.equals(builder.getUserData(DEEP_PARSE_BLOCKS_IN_STATEMENTS));
|
|
}
|
|
|
|
/**
|
|
* @deprecated use {@link BasicJavaParserUtil#done(PsiBuilder.Marker, IElementType, PsiBuilder, WhiteSpaceAndCommentSetHolder)}
|
|
*/
|
|
@Deprecated
|
|
public static void done(final @NotNull PsiBuilder.Marker marker,
|
|
final @NotNull IElementType type,
|
|
final @NotNull WhiteSpaceAndCommentSetHolder commentSetHolder) {
|
|
marker.done(type);
|
|
final WhitespacesAndCommentsBinder left =
|
|
commentSetHolder.getPrecedingCommentSet().contains(type) ? commentSetHolder.getPrecedingCommentBinder(LanguageLevel.HIGHEST)
|
|
: null;
|
|
final WhitespacesAndCommentsBinder right =
|
|
commentSetHolder.getTrailingCommentSet().contains(type) ? commentSetHolder.getTrailingCommentBinder()
|
|
: null;
|
|
marker.setCustomEdgeTokenBinders(left, right);
|
|
}
|
|
|
|
/**
|
|
* @deprecated Use the new Java syntax library instead.
|
|
* See {@link com.intellij.java.syntax.parser.JavaParser}
|
|
*/
|
|
@Deprecated
|
|
public static void done(final @NotNull PsiBuilder.Marker marker,
|
|
final @NotNull IElementType type,
|
|
final @NotNull PsiBuilder builder,
|
|
final @NotNull WhiteSpaceAndCommentSetHolder commentSetHolder) {
|
|
marker.done(type);
|
|
final WhitespacesAndCommentsBinder left =
|
|
commentSetHolder.getPrecedingCommentSet().contains(type) ? commentSetHolder.getPrecedingCommentBinder(getLanguageLevel(builder))
|
|
: null;
|
|
final WhitespacesAndCommentsBinder right =
|
|
commentSetHolder.getTrailingCommentSet().contains(type) ? commentSetHolder.getTrailingCommentBinder()
|
|
: null;
|
|
marker.setCustomEdgeTokenBinders(left, right);
|
|
}
|
|
|
|
/**
|
|
* @deprecated Use the new Java syntax library instead.
|
|
* See {@link com.intellij.java.syntax.parser.JavaParser}
|
|
*/
|
|
@Deprecated
|
|
public static @Nullable IElementType exprType(final @Nullable PsiBuilder.Marker marker) {
|
|
return marker != null ? marker.getTokenType() : null;
|
|
}
|
|
|
|
// used instead of PsiBuilder.error() as it keeps all subsequent error messages
|
|
/**
|
|
* @deprecated Use the new Java syntax library instead.
|
|
* See {@link com.intellij.java.syntax.parser.JavaParser}
|
|
*/
|
|
@Deprecated
|
|
public static void error(final PsiBuilder builder, @NotNull @NlsContexts.ParsingError String message) {
|
|
builder.mark().error(message);
|
|
}
|
|
|
|
/**
|
|
* @deprecated Use the new Java syntax library instead.
|
|
* See {@link com.intellij.java.syntax.parser.JavaParser}
|
|
*/
|
|
@Deprecated
|
|
public static void error(final PsiBuilder builder,
|
|
@NotNull @NlsContexts.ParsingError String message,
|
|
final @Nullable PsiBuilder.Marker before) {
|
|
if (before == null) {
|
|
error(builder, message);
|
|
}
|
|
else {
|
|
before.precede().errorBefore(message, before);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @deprecated Use the new Java syntax library instead.
|
|
* See {@link com.intellij.java.syntax.parser.JavaParser}
|
|
*/
|
|
@Deprecated
|
|
public static boolean expectOrError(PsiBuilder builder,
|
|
TokenSet expected,
|
|
@PropertyKey(resourceBundle = JavaPsiBundle.BUNDLE) String key) {
|
|
if (!PsiBuilderUtil.expect(builder, expected)) {
|
|
error(builder, JavaPsiBundle.message(key));
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @deprecated Use the new Java syntax library instead.
|
|
* See {@link com.intellij.java.syntax.parser.JavaParser}
|
|
*/
|
|
@Deprecated
|
|
public static boolean expectOrError(PsiBuilder builder,
|
|
IElementType expected,
|
|
@PropertyKey(resourceBundle = JavaPsiBundle.BUNDLE) String key) {
|
|
if (!PsiBuilderUtil.expect(builder, expected)) {
|
|
error(builder, JavaPsiBundle.message(key));
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @deprecated Use the new Java syntax library instead.
|
|
* See {@link com.intellij.java.syntax.parser.JavaParser}
|
|
*/
|
|
@Deprecated
|
|
public static void emptyElement(final PsiBuilder builder, final IElementType type) {
|
|
builder.mark().done(type);
|
|
}
|
|
|
|
/**
|
|
* @deprecated Use the new Java syntax library instead.
|
|
* See {@link com.intellij.java.syntax.parser.JavaParser}
|
|
*/
|
|
@Deprecated
|
|
public static void emptyElement(final PsiBuilder.Marker before, final IElementType type) {
|
|
before.precede().doneBefore(type, before);
|
|
}
|
|
|
|
/**
|
|
* @deprecated Use the new Java syntax library instead.
|
|
* See {@link com.intellij.java.syntax.parser.JavaParser}
|
|
*/
|
|
@Deprecated
|
|
public static void semicolon(final PsiBuilder builder) {
|
|
expectOrError(builder, JavaTokenType.SEMICOLON, "expected.semicolon");
|
|
}
|
|
|
|
/**
|
|
* @deprecated Use the new Java syntax library instead.
|
|
* See {@link com.intellij.java.syntax.parser.JavaParser}
|
|
*/
|
|
@Deprecated
|
|
public static PsiBuilder braceMatchingBuilder(final PsiBuilder builder) {
|
|
final PsiBuilder.Marker pos = builder.mark();
|
|
|
|
int braceCount = 1;
|
|
while (!builder.eof()) {
|
|
final IElementType tokenType = builder.getTokenType();
|
|
if (tokenType == JavaTokenType.LBRACE) {
|
|
braceCount++;
|
|
}
|
|
else if (tokenType == JavaTokenType.RBRACE) braceCount--;
|
|
if (braceCount == 0) break;
|
|
builder.advanceLexer();
|
|
}
|
|
final int stopAt = builder.getCurrentOffset();
|
|
|
|
pos.rollbackTo();
|
|
|
|
return stoppingBuilder(builder, stopAt);
|
|
}
|
|
|
|
/**
|
|
* @deprecated Use the new Java syntax library instead.
|
|
* See {@link com.intellij.java.syntax.parser.JavaParser}
|
|
*/
|
|
@Deprecated
|
|
public static PsiBuilder stoppingBuilder(final PsiBuilder builder, final int stopAt) {
|
|
return new PsiBuilderAdapter(builder) {
|
|
@Override
|
|
public IElementType getTokenType() {
|
|
return getCurrentOffset() < stopAt ? super.getTokenType() : null;
|
|
}
|
|
|
|
@Override
|
|
public boolean eof() {
|
|
return getCurrentOffset() >= stopAt || super.eof();
|
|
}
|
|
};
|
|
}
|
|
|
|
@FunctionalInterface
|
|
public interface ParserWrapper {
|
|
void parse(@NotNull SyntaxTreeBuilder builder, @NotNull LanguageLevel languageLevel);
|
|
}
|
|
|
|
/**
|
|
* @deprecated Use the new Java syntax library instead.
|
|
* See {@link com.intellij.java.syntax.parser.JavaParser}
|
|
*/
|
|
@Deprecated
|
|
public static PsiBuilder stoppingBuilder(final PsiBuilder builder, final Predicate<? super Pair<IElementType, String>> condition) {
|
|
return new PsiBuilderAdapter(builder) {
|
|
@Override
|
|
public IElementType getTokenType() {
|
|
final Pair<IElementType, String> input = Pair.create(builder.getTokenType(), builder.getTokenText());
|
|
return condition.test(input) ? null : super.getTokenType();
|
|
}
|
|
|
|
@Override
|
|
public boolean eof() {
|
|
final Pair<IElementType, String> input = Pair.create(builder.getTokenType(), builder.getTokenText());
|
|
return condition.test(input) || super.eof();
|
|
}
|
|
};
|
|
}
|
|
|
|
public static @Nullable ASTNode parseFragmentWithHighestLanguageLevel(
|
|
ASTNode chameleon,
|
|
BasicJavaParserUtil.ParserWrapper wrapper
|
|
) {
|
|
return parseFragment(chameleon, wrapper, true, LanguageLevel.HIGHEST, JavaLexer::new);
|
|
}
|
|
|
|
public static @Nullable ASTNode parseFragment(
|
|
@NotNull ASTNode chameleon,
|
|
@NotNull BasicJavaParserUtil.ParserWrapper wrapper,
|
|
boolean eatAll,
|
|
@NotNull LanguageLevel level) {
|
|
return parseFragment(chameleon, wrapper, eatAll, level, JavaLexer::new);
|
|
}
|
|
|
|
public static @Nullable ASTNode parseFragment(
|
|
@NotNull ASTNode chameleon,
|
|
@NotNull BasicJavaParserUtil.ParserWrapper wrapper,
|
|
boolean eatAll,
|
|
@NotNull LanguageLevel level,
|
|
@NotNull Function<LanguageLevel, ? extends Lexer> javaLexer
|
|
) {
|
|
final PsiElement psi = chameleon.getTreeParent() != null ? chameleon.getTreeParent().getPsi() : chameleon.getPsi();
|
|
assert psi != null : chameleon;
|
|
|
|
PsiSyntaxBuilderFactory factory = PsiSyntaxBuilderFactory.getInstance();
|
|
Lexer lexer = BasicJavaAstTreeUtil.is(chameleon, BASIC_DOC_COMMENT) ? new JavaDocLexer(level) : javaLexer.apply(level);
|
|
PsiSyntaxBuilder psiBuilder = factory.createBuilder(chameleon, lexer, chameleon.getElementType().getLanguage(), chameleon.getChars());
|
|
|
|
long startTime = System.nanoTime();
|
|
SyntaxTreeBuilder builder = psiBuilder.getSyntaxTreeBuilder();
|
|
SyntaxTreeBuilder.Marker root = builder.mark();
|
|
wrapper.parse(builder, level);
|
|
if (!builder.eof()) {
|
|
if (!eatAll) throw new AssertionError("Unexpected token: '" + builder.getTokenText() + "'");
|
|
final SyntaxTreeBuilder.Marker extras = builder.mark();
|
|
while (!builder.eof()) builder.advanceLexer();
|
|
extras.error(JavaPsiBundle.message("unexpected.tokens"));
|
|
}
|
|
ElementTypeConverter converter = ElementTypeConverters.getConverter(JavaLanguage.INSTANCE);
|
|
root.done(ElementTypeConverterKt.convertNotNull(converter, chameleon.getElementType()));
|
|
ASTNode result = psiBuilder.getTreeBuilt().getFirstChildNode();
|
|
ParsingDiagnostics.registerParse(builder, chameleon.getElementType().getLanguage(), System.nanoTime() - startTime);
|
|
return result;
|
|
}
|
|
|
|
|
|
public static @NotNull PsiSyntaxBuilderWithLanguageLevel createSyntaxBuilder(@NotNull ASTNode chameleon,
|
|
@NotNull Function<PsiElement, LanguageLevel> languageLevelFunction,
|
|
@NotNull Function<PsiFile, TokenList> psiAsLexer) {
|
|
PsiElement psi = chameleon.getPsi();
|
|
assert psi != null : chameleon;
|
|
|
|
CharSequence indexedText = psi.getUserData(IndexingDataKeys.FILE_TEXT_CONTENT_KEY);
|
|
|
|
CharSequence text;
|
|
if (TreeUtil.isCollapsedChameleon(chameleon)) {
|
|
text = chameleon.getChars();
|
|
}
|
|
else {
|
|
text = indexedText;
|
|
if (text == null) text = chameleon.getChars();
|
|
}
|
|
|
|
LanguageLevel level = languageLevelFunction.apply(psi);
|
|
Lexer lexer = psi instanceof PsiFile && indexedText != null ? tokenListLexer(psiAsLexer.apply((PsiFile)psi))
|
|
: new JavaLexer(level);
|
|
Language language = psi.getLanguage();
|
|
if (!language.isKindOf(JavaLanguage.INSTANCE)) language = JavaLanguage.INSTANCE;
|
|
PsiSyntaxBuilderFactory factory = PsiSyntaxBuilderFactory.getInstance();
|
|
PsiSyntaxBuilder builder = factory.createBuilder(chameleon, lexer, language, text);
|
|
|
|
return new PsiSyntaxBuilderWithLanguageLevel(builder, level);
|
|
}
|
|
|
|
public static @NotNull PsiSyntaxBuilderWithLanguageLevel createSyntaxBuilder(@NotNull LighterLazyParseableNode chameleon,
|
|
@NotNull Function<PsiElement, LanguageLevel> languageLevelFunction) {
|
|
PsiElement psi = chameleon.getContainingFile();
|
|
assert psi != null : chameleon;
|
|
|
|
PsiSyntaxBuilderFactory factory = PsiSyntaxBuilderFactory.getInstance();
|
|
LanguageLevel level = languageLevelFunction.apply(psi);
|
|
Lexer lexer = new JavaLexer(level);
|
|
PsiSyntaxBuilder builder = factory.createBuilder(chameleon, lexer, chameleon.getTokenType().getLanguage(), chameleon.getText());
|
|
|
|
return new PsiSyntaxBuilderWithLanguageLevel(builder, level);
|
|
}
|
|
|
|
/**
|
|
* @deprecated Use the new Java syntax library instead.
|
|
* See {@link com.intellij.java.syntax.parser.JavaParser}
|
|
*/
|
|
@Deprecated
|
|
public static @NotNull PsiBuilder createBuilder(final @NotNull ASTNode chameleon,
|
|
@NotNull Function<PsiElement, LanguageLevel> languageLevelFunction,
|
|
@NotNull Function<LanguageLevel, com.intellij.lexer.Lexer> lexerFunction,
|
|
@NotNull Function<PsiFile, com.intellij.lexer.TokenList> psiAsLexer) {
|
|
final PsiElement psi = chameleon.getPsi();
|
|
assert psi != null : chameleon;
|
|
final Project project = psi.getProject();
|
|
|
|
CharSequence indexedText = psi.getUserData(IndexingDataKeys.FILE_TEXT_CONTENT_KEY);
|
|
|
|
CharSequence text;
|
|
if (TreeUtil.isCollapsedChameleon(chameleon)) {
|
|
text = chameleon.getChars();
|
|
}
|
|
else {
|
|
text = indexedText;
|
|
if (text == null) text = chameleon.getChars();
|
|
}
|
|
|
|
LanguageLevel level = languageLevelFunction.apply(psi);
|
|
com.intellij.lexer.Lexer lexer = psi instanceof PsiFile && indexedText != null ? psiAsLexer.apply((PsiFile)psi).asLexer()
|
|
: lexerFunction.apply(level);
|
|
Language language = psi.getLanguage();
|
|
if (!language.isKindOf(JavaLanguage.INSTANCE)) language = JavaLanguage.INSTANCE;
|
|
PsiBuilder builder = PsiBuilderFactory.getInstance().createBuilder(project, chameleon, lexer, language, text);
|
|
setLanguageLevel(builder, level);
|
|
|
|
return builder;
|
|
}
|
|
|
|
/**
|
|
* @deprecated Use the new Java syntax library instead.
|
|
* See {@link com.intellij.java.syntax.parser.JavaParser}
|
|
*/
|
|
@Deprecated
|
|
public static @NotNull PsiBuilder createBuilder(@NotNull LighterLazyParseableNode chameleon,
|
|
@NotNull Function<PsiElement, LanguageLevel> languageLevelFunction,
|
|
@NotNull Function<LanguageLevel, com.intellij.lexer.Lexer> lexerFunction) {
|
|
final PsiElement psi = chameleon.getContainingFile();
|
|
assert psi != null : chameleon;
|
|
final Project project = psi.getProject();
|
|
|
|
final PsiBuilderFactory factory = PsiBuilderFactory.getInstance();
|
|
final LanguageLevel level = languageLevelFunction.apply(psi);
|
|
final com.intellij.lexer.Lexer lexer = lexerFunction.apply(level);
|
|
final PsiBuilder builder =
|
|
factory.createBuilder(project, chameleon, lexer, chameleon.getTokenType().getLanguage(), chameleon.getText());
|
|
setLanguageLevel(builder, level);
|
|
|
|
return builder;
|
|
}
|
|
} |