HQL semantic tokens provider initial work

This commit is contained in:
aboyko
2024-04-15 23:54:09 -04:00
parent fc0ed8840f
commit 61da7dbe91
14 changed files with 17808 additions and 17 deletions

View File

@@ -1,6 +1,10 @@
/antlr-4.13.1.jar
/src/main/java/org/springframework/ide/vscode/jpql/antlr/parser/Jpql.interp
/src/main/java/org/springframework/ide/vscode/jpql/antlr/parser/Jpql.tokens
/src/main/java/org/springframework/ide/vscode/jpql/antlr/parser/JpqlLexer.interp
/src/main/java/org/springframework/ide/vscode/jpql/antlr/parser/JpqlLexer.tokens
/src/main/java/org/springframework/ide/vscode/parser/jpql/Jpql.interp
/src/main/java/org/springframework/ide/vscode/parser/jpql/Jpql.tokens
/src/main/java/org/springframework/ide/vscode/parser/jpql/JpqlLexer.interp
/src/main/java/org/springframework/ide/vscode/parser/jpql/JpqlLexer.tokens
/src/main/java/org/springframework/ide/vscode/parser/hql/Hql.interp
/src/main/java/org/springframework/ide/vscode/parser/hql/Hql.tokens
/src/main/java/org/springframework/ide/vscode/parser/hql/HqlLexer.interp
/src/main/java/org/springframework/ide/vscode/parser/hql/HqlLexer.tokens
/target

View File

@@ -6,5 +6,6 @@ if [ ! -f antlr-${ANTLR_VERSION}.jar ]; then
fi
workdir=$(pwd)
cd grammars
java -jar ${workdir}/antlr-${ANTLR_VERSION}.jar Jpql.g4 -o ${workdir}/src/main/java/org/springframework/ide/vscode/jpql/antlr/parser -package org.springframework.ide.vscode.jpql.antlr.parser
java -jar ${workdir}/antlr-${ANTLR_VERSION}.jar Jpql.g4 -o ${workdir}/src/main/java/org/springframework/ide/vscode/parser/jpql -package org.springframework.ide.vscode.parser.jpql
java -jar ${workdir}/antlr-${ANTLR_VERSION}.jar Hql.g4 -o ${workdir}/src/main/java/org/springframework/ide/vscode//parser/hql -package org.springframework.ide.vscode.parser.hql

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
// Generated from Jpql.g4 by ANTLR 4.13.1
package org.springframework.ide.vscode.jpql.antlr.parser;
package org.springframework.ide.vscode.parser.jpql;
/**
* JPQL per https://jakarta.ee/specifications/persistence/3.1/jakarta-persistence-spec-3.1.html#bnf

View File

@@ -1,5 +1,5 @@
// Generated from Jpql.g4 by ANTLR 4.13.1
package org.springframework.ide.vscode.jpql.antlr.parser;
package org.springframework.ide.vscode.parser.jpql;
/**
* JPQL per https://jakarta.ee/specifications/persistence/3.1/jakarta-persistence-spec-3.1.html#bnf

View File

@@ -1,5 +1,5 @@
// Generated from Jpql.g4 by ANTLR 4.13.1
package org.springframework.ide.vscode.jpql.antlr.parser;
package org.springframework.ide.vscode.parser.jpql;
/**
* JPQL per https://jakarta.ee/specifications/persistence/3.1/jakarta-persistence-spec-3.1.html#bnf

View File

@@ -1,5 +1,5 @@
// Generated from Jpql.g4 by ANTLR 4.13.1
package org.springframework.ide.vscode.jpql.antlr.parser;
package org.springframework.ide.vscode.parser.jpql;
/**
* JPQL per https://jakarta.ee/specifications/persistence/3.1/jakarta-persistence-spec-3.1.html#bnf

View File

@@ -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;
}
}

View File

@@ -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 {

View File

@@ -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);
}
}