HQL semantic tokens provider initial work
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2024 Broadcom, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* https://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Broadcom, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.data.jpa.queries;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Stack;
|
||||
|
||||
import org.antlr.v4.runtime.CharStreams;
|
||||
import org.antlr.v4.runtime.CommonTokenStream;
|
||||
import org.antlr.v4.runtime.Token;
|
||||
import org.antlr.v4.runtime.tree.ErrorNode;
|
||||
import org.antlr.v4.runtime.tree.TerminalNode;
|
||||
import org.springframework.ide.vscode.commons.languageserver.semantic.tokens.SemanticTokenData;
|
||||
import org.springframework.ide.vscode.commons.languageserver.semantic.tokens.SemanticTokensDataProvider;
|
||||
import org.springframework.ide.vscode.parser.hql.HqlBaseListener;
|
||||
import org.springframework.ide.vscode.parser.hql.HqlLexer;
|
||||
import org.springframework.ide.vscode.parser.hql.HqlParser;
|
||||
import org.springframework.ide.vscode.parser.hql.HqlParser.EntityNameContext;
|
||||
import org.springframework.ide.vscode.parser.hql.HqlParser.SimplePathElementContext;
|
||||
|
||||
public class HqlSemanticTokens implements SemanticTokensDataProvider {
|
||||
|
||||
private static List<String> TOKEN_TYPES = List.of("keyword", "type", "class", "string", "number", "operator",
|
||||
"variable", "method", "modifier", "regexp");
|
||||
|
||||
@Override
|
||||
public List<String> getTokenTypes() {
|
||||
return TOKEN_TYPES;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SemanticTokenData> computeTokens(String text, int initialOffset) {
|
||||
List<SemanticTokenData> tokens = new ArrayList<>();
|
||||
HqlLexer lexer = new HqlLexer(CharStreams.fromString(text));
|
||||
CommonTokenStream antlrTokens = new CommonTokenStream(lexer);
|
||||
HqlParser parser = new HqlParser(antlrTokens);
|
||||
LinkedHashSet<Token> coloredTokens = new LinkedHashSet<>();
|
||||
|
||||
Stack<SimplePathElementContext> pathContexts = new Stack<>();
|
||||
|
||||
parser.addParseListener(new HqlBaseListener() {
|
||||
|
||||
private void addToken(Token token, String tokenType) {
|
||||
tokens.add(new SemanticTokenData(token.getStartIndex() + initialOffset,
|
||||
token.getStartIndex() + token.getText().length() + initialOffset, tokenType, new String[0]));
|
||||
coloredTokens.add(token);
|
||||
}
|
||||
|
||||
private void processTerminalNode(TerminalNode node) {
|
||||
if (coloredTokens.contains(node.getSymbol())) {
|
||||
return;
|
||||
}
|
||||
int type = node.getSymbol().getType();
|
||||
switch (type) {
|
||||
case HqlParser.STRINGLITERAL:
|
||||
case HqlParser.CHARACTER:
|
||||
addToken(node.getSymbol(), "string");
|
||||
break;
|
||||
case HqlParser.HEXLITERAL:
|
||||
case HqlParser.INTEGER_LITERAL:
|
||||
case HqlParser.FLOAT_LITERAL:
|
||||
case HqlParser.BINARY_LITERAL:
|
||||
addToken(node.getSymbol(), "number");
|
||||
break;
|
||||
case HqlParser.IDENTIFICATION_VARIABLE:
|
||||
if (pathContexts.isEmpty()) {
|
||||
addToken(node.getSymbol(), "variable");
|
||||
} else {
|
||||
addToken(node.getSymbol(), "method");
|
||||
}
|
||||
break;
|
||||
case HqlParser.JAVASTRINGLITERAL:
|
||||
addToken(node.getSymbol(), "class");
|
||||
break;
|
||||
case HqlParser.WS:
|
||||
break;
|
||||
// case JpqlParser.SPEL:
|
||||
// addToken(node.getSymbol(), "regexp");
|
||||
// break;
|
||||
default:
|
||||
if (HqlParser.WS < type && type <= HqlParser.YEAR) {
|
||||
addToken(node.getSymbol(), "keyword");
|
||||
} else {
|
||||
addToken(node.getSymbol(), "modifier");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitTerminal(TerminalNode node) {
|
||||
processTerminalNode(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitErrorNode(ErrorNode node) {
|
||||
processTerminalNode(node);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void enterEntityName(EntityNameContext ctx) {
|
||||
addToken(ctx.getStart(), "class");
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void enterSimplePathElement(SimplePathElementContext ctx) {
|
||||
pathContexts.push(ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exitSimplePathElement(SimplePathElementContext ctx) {
|
||||
pathContexts.pop();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
parser.ql_statement();
|
||||
return tokens;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -21,14 +21,14 @@ import org.antlr.v4.runtime.tree.ErrorNode;
|
||||
import org.antlr.v4.runtime.tree.TerminalNode;
|
||||
import org.springframework.ide.vscode.commons.languageserver.semantic.tokens.SemanticTokenData;
|
||||
import org.springframework.ide.vscode.commons.languageserver.semantic.tokens.SemanticTokensDataProvider;
|
||||
import org.springframework.ide.vscode.jpql.antlr.parser.JpqlBaseListener;
|
||||
import org.springframework.ide.vscode.jpql.antlr.parser.JpqlLexer;
|
||||
import org.springframework.ide.vscode.jpql.antlr.parser.JpqlParser;
|
||||
import org.springframework.ide.vscode.jpql.antlr.parser.JpqlParser.Collection_valued_fieldContext;
|
||||
import org.springframework.ide.vscode.jpql.antlr.parser.JpqlParser.Entity_nameContext;
|
||||
import org.springframework.ide.vscode.jpql.antlr.parser.JpqlParser.Entity_type_literalContext;
|
||||
import org.springframework.ide.vscode.jpql.antlr.parser.JpqlParser.Single_valued_object_fieldContext;
|
||||
import org.springframework.ide.vscode.jpql.antlr.parser.JpqlParser.State_fieldContext;
|
||||
import org.springframework.ide.vscode.parser.jpql.JpqlBaseListener;
|
||||
import org.springframework.ide.vscode.parser.jpql.JpqlLexer;
|
||||
import org.springframework.ide.vscode.parser.jpql.JpqlParser;
|
||||
import org.springframework.ide.vscode.parser.jpql.JpqlParser.Collection_valued_fieldContext;
|
||||
import org.springframework.ide.vscode.parser.jpql.JpqlParser.Entity_nameContext;
|
||||
import org.springframework.ide.vscode.parser.jpql.JpqlParser.Entity_type_literalContext;
|
||||
import org.springframework.ide.vscode.parser.jpql.JpqlParser.Single_valued_object_fieldContext;
|
||||
import org.springframework.ide.vscode.parser.jpql.JpqlParser.State_fieldContext;
|
||||
|
||||
public class JpqlSemanticTokens implements SemanticTokensDataProvider {
|
||||
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2024 Broadcom, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* https://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Broadcom, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.data.jpa.queries;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.ide.vscode.commons.languageserver.semantic.tokens.SemanticTokenData;
|
||||
|
||||
public class HqlSemanticTokensTest {
|
||||
|
||||
private HqlSemanticTokens provider = new HqlSemanticTokens();
|
||||
|
||||
@Test
|
||||
void simpleQuery_1() {
|
||||
List<SemanticTokenData> tokens = provider.computeTokens("SELECT owner FROM Owner owner", 0);
|
||||
assertThat(tokens.get(0)).isEqualTo(new SemanticTokenData(0, 6, "keyword", new String[0]));
|
||||
assertThat(tokens.get(1)).isEqualTo(new SemanticTokenData(7, 12, "variable", new String[0]));
|
||||
assertThat(tokens.get(2)).isEqualTo(new SemanticTokenData(13, 17, "keyword", new String[0]));
|
||||
assertThat(tokens.get(3)).isEqualTo(new SemanticTokenData(18, 23, "class", new String[0]));
|
||||
assertThat(tokens.get(4)).isEqualTo(new SemanticTokenData(24, 29, "variable", new String[0]));
|
||||
|
||||
assertThat(tokens.size()).isEqualTo(5);
|
||||
}
|
||||
|
||||
@Test
|
||||
void initialOffset() {
|
||||
List<SemanticTokenData> tokens = provider.computeTokens("SELECT owner FROM Owner owner", 3);
|
||||
assertThat(tokens.get(0)).isEqualTo(new SemanticTokenData(3, 9, "keyword", new String[0]));
|
||||
assertThat(tokens.get(1)).isEqualTo(new SemanticTokenData(10, 15, "variable", new String[0]));
|
||||
assertThat(tokens.get(2)).isEqualTo(new SemanticTokenData(16, 20, "keyword", new String[0]));
|
||||
assertThat(tokens.get(3)).isEqualTo(new SemanticTokenData(21, 26, "class", new String[0]));
|
||||
assertThat(tokens.get(4)).isEqualTo(new SemanticTokenData(27, 32, "variable", new String[0]));
|
||||
|
||||
assertThat(tokens.size()).isEqualTo(5);
|
||||
}
|
||||
|
||||
@Test
|
||||
void query_with_conflicting_groupby() {
|
||||
List<SemanticTokenData> tokens = provider.computeTokens("SELECT g FROM Group g GROUP BY g.name", 0);
|
||||
assertThat(tokens.get(0)).isEqualTo(new SemanticTokenData(0, 6, "keyword", new String[0]));
|
||||
assertThat(tokens.get(1)).isEqualTo(new SemanticTokenData(7, 8, "variable", new String[0]));
|
||||
assertThat(tokens.get(2)).isEqualTo(new SemanticTokenData(9, 13, "keyword", new String[0]));
|
||||
assertThat(tokens.get(3)).isEqualTo(new SemanticTokenData(14, 19, "class", new String[0]));
|
||||
assertThat(tokens.get(4)).isEqualTo(new SemanticTokenData(20, 21, "variable", new String[0]));
|
||||
assertThat(tokens.get(5)).isEqualTo(new SemanticTokenData(22, 27, "keyword", new String[0]));
|
||||
assertThat(tokens.get(6)).isEqualTo(new SemanticTokenData(28, 30, "keyword", new String[0]));
|
||||
assertThat(tokens.get(7)).isEqualTo(new SemanticTokenData(31, 32, "variable", new String[0]));
|
||||
assertThat(tokens.get(8)).isEqualTo(new SemanticTokenData(32, 33, "modifier", new String[0]));
|
||||
assertThat(tokens.get(9)).isEqualTo(new SemanticTokenData(33, 37, "method", new String[0]));
|
||||
|
||||
assertThat(tokens.size()).isEqualTo(10);
|
||||
}
|
||||
|
||||
@Test
|
||||
void query_with_parameter() {
|
||||
List<SemanticTokenData> tokens = provider.computeTokens("SELECT DISTINCT owner FROM Owner owner left join owner.pets WHERE owner.lastName LIKE :lastName%", 0);
|
||||
assertThat(tokens.get(0)).isEqualTo(new SemanticTokenData(0, 6, "keyword", new String[0])); // SELECT
|
||||
assertThat(tokens.get(1)).isEqualTo(new SemanticTokenData(7, 15, "keyword", new String[0])); // DISTINCT
|
||||
assertThat(tokens.get(2)).isEqualTo(new SemanticTokenData(16, 21, "variable", new String[0])); // owner
|
||||
assertThat(tokens.get(3)).isEqualTo(new SemanticTokenData(22, 26, "keyword", new String[0])); // FROM
|
||||
assertThat(tokens.get(4)).isEqualTo(new SemanticTokenData(27, 32, "class", new String[0])); // Owner
|
||||
assertThat(tokens.get(5)).isEqualTo(new SemanticTokenData(33, 38, "variable", new String[0])); // owner
|
||||
assertThat(tokens.get(6)).isEqualTo(new SemanticTokenData(39, 43, "keyword", new String[0])); // left
|
||||
assertThat(tokens.get(7)).isEqualTo(new SemanticTokenData(44, 48, "keyword", new String[0])); // join
|
||||
assertThat(tokens.get(8)).isEqualTo(new SemanticTokenData(50, 55, "variable", new String[0])); // owner
|
||||
assertThat(tokens.get(9)).isEqualTo(new SemanticTokenData(55, 56, "modifier", new String[0])); // .
|
||||
assertThat(tokens.get(10)).isEqualTo(new SemanticTokenData(56, 60, "method", new String[0])); // pets
|
||||
assertThat(tokens.get(11)).isEqualTo(new SemanticTokenData(61, 66, "keyword", new String[0])); // WHERE
|
||||
assertThat(tokens.get(12)).isEqualTo(new SemanticTokenData(67, 72, "variable", new String[0])); // owner
|
||||
assertThat(tokens.get(13)).isEqualTo(new SemanticTokenData(72, 73, "modifier", new String[0])); // .
|
||||
assertThat(tokens.get(14)).isEqualTo(new SemanticTokenData(73, 81, "method", new String[0])); // lastName
|
||||
assertThat(tokens.get(15)).isEqualTo(new SemanticTokenData(82, 86, "keyword", new String[0])); // LIKE
|
||||
assertThat(tokens.get(16)).isEqualTo(new SemanticTokenData(87, 88, "modifier", new String[0])); // :
|
||||
assertThat(tokens.get(17)).isEqualTo(new SemanticTokenData(88, 96, "variable", new String[0])); // lastName
|
||||
assertThat(tokens.get(18)).isEqualTo(new SemanticTokenData(96, 97, "modifier", new String[0])); // lastName
|
||||
|
||||
assertThat(tokens.size()).isEqualTo(19);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user