From bcae7283b13ea5180a42dcdbd080acc02cc386cf Mon Sep 17 00:00:00 2001 From: Yuri Blankenstein Date: Tue, 2 Jun 2026 08:33:13 +0200 Subject: [PATCH] Sync with OfflineMBT --- .../actions/utilities/ActionsUtilities.xtend | 17 +++++++- .../common/lang/utilities/EcoreUtil3.java | 23 ++++++++-- .../lang/validation/BaseValidator.xtend | 4 ++ .../ExpressionIdeProposalProvider.xtend | 12 +++++- .../expressions/utilities/ProposalHelper.java | 42 ++++++++++++------- .../types/validation/TypesValidator.xtend | 24 +++-------- 6 files changed, 81 insertions(+), 41 deletions(-) diff --git a/bundles/nl.esi.xtext.actions/src/nl/esi/xtext/actions/utilities/ActionsUtilities.xtend b/bundles/nl.esi.xtext.actions/src/nl/esi/xtext/actions/utilities/ActionsUtilities.xtend index f1e110b..81bfd19 100644 --- a/bundles/nl.esi.xtext.actions/src/nl/esi/xtext/actions/utilities/ActionsUtilities.xtend +++ b/bundles/nl.esi.xtext.actions/src/nl/esi/xtext/actions/utilities/ActionsUtilities.xtend @@ -18,6 +18,7 @@ import nl.esi.xtext.actions.actions.PCFragment import nl.esi.xtext.actions.actions.PCFragmentDefinition import nl.esi.xtext.actions.actions.PCFragmentReference import nl.esi.xtext.actions.actions.RecordFieldAssignmentAction +import nl.esi.xtext.expressions.expression.Expression import nl.esi.xtext.expressions.expression.ExpressionRecordAccess import nl.esi.xtext.expressions.expression.ExpressionVariable @@ -57,12 +58,26 @@ class ActionsUtilities { } def static getFields(RecordFieldAssignmentAction action) { + return action.fieldAccess.fields + } + + def static getField(RecordFieldAssignmentAction action) { + return action.fieldAccess.field + } + + def static getFields(Expression expression) { val fields = newLinkedList() - var record = action.fieldAccess + var record = expression while (record instanceof ExpressionRecordAccess) { fields.addFirst(record.field) record = record.record } return fields } + + def static getField(Expression expression) { + if (expression instanceof ExpressionRecordAccess) { + return expression.field + } + } } \ No newline at end of file diff --git a/bundles/nl.esi.xtext.common.lang/src/nl/esi/xtext/common/lang/utilities/EcoreUtil3.java b/bundles/nl.esi.xtext.common.lang/src/nl/esi/xtext/common/lang/utilities/EcoreUtil3.java index b342e54..ed8fa0e 100644 --- a/bundles/nl.esi.xtext.common.lang/src/nl/esi/xtext/common/lang/utilities/EcoreUtil3.java +++ b/bundles/nl.esi.xtext.common.lang/src/nl/esi/xtext/common/lang/utilities/EcoreUtil3.java @@ -43,6 +43,7 @@ import org.eclipse.xtext.serializer.impl.Serializer; import org.eclipse.xtext.util.ITextRegion; +import com.google.common.base.Strings; import com.google.inject.Guice; import com.google.inject.Module; @@ -296,7 +297,7 @@ public static final class ValidationException extends Exception { private static final long serialVersionUID = 4543121779695994392L; public ValidationException(Diagnostic diagnostic) { - super(createMessage(diagnostic), new DiagnosticException(diagnostic)); + super(createMessage(diagnostic, ""), new DiagnosticException(diagnostic)); } @Override @@ -304,10 +305,24 @@ public synchronized DiagnosticException getCause() { return DiagnosticException.class.cast(super.getCause()); } - private static String createMessage(Diagnostic diagnostic) { + private static String createMessage(Diagnostic diagnostic, String indent) { + StringBuilder message = new StringBuilder(indent); + message.append(switch (diagnostic.getSeverity()) { + case Diagnostic.INFO -> "INFO: "; + case Diagnostic.WARNING -> "WARNING: "; + case Diagnostic.ERROR -> "ERROR: "; + default -> ""; + }); + message.append(diagnostic.getMessage()); + + String cIndent = Strings.isNullOrEmpty(indent) ? "- " : " " + indent; String details = diagnostic.getChildren().stream().filter(c -> c.getSeverity() != Diagnostic.OK) - .map(c -> "- " + c.getMessage()).collect(Collectors.joining("\n")); - return diagnostic.getMessage() + (details.isEmpty() ? "" : "\n" + details); + .map(c -> createMessage(c, cIndent)).collect(Collectors.joining("\n")); + if (!details.isEmpty()) { + message.append("\n").append(details); + } + + return message.toString(); } } } diff --git a/bundles/nl.esi.xtext.common.lang/src/nl/esi/xtext/common/lang/validation/BaseValidator.xtend b/bundles/nl.esi.xtext.common.lang/src/nl/esi/xtext/common/lang/validation/BaseValidator.xtend index b5a049e..4180121 100644 --- a/bundles/nl.esi.xtext.common.lang/src/nl/esi/xtext/common/lang/validation/BaseValidator.xtend +++ b/bundles/nl.esi.xtext.common.lang/src/nl/esi/xtext/common/lang/validation/BaseValidator.xtend @@ -96,4 +96,8 @@ class BaseValidator extends AbstractBaseValidator { } } } + + protected static def Iterable getDuplicatesBy(Iterable source, (T)=>Object functor) { + return source.groupBy(functor).values.filter[size > 1].flatten + } } diff --git a/bundles/nl.esi.xtext.expressions.ide/src/nl/esi/xtext/expressions/ide/contentassist/ExpressionIdeProposalProvider.xtend b/bundles/nl.esi.xtext.expressions.ide/src/nl/esi/xtext/expressions/ide/contentassist/ExpressionIdeProposalProvider.xtend index 28424dc..4492556 100644 --- a/bundles/nl.esi.xtext.expressions.ide/src/nl/esi/xtext/expressions/ide/contentassist/ExpressionIdeProposalProvider.xtend +++ b/bundles/nl.esi.xtext.expressions.ide/src/nl/esi/xtext/expressions/ide/contentassist/ExpressionIdeProposalProvider.xtend @@ -88,7 +88,7 @@ class ExpressionIdeProposalProvider extends AbstractExpressionIdeProposalProvide } try { - val defaultValue = ProposalHelper.defaultValue(typeAnn, targetName) + val defaultValue = createDefaultValueEntry(typeAnn, targetName, context) val proposal = proposalCreator.createProposal(defaultValue, context, [ entry | entry.kind = ContentAssistEntry.KIND_SNIPPET entry.label = ProposalHelper.getTypeName(typeAnn) @@ -103,6 +103,10 @@ class ExpressionIdeProposalProvider extends AbstractExpressionIdeProposalProvide } } + protected def String createDefaultValueEntry(TypeAnnotation typeAnn, String targetName, ContentAssistContext context) { + return ProposalHelper.defaultValue(typeAnn, targetName) + } + protected def createDefaultValue(Type type, String targetName, ContentAssistContext context, IIdeContentProposalAcceptor acceptor) { if (type instanceof TypeReference && type.type instanceof EnumTypeDecl) { createEnumValues(type.type as EnumTypeDecl, context, acceptor) @@ -110,7 +114,7 @@ class ExpressionIdeProposalProvider extends AbstractExpressionIdeProposalProvide } try { - val defaultValue = ProposalHelper.defaultValue(type, targetName) + val defaultValue = createDefaultValueEntry(type, targetName, context) val proposal = proposalCreator.createProposal(defaultValue, context, [ entry | entry.kind = ContentAssistEntry.KIND_SNIPPET entry.label = ProposalHelper.getTypeName(type) @@ -125,6 +129,10 @@ class ExpressionIdeProposalProvider extends AbstractExpressionIdeProposalProvide } } + protected def String createDefaultValueEntry(Type type, String targetName, ContentAssistContext context) { + return ProposalHelper.defaultValue(type, targetName) + } + protected def createEnumValues(EnumTypeDecl type, ContentAssistContext context, IIdeContentProposalAcceptor acceptor) { for (literal : type.literals) { val value = '''«type.name»::«literal.name»''' diff --git a/bundles/nl.esi.xtext.expressions/src/nl/esi/xtext/expressions/utilities/ProposalHelper.java b/bundles/nl.esi.xtext.expressions/src/nl/esi/xtext/expressions/utilities/ProposalHelper.java index 3cdc453..75ba2ee 100644 --- a/bundles/nl.esi.xtext.expressions/src/nl/esi/xtext/expressions/utilities/ProposalHelper.java +++ b/bundles/nl.esi.xtext.expressions/src/nl/esi/xtext/expressions/utilities/ProposalHelper.java @@ -13,10 +13,13 @@ import java.util.List; import java.util.function.Function; +import java.util.function.Predicate; import java.util.stream.Collectors; import org.eclipse.emf.ecore.util.EcoreUtil; +import com.google.common.base.Predicates; + import nl.esi.xtext.expressions.expression.Expression; import nl.esi.xtext.expressions.expression.ExpressionAddition; import nl.esi.xtext.expressions.expression.ExpressionAnd; @@ -56,7 +59,6 @@ import nl.esi.xtext.types.types.MapTypeConstructor; import nl.esi.xtext.types.types.MapTypeDecl; import nl.esi.xtext.types.types.RecordField; -import nl.esi.xtext.types.types.RecordFieldKind; import nl.esi.xtext.types.types.RecordTypeDecl; import nl.esi.xtext.types.types.SimpleTypeDecl; import nl.esi.xtext.types.types.Type; @@ -84,16 +86,24 @@ public static String getTypeName(TypeAnnotation typeAnn) { } public static String defaultValue(TypeAnnotation typeAnn, String targetName) throws UnsupportedTypeException { - return createDefaultValueEntry(typeAnn.getType(), targetName, ""); + return createDefaultValueEntry(typeAnn.getType(), targetName, "", Predicates.alwaysTrue()); + } + + public static String defaultValue(TypeAnnotation typeAnn, String targetName, Predicate filter) throws UnsupportedTypeException { + return createDefaultValueEntry(typeAnn.getType(), targetName, "", filter); } public static String defaultValue(Type type, String targetName) throws UnsupportedTypeException { - return createDefaultValue(type, targetName, ""); + return createDefaultValue(type, targetName, "", Predicates.alwaysTrue()); + } + + public static String defaultValue(Type type, String targetName, Predicate filter) throws UnsupportedTypeException { + return createDefaultValue(type, targetName, "", filter); } - private static String createDefaultValue(Type type, String targetName, String indent) throws UnsupportedTypeException { + private static String createDefaultValue(Type type, String targetName, String indent, Predicate filter) throws UnsupportedTypeException { if (type instanceof TypeReference) { - return createDefaultValueEntry(type, targetName, indent); + return createDefaultValueEntry(type, targetName, indent, filter); } else if (type instanceof VectorTypeConstructor) { return "<" + getTypeName(type) + ">[]"; } else if (type instanceof MapTypeConstructor) { @@ -102,17 +112,17 @@ private static String createDefaultValue(Type type, String targetName, String in throw new UnsupportedTypeException(type); } - private static String createDefaultValueEntry(Type type, String targetName, String indent) throws UnsupportedTypeException { + private static String createDefaultValueEntry(Type type, String targetName, String indent, Predicate filter) throws UnsupportedTypeException { if (type instanceof TypeReference) { - return createDefaultValue(type.getType(), targetName, indent); + return createDefaultValue(type.getType(), targetName, indent, filter); } else if (type instanceof VectorTypeConstructor vecType) { if (vecType.getDimensions().size() > 1) { - return createDefaultValue(getOuterDimension(vecType), null, indent); + return createDefaultValue(getOuterDimension(vecType), null, indent, filter); } - return createDefaultValue(type.getType(), targetName, indent); + return createDefaultValue(type.getType(), targetName, indent, filter); } else if (type instanceof MapTypeConstructor mapType) { - String key = createDefaultValue(type.getType(), null, indent); - String value = createDefaultValue(mapType.getValueType(), null, indent); + String key = createDefaultValue(type.getType(), null, indent, filter); + String value = createDefaultValue(mapType.getValueType(), null, indent, filter); return key + " -> " + value; } throw new UnsupportedTypeException(type); @@ -124,9 +134,9 @@ private static Type getOuterDimension(VectorTypeConstructor vectorType) { return outerDimension; } - private static String createDefaultValue(TypeDecl type, String targetName, String indent) throws UnsupportedTypeException { + private static String createDefaultValue(TypeDecl type, String targetName, String indent, Predicate filter) throws UnsupportedTypeException { if (type instanceof SimpleTypeDecl simpleType) { - if (simpleType.getBase() != null) return createDefaultValue(simpleType.getBase(), targetName, indent); + if (simpleType.getBase() != null) return createDefaultValue(simpleType.getBase(), targetName, indent, filter); else if (simpleType.getName().equals("int")) return "0"; else if (simpleType.getName().equals("real")) return "0.0"; else if (simpleType.getName().equals("bool")) return "true"; @@ -139,16 +149,16 @@ private static String createDefaultValue(TypeDecl type, String targetName, Strin } else if (type instanceof MapTypeDecl) { return "{}"; } else if (type instanceof RecordTypeDecl recType) { - List recFields = getAllFields(recType).stream().filter(f -> !RecordFieldKind.SYMBOLIC.equals(f.getKind())).toList(); + List recFields = getAllFields(recType).stream().filter(filter).toList(); if (recFields.size() > 1) { String fieldIndent = indent + "\t"; String value = recFields.stream() - .map(f -> String.format("%s%s = %s", fieldIndent, f.getName(), createDefaultValue(f.getType(), f.getName(), fieldIndent))) + .map(f -> String.format("%s%s = %s", fieldIndent, f.getName(), createDefaultValue(f.getType(), f.getName(), fieldIndent, filter))) .collect(Collectors.joining(",\n")); return String.format("%s {\n%s\n%s}", type.getName(), value, indent); } else { String value = recFields.stream() - .map(f -> String.format("%s = %s", f.getName(), createDefaultValue(f.getType(), f.getName(), indent))) + .map(f -> String.format("%s = %s", f.getName(), createDefaultValue(f.getType(), f.getName(), indent, filter))) .collect(Collectors.joining(",\n")); return String.format("%s { %s }", type.getName(), value); } diff --git a/bundles/nl.esi.xtext.types/src/nl/esi/xtext/types/validation/TypesValidator.xtend b/bundles/nl.esi.xtext.types/src/nl/esi/xtext/types/validation/TypesValidator.xtend index 760d31e..423b501 100644 --- a/bundles/nl.esi.xtext.types/src/nl/esi/xtext/types/validation/TypesValidator.xtend +++ b/bundles/nl.esi.xtext.types/src/nl/esi/xtext/types/validation/TypesValidator.xtend @@ -14,18 +14,18 @@ package nl.esi.xtext.types.validation import com.google.inject.Inject import java.util.Set +import nl.esi.xtext.common.lang.base.BasePackage +import nl.esi.xtext.common.lang.base.ModelContainer import nl.esi.xtext.types.BasicTypes import nl.esi.xtext.types.scoping.TypesImportUriGlobalScopeProvider import nl.esi.xtext.types.types.EnumTypeDecl +import nl.esi.xtext.types.types.GenericsTypeParam import nl.esi.xtext.types.types.MapTypeConstructor -import nl.esi.xtext.types.types.RecordFieldKind import nl.esi.xtext.types.types.RecordTypeDecl import nl.esi.xtext.types.types.SimpleTypeDecl import nl.esi.xtext.types.types.Type import nl.esi.xtext.types.types.TypesModel import nl.esi.xtext.types.types.TypesPackage -import nl.esi.xtext.common.lang.base.BasePackage -import nl.esi.xtext.common.lang.base.ModelContainer import org.eclipse.core.runtime.Platform import org.eclipse.emf.ecore.EClass import org.eclipse.emf.ecore.EValidator @@ -34,7 +34,6 @@ import org.eclipse.xtext.validation.Check import org.eclipse.xtext.validation.EValidatorRegistrar import static extension nl.esi.xtext.types.utilities.TypeUtilities.* -import nl.esi.xtext.types.types.GenericsTypeParam /** * This class contains custom validation rules. @@ -240,19 +239,19 @@ class TypesValidator extends AbstractTypesValidator { * + the chain of dependencies induced by record fields contains no cycles. * Clarification: record R1 with field A of type R2, R2 with field b of type R1 is not allowed. * Fields of vector types with base another record type should also be considered. - * In XPlus, records are treated as values. The structure mentioned above can never be created. + * In ComMA, records are treated as values. The structure mentioned above can never be created. */ /* * This methods implements the mechanism for registering third-party extensions to the - * validators of the XPlus languages. + * validators of the ComMA languages. * All languages that directly or indirectly extend Types language inherit it. */ @Inject override register(EValidatorRegistrar registrar) { super.register(registrar) - val VALIDATOR_ID = "nl.esi.xtext.types.xplusValidator" + val VALIDATOR_ID = "nl.esi.xtext.types.commaValidator" val reg = Platform.getExtensionRegistry() if(reg !== null){ val extensions = reg.getConfigurationElementsFor(VALIDATOR_ID) @@ -266,15 +265,4 @@ class TypesValidator extends AbstractTypesValidator { } } } - - /** - * All fields of a record cannot be marked symbolic. - */ - @Check - def checkSymbolicField(RecordTypeDecl type) { - val allField = type.allFields - if (allField.forall[kind != RecordFieldKind::CONCRETE]) { - error('''At least 1 field must be concrete for record «type.name»''', TypesPackage.Literals.RECORD_TYPE_DECL__FIELDS) - } - } }