Input parameters for SPEL grammar

This commit is contained in:
aboyko
2024-08-14 11:51:08 -04:00
parent 5836f41f8b
commit ece638010f
8 changed files with 1117 additions and 836 deletions

View File

@@ -12,16 +12,26 @@ package org.springframework.ide.vscode.boot.java.spel;
import static org.springframework.ide.vscode.parser.spel.SpelLexer.*;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import org.antlr.v4.runtime.ANTLRErrorListener;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.ConsoleErrorListener;
import org.antlr.v4.runtime.Parser;
import org.antlr.v4.runtime.RecognitionException;
import org.antlr.v4.runtime.Recognizer;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.atn.ATNConfigSet;
import org.antlr.v4.runtime.dfa.DFA;
import org.antlr.v4.runtime.tree.ErrorNode;
import org.antlr.v4.runtime.tree.TerminalNode;
import org.springframework.ide.vscode.commons.languageserver.semantic.tokens.SemanticTokenData;
@@ -32,15 +42,26 @@ import org.springframework.ide.vscode.parser.spel.SpelParser.BeanReferenceContex
import org.springframework.ide.vscode.parser.spel.SpelParser.ConstructorReferenceContext;
import org.springframework.ide.vscode.parser.spel.SpelParser.DottedNodeContext;
import org.springframework.ide.vscode.parser.spel.SpelParser.FunctionOrVarContext;
import org.springframework.ide.vscode.parser.spel.SpelParser.InputParameterContext;
import org.springframework.ide.vscode.parser.spel.SpelParser.MethodOrPropertyContext;
import org.springframework.ide.vscode.parser.spel.SpelParser.PossiblyQualifiedIdContext;
import org.springframework.ide.vscode.parser.spel.SpelParserBaseListener;
public class SpelSemanticTokens implements SemanticTokensDataProvider {
private final Optional<Consumer<RecognitionException>> parseErrorHandler;
public SpelSemanticTokens(Optional<Consumer<RecognitionException>> parseErrorHandler) {
this.parseErrorHandler = parseErrorHandler;
}
public SpelSemanticTokens() {
this(Optional.empty());
}
@Override
public List<String> getTokenTypes() {
return List.of("operator", "keyword", "type", "string", "number", "method", "property");
return List.of("operator", "keyword", "type", "string", "number", "method", "property", "parameter");
}
@Override
@@ -54,6 +75,8 @@ public class SpelSemanticTokens implements SemanticTokensDataProvider {
lexer.removeErrorListener(ConsoleErrorListener.INSTANCE);
parser.removeErrorListener(ConsoleErrorListener.INSTANCE);
List<SemanticTokenData> tokens = new ArrayList<>();
parser.addParseListener(new SpelParserBaseListener() {
private void processTerminalNode(TerminalNode node) {
@@ -119,7 +142,6 @@ public class SpelSemanticTokens implements SemanticTokensDataProvider {
case IDENTIFIER:
semantics.put(node.getSymbol(), "variable");
break;
case NUMERIC_LITERAL:
case INTEGER_LITERAL:
case REAL_LITERAL:
semantics.put(node.getSymbol(), "number");
@@ -178,6 +200,17 @@ public class SpelSemanticTokens implements SemanticTokensDataProvider {
}
}
@Override
public void exitInputParameter(InputParameterContext ctx) {
semantics.remove(ctx.LSQUARE().getSymbol());
semantics.remove(ctx.INTEGER_LITERAL().getSymbol());
semantics.remove(ctx.RSQUARE().getSymbol());
int start = ctx.getStart().getStartIndex() + initialOffset;
int end = start + ctx.getText().length();
tokens.add(new SemanticTokenData(start, end, "parameter", new String[0]));
}
@Override
public void visitTerminal(TerminalNode node) {
processTerminalNode(node);
@@ -190,13 +223,38 @@ public class SpelSemanticTokens implements SemanticTokensDataProvider {
});
parser.addErrorListener(new ANTLRErrorListener() {
@Override
public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol, int line, int charPositionInLine,
String msg, RecognitionException e) {
parseErrorHandler.ifPresent(h -> h.accept(e));
}
@Override
public void reportContextSensitivity(Parser recognizer, DFA dfa, int startIndex, int stopIndex, int prediction,
ATNConfigSet configs) {
}
@Override
public void reportAttemptingFullContext(Parser recognizer, DFA dfa, int startIndex, int stopIndex,
BitSet conflictingAlts, ATNConfigSet configs) {
}
@Override
public void reportAmbiguity(Parser recognizer, DFA dfa, int startIndex, int stopIndex, boolean exact,
BitSet ambigAlts, ATNConfigSet configs) {
}
});
parser.spelExpr();
List<SemanticTokenData> tokens = semantics.entrySet().stream()
tokens.addAll(semantics.entrySet().stream()
.map(e -> new SemanticTokenData(e.getKey().getStartIndex() + initialOffset,
e.getKey().getStartIndex() + e.getKey().getText().length() + initialOffset, e.getValue(),
new String[0]))
.collect(Collectors.toList());
.collect(Collectors.toList()));
Collections.sort(tokens);

View File

@@ -13,13 +13,15 @@ package org.springframework.ide.vscode.boot.java.spel;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.List;
import java.util.Optional;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.ide.vscode.commons.languageserver.semantic.tokens.SemanticTokenData;
public class SpelSemanticTokensTest {
private SpelSemanticTokens provider = new SpelSemanticTokens();
private SpelSemanticTokens provider = new SpelSemanticTokens(Optional.of(Assertions::fail));
@Test
void simpleCompare() {
@@ -124,5 +126,22 @@ public class SpelSemanticTokensTest {
assertThat(tokens.get(7)).isEqualTo(new SemanticTokenData(37, 38, "operator", new String[0]));
assertThat(tokens.get(8)).isEqualTo(new SemanticTokenData(38, 39, "operator", new String[0]));
}
//https://github.com/spring-projects/sts4/issues/1320
@Test
void inputParameter() {
List<SemanticTokenData> tokens = provider.computeTokens("[1].size().longValue()", 0);
assertThat(tokens.size()).isEqualTo(9);
assertThat(tokens.get(0)).isEqualTo(new SemanticTokenData(0, 3, "parameter", new String[0])); // [1]
assertThat(tokens.get(1)).isEqualTo(new SemanticTokenData(3, 4, "operator", new String[0])); // .
assertThat(tokens.get(2)).isEqualTo(new SemanticTokenData(4, 8, "method", new String[0])); // size
assertThat(tokens.get(3)).isEqualTo(new SemanticTokenData(8, 9, "operator", new String[0])); // (
assertThat(tokens.get(4)).isEqualTo(new SemanticTokenData(9, 10, "operator", new String[0])); // )
assertThat(tokens.get(5)).isEqualTo(new SemanticTokenData(10, 11, "operator", new String[0])); // .
assertThat(tokens.get(6)).isEqualTo(new SemanticTokenData(11, 20, "method", new String[0])); // longValue
assertThat(tokens.get(7)).isEqualTo(new SemanticTokenData(20, 21, "operator", new String[0])); // (
assertThat(tokens.get(8)).isEqualTo(new SemanticTokenData(21, 22, "operator", new String[0])); // )
}
}