diff --git a/.github/actions/maven-build/action.yml b/.github/actions/maven-build/action.yml index 3600fbe84..8817595ea 100644 --- a/.github/actions/maven-build/action.yml +++ b/.github/actions/maven-build/action.yml @@ -5,13 +5,13 @@ inputs: build-command: description: The Maven command to build the project. The default is `package`. required: false - type: string + # type: string - `type` field is not supported (yet). See comment below. default: package run-tests: description: Whether or not to run tests. The default is true. required: false - type: boolean - default: true + # type: boolean - This is not supported (yet). All inputs are of type `string`. See https://github.com/actions/runner/issues/2238. + default: 'true' runs: using: "composite" @@ -28,4 +28,4 @@ runs: server-password: CI_DEPLOY_PASSWORD - name: Build with Maven shell: bash - run: mvn -B -U clean ${{ inputs.build-command }}${{ inputs.run-tests && '' || ' -DskipTests' }} + run: mvn -B -U clean ${{ inputs.build-command }}${{ inputs.run-tests == 'false' && ' -DskipTests' || '' }} diff --git a/rosetta-ide/src/main/java/com/regnosys/rosetta/ide/RosettaIdeModule.xtend b/rosetta-ide/src/main/java/com/regnosys/rosetta/ide/RosettaIdeModule.xtend index 6a53e80d7..220d0366b 100644 --- a/rosetta-ide/src/main/java/com/regnosys/rosetta/ide/RosettaIdeModule.xtend +++ b/rosetta-ide/src/main/java/com/regnosys/rosetta/ide/RosettaIdeModule.xtend @@ -13,7 +13,6 @@ import com.regnosys.rosetta.ide.inlayhints.IInlayHintsService import com.regnosys.rosetta.ide.util.RangeUtils import com.regnosys.rosetta.ide.semantictokens.ISemanticTokenTypesProvider import com.regnosys.rosetta.ide.semantictokens.ISemanticTokenModifiersProvider -import com.regnosys.rosetta.ide.semantictokens.lsp.LSPSemanticTokenModifiersProvider import com.regnosys.rosetta.ide.semantictokens.ISemanticTokensService import com.regnosys.rosetta.ide.semantictokens.RosettaSemanticTokensService import com.regnosys.rosetta.ide.semantictokens.RosettaSemanticTokenTypesProvider @@ -30,6 +29,7 @@ import com.regnosys.rosetta.ide.contentassist.cancellable.ICancellableContentAss import com.regnosys.rosetta.ide.contentassist.cancellable.CancellableRosettaParser import com.regnosys.rosetta.ide.contentassist.cancellable.CancellableContentAssistService import com.regnosys.rosetta.ide.contentassist.cancellable.RosettaOperationCanceledManager +import com.regnosys.rosetta.ide.semantictokens.RosettaSemanticTokenModifiersProvider /** * Use this class to register ide components. @@ -61,7 +61,7 @@ class RosettaIdeModule extends AbstractRosettaIdeModule { } def Class bindISemanticTokenModifiersProvider() { - LSPSemanticTokenModifiersProvider + RosettaSemanticTokenModifiersProvider } def Class bindISemanticTokensService() { diff --git a/rosetta-ide/src/main/java/com/regnosys/rosetta/ide/hover/RosettaDocumentationProvider.java b/rosetta-ide/src/main/java/com/regnosys/rosetta/ide/hover/RosettaDocumentationProvider.java new file mode 100644 index 000000000..7f402524d --- /dev/null +++ b/rosetta-ide/src/main/java/com/regnosys/rosetta/ide/hover/RosettaDocumentationProvider.java @@ -0,0 +1,44 @@ +package com.regnosys.rosetta.ide.hover; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import javax.inject.Inject; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.xtext.documentation.IEObjectDocumentationProvider; + +import com.regnosys.rosetta.rosetta.RosettaDefinable; +import com.regnosys.rosetta.rosetta.RosettaSymbol; +import com.regnosys.rosetta.types.CardinalityProvider; + +public class RosettaDocumentationProvider implements IEObjectDocumentationProvider { + + @Inject + private CardinalityProvider cardinalityProvider; + + @Override + public String getDocumentation(EObject o) { + List docs = new ArrayList<>(); + if (o instanceof RosettaSymbol) { + RosettaSymbol symbol = (RosettaSymbol)o; + boolean isMulti = cardinalityProvider.isSymbolMulti(symbol); + + if (isMulti) { + docs.add("**Multi cardinality.**"); + } + } + if (o instanceof RosettaDefinable) { + RosettaDefinable objectWithDocs = (RosettaDefinable)o; + if (objectWithDocs.getDefinition() != null) { + docs.add(objectWithDocs.getDefinition()); + } + } + if (docs.isEmpty()) { + return null; + } + return docs.stream().collect(Collectors.joining("\n\n")); + } + +} diff --git a/rosetta-ide/src/main/java/com/regnosys/rosetta/ide/hover/RosettaDocumentationProvider.xtend b/rosetta-ide/src/main/java/com/regnosys/rosetta/ide/hover/RosettaDocumentationProvider.xtend deleted file mode 100644 index 6bd959e3a..000000000 --- a/rosetta-ide/src/main/java/com/regnosys/rosetta/ide/hover/RosettaDocumentationProvider.xtend +++ /dev/null @@ -1,17 +0,0 @@ -package com.regnosys.rosetta.ide.hover - -import org.eclipse.xtext.documentation.IEObjectDocumentationProvider -import org.eclipse.emf.ecore.EObject -import com.regnosys.rosetta.rosetta.RosettaDefinable - -class RosettaDocumentationProvider implements IEObjectDocumentationProvider { - - // If we want to the hover to show the type/cardinality, then we can use the below providers. - // @Inject extension RosettaTypeProvider - // @Inject extension CardinalityProvider - - override getDocumentation(EObject o) { - return if(o instanceof RosettaDefinable) o.definition else null - } - -} diff --git a/rosetta-ide/src/main/java/com/regnosys/rosetta/ide/semantictokens/AbstractSemanticTokensService.java b/rosetta-ide/src/main/java/com/regnosys/rosetta/ide/semantictokens/AbstractSemanticTokensService.java index 90927a6b9..fe1b497db 100644 --- a/rosetta-ide/src/main/java/com/regnosys/rosetta/ide/semantictokens/AbstractSemanticTokensService.java +++ b/rosetta-ide/src/main/java/com/regnosys/rosetta/ide/semantictokens/AbstractSemanticTokensService.java @@ -96,7 +96,7 @@ private int getTokenModifiersRepr(final ISemanticTokenModifier[] tokenModifiers) if (repr == -1) { throw new Error(String.format("Token modifier `%s` not found. Did you forget to bind it in the `%s`?", mod.getValue(), ISemanticTokenModifiersProvider.class.getSimpleName())); } - bitmask |= repr; + bitmask |= 1 << repr; } return bitmask; } @@ -118,7 +118,7 @@ protected SemanticToken createSemanticToken(EObject tokenObject, ISemanticTokenT } protected SemanticToken createSemanticToken(EObject tokenObject, EStructuralFeature feature, ISemanticTokenType tokenType, ISemanticTokenModifier... tokenModifiers) { - return createSemanticToken(tokenObject, feature, -1, tokenType); + return createSemanticToken(tokenObject, feature, -1, tokenType, tokenModifiers); } protected SemanticToken createSemanticToken(EObject tokenObject, EStructuralFeature feature, int featureIndex, ISemanticTokenType tokenType, ISemanticTokenModifier... tokenModifiers) { diff --git a/rosetta-ide/src/main/java/com/regnosys/rosetta/ide/semantictokens/RosettaSemanticTokenModifiersEnum.java b/rosetta-ide/src/main/java/com/regnosys/rosetta/ide/semantictokens/RosettaSemanticTokenModifiersEnum.java new file mode 100644 index 000000000..6e2d8e88a --- /dev/null +++ b/rosetta-ide/src/main/java/com/regnosys/rosetta/ide/semantictokens/RosettaSemanticTokenModifiersEnum.java @@ -0,0 +1,20 @@ +package com.regnosys.rosetta.ide.semantictokens; + +public enum RosettaSemanticTokenModifiersEnum implements ISemanticTokenModifier { + // Predefined by the LS protocol: + DEFAULT_LIBRARY("defaultLibrary"), + // Custom ones + SINGLE_CARDINALITY("singleCardinality"), + MULTI_CARDINALITY("multiCardinality"); + + private final String value; + + RosettaSemanticTokenModifiersEnum(String value) { + this.value = value; + } + + @Override + public String getValue() { + return this.value; + } +} diff --git a/rosetta-ide/src/main/java/com/regnosys/rosetta/ide/semantictokens/RosettaSemanticTokenModifiersProvider.java b/rosetta-ide/src/main/java/com/regnosys/rosetta/ide/semantictokens/RosettaSemanticTokenModifiersProvider.java new file mode 100644 index 000000000..4e2e0f435 --- /dev/null +++ b/rosetta-ide/src/main/java/com/regnosys/rosetta/ide/semantictokens/RosettaSemanticTokenModifiersProvider.java @@ -0,0 +1,11 @@ +package com.regnosys.rosetta.ide.semantictokens; + +import java.util.Arrays; +import java.util.List; + +public class RosettaSemanticTokenModifiersProvider implements ISemanticTokenModifiersProvider { + @Override + public List getSemanticTokenModifiers() { + return Arrays.asList(RosettaSemanticTokenModifiersEnum.values()); + } +} diff --git a/rosetta-ide/src/main/java/com/regnosys/rosetta/ide/semantictokens/RosettaSemanticTokenTypesEnum.java b/rosetta-ide/src/main/java/com/regnosys/rosetta/ide/semantictokens/RosettaSemanticTokenTypesEnum.java index e0f1da623..bf7f34d74 100644 --- a/rosetta-ide/src/main/java/com/regnosys/rosetta/ide/semantictokens/RosettaSemanticTokenTypesEnum.java +++ b/rosetta-ide/src/main/java/com/regnosys/rosetta/ide/semantictokens/RosettaSemanticTokenTypesEnum.java @@ -17,7 +17,9 @@ public enum RosettaSemanticTokenTypesEnum implements ISemanticTokenType { DOCUMENT_SEGMENT("documentSegment"), META_MEMBER("metaMember"), INLINE_PARAMETER("inlineParameter"), - RULE("rule"); + OUTPUT("output"), + RULE("rule"), + IMPLICIT_VARIABLE("implicitVariable"); private final String value; diff --git a/rosetta-ide/src/main/java/com/regnosys/rosetta/ide/semantictokens/RosettaSemanticTokensService.java b/rosetta-ide/src/main/java/com/regnosys/rosetta/ide/semantictokens/RosettaSemanticTokensService.java index 0c54259ed..70396bd2a 100644 --- a/rosetta-ide/src/main/java/com/regnosys/rosetta/ide/semantictokens/RosettaSemanticTokensService.java +++ b/rosetta-ide/src/main/java/com/regnosys/rosetta/ide/semantictokens/RosettaSemanticTokensService.java @@ -2,7 +2,9 @@ import javax.inject.Inject; +import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.EStructuralFeature; import com.regnosys.rosetta.RosettaExtensions; import com.regnosys.rosetta.rosetta.RegulatoryDocumentReference; @@ -21,13 +23,19 @@ import com.regnosys.rosetta.rosetta.TypeCall; import com.regnosys.rosetta.rosetta.TypeParameter; import com.regnosys.rosetta.rosetta.expression.ClosureParameter; +import com.regnosys.rosetta.rosetta.expression.ConstructorKeyValuePair; import com.regnosys.rosetta.rosetta.expression.RosettaFeatureCall; +import com.regnosys.rosetta.rosetta.expression.RosettaImplicitVariable; import com.regnosys.rosetta.rosetta.expression.RosettaSymbolReference; import com.regnosys.rosetta.rosetta.simple.AnnotationRef; import com.regnosys.rosetta.rosetta.simple.Attribute; import com.regnosys.rosetta.rosetta.simple.Data; import com.regnosys.rosetta.rosetta.simple.Function; +import com.regnosys.rosetta.rosetta.simple.Operation; +import com.regnosys.rosetta.rosetta.simple.RosettaRuleReference; +import com.regnosys.rosetta.rosetta.simple.Segment; import com.regnosys.rosetta.rosetta.simple.ShortcutDeclaration; +import com.regnosys.rosetta.types.CardinalityProvider; import static com.regnosys.rosetta.rosetta.RosettaPackage.Literals.*; import static com.regnosys.rosetta.rosetta.expression.ExpressionPackage.Literals.*; @@ -38,11 +46,13 @@ import java.util.Optional; import static com.regnosys.rosetta.ide.semantictokens.RosettaSemanticTokenTypesEnum.*; -import static com.regnosys.rosetta.ide.semantictokens.lsp.LSPSemanticTokenModifiersEnum.*; +import static com.regnosys.rosetta.ide.semantictokens.RosettaSemanticTokenModifiersEnum.*; public class RosettaSemanticTokensService extends AbstractSemanticTokensService { @Inject private RosettaExtensions extensions; + @Inject + private CardinalityProvider cardinalityProvider; @Inject public RosettaSemanticTokensService(ISemanticTokenTypesProvider tokenTypesProvider, @@ -66,6 +76,18 @@ private Optional typeToToken(RosettaType t) { } return Optional.empty(); } + private RosettaSemanticTokenModifiersEnum getCardinalityModifier(RosettaSymbol symbol) { + if (cardinalityProvider.isSymbolMulti(symbol)) { + return MULTI_CARDINALITY; + } + return SINGLE_CARDINALITY; + } + private RosettaSemanticTokenModifiersEnum getCardinalityModifier(RosettaImplicitVariable implicitVar) { + if (cardinalityProvider.isMulti(implicitVar)) { + return MULTI_CARDINALITY; + } + return SINGLE_CARDINALITY; + } @MarkSemanticToken public Optional markType(TypeCall typeCall) { @@ -95,49 +117,149 @@ public SemanticToken markMetaMemberInAnnotation(AnnotationRef annotation) { return null; } + private SemanticToken markFunction(EObject objectToMark, EStructuralFeature featureToMark, Function function) { + return createSemanticToken(objectToMark, featureToMark, RosettaSemanticTokenTypesEnum.FUNCTION, getCardinalityModifier(function)); + } + @MarkSemanticToken + public SemanticToken markFunctionDeclaration(Function function) { + return markFunction(function, ROSETTA_NAMED__NAME, function); + } + + private SemanticToken markRule(EObject objectToMark, EStructuralFeature featureToMark, RosettaRule rule) { + return createSemanticToken(objectToMark, featureToMark, RosettaSemanticTokenTypesEnum.RULE, getCardinalityModifier(rule)); + } + @MarkSemanticToken + public SemanticToken markRuleDeclaration(RosettaRule rule) { + return markRule(rule, ROSETTA_NAMED__NAME, rule); + } + @MarkSemanticToken + public SemanticToken markRuleReference(RosettaRuleReference ruleRef) { + RosettaRule rule = ruleRef.getReportingRule(); + if (extensions.isResolved(rule)) { + return markRule(ruleRef, ROSETTA_RULE_REFERENCE__REPORTING_RULE, rule); + } + return null; + } + + private SemanticToken markAttribute(EObject objectToMark, EStructuralFeature featureToMark, Attribute attribute) { + RosettaSemanticTokenTypesEnum tokenType = null; + EReference containmentFeature = attribute.eContainmentFeature(); + if (containmentFeature.equals(FUNCTION__INPUTS)) { + tokenType = PARAMETER; + } else if (containmentFeature.equals(FUNCTION__OUTPUT)) { + tokenType = OUTPUT; + } else if (containmentFeature.equals(DATA__ATTRIBUTES)) { + tokenType = PROPERTY; + } + + if (tokenType == null) { + return null; + } + return createSemanticToken(objectToMark, featureToMark, tokenType, getCardinalityModifier(attribute)); + } + @MarkSemanticToken + public SemanticToken markAttributeDeclaration(Attribute attribute) { + return markAttribute(attribute, ROSETTA_NAMED__NAME, attribute); + } + + private SemanticToken markClosureParameter(EObject objectToMark, EStructuralFeature featureToMark, ClosureParameter param) { + return createSemanticToken(objectToMark, featureToMark, INLINE_PARAMETER, getCardinalityModifier(param)); + } + @MarkSemanticToken + public SemanticToken markClosureParameterDeclaration(ClosureParameter param) { + return markClosureParameter(param, ROSETTA_NAMED__NAME, param); + } + + private SemanticToken markAlias(EObject objectToMark, EStructuralFeature featureToMark, ShortcutDeclaration alias) { + return createSemanticToken(objectToMark, featureToMark, VARIABLE, getCardinalityModifier(alias)); + } + @MarkSemanticToken + public SemanticToken markAliasDeclaration(ShortcutDeclaration alias) { + return markAlias(alias, ROSETTA_NAMED__NAME, alias); + } + + @MarkSemanticToken + public List markOperation(Operation operation) { + List result = new ArrayList<>(); + RosettaSymbol assignRoot = operation.getAssignRoot(); + if (extensions.isResolved(assignRoot)) { + SemanticToken assignRootToken = markSymbol(operation, OPERATION__ASSIGN_ROOT, assignRoot); + if (assignRootToken != null) { + result.add(assignRootToken); + } + } + for (Segment seg : operation.pathAsSegmentList()) { + Attribute segAttr = seg.getAttribute(); + if (extensions.isResolved(segAttr)) { + SemanticToken segmentToken = markAttribute(seg, SEGMENT__ATTRIBUTE, segAttr); + if (segmentToken != null) { + result.add(segmentToken); + } + } + } + return result; + } + + @MarkSemanticToken + public SemanticToken markConstructorFeature(ConstructorKeyValuePair pair) { + RosettaFeature feature = pair.getKey(); + if (extensions.isResolved(feature)) { + return markFeature(pair, CONSTRUCTOR_KEY_VALUE_PAIR__KEY, feature); + } + return null; + } + + private SemanticToken markFeature(EObject objectToMark, EStructuralFeature featureToMark, RosettaFeature feature) { + if (feature instanceof RosettaEnumValue) { + return createSemanticToken(objectToMark, featureToMark, ENUM_MEMBER); + } else if (feature instanceof Attribute) { + return markAttribute(objectToMark, featureToMark, (Attribute)feature); + } else if (feature instanceof RosettaMetaType) { + return createSemanticToken(objectToMark, featureToMark, META_MEMBER); + } + return null; + } @MarkSemanticToken public SemanticToken markFeature(RosettaFeatureCall featureCall) { RosettaFeature feature = featureCall.getFeature(); if (extensions.isResolved(feature)) { - if (feature instanceof RosettaEnumValue) { - return createSemanticToken(featureCall, ROSETTA_FEATURE_CALL__FEATURE, ENUM_MEMBER); - } else if (feature instanceof Attribute) { - return createSemanticToken(featureCall, ROSETTA_FEATURE_CALL__FEATURE, PROPERTY); - } else if (feature instanceof RosettaMetaType) { - return createSemanticToken(featureCall, ROSETTA_FEATURE_CALL__FEATURE, META_MEMBER); - } + return markFeature(featureCall, ROSETTA_FEATURE_CALL__FEATURE, feature); } return null; } + private SemanticToken markSymbol(EObject objectToMark, EStructuralFeature featureToMark, RosettaSymbol symbol) { + if (symbol instanceof Attribute) { + return markAttribute(objectToMark, featureToMark, (Attribute)symbol); + } else if (symbol instanceof ClosureParameter) { + return markClosureParameter(objectToMark, featureToMark, (ClosureParameter)symbol); + } else if (symbol instanceof Function) { + return markFunction(objectToMark, featureToMark, (Function)symbol); + } else if (symbol instanceof RosettaExternalFunction) { + return createSemanticToken(objectToMark, featureToMark, RosettaSemanticTokenTypesEnum.FUNCTION, DEFAULT_LIBRARY); + } else if (symbol instanceof RosettaRule) { + return markRule(objectToMark, featureToMark, (RosettaRule)symbol); + } else if (symbol instanceof ShortcutDeclaration) { + return markAlias(objectToMark, featureToMark, (ShortcutDeclaration)symbol); + } else if (symbol instanceof TypeParameter) { + return createSemanticToken(objectToMark, featureToMark, PARAMETER); + } + return null; + } @MarkSemanticToken - public SemanticToken markRosettaReference(RosettaSymbolReference reference) { + public SemanticToken markSymbolReference(RosettaSymbolReference reference) { RosettaSymbol symbol = reference.getSymbol(); if (extensions.isResolved(symbol)) { - if (symbol instanceof Attribute) { - EReference containmentFeature = symbol.eContainmentFeature(); - if (containmentFeature.equals(FUNCTION__INPUTS)) { - return createSemanticToken(reference, ROSETTA_SYMBOL_REFERENCE__SYMBOL, PARAMETER); - } else if (containmentFeature.equals(DATA__ATTRIBUTES)) { - return createSemanticToken(reference, ROSETTA_SYMBOL_REFERENCE__SYMBOL, PROPERTY); - } - return null; - } else if (symbol instanceof ClosureParameter) { - return createSemanticToken(reference, ROSETTA_SYMBOL_REFERENCE__SYMBOL, INLINE_PARAMETER); - } else if (symbol instanceof Function) { - return createSemanticToken(reference, ROSETTA_SYMBOL_REFERENCE__SYMBOL, RosettaSemanticTokenTypesEnum.FUNCTION); - } else if (symbol instanceof RosettaExternalFunction) { - return createSemanticToken(reference, ROSETTA_SYMBOL_REFERENCE__SYMBOL, RosettaSemanticTokenTypesEnum.FUNCTION, DEFAULT_LIBRARY); - } else if (symbol instanceof RosettaRule) { - return createSemanticToken(reference, ROSETTA_SYMBOL_REFERENCE__SYMBOL, RosettaSemanticTokenTypesEnum.RULE); - } else if (symbol instanceof ShortcutDeclaration) { - return createSemanticToken(reference, ROSETTA_SYMBOL_REFERENCE__SYMBOL, VARIABLE); - } else if (symbol instanceof RosettaType) { - return createSemanticToken(reference, ROSETTA_SYMBOL_REFERENCE__SYMBOL, typeToToken((RosettaType)symbol).get()); - } else if (symbol instanceof TypeParameter) { - return createSemanticToken(reference, ROSETTA_SYMBOL_REFERENCE__SYMBOL, PARAMETER); - } + return markSymbol(reference, ROSETTA_SYMBOL_REFERENCE__SYMBOL, symbol); } return null; } + + @MarkSemanticToken + public SemanticToken markImplicitVariable(RosettaImplicitVariable implicitVar) { + if (implicitVar.isGenerated()) { + return null; + } + return createSemanticToken(implicitVar, ROSETTA_NAMED__NAME, IMPLICIT_VARIABLE, getCardinalityModifier(implicitVar)); + } } diff --git a/rosetta-ide/src/test/java/com/regnosys/rosetta/ide/semantictokens/tests/SemanticTokenTest.xtend b/rosetta-ide/src/test/java/com/regnosys/rosetta/ide/semantictokens/tests/SemanticTokenTest.xtend index 5e9f728b0..0363c424f 100644 --- a/rosetta-ide/src/test/java/com/regnosys/rosetta/ide/semantictokens/tests/SemanticTokenTest.xtend +++ b/rosetta-ide/src/test/java/com/regnosys/rosetta/ide/semantictokens/tests/SemanticTokenTest.xtend @@ -1,11 +1,9 @@ package com.regnosys.rosetta.ide.semantictokens.tests import org.junit.jupiter.api.Test -import static org.junit.jupiter.api.Assertions.* import com.regnosys.rosetta.ide.tests.AbstractRosettaLanguageServerTest -import static com.regnosys.rosetta.ide.semantictokens.RosettaSemanticTokenTypesEnum.* -class SemanticTokenTest extends AbstractRosettaLanguageServerTest { +class SemanticTokenTest extends AbstractRosettaLanguageServerTest { @Test def testAttributeTypesAreMarked() { testSemanticToken[ @@ -22,33 +20,16 @@ class SemanticTokenTest extends AbstractRosettaLanguageServerTest { d date (0..*) ''' it.model = model - it.assertSemanticTokens = [ - assertEquals(4, size) - get(0) => [ // int - assertEquals(TYPE_ALIAS, tokenType) - assertEquals(6, line) - assertEquals(3, startChar) - assertEquals(3, length) - ] - get(1) => [ // Bar - assertEquals(ENUM, tokenType) - assertEquals(7, line) - assertEquals(3, startChar) - assertEquals(3, length) - ] - get(2) => [ // Foo - assertEquals(TYPE, tokenType) - assertEquals(8, line) - assertEquals(3, startChar) - assertEquals(3, length) - ] - get(3) => [ // date - assertEquals(RECORD_TYPE, tokenType) - assertEquals(9, line) - assertEquals(3, startChar) - assertEquals(4, length) - ] - ] + it.expectedSemanticTokenItems = ''' + property.singleCardinality: 6:1:1 + typeAlias: 6:3:3 + property.singleCardinality: 7:1:1 + enum: 7:3:3 + property.singleCardinality: 8:1:1 + type: 8:3:3 + property.multiCardinality: 9:1:1 + recordType: 9:3:4 + ''' ] } @@ -66,27 +47,15 @@ class SemanticTokenTest extends AbstractRosettaLanguageServerTest { ''' it.model = model it.assertNoIssues = false - it.assertSemanticTokens = [ - assertEquals(3, size) - get(0) => [ // int - assertEquals(TYPE_ALIAS, tokenType) - assertEquals(3, line) - assertEquals(3, startChar) - assertEquals(3, length) - ] - get(1) => [ // Foo - assertEquals(TYPE, tokenType) - assertEquals(5, line) - assertEquals(3, startChar) - assertEquals(3, length) - ] - get(2) => [ // date - assertEquals(RECORD_TYPE, tokenType) - assertEquals(6, line) - assertEquals(3, startChar) - assertEquals(4, length) - ] - ] + it.expectedSemanticTokenItems = ''' + property.singleCardinality: 3:1:1 + typeAlias: 3:3:3 + property.singleCardinality: 4:1:1 + property.singleCardinality: 5:1:1 + type: 5:3:3 + property.multiCardinality: 6:1:1 + recordType: 6:3:4 + ''' ] } @@ -101,27 +70,11 @@ class SemanticTokenTest extends AbstractRosettaLanguageServerTest { library function bar(param int) date ''' it.model = model - it.assertSemanticTokens = [ - assertEquals(3, size) - get(0) => [ // string - assertEquals(BASIC_TYPE, tokenType) - assertEquals(2, line) - assertEquals(13, startChar) - assertEquals(6, length) - ] - get(1) => [ // int - assertEquals(TYPE_ALIAS, tokenType) - assertEquals(4, line) - assertEquals(27, startChar) - assertEquals(3, length) - ] - get(2) => [ // date - assertEquals(RECORD_TYPE, tokenType) - assertEquals(4, line) - assertEquals(32, startChar) - assertEquals(4, length) - ] - ] + it.expectedSemanticTokenItems = ''' + basicType: 2:13:6 + typeAlias: 4:27:3 + recordType: 4:32:4 + ''' ] } @@ -145,39 +98,14 @@ class SemanticTokenTest extends AbstractRosettaLanguageServerTest { "Y" ''' it.model = model - it.assertSemanticTokens = [ - assertEquals(5, size) - get(0) => [ // number - assertEquals(BASIC_TYPE, tokenType) - assertEquals(10, line) - assertEquals(28, startChar) - assertEquals(6, length) - ] - get(1) => [ // Part45 - assertEquals(DOCUMENT_CORPUS, tokenType) - assertEquals(11, line) - assertEquals(27, startChar) - assertEquals(6, length) - ] - get(2) => [ // appendix - assertEquals(DOCUMENT_SEGMENT, tokenType) - assertEquals(11, line) - assertEquals(34, startChar) - assertEquals(8, length) - ] - get(3) => [ // dataElement - assertEquals(DOCUMENT_SEGMENT, tokenType) - assertEquals(11, line) - assertEquals(47, startChar) - assertEquals(11, length) - ] - get(4) => [ // field - assertEquals(DOCUMENT_SEGMENT, tokenType) - assertEquals(11, line) - assertEquals(63, startChar) - assertEquals(5, length) - ] - ] + it.expectedSemanticTokenItems = ''' + rule.singleCardinality: 10:15:7 + basicType: 10:28:6 + documentCorpus: 11:27:6 + documentSegment: 11:34:8 + documentSegment: 11:47:11 + documentSegment: 11:63:5 + ''' ] } @@ -206,28 +134,12 @@ class SemanticTokenTest extends AbstractRosettaLanguageServerTest { ''' it.model = model it.assertNoIssues = false; - it.assertSemanticTokens = [ - assertEquals(2, size) - get(0) => [ // int - assertEquals(TYPE_ALIAS, tokenType) - assertEquals(11, line) - assertEquals(3, startChar) - assertEquals(3, length) - ] - get(1) => [ // number - assertEquals(BASIC_TYPE, tokenType) - assertEquals(13, line) - assertEquals(28, startChar) - assertEquals(6, length) - ] - // TODO -// get(2) => [ // string -// assertEquals(BASIC_TYPE, tokenType) -// assertEquals(17, line) -// assertEquals(3, startChar) -// assertEquals(6, length) -// ] - ] + it.expectedSemanticTokenItems = ''' + property.singleCardinality: 11:1:1 + typeAlias: 11:3:3 + rule.singleCardinality: 13:15:7 + basicType: 13:28:6 + ''' ] } @@ -244,15 +156,12 @@ class SemanticTokenTest extends AbstractRosettaLanguageServerTest { [metadata scheme] ''' it.model = model - it.assertSemanticTokens = [ - assertEquals(3, size) - get(2) => [ // scheme - assertEquals(META_MEMBER, tokenType) - assertEquals(6, line) - assertEquals(12, startChar) - assertEquals(6, length) - ] - ] + it.expectedSemanticTokenItems = ''' + basicType: 2:16:6 + property.singleCardinality: 5:1:3 + basicType: 5:5:6 + metaMember: 6:12:6 + ''' ] } @@ -270,9 +179,11 @@ class SemanticTokenTest extends AbstractRosettaLanguageServerTest { ''' it.model = model it.assertNoIssues = false; - it.assertSemanticTokens = [ - assertEquals(2, size) - ] + it.expectedSemanticTokenItems = ''' + basicType: 2:16:6 + property.singleCardinality: 5:1:3 + basicType: 5:5:6 + ''' ] } @@ -302,63 +213,44 @@ class SemanticTokenTest extends AbstractRosettaLanguageServerTest { then foo -> bar -> scheme ''' it.model = model - it.assertSemanticTokens = [ - assertEquals(13, size) - get(4) => [ // Foo - assertEquals(TYPE, tokenType) - assertEquals(14, line) - assertEquals(6, startChar) - assertEquals(3, length) - ] - get(5) => [ // string - assertEquals(BASIC_TYPE, tokenType) - assertEquals(16, line) - assertEquals(9, startChar) - assertEquals(6, length) - ] - get(6) => [ // foo - assertEquals(PARAMETER, tokenType) - assertEquals(18, line) - assertEquals(5, startChar) - assertEquals(3, length) - ] - get(7) => [ // a - assertEquals(PROPERTY, tokenType) - assertEquals(18, line) - assertEquals(12, startChar) - assertEquals(1, length) - ] - get(8) => [ // A - assertEquals(ENUM, tokenType) - assertEquals(18, line) - assertEquals(16, startChar) - assertEquals(1, length) - ] - get(9) => [ // V - assertEquals(ENUM_MEMBER, tokenType) - assertEquals(18, line) - assertEquals(21, startChar) - assertEquals(1, length) - ] - get(10) => [ // foo - assertEquals(PARAMETER, tokenType) - assertEquals(19, line) - assertEquals(7, startChar) - assertEquals(3, length) - ] - get(11) => [ // bar - assertEquals(PROPERTY, tokenType) - assertEquals(19, line) - assertEquals(14, startChar) - assertEquals(3, length) - ] - get(12) => [ // scheme - assertEquals(META_MEMBER, tokenType) - assertEquals(19, line) - assertEquals(21, startChar) - assertEquals(6, length) - ] - ] + it.expectedSemanticTokenItems = ''' + basicType: 2:16:6 + property.singleCardinality: 8:1:3 + basicType: 8:5:6 + metaMember: 9:12:6 + property.singleCardinality: 10:1:1 + enum: 10:3:1 + function.singleCardinality: 12:5:3 + parameter.singleCardinality: 14:2:3 + type: 14:6:3 + output.singleCardinality: 16:2:6 + basicType: 16:9:6 + output.singleCardinality: 17:5:6 + parameter.singleCardinality: 18:5:3 + property.singleCardinality: 18:12:1 + enumMember: 18:21:1 + parameter.singleCardinality: 19:7:3 + property.singleCardinality: 19:14:3 + metaMember: 19:21:6 + ''' + ] + } + + @Test + def testGeneratedImplicitVariableDoesNotCauseSemanticToken() { + testSemanticToken[ + val model = ''' + namespace test + + reporting rule Test from string: + /* item */ extract (["a", "b"]) + then /* item */ only-element + ''' + it.model = model + it.expectedSemanticTokenItems = ''' + rule.singleCardinality: 2:15:4 + basicType: 2:25:6 + ''' ] } } \ No newline at end of file diff --git a/rosetta-ide/src/test/java/com/regnosys/rosetta/ide/tests/AbstractRosettaLanguageServerTest.xtend b/rosetta-ide/src/test/java/com/regnosys/rosetta/ide/tests/AbstractRosettaLanguageServerTest.xtend index d8b2d8d77..fe32949f8 100644 --- a/rosetta-ide/src/test/java/com/regnosys/rosetta/ide/tests/AbstractRosettaLanguageServerTest.xtend +++ b/rosetta-ide/src/test/java/com/regnosys/rosetta/ide/tests/AbstractRosettaLanguageServerTest.xtend @@ -128,6 +128,9 @@ abstract class AbstractRosettaLanguageServerTest extends AbstractLanguageServerT assertEquals(expectedSemanticTokenItems, result.toExpectation) } } + protected def dispatch String toExpectation(SemanticToken it) { + '''«tokenType.value»«IF !tokenModifiers.empty».«FOR tokenMod : tokenModifiers SEPARATOR '.'»«tokenMod.value»«ENDFOR»«ENDIF»: «line»:«startChar»:«length»''' + } protected override FileInfo initializeContext(TextDocumentConfiguration configuration) { configuration.filesInScope = new HashMap(configuration.filesInScope) + #{ diff --git a/rosetta-ide/vscode/package.json b/rosetta-ide/vscode/package.json index be1d8ac2e..f714276aa 100644 --- a/rosetta-ide/vscode/package.json +++ b/rosetta-ide/vscode/package.json @@ -64,11 +64,30 @@ { "id": "inlineParameter", "superType": "parameter", - "description": "An parameter of an inline function." + "description": "A parameter of an inline function." + }, + { + "id": "output", + "superType": "parameter", + "description": "The output parameter of a function." }, { "id": "rule", "description": "A reference to a rule." + }, + { + "id": "implicitVariable", + "description": "The implicit variable (`item`)." + } + ], + "semanticTokenModifiers": [ + { + "id": "singleCardinality", + "description": "A symbol of single cardinality." + }, + { + "id": "multiCardinality", + "description": "A symbol of multi cardinality." } ], "semanticTokenScopes": [ @@ -88,10 +107,12 @@ "enumMember": ["variable.other.enummember.rosetta"], "variable": ["variable.other.alias.rosetta"], "parameter": ["variable.parameter.input.rosetta"], + "output": ["variable.parameter.output.rosetta"], "inlineParameter": ["variable.parameter.inline.rosetta"], "function": ["entity.name.function.rosetta"], "function.defaultLibrary": ["entity.name.function.builtin.rosetta"], - "rule": ["entity.name.rule.rosetta"] + "rule": ["entity.name.rule.rosetta"], + "implicitVariable": ["constant.language.rosetta"] } } ] diff --git a/rosetta-lang/model/Rosetta.xcore b/rosetta-lang/model/Rosetta.xcore index 6a1bb384c..9d1d44a26 100644 --- a/rosetta-lang/model/Rosetta.xcore +++ b/rosetta-lang/model/Rosetta.xcore @@ -513,8 +513,7 @@ class RosettaReport extends RosettaRootElement { } } -class RosettaRule extends RosettaRootElement, RosettaCallableWithArgs, RosettaDefinable { - contains RosettaDocReference[] references +class RosettaRule extends RosettaRootElement, RosettaCallableWithArgs, RosettaDefinable, References { contains RosettaExpression expression String identifier contains TypeCall input diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/Rosetta.xtext b/rosetta-lang/src/main/java/com/regnosys/rosetta/Rosetta.xtext index 9cd48d82f..9c07447b3 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/Rosetta.xtext +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/Rosetta.xtext @@ -859,7 +859,7 @@ RosettaRuleReference: RosettaRule: ('reporting'|'eligibility') 'rule' RosettaNamed ('from' input=TypeCall)? ':' =>RosettaDefinable? ( - (references += RosettaDocReference)* + References* (expression = RosettaCalcExpression ('as' identifier=STRING)?) ) diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/resource/RosettaModelDescription.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/resource/RosettaModelDescription.java index d52702ba5..9d93c50ee 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/resource/RosettaModelDescription.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/resource/RosettaModelDescription.java @@ -12,10 +12,10 @@ public class RosettaModelDescription extends EObjectDescription { public static final String IMPORTS = "IMPORTS"; public RosettaModelDescription(QualifiedName qualifiedName, RosettaModel model) { - super(qualifiedName, model, Map.of(IMPORTS, getSortedImportsList(model))); + super(qualifiedName, model, Map.of(IMPORTS, getImportsList(model))); } - private static String getSortedImportsList(RosettaModel model) { + private static String getImportsList(RosettaModel model) { return model.getImports().stream() .map(imp -> imp.getImportedNamespace()) .collect(Collectors.joining(";")); diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/RosettaSimpleValidator.xtend b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/RosettaSimpleValidator.xtend index 12e2ae031..886904fcb 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/RosettaSimpleValidator.xtend +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/RosettaSimpleValidator.xtend @@ -1011,7 +1011,7 @@ class RosettaSimpleValidator extends AbstractDeclarativeValidator { // check type val ruleType = rule.expression.RType - if (ruleType !== null && ruleType != MISSING && !ruleType.isSubtypeOf(attrType)) { + if (ruleType !== null && ruleType != MISSING && attrType !== null && attrType != MISSING && !ruleType.isSubtypeOf(attrType)) { val typeError = '''Type mismatch - report field «attr.name» has type «attrType.name» ''' + '''whereas the reporting rule «rule.name» has type «ruleType».''' error(typeError, ruleRef, ROSETTA_RULE_REFERENCE__REPORTING_RULE) diff --git a/rosetta-tools/src/main/java/com/regnosys/rosetta/tools/minimalexampleproducer/UnnecessaryElementsRemover.java b/rosetta-tools/src/main/java/com/regnosys/rosetta/tools/minimalexampleproducer/UnnecessaryElementsRemover.java index b4af03a46..fcad17914 100644 --- a/rosetta-tools/src/main/java/com/regnosys/rosetta/tools/minimalexampleproducer/UnnecessaryElementsRemover.java +++ b/rosetta-tools/src/main/java/com/regnosys/rosetta/tools/minimalexampleproducer/UnnecessaryElementsRemover.java @@ -44,7 +44,7 @@ public class UnnecessaryElementsRemover { sPckg.getAnnotated(), Set.of(sPckg.getAnnotated_Annotations()), pckg.getRosettaModel(), Set.of(pckg.getRosettaModel_Elements()), sPckg.getData(), Set.of(sPckg.getData_Attributes(), sPckg.getData_Conditions()), - pckg.getRosettaRule(), Set.of(pckg.getRosettaRule_References()), + sPckg.getReferences(), Set.of(sPckg.getReferences_References()), sPckg.getFunction(), Set.of(sPckg.getFunction_Shortcuts(), sPckg.getFunction_Conditions(), sPckg.getFunction_Operations(), sPckg.getFunction_PostConditions()), pckg.getRosettaEnumeration(), Set.of(pckg.getRosettaEnumeration_EnumValues()) );