Skip to content

Commit

Permalink
Fix multiline semantic highlighting for 'implements', 'extends', and …
Browse files Browse the repository at this point in the history
…'permits' keywords

Signed-off-by: Hope Hadfield <hhadfiel@redhat.com>
  • Loading branch information
hopehadfield committed Dec 8, 2023
1 parent d8f676c commit a1cb9e1
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -464,24 +464,33 @@ public boolean visit(RecordDeclaration node) {
public boolean visit(TypeDeclaration node) {
acceptNode(node.getJavadoc());
acceptNodeList(node.modifiers());
tokenizeGapBeforeTypeDeclarationName(node, (scannerToken, tokenOffset, tokenLength) -> {
tokenizeGapBeforeAndAfterTypeDeclarationName(node, (scannerToken, tokenOffset, tokenLength) -> {
switch (scannerToken) {
case ITerminalSymbols.TokenNameclass:
case ITerminalSymbols.TokenNameinterface:
addToken(tokenOffset, tokenLength, TokenType.MODIFIER, 0);
acceptNode(node.getName());
acceptNodeList(node.typeParameters());
break; // "class" or "interface" keyword tokens
case ITerminalSymbols.TokenNameextends:
addToken(tokenOffset, tokenLength, TokenType.MODIFIER, 0);
acceptNode(node.getSuperclassType());
break; // "extends" keyword token
case ITerminalSymbols.TokenNameimplements:
addToken(tokenOffset, tokenLength, TokenType.MODIFIER, 0);
acceptNodeList(node.superInterfaceTypes());
break; // "implements" keyword token
default:
break; // ignore other tokens
}
});
acceptNode(node.getName());
acceptNodeList(node.typeParameters());
acceptNode(node.getSuperclassType());
acceptNodeList(node.superInterfaceTypes());
acceptNodeList(node.bodyDeclarations());
if (DOMASTUtil.isFeatureSupportedinAST(cu.getAST(), Modifier.SEALED)) {
if (node.getRestrictedIdentifierStartPosition() != -1) {
addToken(node.getRestrictedIdentifierStartPosition(), 7, TokenType.MODIFIER, 0);
}
acceptNodeList(node.permittedTypes());
}
acceptNodeList(node.bodyDeclarations());
return false;
}

Expand Down Expand Up @@ -570,8 +579,10 @@ private void tokenizeWithScanner(int startPosition, int endPosition, ScannerToke
}

/**
* Uses an {@link IScanner} (if available) to tokenize the gap in the AST just before {@link TypeDeclaration#getName()},
* and visits the scanner tokens in order of occurrence in the source range.
* Uses an {@link IScanner} (if available) to tokenize the gap in the AST just before and after {@link TypeDeclaration#getName()},
* and visits the scanner tokens in order of occurrence in the source range. For example, the following would be tokenized as seen between the square brackets:
* <br><br>
* <code>public[ class FooBar extends Foo implements ]Bar</code>
*
* <p>
* <strong>NOTE:</strong> If semantic tokens are added by the visitor, the scan range MUST NOT intersect
Expand All @@ -581,7 +592,7 @@ private void tokenizeWithScanner(int startPosition, int endPosition, ScannerToke
* @param typeDeclaration the type declaration node
* @param tokenVisitor the visitor to use for scanner tokens
*/
private void tokenizeGapBeforeTypeDeclarationName(TypeDeclaration typeDeclaration, ScannerTokenVisitor tokenVisitor) {
private void tokenizeGapBeforeAndAfterTypeDeclarationName(TypeDeclaration typeDeclaration, ScannerTokenVisitor tokenVisitor) {
// Try potentially nonexistent start positions, closest first
int gapBeforeNameStart = getEndPosition(typeDeclaration.modifiers());
if (gapBeforeNameStart == -1) {
Expand All @@ -591,7 +602,14 @@ private void tokenizeGapBeforeTypeDeclarationName(TypeDeclaration typeDeclaratio
if (gapBeforeNameStart == -1) {
gapBeforeNameStart = typeDeclaration.getStartPosition();
}
int gapBeforeNameEnd = typeDeclaration.getName().getStartPosition();
// Try potentially nonexistent end positions, farthest first
int gapBeforeNameEnd = !typeDeclaration.superInterfaceTypes().isEmpty() ? ((ASTNode) (typeDeclaration.superInterfaceTypes().get(0))).getStartPosition() : -1;
if (gapBeforeNameEnd == -1) {
gapBeforeNameEnd = typeDeclaration.getSuperclassType() != null ? typeDeclaration.getSuperclassType().getStartPosition() : -1;
}
if (gapBeforeNameEnd == -1) {
gapBeforeNameEnd = typeDeclaration.getName().getStartPosition();
}
tokenizeWithScanner(gapBeforeNameStart, gapBeforeNameEnd, tokenVisitor);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,7 @@ protected InnerRecord() {
}

public interface TestInterface{}

class TestClass implements TestInterface{}
class TestExtends extends TestClass{}
class TestCombo extends TestClass implements TestInterface{}
}
Original file line number Diff line number Diff line change
Expand Up @@ -219,9 +219,28 @@ public void testSemanticTokens_Constructors() throws JavaModelException {
.assertNextToken("integer", "recordComponent", "declaration")
.assertNextToken("protected", "modifier")
.assertNextToken("InnerRecord", "record", "protected", "constructor", "declaration")

.assertNextToken("public", "modifier")
.assertNextToken("interface", "modifier")
.assertNextToken("TestInterface", "interface", "public", "static", "declaration")

.assertNextToken("class", "modifier")
.assertNextToken("TestClass", "class", "declaration")
.assertNextToken("implements", "modifier")
.assertNextToken("TestInterface", "interface", "public", "static")

.assertNextToken("class", "modifier")
.assertNextToken("TestExtends", "class", "declaration")
.assertNextToken("extends", "modifier")
.assertNextToken("TestClass", "class")

.assertNextToken("class", "modifier")
.assertNextToken("TestCombo", "class", "declaration")
.assertNextToken("extends", "modifier")
.assertNextToken("TestClass", "class")
.assertNextToken("implements", "modifier")
.assertNextToken("TestInterface", "interface", "public", "static")

.endAssertion();
}

Expand Down

0 comments on commit a1cb9e1

Please sign in to comment.