Skip to content

Commit

Permalink
fix: Qute highlight errors due to formatting
Browse files Browse the repository at this point in the history
Fixes #966

Signed-off-by: azerr <azerr@redhat.com>
  • Loading branch information
angelozerr authored and fbricon committed Jul 10, 2024
1 parent df3dcbb commit 48ec48e
Show file tree
Hide file tree
Showing 9 changed files with 250 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@ public class ParameterScanner extends AbstractScanner<TokenType, ScannerState> {

private static final int[] PAREN_COMMA = new int[] { '(', ')', ',' };

private static final Predicate<Integer> PARAM_SPLITTED_BY_ONLY_SPACE = ch -> {
return ch != ' ';
private static final Predicate<Integer> PARAM_SPLIT_BY_ONLY_SPACE_OR_PAREN = ch -> {
return ch != ' ' && ch != '(';
};

private static final Predicate<Integer> PARAM_SPLITTED_BY_SPACE_OR_EQUALS = ch -> {
return ch != ' ' && ch != '=';
private static final Predicate<Integer> PARAM_SPLIT_BY_SPACE_OR_EQUALS_OR_PAREN = ch -> {
return ch != ' ' && ch != '=' && ch != '(';
};

public static ParameterScanner createScanner(String input) {
Expand Down Expand Up @@ -82,6 +82,7 @@ protected TokenType internalScan() {
return parseParameterName(offset);
} else {
if (hasNextParameterNameOrValue()) {
adjustPositionWithComma();
state = ScannerState.WithinParameter;
return finishToken(offset, TokenType.ParameterName);
}
Expand Down Expand Up @@ -116,8 +117,9 @@ protected TokenType internalScan() {
state = ScannerState.WithinParameters;
return finishToken(offset, TokenType.ParameterValue);
}

if (hasNextParameterNameOrValue()) {
adjustPositionWithComma();
state = ScannerState.WithinParameters;
return finishToken(offset, TokenType.ParameterValue);
}
Expand All @@ -129,11 +131,37 @@ protected TokenType internalScan() {
return finishToken(offset, TokenType.Unknown, errorMessage);
}

private void adjustPositionWithComma() {
if (stream.peekChar() == '(') {
// ex: utils.values(a, b)
// in '#for items in utils.values(a, b)
// we need to adjust the end parameter name/value offset after the ')'
stream.advance(1);
int bracket = 1;
while(stream.advanceUntilChar(PAREN_COMMA)) {
int p = stream.peekChar();
stream.advance(1);
if (p == '(') {
bracket++;
} else if (p == ')') {
bracket--;
if(bracket ==0) {
break;
}
} }
}
if (!stream.eos() && stream.peekChar(1) != ' ') {
if (hasNextParameterNameOrValue()) {
adjustPositionWithComma();
}
}
}

private boolean hasNextParameterNameOrValue() {
if (splitWithEquals) {
return stream.advanceWhileChar(PARAM_SPLITTED_BY_SPACE_OR_EQUALS) > 0;
return stream.advanceWhileChar(PARAM_SPLIT_BY_SPACE_OR_EQUALS_OR_PAREN) > 0;
}
return stream.advanceWhileChar(PARAM_SPLITTED_BY_ONLY_SPACE) > 0;
return stream.advanceWhileChar(PARAM_SPLIT_BY_ONLY_SPACE_OR_PAREN) > 0;
}

private TokenType parseParameterName(int offset) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
import com.redhat.qute.parser.expression.PropertyPart;
import com.redhat.qute.parser.template.CaseOperator;
import com.redhat.qute.parser.template.Expression;
import com.redhat.qute.parser.template.ExpressionParameter;
import com.redhat.qute.parser.template.JavaTypeInfoProvider;
import com.redhat.qute.parser.template.LiteralSupport;
import com.redhat.qute.parser.template.Node;
Expand Down Expand Up @@ -256,6 +257,11 @@ private void validateDataModel(Node parent, Template template, QuteValidationSet
switch (section.getSectionKind()) {
case FOR:
case EACH:
Part lastPart = expression.getLastPart();
if (result != null) {
result = validateIterable(lastPart, section, result, result.getSignature(),
diagnostics);
}
String alias = ((LoopSection) section).getAlias();
currentContext.put(alias, result);
break;
Expand Down Expand Up @@ -461,10 +467,11 @@ private static boolean canChangeContext(Section section) {
* Validate #include section.
*
* @param includeSection the include section
* @param project
* @param project
* @param diagnostics the diagnostics to fill.
*/
private static void validateIncludeSection(IncludeSection includeSection, QuteProject project, List<Diagnostic> diagnostics) {
private static void validateIncludeSection(IncludeSection includeSection, QuteProject project,
List<Diagnostic> diagnostics) {
Parameter templateParameter = includeSection.getTemplateParameter();
if (templateParameter != null) {
// Validate template id, only if project exists.
Expand Down Expand Up @@ -563,8 +570,7 @@ private ResolvedJavaTypeInfo validateExpression(Expression expression, Section o
if (QuteCompletableFutures.isResolvingJavaTypeOrNull(resolvedLiteralType)) {
return null;
}
return validateIterable(expression.getLastPart(), ownerSection, resolvedLiteralType,
resolvedLiteralType.getName(), diagnostics);
return resolvedLiteralType;
}
// The expression reference Java data model (ex : {item})
ResolvedJavaTypeInfo resolvedJavaType = null;
Expand Down Expand Up @@ -911,7 +917,7 @@ private ResolvedJavaTypeInfo validatePropertyPart(PropertyPart part, Section own
}
}
}

if (javaMember.getJavaElementKind() == JavaElementKind.METHOD && ((JavaMethodInfo) javaMember).isVoidMethod()) {
return null;
}
Expand Down Expand Up @@ -1232,26 +1238,24 @@ private ResolvedJavaTypeInfo validateJavaTypePart(Part part, Section ownerSectio
return null;
}

return validateIterable(part, ownerSection, resolvedJavaType, javaTypeToResolve, diagnostics);
return resolvedJavaType;
}

private ResolvedJavaTypeInfo validateIterable(Part part, Section ownerSection,
ResolvedJavaTypeInfo resolvedJavaType, String javaTypeToResolve, List<Diagnostic> diagnostics) {
if (part != null && part.isLast() && ownerSection != null && ownerSection.isIterable()) {
// The expression is declared inside an iterable section like #for, #each.
// Ex: {#for item in items}
if (!resolvedJavaType.isIterable() && !resolvedJavaType.isInteger()) {
// The Java class is not an iterable class like
// - java.util.List
// - object array
// - integer
String expression = part.getParent().getContent();
Range range = QutePositionUtility.createRange(part);
Diagnostic diagnostic = createDiagnostic(range, DiagnosticSeverity.Error, QuteErrorCode.IterationError,
expression, javaTypeToResolve);
diagnostics.add(diagnostic);
return null;
}
// The expression is declared inside an iterable section like #for, #each.
// Ex: {#for item in items}
if (!resolvedJavaType.isIterable() && !resolvedJavaType.isInteger()) {
// The Java class is neither an integer nor an iterable class like:
// - java.util.List
// - object array
// - integer
String expression = part.getParent().getContent();
Range range = QutePositionUtility.createRange(part);
Diagnostic diagnostic = createDiagnostic(range, DiagnosticSeverity.Error, QuteErrorCode.IterationError,
expression, javaTypeToResolve);
diagnostics.add(diagnostic);
return null;
}
return resolvedJavaType;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,45 @@ public void forSection() {
assertOffsetAndToken(8, TokenType.ParameterName, "items");
assertOffsetAndToken(13, TokenType.EOS, "");
}

@Test
public void forSectionWithMethod() {
// {#for item in map.values()}
scanner = ParameterScanner.createScanner("item in map.values()");
assertOffsetAndToken(0, TokenType.ParameterName, "item");
assertOffsetAndToken(4, TokenType.Whitespace, " ");
assertOffsetAndToken(5, TokenType.ParameterName, "in");
assertOffsetAndToken(7, TokenType.Whitespace, " ");
assertOffsetAndToken(8, TokenType.ParameterName, "map.values()");
assertOffsetAndToken(20, TokenType.EOS, "");
}

@Test
public void forSectionWithTwoMethods() {
// {#for item in map.get('foo', 10).values()}
scanner = ParameterScanner.createScanner("item in map.get('foo', 10).values()");
assertOffsetAndToken(0, TokenType.ParameterName, "item");
assertOffsetAndToken(4, TokenType.Whitespace, " ");
assertOffsetAndToken(5, TokenType.ParameterName, "in");
assertOffsetAndToken(7, TokenType.Whitespace, " ");
assertOffsetAndToken(8, TokenType.ParameterName, "map.get('foo', 10).values()");
assertOffsetAndToken(36, TokenType.EOS, "");
}

@Test
public void forSectionWithTwoMethodsAndOneParameter() {
// {#for item in map.get('foo', 10).values() bar}
scanner = ParameterScanner.createScanner("item in map.get('foo', 10).values() bar");
assertOffsetAndToken(0, TokenType.ParameterName, "item");
assertOffsetAndToken(4, TokenType.Whitespace, " ");
assertOffsetAndToken(5, TokenType.ParameterName, "in");
assertOffsetAndToken(7, TokenType.Whitespace, " ");
assertOffsetAndToken(8, TokenType.ParameterName, "map.get('foo', 10).values()");
assertOffsetAndToken(36, TokenType.Whitespace, " ");
assertOffsetAndToken(39, TokenType.ParameterName, "bar");
assertOffsetAndToken(42, TokenType.EOS, "");
}

@Test
public void letSection() {
// {#let myParent=order.item.parent myPrice=order.price}
Expand Down Expand Up @@ -94,6 +132,65 @@ public void stringParameter() {
assertOffsetAndToken(16, TokenType.EOS, "");
}

@Test
public void parameterWithMethod() {
// {#let foo= bar(0,1)}
scanner = ParameterScanner.createScanner("foo= bar(0,1)");
assertOffsetAndToken(0, TokenType.ParameterName, "foo");
assertOffsetAndToken(3, TokenType.Assign, "=");
assertOffsetAndToken(4, TokenType.Whitespace, " ");
assertOffsetAndToken(5, TokenType.ParameterValue, "bar(0,1)");
assertOffsetAndToken(13, TokenType.EOS, "");
}

@Test
public void parameterWithMethodAndSpaceInParameter() {
// {#let foo= bar(0,1)}
scanner = ParameterScanner.createScanner("foo= bar(0, 1)");
assertOffsetAndToken(0, TokenType.ParameterName, "foo");
assertOffsetAndToken(3, TokenType.Assign, "=");
assertOffsetAndToken(4, TokenType.Whitespace, " ");
assertOffsetAndToken(5, TokenType.ParameterValue, "bar(0, 1)");
assertOffsetAndToken(15, TokenType.EOS, "");
}

@Test
public void parameterWithSeveralMethodAndSpaceInParameter() {
// {#let p1 = 10 p2= bar(0, 1) p3= 10 p4 = foo( 0, 1, 3)}
scanner = ParameterScanner.createScanner("p1 = 10 p2= bar(0, 1) p3= 10 p4 = foo( 0, 1, 3)");

// p1 = 10
assertOffsetAndToken(0, TokenType.ParameterName, "p1");
assertOffsetAndToken(2, TokenType.Whitespace, " ");
assertOffsetAndToken(3, TokenType.Assign, "=");
assertOffsetAndToken(4, TokenType.Whitespace, " ");
assertOffsetAndToken(5, TokenType.ParameterValue, "10");
assertOffsetAndToken(7, TokenType.Whitespace, " ");

// p2= bar(0, 1)
assertOffsetAndToken(9, TokenType.ParameterName, "p2");
assertOffsetAndToken(11, TokenType.Assign, "=");
assertOffsetAndToken(12, TokenType.Whitespace, " ");
assertOffsetAndToken(13, TokenType.ParameterValue, "bar(0, 1)");

// p3= 10
assertOffsetAndToken(23, TokenType.Whitespace, " ");
assertOffsetAndToken(24, TokenType.ParameterName, "p3");
assertOffsetAndToken(26, TokenType.Assign, "=");
assertOffsetAndToken(27, TokenType.Whitespace, " ");
assertOffsetAndToken(28, TokenType.ParameterValue, "10");

// p4 = bar3(0, 1)
assertOffsetAndToken(30, TokenType.Whitespace, " ");
assertOffsetAndToken(32, TokenType.ParameterName, "p4");
assertOffsetAndToken(34, TokenType.Whitespace, " ");
assertOffsetAndToken(35, TokenType.Assign, "=");
assertOffsetAndToken(36, TokenType.Whitespace, " ");
assertOffsetAndToken(37, TokenType.ParameterValue, "foo( 0, 1, 3)");

assertOffsetAndToken(52, TokenType.EOS, "");
}

public void assertOffsetAndToken(int tokenOffset, TokenType tokenType) {
TokenType token = scanner.scan();
assertEquals(tokenOffset, scanner.getTokenOffset());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ private void createBinaryTypes(List<ResolvedJavaTypeInfo> cache) {
list.setExtendedTypes(Arrays.asList("java.util.Collection<E>"));
registerMethod("size() : int", list);
registerMethod("get(index : int) : E", list);
registerMethod("subList(fromIndex : int, toIndex: int) : java.util.List<E>", list);

// Set
ResolvedJavaTypeInfo set = createResolvedJavaTypeInfo("java.util.Set<E>", cache, true);
Expand All @@ -113,6 +114,7 @@ private void createBinaryTypes(List<ResolvedJavaTypeInfo> cache) {
registerMethod("keySet() : java.util.Set<K>", map);
registerMethod("values() : java.util.Collection<V>", map);
registerMethod("entrySet() : java.util.Set<java.util.Map$Entry<K,V>>", map);
registerMethod("get(key : K) : V", map);

// Map.Entry
ResolvedJavaTypeInfo mapEntry = createResolvedJavaTypeInfo("java.util.Map$Entry<K,V>", cache, true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,11 @@ public void completion() throws Exception {
// test to check @java.lang.Integer(base : T[]) : T is not returned by the
// completion
testCompletionFor(template, //
11, //
12, //
c("iterator() : Iterator<Item>", "iterator", r(1, 13, 1, 13)), //
c("size() : int", "size", r(1, 13, 1, 13)), //
c("get(index : int) : Item", "get(${1:index})$0", r(1, 13, 1, 13)), //
c("subList(fromIndex : int, toIndex : int) : List<Item>", "subList(${1:fromIndex}, ${2:toIndex})$0", r(1, 13, 1, 13)), //
c("raw(base : Object) : RawString", "raw", r(1, 13, 1, 13)), //
c("safe(base : Object) : RawString", "safe", r(1, 13, 1, 13)));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,11 @@ public void completion() throws Exception {
// test to check @java.lang.Integer(base : T[]) : T is not returned by the
// completion
testCompletionFor(template, //
11, //
12, //
c("iterator() : Iterator<Item>", "iterator", r(1, 13, 1, 13)), //
c("size() : int", "size", r(1, 13, 1, 13)), //
c("get(index : int) : Item", "get(${1:index})$0", r(1, 13, 1, 13)), //
c("subList(fromIndex : int, toIndex : int) : List<Item>", "subList(${1:fromIndex}, ${2:toIndex})$0", r(1, 13, 1, 13)), //
c("raw(base : Object) : RawString", "raw", r(1, 13, 1, 13)), //
c("safe(base : Object) : RawString", "safe", r(1, 13, 1, 13)));

Expand Down
Loading

0 comments on commit 48ec48e

Please sign in to comment.