Lexer for sealed classes: IDEA-223111

GitOrigin-RevId: 03526b93d73bd1a96d0db58b008163db9c573fb2
This commit is contained in:
Roman.Ivanov
2020-06-11 08:12:25 +03:00
committed by intellij-monorepo-bot
parent 6a3633295a
commit ea992d5d4c
5 changed files with 96 additions and 5 deletions
@@ -142,4 +142,8 @@ public interface JavaTokenType extends TokenType {
IElementType VAR_KEYWORD = new IJavaElementType("VAR");
IElementType YIELD_KEYWORD = new IJavaElementType("YIELD");
IElementType RECORD_KEYWORD = new IJavaElementType("RECORD");
IElementType SEALED_KEYWORD = new IJavaElementType("SEALED");
IElementType NON_SEALED_KEYWORD = new IKeywordElementType("NON_SEALED");
IElementType PERMITS_KEYWORD = new IJavaElementType("PERMITS");
}
@@ -75,4 +75,8 @@ public interface PsiKeyword extends PsiJavaToken {
String YIELD = "yield";
String RECORD = "record";
String SEALED = "sealed";
String PERMITS = "permits";
String NON_SEALED = "non-sealed";
}
@@ -25,12 +25,17 @@ public class JavaLexer extends LexerBase {
ABSTRACT, BOOLEAN, BREAK, BYTE, CASE, CATCH, CHAR, CLASS, CONST, CONTINUE, DEFAULT, DO, DOUBLE, ELSE, EXTENDS, FINAL, FINALLY,
FLOAT, FOR, GOTO, IF, IMPLEMENTS, IMPORT, INSTANCEOF, INT, INTERFACE, LONG, NATIVE, NEW, PACKAGE, PRIVATE, PROTECTED, PUBLIC,
RETURN, SHORT, STATIC, STRICTFP, SUPER, SWITCH, SYNCHRONIZED, THIS, THROW, THROWS, TRANSIENT, TRY, VOID, VOLATILE, WHILE,
TRUE, FALSE, NULL);
TRUE, FALSE, NULL, NON_SEALED);
private static final Set<CharSequence> JAVA9_KEYWORDS =
new THashSet<>(Arrays.asList(OPEN, MODULE, REQUIRES, EXPORTS, OPENS, USES, PROVIDES, TRANSITIVE, TO, WITH),
CharSequenceHashingStrategy.CASE_SENSITIVE);
// Minus is a single token that may separate identifiers that can form "non-sealed" keyword
// For incremental lexing it is important that at "-" lexer is not in the initial state so we will lex one more token
private static final int INITIAL_STATE = 0;
private static final int MAYBE_NON_SEALED_STATE = -1;
public static boolean isKeyword(String id, @NotNull LanguageLevel level) {
return KEYWORDS.contains(id) ||
level.isAtLeast(LanguageLevel.JDK_1_4) && ASSERT.equals(id) ||
@@ -41,19 +46,24 @@ public class JavaLexer extends LexerBase {
return id != null &&
(level.isAtLeast(LanguageLevel.JDK_1_9) && JAVA9_KEYWORDS.contains(id) ||
level.isAtLeast(LanguageLevel.JDK_10) && VAR.contentEquals(id) ||
level.isAtLeast(LanguageLevel.JDK_14_PREVIEW) && RECORD.contentEquals(id) ||
level.isAtLeast(LanguageLevel.JDK_14) && YIELD.contentEquals(id) ||
level.isAtLeast(LanguageLevel.JDK_14_PREVIEW) && RECORD.contentEquals(id));
(level.isAtLeast(LanguageLevel.JDK_15_PREVIEW) && (SEALED.contentEquals(id) || PERMITS.contentEquals(id)))
);
}
private final _JavaLexer myFlexLexer;
private final LanguageLevel myLevel;
private CharSequence myBuffer;
private char @Nullable [] myBufferArray;
private int myBufferIndex;
private int myBufferEndOffset;
private int myTokenEndOffset; // positioned after the last symbol of the current token
private IElementType myTokenType;
private int myState = INITIAL_STATE;
public JavaLexer(@NotNull LanguageLevel level) {
myLevel = level;
myFlexLexer = new _JavaLexer(level);
}
@@ -70,7 +80,7 @@ public class JavaLexer extends LexerBase {
@Override
public int getState() {
return 0;
return myState;
}
@Override
@@ -106,6 +116,7 @@ public class JavaLexer extends LexerBase {
myBufferIndex = myTokenEndOffset;
boolean wasMinus = false;
char c = charAt(myBufferIndex);
switch (c) {
case ' ':
@@ -162,10 +173,36 @@ public class JavaLexer extends LexerBase {
myTokenEndOffset = getClosingQuote(myBufferIndex + 1, c);
}
break;
case 'n':
if (myLevel.isAtLeast(LanguageLevel.JDK_15_PREVIEW) &&
myBufferIndex + 9 < myBufferEndOffset &&
charAt(myBufferIndex + 1) == 'o' &&
charAt(myBufferIndex + 2) == 'n' &&
charAt(myBufferIndex + 3) == '-' &&
charAt(myBufferIndex + 4) == 's' &&
charAt(myBufferIndex + 5) == 'e' &&
charAt(myBufferIndex + 6) == 'a' &&
charAt(myBufferIndex + 7) == 'l' &&
charAt(myBufferIndex + 8) == 'e' &&
charAt(myBufferIndex + 9) == 'd') {
myTokenType = JavaTokenType.NON_SEALED_KEYWORD;
myTokenEndOffset = myBufferIndex + 10;
}
else {
flexLocateToken();
}
break;
case '-':
wasMinus = true;
// fallthrough
default:
flexLocateToken();
}
if (wasMinus) {
myState = MAYBE_NON_SEALED_STATE;
} else {
myState = INITIAL_STATE;
}
if (myTokenEndOffset > myBufferEndOffset) {
myTokenEndOffset = myBufferEndOffset;
@@ -12,6 +12,7 @@ import com.intellij.openapi.editor.colors.EditorColorsManager;
import com.intellij.openapi.editor.ex.EditorEx;
import com.intellij.openapi.editor.highlighter.EditorHighlighter;
import com.intellij.openapi.editor.highlighter.HighlighterIterator;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.JavaDocTokenType;
import com.intellij.psi.JavaTokenType;
import com.intellij.testFramework.LightJavaCodeInsightTestCase;
@@ -24,6 +25,11 @@ public class JavaHighlighterTest extends LightJavaCodeInsightTestCase {
private Document myDocument;
private final ArrayList<Editor> myEditorsToRelease = new ArrayList<>();
@Override
protected LanguageLevel getLanguageLevel() {
return LanguageLevel.JDK_15_PREVIEW;
}
@Override
protected void tearDown() throws Exception {
try {
@@ -158,6 +164,23 @@ public class JavaHighlighterTest extends LightJavaCodeInsightTestCase {
CheckHighlighterConsistency.performCheck(editor);
}
public void testNonSealedEditing() {
Editor editor = initDocument("non-sealed class A {\n" +
" \n" +
"} ");
WriteCommandAction.runWriteCommandAction(getProject(), () -> {
int idx = myDocument.getText().lastIndexOf(" class");
myDocument.deleteString(idx - 1, idx);
});
CheckHighlighterConsistency.performCheck(editor);
WriteCommandAction.runWriteCommandAction(getProject(), () -> {
int idx = myDocument.getText().lastIndexOf(" class");
myDocument.insertString(idx, "d");
});
CheckHighlighterConsistency.performCheck(editor);
}
private Editor initDocument(String text) {
EditorFactory editorFactory = EditorFactory.getInstance();
myDocument = editorFactory.createDocument(text);
@@ -165,7 +188,7 @@ public class JavaHighlighterTest extends LightJavaCodeInsightTestCase {
myHighlighter = HighlighterFactory
.createHighlighter(JavaFileType.INSTANCE, EditorColorsManager.getInstance().getGlobalScheme(), getProject());
((EditorEx) editor).setHighlighter(myHighlighter);
((EditorEx)editor).setHighlighter(myHighlighter);
myEditorsToRelease.add(editor);
return editor;
@@ -0,0 +1,23 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.java.lexer;
import com.intellij.lang.java.JavaParserDefinition;
import com.intellij.lexer.Lexer;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.testFramework.LexerTestCase;
public class Java15LexerTest extends LexerTestCase {
public void testNonSealed() {
doTest("non-sealed", "NON_SEALED ('non-sealed')");
}
@Override
protected Lexer createLexer() {
return JavaParserDefinition.createLexer(LanguageLevel.JDK_15_PREVIEW);
}
@Override
protected String getDirPath() {
return "";
}
}