- * The {{MagikIndexer}} does the real indexing, this pre-indexing is required to not be bound
- * to the load order of the session, but still get the correct inheritance hierarchy.
- *
- *
- *
- * Does a two-step part to correctly build the hierachy:
- * 1. Gather all types (exemplars, mixins)
- * 2. Builds the complete type hierarchy
- *
- */
-public class MagikPreIndexer {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(MagikPreIndexer.class);
-
- private static final String DEF_MIXIN = "def_mixin";
- private static final String DEF_INDEXED_EXEMPLAR = "def_indexed_exemplar";
- private static final String DEF_SLOTTED_EXEMPLAR = "def_slotted_exemplar";
- private static final String DEF_ENUMERATION = "def_enumeration";
- private static final String DEF_PACKAGE = "def_package";
-
- private final ITypeKeeper typeKeeper;
-
- public MagikPreIndexer(ITypeKeeper typeKeeper) {
- this.typeKeeper = typeKeeper;
- }
-
- /**
- * Index all magik file(s).
- * @param paths Paths to index.
- * @throws IOException -
- */
- public void indexPaths(Stream paths) throws IOException {
- paths.forEach(this::indexPath);
- }
-
- /**
- * Index a single magik file.
- * @param path Path to magik file.
- */
- @SuppressWarnings("checkstyle:IllegalCatch")
- public void indexPath(Path path) {
- LOGGER.debug("Scanning file: {}", path);
- try {
- final MagikFile magikFile = new MagikFile(path);
-
- // Ensure its worth parsing this file.
- final String code = magikFile.getSource();
- if (!this.sourceContainsDefinition(code)) {
- return;
- }
-
- magikFile.getDefinitions()
- .forEach(this::handleDefinition);
- } catch (IOException exception) {
- LOGGER.error(exception.getMessage(), exception);
- } catch (Exception exception) {
- LOGGER.error("Error pre-indexing file: " + path, exception);
- }
- }
-
- private boolean sourceContainsDefinition(final String code) {
- final String codeLowered = code.toLowerCase();
- return codeLowered.contains(DEF_PACKAGE)
- || codeLowered.contains(DEF_ENUMERATION)
- || codeLowered.contains(DEF_SLOTTED_EXEMPLAR)
- || codeLowered.contains(DEF_INDEXED_EXEMPLAR)
- || codeLowered.contains(DEF_MIXIN);
- }
-
- private void handleDefinition(final Definition definition) {
- if (definition instanceof PackageDefinition) {
- final PackageDefinition packageDefinition = (PackageDefinition) definition;
- this.handleDefinition(packageDefinition);
- } else if (definition instanceof IndexedExemplarDefinition) {
- final IndexedExemplarDefinition indexedExemplarDefinition = (IndexedExemplarDefinition) definition;
- this.handleDefinition(indexedExemplarDefinition);
- } else if (definition instanceof EnumerationDefinition) {
- final EnumerationDefinition enumerationDefinition = (EnumerationDefinition) definition;
- this.handleDefinition(enumerationDefinition);
- } else if (definition instanceof SlottedExemplarDefinition) {
- final SlottedExemplarDefinition slottedExemplarDefinition = (SlottedExemplarDefinition) definition;
- this.handleDefinition(slottedExemplarDefinition);
- } else if (definition instanceof MixinDefinition) {
- final MixinDefinition mixinDefinition = (MixinDefinition) definition;
- this.handleDefinition(mixinDefinition);
- }
- }
-
- private void handleDefinition(final PackageDefinition definition) {
- final String pakkageName = definition.getName();
- final Package pakkage;
- if (this.typeKeeper.hasPackage(pakkageName)) {
- pakkage = this.typeKeeper.getPackage(pakkageName);
- } else {
- pakkage = new Package(pakkageName);
- this.typeKeeper.addPackage(pakkage);
- }
-
- definition.getUses().stream()
- .forEach(use -> {
- final Package usePakkage;
- if (!this.typeKeeper.hasPackage(use)) {
- usePakkage = new Package(use);
- this.typeKeeper.addPackage(usePakkage);
- } else {
- usePakkage = this.typeKeeper.getPackage(use);
- }
-
- pakkage.addUse(usePakkage);
- });
-
- LOGGER.debug("Indexed package: {}", pakkage);
- }
-
- private void handleDefinition(final IndexedExemplarDefinition definition) {
- final GlobalReference globalRef = definition.getGlobalReference();
- this.ensurePackage(globalRef);
-
- final MagikType magikType = new IndexedType(globalRef);
- this.typeKeeper.addType(magikType);
- LOGGER.debug("Indexed indexed exemplar: {}", magikType);
- }
-
- private void handleDefinition(final EnumerationDefinition definition) {
- final GlobalReference globalRef = definition.getGlobalReference();
- this.ensurePackage(globalRef);
-
- final MagikType magikType = new SlottedType(globalRef);
- this.typeKeeper.addType(magikType);
- LOGGER.debug("Indexed enumeration: {}", magikType);
- }
-
- private void handleDefinition(final SlottedExemplarDefinition definition) {
- final GlobalReference globalRef = definition.getGlobalReference();
- this.ensurePackage(globalRef);
-
- final MagikType magikType = new SlottedType(globalRef);
- this.typeKeeper.addType(magikType);
- LOGGER.debug("Indexed slotted exemplar: {}", magikType);
- }
-
- private void handleDefinition(final MixinDefinition definition) {
- final GlobalReference globalRef = definition.getGlobalReference();
- this.ensurePackage(globalRef);
-
- final MagikType magikType = new IntrinsicType(globalRef);
- this.typeKeeper.addType(magikType);
- LOGGER.debug("Indexed mixin: {}", magikType);
- }
-
- private Package ensurePackage(final GlobalReference globalReference) {
- final String pakkageName = globalReference.getPakkage();
- if (!this.typeKeeper.hasPackage(pakkageName)) {
- final Package pakkage = new Package(pakkageName);
- this.typeKeeper.addPackage(pakkage);
- LOGGER.debug("Indexed package: {}", pakkage);
- }
-
- return this.typeKeeper.getPackage(pakkageName);
- }
-
-}
diff --git a/magik-language-server/src/main/java/nl/ramsolutions/sw/magik/languageserver/munit/MUnitTestItemProvider.java b/magik-language-server/src/main/java/nl/ramsolutions/sw/magik/languageserver/munit/MUnitTestItemProvider.java
index 5e8ee990..b7b9f2d4 100644
--- a/magik-language-server/src/main/java/nl/ramsolutions/sw/magik/languageserver/munit/MUnitTestItemProvider.java
+++ b/magik-language-server/src/main/java/nl/ramsolutions/sw/magik/languageserver/munit/MUnitTestItemProvider.java
@@ -15,20 +15,20 @@
import nl.ramsolutions.sw.magik.analysis.Location;
import nl.ramsolutions.sw.magik.analysis.typing.ITypeKeeper;
import nl.ramsolutions.sw.magik.analysis.typing.types.AbstractType;
-import nl.ramsolutions.sw.magik.analysis.typing.types.GlobalReference;
import nl.ramsolutions.sw.magik.analysis.typing.types.Method;
+import nl.ramsolutions.sw.magik.analysis.typing.types.TypeString;
import nl.ramsolutions.sw.magik.languageserver.Lsp4jConversion;
import org.eclipse.lsp4j.ServerCapabilities;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
- * MUnit {{@code TestItem}} provider.
+ * MUnit {@code TestItem} provider.
*/
public class MUnitTestItemProvider {
private static final Logger LOGGER = LoggerFactory.getLogger(MUnitTestItemProvider.class);
- private static final GlobalReference MUNIT_TEST_CASE_EXEMPLAR_NAME = GlobalReference.of("sw:test_case");
+ private static final TypeString MUNIT_TEST_CASE_EXEMPLAR_NAME = TypeString.of("sw:test_case");
private static final String MUNIT_TEST_METHOD_PREFIX = "test";
private final ITypeKeeper typeKeeper;
@@ -46,7 +46,7 @@ public void setCapabilities(final ServerCapabilities capabilities) {
}
/**
- * Get {{@Code TestItem}}s.
+ * Get {@Link TestItem}s.
*
*
* A hierarchy per product is provided:
diff --git a/magik-language-server/src/main/java/nl/ramsolutions/sw/magik/languageserver/references/ReferencesProvider.java b/magik-language-server/src/main/java/nl/ramsolutions/sw/magik/languageserver/references/ReferencesProvider.java
index 714d53d8..38ae479a 100644
--- a/magik-language-server/src/main/java/nl/ramsolutions/sw/magik/languageserver/references/ReferencesProvider.java
+++ b/magik-language-server/src/main/java/nl/ramsolutions/sw/magik/languageserver/references/ReferencesProvider.java
@@ -1,9 +1,13 @@
package nl.ramsolutions.sw.magik.languageserver.references;
import com.sonar.sslr.api.AstNode;
+import java.util.Collection;
import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
import java.util.Objects;
+import java.util.Set;
+import java.util.function.Predicate;
import java.util.stream.Collectors;
import nl.ramsolutions.sw.magik.MagikTypedFile;
import nl.ramsolutions.sw.magik.analysis.AstQuery;
@@ -12,8 +16,8 @@
import nl.ramsolutions.sw.magik.analysis.helpers.PackageNodeHelper;
import nl.ramsolutions.sw.magik.analysis.typing.ITypeKeeper;
import nl.ramsolutions.sw.magik.analysis.typing.types.AbstractType;
-import nl.ramsolutions.sw.magik.analysis.typing.types.GlobalReference;
import nl.ramsolutions.sw.magik.analysis.typing.types.Method;
+import nl.ramsolutions.sw.magik.analysis.typing.types.TypeString;
import nl.ramsolutions.sw.magik.analysis.typing.types.UndefinedType;
import nl.ramsolutions.sw.magik.api.MagikGrammar;
import nl.ramsolutions.sw.magik.languageserver.Lsp4jConversion;
@@ -59,73 +63,88 @@ public List provideReferences(final MagikTypedFile magikFile, final Po
final AstNode wantedNode = currentNode.getFirstAncestor(
MagikGrammar.METHOD_INVOCATION,
- MagikGrammar.METHOD_DEFINITION);
+ MagikGrammar.METHOD_DEFINITION,
+ MagikGrammar.ATOM,
+ MagikGrammar.CONDITION_NAME);
LOGGER.trace("Wanted node: {}", wantedNode);
+ final PackageNodeHelper packageHelper = new PackageNodeHelper(wantedNode);
if (wantedNode == null) {
return Collections.emptyList();
} else if (wantedNode.is(MagikGrammar.METHOD_INVOCATION)) {
final MethodInvocationNodeHelper helper = new MethodInvocationNodeHelper(wantedNode);
final String methodName = helper.getMethodName();
LOGGER.debug("Getting references to method: {}", methodName);
- return this.referencesToMethod(typeKeeper, methodName);
+ return this.referencesToMethod(typeKeeper, UndefinedType.SERIALIZED_NAME, methodName);
} else if (wantedNode.is(MagikGrammar.METHOD_DEFINITION)) {
final MethodDefinitionNodeHelper helper = new MethodDefinitionNodeHelper(wantedNode);
- final PackageNodeHelper packageHelper = new PackageNodeHelper(wantedNode);
final String identifier = currentNode.getTokenValue();
// Name of the method.
- if (this.nodeIsMethodDefinitionMethodName(currentNode, wantedNode)) {
- String methodName = helper.getMethodName();
+ if (currentNode.getFirstAncestor(MagikGrammar.METHOD_NAME) != null) {
+ final String methodName = helper.getMethodName();
LOGGER.debug("Getting references to method: {}", methodName);
- return this.referencesToMethod(typeKeeper, methodName);
- } else if (this.nodeIsMethodDefinitionExemplarName(currentNode, wantedNode)) {
+ return this.referencesToMethod(typeKeeper, UndefinedType.SERIALIZED_NAME, methodName);
+ } else if (currentNode.getFirstAncestor(MagikGrammar.EXEMPLAR_NAME) != null) {
// Must be the exemplar name.
final String pakkage = packageHelper.getCurrentPackage();
- final GlobalReference globalRef = GlobalReference.of(pakkage, identifier);
- final AbstractType type = magikFile.getTypeKeeper().getType(globalRef);
+ final TypeString typeString = TypeString.of(identifier, pakkage);
+ final AbstractType type = typeKeeper.getType(typeString);
if (type != UndefinedType.INSTANCE) {
final String typeName = type.getFullName();
LOGGER.debug("Getting references to type: {}", typeName);
return this.referencesToType(typeKeeper, typeName);
}
- } else {
- // A random identifier, regard it as a type.
- final String pakkage = packageHelper.getCurrentPackage();
- final GlobalReference globalRef = GlobalReference.of(pakkage, identifier);
- final AbstractType type = magikFile.getTypeKeeper().getType(globalRef);
- if (type != UndefinedType.INSTANCE) {
- final String typeName = type.getFullName();
- LOGGER.debug("Getting references to method: {}", typeName);
- return this.referencesToType(typeKeeper, typeName);
- }
}
+ } else if (wantedNode.is(MagikGrammar.ATOM)
+ && wantedNode.getFirstChild().is(MagikGrammar.IDENTIFIER)) {
+ // A random identifier, regard it as a type.
+ final String pakkage = packageHelper.getCurrentPackage();
+ final String identifier = currentNode.getTokenValue();
+ final TypeString typeString = TypeString.of(identifier, pakkage);
+ final AbstractType type = typeKeeper.getType(typeString);
+ if (type != UndefinedType.INSTANCE) {
+ final String typeName = type.getFullName();
+ LOGGER.debug("Getting references to type: {}", typeName);
+ return this.referencesToType(typeKeeper, typeName);
+ }
+ } else if (wantedNode.is(MagikGrammar.CONDITION_NAME)) {
+ final String conditionName = currentNode.getTokenValue();
+ LOGGER.debug("Getting references to condition: {}", conditionName);
+ return this.referencesToCondition(typeKeeper, conditionName);
}
return Collections.emptyList();
}
- private boolean nodeIsMethodDefinitionMethodName(final AstNode currentNode, final AstNode methodDefinitionNode) {
- // TODO: This does not support [] methods.
- final List identifierNodes = methodDefinitionNode.getChildren(MagikGrammar.IDENTIFIER);
- final AstNode methodNameNode = identifierNodes.get(1);
- return methodNameNode.getToken() == currentNode.getToken();
- }
-
- private boolean nodeIsMethodDefinitionExemplarName(final AstNode currentNode, final AstNode methodDefinitionNode) {
- final List identifierNodes = methodDefinitionNode.getChildren(MagikGrammar.IDENTIFIER);
- final AstNode exemplarNameNode = identifierNodes.get(0);
- return exemplarNameNode.getToken() == currentNode.getToken();
- }
-
- private List referencesToMethod(final ITypeKeeper typeKeeper, final String methodName) {
+ private List referencesToMethod(
+ final ITypeKeeper typeKeeper, final String typeName, final String methodName) {
LOGGER.debug("Finding references to method: {}", methodName);
+ // Build set of types which may contain this method: type + ancestors.
+ final AbstractType type =
+ typeName.equals(UndefinedType.SERIALIZED_NAME)
+ ? UndefinedType.INSTANCE
+ : typeKeeper.getType(TypeString.of(typeName));
+ final Set wantedTypes = new HashSet<>();
+ wantedTypes.add(UndefinedType.INSTANCE); // For unreasoned/undetermined calls.
+ wantedTypes.add(type);
+ wantedTypes.addAll(type.getAncestors());
+
+ final Collection wantedMethodUsages = wantedTypes.stream()
+ .map(wantedType -> {
+ final String wantedTypeName = wantedType.getFullName();
+ final TypeString wantedTypeRef = TypeString.of(wantedTypeName);
+ return new Method.MethodUsage(wantedTypeRef, methodName);
+ })
+ .collect(Collectors.toSet());
+ final Predicate filterPredicate = methodUsage -> wantedMethodUsages.contains(methodUsage);
+
// Find references.
return typeKeeper.getTypes().stream()
- .flatMap(type -> type.getMethods().stream())
- .filter(method -> method.getCalledMethods().contains(methodName))
- // TODO: can't we get the location to the usage?
- .map(Method::getLocation)
+ .flatMap(type_ -> type_.getMethods().stream())
+ .flatMap(method -> method.getMethodUsages().stream())
+ .filter(filterPredicate::test)
+ .map(Method.MethodUsage::getLocation)
.filter(Objects::nonNull)
.map(Lsp4jConversion::locationToLsp4j)
.collect(Collectors.toList());
@@ -134,15 +153,41 @@ private List referencesToMethod(final ITypeKeeper typeKeeper, final St
private List referencesToType(final ITypeKeeper typeKeeper, final String typeName) {
LOGGER.debug("Finding references to type: {}", typeName);
+ final AbstractType type = typeKeeper.getType(TypeString.of(typeName));
+ final Set wantedTypes = new HashSet<>();
+ wantedTypes.add(type);
+ // wantedTypes.addAll(type.getAncestors()); // TODO: Ancestors or descendants?
+
+ final Collection wantedTypeUsages = wantedTypes.stream()
+ .map(wantedType -> {
+ final String wantedTypeName = wantedType.getFullName();
+ final TypeString wantedTypeRef = TypeString.of(wantedTypeName);
+ return new Method.GlobalUsage(wantedTypeRef, null);
+ })
+ .collect(Collectors.toSet());
+ final Predicate filterPredicate = typeUsage -> wantedTypeUsages.contains(typeUsage);
+
// Find references.
return typeKeeper.getTypes().stream()
- .flatMap(type -> type.getMethods().stream())
- .filter(method -> method.getUsedTypes().contains(typeName))
- // TODO: can't we get the location to the usage?
- .map(Method::getLocation)
+ .flatMap(type_ -> type_.getMethods().stream())
+ .flatMap(method -> method.getTypeUsages().stream())
+ .filter(filterPredicate::test)
+ .map(Method.GlobalUsage::getLocation)
.filter(Objects::nonNull)
.map(Lsp4jConversion::locationToLsp4j)
.collect(Collectors.toList());
}
+ private List referencesToCondition(final ITypeKeeper typeKeeper, final String conditionName) {
+ LOGGER.debug("Finding references to condition: {}", conditionName);
+
+ return typeKeeper.getTypes().stream()
+ .flatMap(type -> type.getLocalMethods().stream())
+ .flatMap(method -> method.getConditionUsages().stream())
+ .filter(conditionUsage -> conditionUsage.getConditionName().equals(conditionName))
+ .map(Method.ConditionUsage::getLocation)
+ .map(Lsp4jConversion::locationToLsp4j)
+ .collect(Collectors.toList());
+ }
+
}
diff --git a/magik-language-server/src/main/java/nl/ramsolutions/sw/magik/languageserver/rename/RenameProvider.java b/magik-language-server/src/main/java/nl/ramsolutions/sw/magik/languageserver/rename/RenameProvider.java
index b69c314c..40dd3b29 100644
--- a/magik-language-server/src/main/java/nl/ramsolutions/sw/magik/languageserver/rename/RenameProvider.java
+++ b/magik-language-server/src/main/java/nl/ramsolutions/sw/magik/languageserver/rename/RenameProvider.java
@@ -15,12 +15,13 @@
import nl.ramsolutions.sw.magik.api.MagikGrammar;
import nl.ramsolutions.sw.magik.languageserver.Lsp4jConversion;
import org.eclipse.lsp4j.Position;
+import org.eclipse.lsp4j.PrepareRenameDefaultBehavior;
import org.eclipse.lsp4j.PrepareRenameResult;
import org.eclipse.lsp4j.RenameOptions;
import org.eclipse.lsp4j.ServerCapabilities;
import org.eclipse.lsp4j.TextEdit;
import org.eclipse.lsp4j.WorkspaceEdit;
-import org.eclipse.lsp4j.jsonrpc.messages.Either;
+import org.eclipse.lsp4j.jsonrpc.messages.Either3;
/**
* Rename provider.
@@ -43,7 +44,7 @@ public void setCapabilities(final ServerCapabilities capabilities) {
* @param position Position in magik source.
* @return Prepare rename or null if no rename possible.
*/
- public Either providePrepareRename(
+ public Either3 providePrepareRename(
final MagikTypedFile magikFile, final Position position) {
// Parse magik.
final AstNode topNode = magikFile.getTopNode();
@@ -70,7 +71,7 @@ public Either providePrepareRename
final org.eclipse.lsp4j.Range rangeLsp4j = Lsp4jConversion.rangeToLsp4j(range);
final String identifier = node.getTokenOriginalValue();
final PrepareRenameResult result = new PrepareRenameResult(rangeLsp4j, identifier);
- return Either.forRight(result);
+ return Either3.forSecond(result);
}
/**
@@ -125,7 +126,7 @@ private ScopeEntry findScopeEntry(final MagikTypedFile magikFile, final AstNode
return null;
}
- final String identifier = node.getTokenOriginalValue();
+ final String identifier = node.getTokenValue();
return scope.getScopeEntry(identifier);
}
diff --git a/magik-language-server/src/main/java/nl/ramsolutions/sw/magik/languageserver/semantictokens/SemanticTokenProvider.java b/magik-language-server/src/main/java/nl/ramsolutions/sw/magik/languageserver/semantictokens/SemanticTokenProvider.java
index d36e8e78..55b15857 100644
--- a/magik-language-server/src/main/java/nl/ramsolutions/sw/magik/languageserver/semantictokens/SemanticTokenProvider.java
+++ b/magik-language-server/src/main/java/nl/ramsolutions/sw/magik/languageserver/semantictokens/SemanticTokenProvider.java
@@ -1,6 +1,7 @@
package nl.ramsolutions.sw.magik.languageserver.semantictokens;
import com.sonar.sslr.api.AstNode;
+import com.sonar.sslr.api.GenericTokenType;
import com.sonar.sslr.api.Token;
import java.io.IOException;
import java.net.URI;
@@ -11,7 +12,6 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;
import nl.ramsolutions.sw.magik.MagikTypedFile;
-import nl.ramsolutions.sw.magik.api.ExtendedTokenType;
import org.eclipse.lsp4j.DocumentFilter;
import org.eclipse.lsp4j.SemanticTokens;
import org.eclipse.lsp4j.SemanticTokensLegend;
@@ -84,7 +84,7 @@ private SemanticToken createStartSemanticToken() {
.setLine(1)
.setColumn(0)
.setValueAndOriginalValue("")
- .setType(ExtendedTokenType.OTHER)
+ .setType(GenericTokenType.UNKNOWN_CHAR)
.setURI(URI.create("magik://dummy"))
.build();
return new SemanticToken(startToken, SemanticToken.Type.CLASS, Collections.emptySet());
diff --git a/magik-language-server/src/main/java/nl/ramsolutions/sw/magik/languageserver/semantictokens/SemanticTokenWalker.java b/magik-language-server/src/main/java/nl/ramsolutions/sw/magik/languageserver/semantictokens/SemanticTokenWalker.java
index 474ccdb9..62f7b2f6 100644
--- a/magik-language-server/src/main/java/nl/ramsolutions/sw/magik/languageserver/semantictokens/SemanticTokenWalker.java
+++ b/magik-language-server/src/main/java/nl/ramsolutions/sw/magik/languageserver/semantictokens/SemanticTokenWalker.java
@@ -18,8 +18,8 @@
import nl.ramsolutions.sw.magik.analysis.scope.Scope;
import nl.ramsolutions.sw.magik.analysis.scope.ScopeEntry;
import nl.ramsolutions.sw.magik.analysis.typing.ITypeKeeper;
-import nl.ramsolutions.sw.magik.analysis.typing.types.GlobalReference;
import nl.ramsolutions.sw.magik.analysis.typing.types.MagikType;
+import nl.ramsolutions.sw.magik.analysis.typing.types.TypeString;
import nl.ramsolutions.sw.magik.api.MagikGrammar;
import nl.ramsolutions.sw.magik.api.MagikKeyword;
import nl.ramsolutions.sw.magik.api.MagikOperator;
@@ -163,14 +163,14 @@ private void walkCommentToken(final Token token) {
NewDocGrammar.TYPE_NAME, NewDocGrammar.TYPE_CLONE, NewDocGrammar.TYPE_SELF);
typeNodes.forEach(typeTypeNode -> {
final String identifier = typeTypeNode.getTokenValue();
- final GlobalReference globalRef = identifier.indexOf(':') != -1
- ? GlobalReference.of(identifier)
- : GlobalReference.of(this.currentPakkage, identifier);
+ final TypeString typeString = identifier.indexOf(':') != -1
+ ? TypeString.of(identifier)
+ : TypeString.of(identifier, this.currentPakkage);
if (typeTypeNode.is(NewDocGrammar.TYPE_CLONE, NewDocGrammar.TYPE_SELF)) {
final Set constModifier =
Set.of(SemanticToken.Modifier.DOCUMENTATION, SemanticToken.Modifier.READONLY);
this.addSemanticToken(typeTypeNode, SemanticToken.Type.CLASS, constModifier);
- } else if (this.isKnownType(globalRef)) {
+ } else if (this.isKnownType(typeString)) {
this.addSemanticToken(typeTypeNode, SemanticToken.Type.CLASS, docModifier);
}
});
@@ -242,8 +242,12 @@ protected void walkPostIdentifier(final AstNode node) {
return;
}
- if (parentNode.is(MagikGrammar.METHOD_DEFINITION)) {
- this.walkPostIdentifierMethodDefintion(node);
+ if (parentNode.is(MagikGrammar.EXEMPLAR_NAME)) {
+ this.addSemanticToken(node, SemanticToken.Type.CLASS);
+ } else if (parentNode.is(MagikGrammar.METHOD_NAME)) {
+ this.addSemanticToken(node, SemanticToken.Type.METHOD);
+ } else if (parentNode.is(MagikGrammar.CONDITION_NAME)) {
+ this.addSemanticToken(node, SemanticToken.Type.CLASS);
} else if (parentNode.is(MagikGrammar.ATOM)
&& !this.isPartOfProcedureInvocation(node)) {
this.walkPostIdentifierAtom(node);
@@ -283,19 +287,6 @@ private void walkPostIdentifierFor(final AstNode node) {
this.addSemanticToken(node, SemanticToken.Type.VARIABLE);
}
- private void walkPostIdentifierMethodDefintion(final AstNode node) {
- final AstNode methodDefinitionNode = node.getParent();
- final List identifierNodes = methodDefinitionNode.getChildren(MagikGrammar.IDENTIFIER);
- final int index = identifierNodes.indexOf(node);
- if (index == 0) {
- final AstNode typeIdentifierNode = identifierNodes.get(0);
- this.addSemanticToken(typeIdentifierNode, SemanticToken.Type.CLASS);
- } else if (index == 1) {
- final AstNode methodIdentifierNode = identifierNodes.get(1);
- this.addSemanticToken(methodIdentifierNode, SemanticToken.Type.METHOD);
- }
- }
-
private void walkPostIdentifierAtom(final AstNode node) {
final GlobalScope globalScope = this.magikFile.getGlobalScope();
final Scope scope = globalScope.getScopeForNode(node);
@@ -321,10 +312,8 @@ private void walkPostIdentifierAtom(final AstNode node) {
case GLOBAL:
case DYNAMIC:
- final GlobalReference globalRef = identifier.indexOf(':') != -1
- ? GlobalReference.of(identifier)
- : GlobalReference.of(this.currentPakkage, identifier);
- if (this.isKnownType(globalRef)) {
+ final TypeString typeString = TypeString.of(identifier, this.currentPakkage);
+ if (this.isKnownType(typeString)) {
this.addSemanticToken(
node, SemanticToken.Type.CLASS, Set.of(SemanticToken.Modifier.VARIABLE_GLOBAL));
} else {
@@ -362,9 +351,9 @@ protected void walkPostPackageSpecification(final AstNode node) {
this.currentPakkage = helper.getCurrentPackage();
}
- private boolean isKnownType(final GlobalReference globalReference) {
+ private boolean isKnownType(final TypeString typeString) {
final ITypeKeeper typeKeeper = this.magikFile.getTypeKeeper();
- return typeKeeper.getType(globalReference) instanceof MagikType;
+ return typeKeeper.getType(typeString) instanceof MagikType;
}
}
diff --git a/magik-language-server/src/main/java/nl/ramsolutions/sw/magik/languageserver/signaturehelp/SignatureHelpProvider.java b/magik-language-server/src/main/java/nl/ramsolutions/sw/magik/languageserver/signaturehelp/SignatureHelpProvider.java
index abb39887..1b6bd4d3 100644
--- a/magik-language-server/src/main/java/nl/ramsolutions/sw/magik/languageserver/signaturehelp/SignatureHelpProvider.java
+++ b/magik-language-server/src/main/java/nl/ramsolutions/sw/magik/languageserver/signaturehelp/SignatureHelpProvider.java
@@ -12,8 +12,8 @@
import nl.ramsolutions.sw.magik.analysis.typing.LocalTypeReasoner;
import nl.ramsolutions.sw.magik.analysis.typing.types.AbstractType;
import nl.ramsolutions.sw.magik.analysis.typing.types.ExpressionResult;
-import nl.ramsolutions.sw.magik.analysis.typing.types.GlobalReference;
import nl.ramsolutions.sw.magik.analysis.typing.types.SelfType;
+import nl.ramsolutions.sw.magik.analysis.typing.types.TypeString;
import nl.ramsolutions.sw.magik.analysis.typing.types.UndefinedType;
import nl.ramsolutions.sw.magik.api.MagikGrammar;
import nl.ramsolutions.sw.magik.languageserver.Lsp4jConversion;
@@ -43,10 +43,10 @@ public void setCapabilities(final ServerCapabilities capabilities) {
}
/**
- * Provide a {{SignatureHelp}} for {{position}} in {{path}}.
+ * Provide a {@link SignatureHelp} for {@code position} in {@code path}.
* @param magikFile Magik file.
* @param position Position in file.
- * @return {{SignatureHelp}}.
+ * @return {@link SignatureHelp}.
*/
public SignatureHelp provideSignatureHelp(final MagikTypedFile magikFile, final Position position) {
// Get intended method and called type.
@@ -68,7 +68,7 @@ public SignatureHelp provideSignatureHelp(final MagikTypedFile magikFile, final
final AstNode previousSiblingNode = currentNode.getPreviousSibling();
final ExpressionResult result = reasoner.getNodeType(previousSiblingNode);
final ITypeKeeper typeKeeper = magikFile.getTypeKeeper();
- final AbstractType unsetType = typeKeeper.getType(GlobalReference.of("sw:unset"));
+ final AbstractType unsetType = typeKeeper.getType(TypeString.of("sw:unset"));
AbstractType type = result.get(0, unsetType);
LOGGER.debug("Provide signature for type: {}, method: {}", type.getFullName(), methodName);
@@ -85,8 +85,8 @@ public SignatureHelp provideSignatureHelp(final MagikTypedFile magikFile, final
if (type == SelfType.INSTANCE) {
final AstNode methodDefNode = currentNode.getFirstAncestor(MagikGrammar.METHOD_DEFINITION);
final MethodDefinitionNodeHelper methodDefHelper = new MethodDefinitionNodeHelper(methodDefNode);
- final GlobalReference globalRef = methodDefHelper.getTypeGlobalReference();
- type = typeKeeper.getType(globalRef);
+ final TypeString typeString = methodDefHelper.getTypeString();
+ type = typeKeeper.getType(typeString);
}
// Provide methods for this type with the name.
sigInfos = type.getMethods().stream()
diff --git a/magik-language-server/src/main/java/nl/ramsolutions/sw/magik/languageserver/symbol/SymbolProvider.java b/magik-language-server/src/main/java/nl/ramsolutions/sw/magik/languageserver/symbol/SymbolProvider.java
index bd2373f7..72f91159 100644
--- a/magik-language-server/src/main/java/nl/ramsolutions/sw/magik/languageserver/symbol/SymbolProvider.java
+++ b/magik-language-server/src/main/java/nl/ramsolutions/sw/magik/languageserver/symbol/SymbolProvider.java
@@ -11,11 +11,13 @@
import nl.ramsolutions.sw.magik.analysis.Location;
import nl.ramsolutions.sw.magik.analysis.typing.ITypeKeeper;
import nl.ramsolutions.sw.magik.analysis.typing.types.AbstractType;
+import nl.ramsolutions.sw.magik.analysis.typing.types.Condition;
import nl.ramsolutions.sw.magik.analysis.typing.types.Method;
import nl.ramsolutions.sw.magik.languageserver.Lsp4jConversion;
import org.eclipse.lsp4j.ServerCapabilities;
-import org.eclipse.lsp4j.SymbolInformation;
import org.eclipse.lsp4j.SymbolKind;
+import org.eclipse.lsp4j.WorkspaceSymbol;
+import org.eclipse.lsp4j.jsonrpc.messages.Either;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -42,11 +44,11 @@ public void setCapabilities(final ServerCapabilities capabilities) {
}
/**
- * Get symbols matching {{query}}.
+ * Get symbols matching {@code query}.
* @param query Query to match against.
- * @return {{SymbolInformation}}s with query results.
+ * @return {@link SymbolInformation}s with query results.
*/
- public List getSymbols(final String query) {
+ public List getSymbols(final String query) {
LOGGER.debug("Searching for: '{}'", query);
if (query.trim().isEmpty()) {
@@ -54,10 +56,12 @@ public List getSymbols(final String query) {
}
final Predicate typePredicate;
+ final Predicate conditionPredicate;
final Predicate methodPredicate;
final BiPredicate typeMethodPredicate;
try {
typePredicate = this.buildTypePredicate(query);
+ conditionPredicate = this.buildConditionPredicate(query);
methodPredicate = this.buildMethodPredicate(query);
typeMethodPredicate = this.buildTypeMethodPredicate(query);
} catch (PatternSyntaxException ex) {
@@ -65,17 +69,17 @@ public List getSymbols(final String query) {
return Collections.emptyList();
}
- final List symbolInformations = new ArrayList<>();
+ final List symbolInformations = new ArrayList<>();
for (final AbstractType type : this.typeKeeper.getTypes()) {
if (typePredicate.test(type)) {
final Location location = type.getLocation() != null
? type.getLocation()
: DUMMY_LOCATION;
- final SymbolInformation information = new SymbolInformation(
+ final WorkspaceSymbol symbol = new WorkspaceSymbol(
"Exemplar: " + type.getFullName(),
SymbolKind.Class,
- Lsp4jConversion.locationToLsp4j(location));
- symbolInformations.add(information);
+ Either.forLeft(Lsp4jConversion.locationToLsp4j(location)));
+ symbolInformations.add(symbol);
}
for (final Method method : type.getLocalMethods()) {
@@ -83,36 +87,49 @@ public List getSymbols(final String query) {
final Location location = method.getLocation() != null
? method.getLocation()
: DUMMY_LOCATION;
- final SymbolInformation information = new SymbolInformation(
+ final WorkspaceSymbol symbol = new WorkspaceSymbol(
"Method: " + method.getSignature(),
SymbolKind.Method,
- Lsp4jConversion.locationToLsp4j(location));
- symbolInformations.add(information);
+ Either.forLeft(Lsp4jConversion.locationToLsp4j(location)));
+ symbolInformations.add(symbol);
}
if (typeMethodPredicate.test(type, method)) {
final Location location = method.getLocation() != null
? method.getLocation()
: DUMMY_LOCATION;
- final SymbolInformation information = new SymbolInformation(
+ final WorkspaceSymbol symbol = new WorkspaceSymbol(
"Method: " + method.getSignature(),
SymbolKind.Method,
- Lsp4jConversion.locationToLsp4j(location));
- symbolInformations.add(information);
+ Either.forLeft(Lsp4jConversion.locationToLsp4j(location)));
+ symbolInformations.add(symbol);
}
}
}
+ for (final Condition condition : this.typeKeeper.getConditions()) {
+ if (conditionPredicate.test(condition)) {
+ final Location location = condition.getLocation() != null
+ ? condition.getLocation()
+ : DUMMY_LOCATION;
+ final WorkspaceSymbol symbol = new WorkspaceSymbol(
+ "Condition: " + condition.getName(),
+ SymbolKind.Class,
+ Either.forLeft(Lsp4jConversion.locationToLsp4j(location)));
+ symbolInformations.add(symbol);
+ }
+ }
+
LOGGER.debug("Finished searching for: '{}', result count: {}", query, symbolInformations.size());
return symbolInformations;
}
/**
- * Build {{Predicate}} which matches {{AbstractType}}. This only gives a matchable
+ * Build {@link Predicate} which matches {@link AbstractType}. This only gives a matchable
* predicate if no '.' appears in the query.
*
* @param query Query string
- * @return Predicate to match {{AbstractType}}
+ * @return Predicate to match {@link AbstractType}.
*/
private Predicate buildTypePredicate(final String query) {
final int dotIndex = query.indexOf('.');
@@ -125,10 +142,27 @@ private Predicate buildTypePredicate(final String query) {
}
/**
- * Build {{Predicate}} which matches {{Method}}.
+ * Build {@link Predicate} which matches {@link Condition}. This only gives a matchable
+ * predicate if no '.' appears in the query.
+ *
+ * @param query Query string
+ * @return {@link Predicate} to match {@link Condition}
+ */
+ private Predicate buildConditionPredicate(final String query) {
+ final int dotIndex = query.indexOf('.');
+ if (dotIndex != -1) {
+ return type -> false;
+ }
+
+ final String regexp = ".*" + query + ".*";
+ return condition -> condition.getName().matches(regexp);
+ }
+
+ /**
+ * Build {@link Predicate} which matches {@link Method}.
* This only gives a matchable predicate if no '.' appears in the query.
* @param query Query string
- * @return Predicate to match {{Method}}
+ * @return Predicate to match {@link Method}
*/
private Predicate buildMethodPredicate(final String query) {
final int dotIndex = query.indexOf('.');
@@ -141,10 +175,10 @@ private Predicate buildMethodPredicate(final String query) {
}
/**
- * Build {{BiPredicate}} which matches {{AbstractType}} and {{Method}}.
+ * Build {@link BiPredicate} which matches {@link AbstractType} and {@link Method}.
* This only gives a matchable predicate if '.' appears in the query.
* @param query Query string
- * @return BiPredicate to match {{AbstractType}} and {{Method}}
+ * @return {@link BiPredicate} to match {@link AbstractType} and {@link Method}
*/
private BiPredicate buildTypeMethodPredicate(final String query) {
final int dotIndex = query.indexOf('.');
diff --git a/magik-language-server/src/main/resources/debug-logging.properties b/magik-language-server/src/main/resources/debug-logging.properties
index 64cbf62a..74375952 100644
--- a/magik-language-server/src/main/resources/debug-logging.properties
+++ b/magik-language-server/src/main/resources/debug-logging.properties
@@ -10,8 +10,19 @@ java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter
java.util.logging.SimpleFormatter.format = %1$tF %1$tT %4$-7s %2$s : %5$s %6$s%n
-nl.ramsolutions.sw.magik.analysis.typing.ClassInfoTypeKeeperReader.level = OFF
-nl.ramsolutions.sw.magik.analysis.typing.LocalTypeReasoner.level = OFF
-nl.ramsolutions.sw.magik.languageserver.indexer.MagikIndexer.level = OFF
-nl.ramsolutions.sw.magik.languageserver.indexer.PreMagikIndexer.level = OFF
-nl.ramsolutions.sw.magik.languageserver.indexer.MagikPreIndexer.level = OFF
+nl.ramsolutions.sw.magik.analysis.typing.ClassInfoTypeKeeperReader.level = WARNING
+nl.ramsolutions.sw.magik.analysis.typing.TypeKeeper.level = WARNING
+nl.ramsolutions.sw.magik.analysis.typing.indexer.MagikPreIndexer.level = WARNING
+nl.ramsolutions.sw.magik.analysis.typing.indexer.MagikIndexer.level = WARNING
+nl.ramsolutions.sw.magik.analysis.typing.LocalTypeReasoner.level = WARNING
+
+nl.ramsolutions.sw.magik.languageserver.completion.CompletionProvider.level = WARNING
+nl.ramsolutions.sw.magik.languageserver.diagnostics.DiagnosticsProvider.level = WARNING
+nl.ramsolutions.sw.magik.languageserver.formatting.FormattingProvider.level = WARNING
+nl.ramsolutions.sw.magik.languageserver.hover.HoverProvider.level = WARNING
+nl.ramsolutions.sw.magik.languageserver.implementation.ImplementationProvider.level = WARNING
+nl.ramsolutions.sw.magik.languageserver.references.ReferencesProvider.level = WARNING
+nl.ramsolutions.sw.magik.languageserver.rename.RenameProvider.level = WARNING
+nl.ramsolutions.sw.magik.languageserver.semantictokens.SemanticTokenProvider.level = WARNING
+nl.ramsolutions.sw.magik.languageserver.signaturehelp.SignatureHelpProvider.level = WARNING
+nl.ramsolutions.sw.magik.languageserver.symbol.SymbolProvider.level = WARNING
diff --git a/magik-language-server/src/test/java/nl/ramsolutions/sw/magik/ramsolutions/completion/CompletionProviderTest.java b/magik-language-server/src/test/java/nl/ramsolutions/sw/magik/ramsolutions/completion/CompletionProviderTest.java
index 03d247a7..ffab11db 100644
--- a/magik-language-server/src/test/java/nl/ramsolutions/sw/magik/ramsolutions/completion/CompletionProviderTest.java
+++ b/magik-language-server/src/test/java/nl/ramsolutions/sw/magik/ramsolutions/completion/CompletionProviderTest.java
@@ -11,11 +11,11 @@
import nl.ramsolutions.sw.magik.analysis.typing.ITypeKeeper;
import nl.ramsolutions.sw.magik.analysis.typing.TypeKeeper;
import nl.ramsolutions.sw.magik.analysis.typing.types.AbstractType;
-import nl.ramsolutions.sw.magik.analysis.typing.types.ExpressionResult;
-import nl.ramsolutions.sw.magik.analysis.typing.types.GlobalReference;
+import nl.ramsolutions.sw.magik.analysis.typing.types.ExpressionResultString;
import nl.ramsolutions.sw.magik.analysis.typing.types.MagikType;
+import nl.ramsolutions.sw.magik.analysis.typing.types.MagikType.Sort;
import nl.ramsolutions.sw.magik.analysis.typing.types.Method;
-import nl.ramsolutions.sw.magik.analysis.typing.types.SlottedType;
+import nl.ramsolutions.sw.magik.analysis.typing.types.TypeString;
import nl.ramsolutions.sw.magik.api.MagikKeyword;
import nl.ramsolutions.sw.magik.languageserver.completion.CompletionProvider;
import org.eclipse.lsp4j.CompletionItem;
@@ -69,14 +69,17 @@ void testMethodCompletionBare() {
+ " 1.\n"
+ "_endmethod";
final ITypeKeeper typeKeeper = new TypeKeeper();
- final MagikType integerType = (MagikType) typeKeeper.getType(GlobalReference.of("sw:integer"));
+ final TypeString integerRef = TypeString.of("sw:integer");
+ final MagikType integerType = (MagikType) typeKeeper.getType(integerRef);
integerType.addMethod(
- EnumSet.noneOf(Method.Modifier.class),
null,
+ EnumSet.noneOf(Method.Modifier.class),
"find_me()",
Collections.emptyList(),
null,
- ExpressionResult.UNDEFINED);
+ null,
+ ExpressionResultString.UNDEFINED,
+ new ExpressionResultString());
final Position position = new Position(1, 6); // On '.'.
final List completions = this.getCompletions(code, typeKeeper, position);
@@ -96,15 +99,17 @@ void testMethodCompletionSelf() {
+ " _self.\n"
+ "_endmethod";
final ITypeKeeper typeKeeper = new TypeKeeper();
- final MagikType aType = new SlottedType(GlobalReference.of("user:a"));
- typeKeeper.addType(aType);
+ final TypeString aRef = TypeString.of("user:a");
+ final MagikType aType = new MagikType(typeKeeper, Sort.SLOTTED, aRef);
aType.addMethod(
- EnumSet.noneOf(Method.Modifier.class),
null,
+ EnumSet.noneOf(Method.Modifier.class),
"find_me()",
Collections.emptyList(),
null,
- ExpressionResult.UNDEFINED);
+ null,
+ ExpressionResultString.UNDEFINED,
+ new ExpressionResultString());
final Position position = new Position(1, 10); // On '.'.
final List completions = this.getCompletions(code, typeKeeper, position);
@@ -124,14 +129,17 @@ void testMethodCompletionExisting() {
+ " 1.fi\n"
+ "_endmethod";
final ITypeKeeper typeKeeper = new TypeKeeper();
- final MagikType integerType = (MagikType) typeKeeper.getType(GlobalReference.of("sw:integer"));
+ final TypeString integerRef = TypeString.of("sw:integer");
+ final MagikType integerType = (MagikType) typeKeeper.getType(integerRef);
integerType.addMethod(
- EnumSet.noneOf(Method.Modifier.class),
null,
+ EnumSet.noneOf(Method.Modifier.class),
"find_me()",
Collections.emptyList(),
null,
- ExpressionResult.UNDEFINED);
+ null,
+ ExpressionResultString.UNDEFINED,
+ new ExpressionResultString());
final Position position = new Position(1, 8); // On 'i'.
final List completions = this.getCompletions(code, typeKeeper, position);
@@ -198,9 +206,9 @@ void testGlobalCompletionSlot() {
+ " \n"
+ "_endmethod";
final ITypeKeeper typeKeeper = new TypeKeeper();
- final MagikType aType = new SlottedType(GlobalReference.of("user", "a"));
+ final TypeString aRef = TypeString.of("user:a");
+ final MagikType aType = new MagikType(typeKeeper, Sort.SLOTTED, aRef);
aType.addSlot(null, "slot1");
- typeKeeper.addType(aType);
final Position position = new Position(1, 2); // On ''.
final List completions = this.getCompletions(code, typeKeeper, position);
diff --git a/magik-language-server/src/test/java/nl/ramsolutions/sw/magik/ramsolutions/formatting/FormattingProviderTest.java b/magik-language-server/src/test/java/nl/ramsolutions/sw/magik/ramsolutions/formatting/FormattingProviderTest.java
index c6f4a4db..619711bf 100644
--- a/magik-language-server/src/test/java/nl/ramsolutions/sw/magik/ramsolutions/formatting/FormattingProviderTest.java
+++ b/magik-language-server/src/test/java/nl/ramsolutions/sw/magik/ramsolutions/formatting/FormattingProviderTest.java
@@ -34,9 +34,9 @@ private List getEdits(final String code, final FormattingOptions optio
return provider.provideFormatting(magikFile, options);
}
- // region: Whitespaces
+ // region: Whitespace
@Test
- void testMethodDefintion1() {
+ void testWhitespaceMethodDefintion1() {
final String code = ""
+ "_method a. b(x, y, z)\n"
+ "_endmethod\n";
@@ -46,7 +46,7 @@ void testMethodDefintion1() {
}
@Test
- void testMethodDefintion2() {
+ void testWhitespaceMethodDefintion2() {
final String code = ""
+ "_method a.b (x, y, z)\n"
+ "_endmethod\n";
@@ -56,7 +56,7 @@ void testMethodDefintion2() {
}
@Test
- void testParameters1() {
+ void testWhitespaceParameters1() {
final String code = ""
+ "_method a.b(x,y, z)\n"
+ "_endmethod\n";
@@ -74,7 +74,7 @@ void testParameters1() {
}
@Test
- void testParameters2() {
+ void testWhitespaceParameters2() {
final String code = ""
+ "_method a.b(x, y,z)\n"
+ "_endmethod\n";
@@ -92,7 +92,7 @@ void testParameters2() {
}
@Test
- void testParameters3() {
+ void testWhitespaceParameters3() {
final String code = ""
+ "_method a.b(x, y , z)\n"
+ "_endmethod\n";
@@ -110,7 +110,7 @@ void testParameters3() {
}
@Test
- void testParameters4() {
+ void testWhitespaceParameters4() {
final String code = "print(a,b, c)\n";
final List edits = this.getEdits(code);
@@ -126,7 +126,7 @@ void testParameters4() {
}
@Test
- void testParameters5() {
+ void testWhitespaceParameters5() {
final String code = "print(a, b,c)\n";
final List edits = this.getEdits(code);
@@ -142,7 +142,7 @@ void testParameters5() {
}
@Test
- void testMethodInvocation1() {
+ void testWhitespaceMethodInvocation1() {
final String code = "class .method(a, b, c)\n";
final List edits = this.getEdits(code);
@@ -158,7 +158,7 @@ void testMethodInvocation1() {
}
@Test
- void testMethodInvocation2() {
+ void testWhitespaceMethodInvocation2() {
final String code = "class. method(a, b, c)\n";
final List edits = this.getEdits(code);
@@ -174,7 +174,7 @@ void testMethodInvocation2() {
}
@Test
- void testMethodInvocation3() {
+ void testWhitespaceMethodInvocation3() {
final String code = "class.method (a, b, c)\n";
final List edits = this.getEdits(code);
@@ -190,7 +190,7 @@ void testMethodInvocation3() {
}
@Test
- void testArguments1() {
+ void testWhitespaceArguments1() {
final String code = "prc( a, b, c)\n";
final List edits = this.getEdits(code);
@@ -206,7 +206,7 @@ void testArguments1() {
}
@Test
- void testArguments2() {
+ void testWhitespaceArguments2() {
final String code = "prc(a,b, c)\n";
final List edits = this.getEdits(code);
@@ -222,7 +222,7 @@ void testArguments2() {
}
@Test
- void testArguments3() {
+ void testWhitespaceArguments3() {
final String code = "prc(a, b,c)\n";
final List edits = this.getEdits(code);
@@ -238,7 +238,7 @@ void testArguments3() {
}
@Test
- void testArguments4() {
+ void testWhitespaceArguments4() {
final String code = "prc(a, b , c)\n";
final List edits = this.getEdits(code);
@@ -254,7 +254,15 @@ void testArguments4() {
}
@Test
- void testMethodInvocationMultiLine() {
+ void testWhitespaceArgumentsSelf() {
+ final String code = "prc(_self)\n";
+ final List edits = this.getEdits(code);
+
+ assertThat(edits).isEmpty();
+ }
+
+ @Test
+ void testWhitespaceMethodInvocationMultiLine() {
final String code = ""
+ "obj.\n"
+ "m()\n";
@@ -270,11 +278,29 @@ void testMethodInvocationMultiLine() {
"\t");
assertThat(edit0).isEqualTo(expected0);
}
+
+ @Test
+ void testWhitespaceSimpleVector() {
+ final String code = ""
+ + "{:slot1, _unset, :readable, :public}";
+
+ final List edits = this.getEdits(code);
+ assertThat(edits).isEmpty();
+ }
+
+ @Test
+ void testWhitespaceAssignmentMethod() {
+ final String code = ""
+ + "_self.x() << 10";
+
+ final List edits = this.getEdits(code);
+ assertThat(edits).isEmpty();
+ }
// endregion
// region: Indenting.
@Test
- void testIndentingStatement() {
+ void testIndentBlockStatement() {
final String code = ""
+ "_block\n"
+ "print(1)\n"
@@ -293,7 +319,7 @@ void testIndentingStatement() {
}
@Test
- void testIndentingComments() {
+ void testIndentComments() {
final String code = ""
+ "_block\n"
+ "# comment\n"
@@ -312,7 +338,7 @@ void testIndentingComments() {
}
@Test
- void testCommentsAfterStatement() {
+ void testIndentCommentsAfterStatement() {
final String code = ""
+ "_method a.b(a, b, c)\n"
+ "\tprint(1) # test method\n"
@@ -323,7 +349,7 @@ void testCommentsAfterStatement() {
}
@Test
- void testNewlineExpression() {
+ void testIndentNewlineExpression() {
final String code = ""
+ "_if a() _andif\n"
+ "b()\n"
@@ -361,7 +387,7 @@ void testIndentAssignmentExpression() {
}
@Test
- void testAssignmentExpression() {
+ void testIndentAssignmentExpression2() {
final String code = ""
+ "a << _if x?\n"
+ "_then\n"
@@ -413,6 +439,43 @@ void testAssignmentExpression() {
"\t");
assertThat(edit4).isEqualTo(expected4);
}
+
+ @Test
+ void testIndentArguments() {
+ final String code = ""
+ + "def_slotted_exemplar(\n"
+ + "\t:test_ex,\n"
+ + "\t{\n"
+ + "\t\t{:slot1, _unset}\n"
+ + "\t})\n";
+ final List edits = this.getEdits(code);
+ assertThat(edits).isEmpty();
+ }
+
+ @Test
+ void testIndentIfElif() {
+ final String code = ""
+ + "_if a\n"
+ + "_then\n"
+ + "\tshow(:a)\n"
+ + "_elif b\n"
+ + "_then\n"
+ + "\tshow(:b)\n"
+ + "_else\n"
+ + "\tshow(:c)\n"
+ + "_endif\n";
+ final List edits = this.getEdits(code);
+ assertThat(edits).isEmpty();
+ }
+
+ @Test
+ void testIndentVariableDefinitionAssignment() {
+ final String code = ""
+ + "_local a <<\n"
+ + "\t10";
+ final List edits = this.getEdits(code);
+ assertThat(edits).isEmpty();
+ }
// endregion
// region: Comments
diff --git a/magik-language-server/src/test/java/nl/ramsolutions/sw/magik/ramsolutions/hover/HoverProviderTest.java b/magik-language-server/src/test/java/nl/ramsolutions/sw/magik/ramsolutions/hover/HoverProviderTest.java
index eaa88c6a..79a1347e 100644
--- a/magik-language-server/src/test/java/nl/ramsolutions/sw/magik/ramsolutions/hover/HoverProviderTest.java
+++ b/magik-language-server/src/test/java/nl/ramsolutions/sw/magik/ramsolutions/hover/HoverProviderTest.java
@@ -4,13 +4,14 @@
import java.util.Collections;
import java.util.EnumSet;
import nl.ramsolutions.sw.magik.MagikTypedFile;
+import nl.ramsolutions.sw.magik.analysis.typing.BinaryOperator;
import nl.ramsolutions.sw.magik.analysis.typing.ITypeKeeper;
import nl.ramsolutions.sw.magik.analysis.typing.TypeKeeper;
-import nl.ramsolutions.sw.magik.analysis.typing.types.ExpressionResult;
-import nl.ramsolutions.sw.magik.analysis.typing.types.GlobalReference;
+import nl.ramsolutions.sw.magik.analysis.typing.types.ExpressionResultString;
import nl.ramsolutions.sw.magik.analysis.typing.types.MagikType;
+import nl.ramsolutions.sw.magik.analysis.typing.types.MagikType.Sort;
import nl.ramsolutions.sw.magik.analysis.typing.types.Method;
-import nl.ramsolutions.sw.magik.analysis.typing.types.SlottedType;
+import nl.ramsolutions.sw.magik.analysis.typing.types.TypeString;
import nl.ramsolutions.sw.magik.languageserver.hover.HoverProvider;
import org.eclipse.lsp4j.Hover;
import org.eclipse.lsp4j.MarkupContent;
@@ -37,15 +38,17 @@ private Hover provideHover(final String code, final Position position, final ITy
void testProvideHoverMethodDefinitionName() {
// Set up a method in the TypeKeeper.
final ITypeKeeper typeKeeper = new TypeKeeper();
- final MagikType objectType = (MagikType) typeKeeper.getType(GlobalReference.of("sw:object"));
- final Method method = objectType.addMethod(
- EnumSet.noneOf(Method.Modifier.class),
+ final TypeString objectRef = TypeString.of("sw:object");
+ final MagikType objectType = (MagikType) typeKeeper.getType(objectRef);
+ objectType.addMethod(
null,
+ EnumSet.noneOf(Method.Modifier.class),
"hover_me_method()",
Collections.emptyList(),
null,
- ExpressionResult.UNDEFINED);
- method.setDoc("method_doc");
+ "method_doc",
+ ExpressionResultString.UNDEFINED,
+ new ExpressionResultString());
final String code = ""
+ "_method object.hover_me_method()\n"
@@ -64,9 +67,9 @@ void testProvideHoverMethodDefinitionName() {
void testProvideHoverMethodDefinitionExemplar() {
// Set up a method.
final ITypeKeeper typeKeeper = new TypeKeeper();
- final MagikType hoverMeType = new SlottedType(GlobalReference.of("user:hover_me_type"));
+ final TypeString hoverMeTypeRef = TypeString.of("user:hover_me_type");
+ final MagikType hoverMeType = new MagikType(typeKeeper, Sort.SLOTTED, hoverMeTypeRef);
hoverMeType.setDoc("type_doc");
- typeKeeper.addType(hoverMeType);
final String code = ""
+ "_method hover_me_type.method()\n"
@@ -85,15 +88,17 @@ void testProvideHoverMethodDefinitionExemplar() {
void testProvideHoverMethod() {
// Set up a method.
final ITypeKeeper typeKeeper = new TypeKeeper();
- final MagikType integerType = (MagikType) typeKeeper.getType(GlobalReference.of("sw:integer"));
- final Method method = integerType.addMethod(
- EnumSet.noneOf(Method.Modifier.class),
+ final TypeString integerRef = TypeString.of("sw:integer");
+ final MagikType integerType = (MagikType) typeKeeper.getType(integerRef);
+ integerType.addMethod(
null,
+ EnumSet.noneOf(Method.Modifier.class),
"hover_me()",
Collections.emptyList(),
null,
- ExpressionResult.UNDEFINED);
- method.setDoc("method_doc");
+ "method_doc",
+ ExpressionResultString.UNDEFINED,
+ new ExpressionResultString());
final String code = ""
+ "_method a.b\n"
@@ -132,7 +137,8 @@ void testProvideHoverMethodUnknown() {
void testProvideHoverType() {
// Set up a method.
final ITypeKeeper typeKeeper = new TypeKeeper();
- final MagikType symbolType = (MagikType) typeKeeper.getType(GlobalReference.of("sw:symbol"));
+ final TypeString symbolRef = TypeString.of("sw:symbol");
+ final MagikType symbolType = (MagikType) typeKeeper.getType(symbolRef);
symbolType.setDoc("type_doc");
final String code = ""
@@ -154,15 +160,13 @@ void testProvideHoverType() {
void testProvideHoverTypeUnknown() {
// Set up a method.
final ITypeKeeper typeKeeper = new TypeKeeper();
- final MagikType symbolType = (MagikType) typeKeeper.getType(GlobalReference.of("sw:symbol"));
- symbolType.setDoc("type_doc");
final String code = ""
+ "_method a.b\n"
+ " _local var << some_object\n"
+ " var.hover_me()\n"
+ "_endmethod";
- final Position position = new Position(2, 4);
+ final Position position = new Position(2, 4); // On `var`.
// Hover and test.
final Hover hover = this.provideHover(code, position, typeKeeper);
@@ -175,13 +179,13 @@ void testProvideHoverTypeUnknown() {
void testProvideHoverAssignedVariable() {
// Set up a method.
final ITypeKeeper typeKeeper = new TypeKeeper();
- final MagikType symbolType = (MagikType) typeKeeper.getType(GlobalReference.of("sw:symbol"));
+ final TypeString symbolRef = TypeString.of("sw:symbol");
+ final MagikType symbolType = (MagikType) typeKeeper.getType(symbolRef);
symbolType.setDoc("type_doc");
final String code = ""
+ "_method a.b\n"
+ " _local var << :symbol\n"
- + " var.hover_me()\n"
+ "_endmethod";
final Position position = new Position(1, 11); // On `var`.
@@ -193,4 +197,30 @@ void testProvideHoverAssignedVariable() {
assertThat(content.getValue()).contains("type_doc");
}
+ @Test
+ void testBinaryOperatorTimes() {
+ // Set up a method.
+ final ITypeKeeper typeKeeper = new TypeKeeper();
+ final TypeString integerRef = TypeString.of("sw:integer");
+ final MagikType integerType = (MagikType) typeKeeper.getType(integerRef);
+ integerType.setDoc("type_doc");
+
+ final BinaryOperator binaryOperator =
+ new BinaryOperator(BinaryOperator.Operator.STAR, integerRef, integerRef, integerRef);
+ typeKeeper.addBinaryOperator(binaryOperator);
+
+ final String code = ""
+ + "_method a.b\n"
+ + " _local var << 4 * 4\n"
+ + "_endmethod";
+ final Position position = new Position(1, 20); // On `*`.
+
+ // Hover and test.
+ final Hover hover = this.provideHover(code, position, typeKeeper);
+ final MarkupContent content = hover.getContents().getRight();
+ assertThat(content.getKind()).isEqualTo(MarkupKind.MARKDOWN);
+ assertThat(content.getValue()).contains("integer");
+ assertThat(content.getValue()).contains("type_doc");
+ }
+
}
diff --git a/magik-language-server/src/test/java/nl/ramsolutions/sw/magik/ramsolutions/implementation/ImplementationProviderTest.java b/magik-language-server/src/test/java/nl/ramsolutions/sw/magik/ramsolutions/implementation/ImplementationProviderTest.java
index 9fd9b4c0..0cb9a64d 100644
--- a/magik-language-server/src/test/java/nl/ramsolutions/sw/magik/ramsolutions/implementation/ImplementationProviderTest.java
+++ b/magik-language-server/src/test/java/nl/ramsolutions/sw/magik/ramsolutions/implementation/ImplementationProviderTest.java
@@ -10,10 +10,10 @@
import nl.ramsolutions.sw.magik.analysis.Range;
import nl.ramsolutions.sw.magik.analysis.typing.ITypeKeeper;
import nl.ramsolutions.sw.magik.analysis.typing.TypeKeeper;
-import nl.ramsolutions.sw.magik.analysis.typing.types.ExpressionResult;
-import nl.ramsolutions.sw.magik.analysis.typing.types.GlobalReference;
+import nl.ramsolutions.sw.magik.analysis.typing.types.ExpressionResultString;
import nl.ramsolutions.sw.magik.analysis.typing.types.MagikType;
import nl.ramsolutions.sw.magik.analysis.typing.types.Method;
+import nl.ramsolutions.sw.magik.analysis.typing.types.TypeString;
import nl.ramsolutions.sw.magik.languageserver.Lsp4jConversion;
import nl.ramsolutions.sw.magik.languageserver.implementation.ImplementationProvider;
import org.junit.jupiter.api.Test;
@@ -33,14 +33,17 @@ class ImplementationProviderTest {
@Test
void testProvideImplementation() {
final ITypeKeeper typeKeeper = new TypeKeeper();
- final MagikType integerType = (MagikType) typeKeeper.getType(GlobalReference.of("sw:integer"));
+ final TypeString integerRef = TypeString.of("sw:integer");
+ final MagikType integerType = (MagikType) typeKeeper.getType(integerRef);
integerType.addMethod(
- EnumSet.noneOf(Method.Modifier.class),
EMPTY_LOCATION,
+ EnumSet.noneOf(Method.Modifier.class),
"implementation()",
Collections.emptyList(),
null,
- ExpressionResult.UNDEFINED);
+ null,
+ ExpressionResultString.UNDEFINED,
+ new ExpressionResultString());
final URI uri = URI.create("tests://unittest");
final String code = ""
diff --git a/magik-language-server/src/test/java/nl/ramsolutions/sw/magik/ramsolutions/indexer/MagikIndexerTest.java b/magik-language-server/src/test/java/nl/ramsolutions/sw/magik/ramsolutions/indexer/MagikIndexerTest.java
index fac4c0d3..11be016e 100644
--- a/magik-language-server/src/test/java/nl/ramsolutions/sw/magik/ramsolutions/indexer/MagikIndexerTest.java
+++ b/magik-language-server/src/test/java/nl/ramsolutions/sw/magik/ramsolutions/indexer/MagikIndexerTest.java
@@ -4,11 +4,11 @@
import java.util.Collection;
import nl.ramsolutions.sw.magik.analysis.typing.ITypeKeeper;
import nl.ramsolutions.sw.magik.analysis.typing.TypeKeeper;
+import nl.ramsolutions.sw.magik.analysis.typing.indexer.MagikIndexer;
import nl.ramsolutions.sw.magik.analysis.typing.types.AbstractType;
-import nl.ramsolutions.sw.magik.analysis.typing.types.GlobalReference;
import nl.ramsolutions.sw.magik.analysis.typing.types.Method;
+import nl.ramsolutions.sw.magik.analysis.typing.types.TypeString;
import nl.ramsolutions.sw.magik.analysis.typing.types.UndefinedType;
-import nl.ramsolutions.sw.magik.languageserver.indexer.MagikIndexer;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
@@ -21,7 +21,7 @@ class MagikIndexerTest {
/**
* VSCode runs from module directory, mvn runs from project directory.
*
- * @return Proper {{Path}} to file.
+ * @return Proper {@link Path} to file.
*/
protected Path getPath(final Path relativePath) {
final Path path = Path.of(".").toAbsolutePath().getParent();
@@ -40,8 +40,8 @@ void testFileCreated() {
magikIndexer.indexPathCreated(fixedPath);
// Test type.
- final GlobalReference globalRef = GlobalReference.of("user:test_exemplar");
- final AbstractType type = typeKeeper.getType(globalRef);
+ final TypeString typeString = TypeString.of("user:test_exemplar");
+ final AbstractType type = typeKeeper.getType(typeString);
assertThat(type).isNotEqualTo(UndefinedType.INSTANCE);
// Test methods.
@@ -64,8 +64,8 @@ void testFileChanged() {
magikIndexer.indexPathChanged(fixedPath);
// Test type.
- final GlobalReference globalRef = GlobalReference.of("user:test_exemplar");
- final AbstractType type = typeKeeper.getType(globalRef);
+ final TypeString typeString = TypeString.of("user:test_exemplar");
+ final AbstractType type = typeKeeper.getType(typeString);
assertThat(type).isNotEqualTo(UndefinedType.INSTANCE);
// Test methods.
@@ -85,15 +85,15 @@ void testFileDeleted() {
magikIndexer.indexPathCreated(fixedPath);
// Test type.
- final GlobalReference globalRef = GlobalReference.of("user:test_exemplar");
- final AbstractType type = typeKeeper.getType(globalRef);
+ final TypeString typeString = TypeString.of("user:test_exemplar");
+ final AbstractType type = typeKeeper.getType(typeString);
assertThat(type).isNotEqualTo(UndefinedType.INSTANCE);
- // Pretend update.
+ // Pretend delete.
magikIndexer.indexPathDeleted(fixedPath);
// Test type.
- final AbstractType typeRemoved = typeKeeper.getType(globalRef);
+ final AbstractType typeRemoved = typeKeeper.getType(typeString);
assertThat(typeRemoved).isEqualTo(UndefinedType.INSTANCE);
}
diff --git a/magik-language-server/src/test/java/nl/ramsolutions/sw/magik/ramsolutions/references/ReferencesProviderTest.java b/magik-language-server/src/test/java/nl/ramsolutions/sw/magik/ramsolutions/references/ReferencesProviderTest.java
index dd2c8688..ab61946f 100644
--- a/magik-language-server/src/test/java/nl/ramsolutions/sw/magik/ramsolutions/references/ReferencesProviderTest.java
+++ b/magik-language-server/src/test/java/nl/ramsolutions/sw/magik/ramsolutions/references/ReferencesProviderTest.java
@@ -10,10 +10,11 @@
import nl.ramsolutions.sw.magik.analysis.Range;
import nl.ramsolutions.sw.magik.analysis.typing.ITypeKeeper;
import nl.ramsolutions.sw.magik.analysis.typing.TypeKeeper;
-import nl.ramsolutions.sw.magik.analysis.typing.types.ExpressionResult;
-import nl.ramsolutions.sw.magik.analysis.typing.types.GlobalReference;
+import nl.ramsolutions.sw.magik.analysis.typing.types.ExpressionResultString;
import nl.ramsolutions.sw.magik.analysis.typing.types.MagikType;
import nl.ramsolutions.sw.magik.analysis.typing.types.Method;
+import nl.ramsolutions.sw.magik.analysis.typing.types.TypeString;
+import nl.ramsolutions.sw.magik.analysis.typing.types.UndefinedType;
import nl.ramsolutions.sw.magik.languageserver.references.ReferencesProvider;
import org.junit.jupiter.api.Test;
@@ -40,15 +41,21 @@ private List getReferences(
@Test
void testProvideMethodReferenceFromMethodInvocation() {
final ITypeKeeper typeKeeper = new TypeKeeper();
- final MagikType integerType = (MagikType) typeKeeper.getType(GlobalReference.of("sw:integer"));
+ final TypeString integerRef = TypeString.of("sw:integer");
+ final MagikType integerType = (MagikType) typeKeeper.getType(integerRef);
final Method referingMethod = integerType.addMethod(
- EnumSet.noneOf(Method.Modifier.class),
EMPTY_LOCATION,
+ EnumSet.noneOf(Method.Modifier.class),
"refering",
Collections.emptyList(),
null,
- ExpressionResult.UNDEFINED);
- referingMethod.addCalledMethod("refering");
+ null,
+ ExpressionResultString.UNDEFINED,
+ new ExpressionResultString());
+ final TypeString undefinedTypeRef = TypeString.of(UndefinedType.SERIALIZED_NAME);
+ final Method.MethodUsage calledMethod =
+ new Method.MethodUsage(undefinedTypeRef, "refering", EMPTY_LOCATION);
+ referingMethod.addCalledMethod(calledMethod);
final String code = ""
+ "_method integer.refering\n"
@@ -62,21 +69,27 @@ void testProvideMethodReferenceFromMethodInvocation() {
@Test
void testProvideMethodReferenceFromMethodDefintion() {
final ITypeKeeper typeKeeper = new TypeKeeper();
- final MagikType integerType = (MagikType) typeKeeper.getType(GlobalReference.of("sw:integer"));
+ final TypeString interRef = TypeString.of("sw:integer");
+ final MagikType integerType = (MagikType) typeKeeper.getType(interRef);
final Method referingMethod = integerType.addMethod(
- EnumSet.noneOf(Method.Modifier.class),
EMPTY_LOCATION,
+ EnumSet.noneOf(Method.Modifier.class),
"refering",
Collections.emptyList(),
null,
- ExpressionResult.UNDEFINED);
- referingMethod.addCalledMethod("refering");
+ null,
+ ExpressionResultString.UNDEFINED,
+ new ExpressionResultString());
+ final TypeString undefinedTypeRef = TypeString.of(UndefinedType.SERIALIZED_NAME);
+ final Method.MethodUsage calledMethod =
+ new Method.MethodUsage(undefinedTypeRef, "refering", EMPTY_LOCATION);
+ referingMethod.addCalledMethod(calledMethod);
final String code = ""
+ "_method integer.refering\n"
+ " _self.refering\n"
+ "_endmethod\n";
- final org.eclipse.lsp4j.Position position = new org.eclipse.lsp4j.Position(0, 20); // On refering
+ final org.eclipse.lsp4j.Position position = new org.eclipse.lsp4j.Position(0, 20); // On `refering`.
final List references = this.getReferences(code, position, typeKeeper);
assertThat(references).hasSize(1);
}
@@ -84,21 +97,25 @@ void testProvideMethodReferenceFromMethodDefintion() {
@Test
void testProvideTypeReferenceFromAtom() {
final ITypeKeeper typeKeeper = new TypeKeeper();
- final MagikType integerType = (MagikType) typeKeeper.getType(GlobalReference.of("sw:integer"));
+ final TypeString integerRef = TypeString.of("sw:integer");
+ final MagikType integerType = (MagikType) typeKeeper.getType(integerRef);
final Method referingMethod = integerType.addMethod(
- EnumSet.noneOf(Method.Modifier.class),
EMPTY_LOCATION,
+ EnumSet.noneOf(Method.Modifier.class),
"refering",
Collections.emptyList(),
null,
- ExpressionResult.UNDEFINED);
- referingMethod.addUsedType("sw:integer");
+ null,
+ ExpressionResultString.UNDEFINED,
+ new ExpressionResultString());
+ final Method.GlobalUsage typeUsage = new Method.GlobalUsage(integerRef, EMPTY_LOCATION);
+ referingMethod.addUsedType(typeUsage);
final String code = ""
+ "_method integer.refering\n"
+ " integer\n"
+ "_endmethod\n";
- final org.eclipse.lsp4j.Position position = new org.eclipse.lsp4j.Position(1, 4); // On integer.
+ final org.eclipse.lsp4j.Position position = new org.eclipse.lsp4j.Position(1, 4); // On `integer`.
final List references = this.getReferences(code, position, typeKeeper);
assertThat(references).hasSize(1);
}
@@ -106,21 +123,25 @@ void testProvideTypeReferenceFromAtom() {
@Test
void testProvideTypeReferenceFromMethodDefinition() {
final ITypeKeeper typeKeeper = new TypeKeeper();
- final MagikType integerType = (MagikType) typeKeeper.getType(GlobalReference.of("sw:integer"));
+ final TypeString integerRef = TypeString.of("sw:integer");
+ final MagikType integerType = (MagikType) typeKeeper.getType(integerRef);
final Method referingMethod = integerType.addMethod(
- EnumSet.noneOf(Method.Modifier.class),
EMPTY_LOCATION,
+ EnumSet.noneOf(Method.Modifier.class),
"refering",
Collections.emptyList(),
null,
- ExpressionResult.UNDEFINED);
- referingMethod.addUsedType("sw:integer");
+ null,
+ ExpressionResultString.UNDEFINED,
+ new ExpressionResultString());
+ final Method.GlobalUsage typeUsage = new Method.GlobalUsage(integerRef, EMPTY_LOCATION);
+ referingMethod.addUsedType(typeUsage);
final String code = ""
+ "_method integer.refering\n"
+ " print(integer)\n"
+ "_endmethod\n";
- final org.eclipse.lsp4j.Position position = new org.eclipse.lsp4j.Position(0, 10); // On integer.
+ final org.eclipse.lsp4j.Position position = new org.eclipse.lsp4j.Position(0, 10); // On `integer`.
final List references = this.getReferences(code, position, typeKeeper);
assertThat(references).hasSize(1);
}
diff --git a/magik-language-server/src/test/java/nl/ramsolutions/sw/magik/ramsolutions/rename/RenameProviderTest.java b/magik-language-server/src/test/java/nl/ramsolutions/sw/magik/ramsolutions/rename/RenameProviderTest.java
index 2961b488..73112aed 100644
--- a/magik-language-server/src/test/java/nl/ramsolutions/sw/magik/ramsolutions/rename/RenameProviderTest.java
+++ b/magik-language-server/src/test/java/nl/ramsolutions/sw/magik/ramsolutions/rename/RenameProviderTest.java
@@ -8,11 +8,12 @@
import nl.ramsolutions.sw.magik.analysis.typing.TypeKeeper;
import nl.ramsolutions.sw.magik.languageserver.rename.RenameProvider;
import org.eclipse.lsp4j.Position;
+import org.eclipse.lsp4j.PrepareRenameDefaultBehavior;
import org.eclipse.lsp4j.PrepareRenameResult;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.TextEdit;
import org.eclipse.lsp4j.WorkspaceEdit;
-import org.eclipse.lsp4j.jsonrpc.messages.Either;
+import org.eclipse.lsp4j.jsonrpc.messages.Either3;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
@@ -23,7 +24,8 @@
@SuppressWarnings("checkstyle:MagicNumber")
class RenameProviderTest {
- private Either getPrepareRename(String code, final Position position) {
+ private Either3 getPrepareRename(
+ String code, final Position position) {
final URI uri = URI.create("tests://unittest");
final ITypeKeeper typeKeeper = new TypeKeeper();
final MagikTypedFile magikFile = new MagikTypedFile(uri, code, typeKeeper);
@@ -48,10 +50,11 @@ void testPrepareRenameLocal() {
+ "_endblock\n";
final Position position = new Position(1, 12); // On `var`.
- final Either either = this.getPrepareRename(code, position);
+ final Either3 either =
+ this.getPrepareRename(code, position);
assertThat(either).isNotNull();
- final PrepareRenameResult prepareRenameResult = either.getRight();
+ final PrepareRenameResult prepareRenameResult = either.getSecond();
assertThat(prepareRenameResult.getRange()).isEqualTo(
new Range(
new Position(1, 11), new Position(1, 14)));
@@ -105,10 +108,11 @@ void testPrepareRenameForVariable() {
+ "_endblock\n";
final Position position = new Position(1, 10); // on `iter_var`.
- final Either either = this.getPrepareRename(code, position);
+ final Either3 either =
+ this.getPrepareRename(code, position);
assertThat(either).isNotNull();
- final PrepareRenameResult prepareRenameResult = either.getRight();
+ final PrepareRenameResult prepareRenameResult = either.getSecond();
assertThat(prepareRenameResult.getRange()).isEqualTo(
new Range(
new Position(1, 9), new Position(1, 17)));
diff --git a/magik-lint/pom.xml b/magik-lint/pom.xml
index 0acd0bcb..95967a6a 100644
--- a/magik-lint/pom.xml
+++ b/magik-lint/pom.xml
@@ -5,7 +5,7 @@
nl.ramsolutions
magik-tools
- 0.5.5-SNAPSHOT
+ 0.6.0
magik-lint
@@ -27,10 +27,6 @@
commons-cli
commons-cli
-
- org.apache.commons
- commons-text
-
org.slf4j
slf4j-api
diff --git a/magik-lint/src/main/java/nl/ramsolutions/sw/magik/lint/Configuration.java b/magik-lint/src/main/java/nl/ramsolutions/sw/magik/lint/Configuration.java
index e14f080b..d7244990 100644
--- a/magik-lint/src/main/java/nl/ramsolutions/sw/magik/lint/Configuration.java
+++ b/magik-lint/src/main/java/nl/ramsolutions/sw/magik/lint/Configuration.java
@@ -36,8 +36,8 @@ public Configuration() {
}
/**
- * Constructor which reads properties from {{path}}.
- * @param path {{Path}} to read properties from.
+ * Constructor which reads properties from {@code path}.
+ * @param path {@link Path} to read properties from.
*/
public Configuration(final Path path) {
LOGGER.debug("Reading configuration from: {}", path.toAbsolutePath());
diff --git a/magik-lint/src/main/java/nl/ramsolutions/sw/magik/lint/LintInstructionsHandler.java b/magik-lint/src/main/java/nl/ramsolutions/sw/magik/lint/LintInstructionsHandler.java
deleted file mode 100644
index 9bea30b2..00000000
--- a/magik-lint/src/main/java/nl/ramsolutions/sw/magik/lint/LintInstructionsHandler.java
+++ /dev/null
@@ -1,179 +0,0 @@
-package nl.ramsolutions.sw.magik.lint;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import javax.annotation.CheckForNull;
-import nl.ramsolutions.sw.magik.MagikFile;
-import nl.ramsolutions.sw.magik.analysis.scope.GlobalScope;
-import nl.ramsolutions.sw.magik.analysis.scope.Scope;
-
-/**
- * Read mlint: a=b;c=d instructions at lines and scopes.
- */
-public class LintInstructionsHandler {
-
- private static final Pattern MLINT_PATTERN_EOL = Pattern.compile(".*# ?mlint: ?(.*)");
- private static final Pattern MLINT_PATTERN_SINGLE = Pattern.compile("^\\s*# ?mlint: ?(.*)");
-
- private final MagikFile magikFile;
- private final Map> scopeInstructions = new HashMap<>();
- private final Map> lineInstructions = new HashMap<>();
-
- /**
- * Constructor.
- * @param magikFile {{MagikVisitorContext}} to use.
- */
- public LintInstructionsHandler(final MagikFile magikFile) {
- this.magikFile = magikFile;
- this.parseInstructionsInScopes();
- this.parseInstructionsFromLines();
- }
-
- // #region: parsing
- /**
- * Extract instructions-string (at the end) from {{str}}.
- * @param str String to extract from.
- * @param single True if it should be the only thing in the string.
- * @return Unparsed instructions-string.
- */
- @CheckForNull
- private String extractInstructionsInStr(final String str, final boolean single) {
- final Pattern pattern = single
- ? MLINT_PATTERN_SINGLE
- : MLINT_PATTERN_EOL;
- final Matcher matcher = pattern.matcher(str);
- if (!matcher.find()) {
- return null;
- }
-
- return matcher.group(1);
- }
-
- /**
- * Parse all mlint instructions.
- * Mlint instructions are key=value pairs, each pair being ;-separated.
- * @param str String to parse.
- * @return Map with parsed instructions.
- */
- private Map parseMLintInstructions(final String str) {
- final Map instructions = new HashMap<>();
-
- Arrays.stream(str.split(";"))
- .map(String::trim)
- .forEach(item -> {
- final String[] parts = item.split("=");
- if (parts.length != 2) {
- return;
- }
-
- final String key = parts[0].trim();
- final String value = parts[1].trim();
- instructions.put(key, value);
- });
-
- return instructions;
- }
- // #endregion
-
- // #region: scopes
- /**
- * Get instructions in {{Scope}} and any ancestor {{Scope}}s.
- * @param line Line in file
- * @param column Column in file
- * @return Map with instructions for the Scope at line/column
- */
- public Map getInstructionsInScope(final int line, final int column) {
- final Map instructions = new HashMap<>();
- final GlobalScope globalScope = this.magikFile.getGlobalScope();
- if (globalScope == null) {
- return instructions;
- }
-
- // ensure we can find a Scope
- final Scope fromScope = globalScope.getScopeForLineColumn(line, column);
- if (fromScope == null) {
- return instructions;
- }
-
- // iterate over all (ancestor) scopes, see if the check is disabled in any scope
- final List scopes = fromScope.getSelfAndAncestorScopes();
- // Reverse such that if a narrower scope overrides a broader scope instruction.
- Collections.reverse(scopes);
- for (final Scope scope : scopes) {
- final Map scopeInstuctions = this.scopeInstructions.get(scope);
- if (scopeInstuctions != null) {
- instructions.putAll(scopeInstuctions);
- }
- }
-
- return instructions;
- }
-
- private void parseInstructionsInScopes() {
- final String[] lines = this.magikFile.getSourceLines();
- final GlobalScope globalScope = this.magikFile.getGlobalScope();
- for (int lineNo = 0; lineNo < lines.length; ++lineNo) {
- final String line = lines[lineNo];
- final String str = this.extractInstructionsInStr(line, true);
- if (str == null) {
- continue;
- }
-
- final Map instructions = this.parseMLintInstructions(str);
- final Scope scope = globalScope.getScopeForLineColumn(lineNo + 1, 0);
- if (scope == null) {
- continue;
- }
-
- final Map instructionsInScope = this.scopeInstructions.get(scope);
- if (instructionsInScope == null) {
- this.scopeInstructions.put(scope, instructions);
- } else {
- instructionsInScope.putAll(instructions);
- }
- }
- }
- // #endregion
-
- // #region: lines
- /**
- * Get instructions at (the end of) {{line}}.
- * @param line Line number to extract from.
- * @return Instructions at {{line}}.
- */
- public Map getInstructionsAtLine(int line) {
- final Map instructions = this.lineInstructions.get(line);
- if (instructions == null) {
- return new HashMap<>();
- }
- return instructions;
- }
-
- /**
- * Read all instructions from all lines.
- */
- private void parseInstructionsFromLines() {
- final String[] lines = this.magikFile.getSourceLines();
- if (lines == null) {
- return;
- }
-
- for (int lineNo = 0; lineNo < lines.length; ++lineNo) {
- final String line = lines[lineNo];
- final String str = this.extractInstructionsInStr(line, false);
- if (str == null) {
- continue;
- }
-
- final Map instructions = this.parseMLintInstructions(str);
- this.lineInstructions.put(lineNo + 1, instructions);
- }
- }
- // #endregion
-
-}
diff --git a/magik-lint/src/main/java/nl/ramsolutions/sw/magik/lint/MagikLint.java b/magik-lint/src/main/java/nl/ramsolutions/sw/magik/lint/MagikLint.java
index 9534e97b..46f353f5 100644
--- a/magik-lint/src/main/java/nl/ramsolutions/sw/magik/lint/MagikLint.java
+++ b/magik-lint/src/main/java/nl/ramsolutions/sw/magik/lint/MagikLint.java
@@ -12,7 +12,9 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Collections;
import java.util.Comparator;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -22,11 +24,14 @@
import nl.ramsolutions.sw.FileCharsetDeterminer;
import nl.ramsolutions.sw.magik.MagikFile;
import nl.ramsolutions.sw.magik.analysis.Location;
+import nl.ramsolutions.sw.magik.analysis.scope.GlobalScope;
+import nl.ramsolutions.sw.magik.analysis.scope.Scope;
import nl.ramsolutions.sw.magik.checks.CheckList;
import nl.ramsolutions.sw.magik.checks.MagikCheck;
import nl.ramsolutions.sw.magik.checks.MagikCheckHolder;
import nl.ramsolutions.sw.magik.checks.MagikIssue;
import nl.ramsolutions.sw.magik.lint.output.Reporter;
+import nl.ramsolutions.sw.magik.parser.CommentInstructionReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.check.Rule;
@@ -39,6 +44,11 @@ public class MagikLint {
private static final Logger LOGGER = LoggerFactory.getLogger(MagikLint.class);
+ private static final CommentInstructionReader.InstructionType MLINT_INSTRUCTION =
+ CommentInstructionReader.InstructionType.createInstructionType("mlint");
+ private static final CommentInstructionReader.InstructionType MLINT_SCOPE_INSTRUCTION =
+ CommentInstructionReader.InstructionType.createScopeInstructionType("mlint");
+
private final Configuration config;
private final Reporter reporter;
@@ -128,21 +138,18 @@ void showChecks(final Writer writer) throws ReflectiveOperationException, IOExce
* @return true if issue is disabled at line.
*/
private boolean isMagikIssueDisabled(
- final MagikIssue magikIssue, final LintInstructionsHandler instructionsHandler) {
+ final MagikFile magikFile,
+ final MagikIssue magikIssue,
+ final CommentInstructionReader instructionReader) {
final MagikCheckHolder holder = magikIssue.check().getHolder();
- if (holder == null) {
- throw new IllegalStateException();
- }
+ Objects.requireNonNull(holder);
final Integer line = magikIssue.startLine();
- final Integer column = magikIssue.startColumn();
- if (line == null || column == null) {
- return false;
- }
final String checkKey = holder.getCheckKeyKebabCase();
- final Map scopeInstructions = instructionsHandler.getInstructionsInScope(line, column);
- final Map lineInstructions = instructionsHandler.getInstructionsAtLine(line);
+ final Map scopeInstructions =
+ MagikLint.getScopeInstructions(magikFile, instructionReader, line);
+ final Map lineInstructions = MagikLint.getLineInstructions(instructionReader, line);
final String[] scopeDisableds = scopeInstructions.getOrDefault("disable", "").split(",");
final String[] lineDisableds = lineInstructions.getOrDefault("disable", "").split(",");
return List.of(scopeDisableds).contains(checkKey)
@@ -150,15 +157,16 @@ private boolean isMagikIssueDisabled(
}
/**
- * Run {{MagikCheckHolder}}s on {{MagikFile}}.
+ * Run {@link MagikCheckHolder}s on {@link MagikFile}.
* @param magikFile File to run on.
- * @param holders {{MagikCheckHolder}}s to run.
- * @return List of {{MagikIssue}}s for the given file.
+ * @param holders {@link MagikCheckHolder}s to run.
+ * @return List of {@link MagikIssue}s for the given file.
*/
private List runChecksOnFile(final MagikFile magikFile, final Iterable holders) {
LOGGER.trace("Thread: {}, checking file: {}", Thread.currentThread().getName(), magikFile);
- final LintInstructionsHandler instructionsHandler = new LintInstructionsHandler(magikFile);
+ final CommentInstructionReader instructionReader = new CommentInstructionReader(
+ magikFile, Set.of(MLINT_INSTRUCTION, MLINT_SCOPE_INSTRUCTION));
final List magikIssues = new ArrayList<>();
// run checks on files
@@ -169,7 +177,7 @@ private List runChecksOnFile(final MagikFile magikFile, final Iterab
try {
final List issues = this.runCheckOnFile(magikFile, holder).stream()
- .filter(magikIssue -> !this.isMagikIssueDisabled(magikIssue, instructionsHandler))
+ .filter(magikIssue -> !this.isMagikIssueDisabled(magikFile, magikIssue, instructionReader))
.collect(Collectors.toList());
magikIssues.addAll(issues);
} catch (ReflectiveOperationException exception) {
@@ -181,7 +189,7 @@ private List runChecksOnFile(final MagikFile magikFile, final Iterab
}
/**
- * Run the linter on {{paths}}.
+ * Run the linter on {@code paths}.
*
* @throws IOException -
* @throws ReflectiveOperationException -
@@ -239,7 +247,7 @@ public void run(final Collection paths) throws IOException, ReflectiveOper
}
/**
- * Run the linter on {{magikFile}}.
+ * Run the linter on {@code magikFile}.
* @param magikFile File to run on.
* @throws ReflectiveOperationException -
*/
@@ -256,7 +264,7 @@ public void run(final MagikFile magikFile) throws ReflectiveOperationException {
* Get all checks, enabled in the given configuration.
*
* @param config Configuration to use
- * @return Collection of {{MagikCheckHolder}}s.
+ * @return Collection of {@link MagikCheckHolder}s.
*/
@SuppressWarnings("unchecked")
public static List getAllChecks(final Configuration config) {
@@ -311,4 +319,61 @@ public static List getAllChecks(final Configuration config) {
return holders;
}
+ /**
+ * Get scope instructions at line.
+ * @param magikFile Magik file.
+ * @param instructionReader Instruction reader to use.
+ * @param line Line of scope.
+ * @return Instructions in scope and ancestor scopes.
+ */
+ public static Map getScopeInstructions(
+ final MagikFile magikFile,
+ final CommentInstructionReader instructionReader,
+ final int line) {
+ final Map instructions = new HashMap<>();
+ final GlobalScope globalScope = magikFile.getGlobalScope();
+ if (globalScope == null) {
+ return instructions;
+ }
+
+ // Ensure we can find a Scope.
+ final String[] sourceLines = magikFile.getSourceLines();
+ final int column = sourceLines[line - 1].length() - 1;
+ final Scope fromScope = globalScope.getScopeForLineColumn(line, column);
+ if (fromScope == null) {
+ return instructions;
+ }
+
+ // Iterate over all ancestor scopes, see if the check is disabled in any scope.
+ final List scopes = fromScope.getSelfAndAncestorScopes();
+ // Reverse such that if a narrower scope overrides a broader scope instruction.
+ Collections.reverse(scopes);
+ for (final Scope scope : scopes) {
+ final Set scopeInstructions =
+ instructionReader.getScopeInstructions(scope, MLINT_SCOPE_INSTRUCTION);
+ final Map parsedScopeInstructions = scopeInstructions.stream()
+ .map(str -> CommentInstructionReader.parseInstructions(str))
+ .reduce(
+ instructions,
+ (acc, elem) -> {
+ acc.putAll(elem);
+ return acc;
+ });
+ instructions.putAll(parsedScopeInstructions);
+ }
+
+ return instructions;
+ }
+
+ private static Map getLineInstructions(
+ final CommentInstructionReader instructionReader,
+ final int line) {
+ final String str = instructionReader.getInstructionsAtLine(line, MLINT_INSTRUCTION);
+ if (str == null) {
+ return Collections.emptyMap();
+ }
+
+ return CommentInstructionReader.parseInstructions(str);
+ }
+
}
diff --git a/magik-lint/src/main/java/nl/ramsolutions/sw/magik/lint/Main.java b/magik-lint/src/main/java/nl/ramsolutions/sw/magik/lint/Main.java
index f79398d5..cdfa670c 100644
--- a/magik-lint/src/main/java/nl/ramsolutions/sw/magik/lint/Main.java
+++ b/magik-lint/src/main/java/nl/ramsolutions/sw/magik/lint/Main.java
@@ -29,59 +29,59 @@
public final class Main {
private static final Options OPTIONS;
- private static final String OPTION_MSG_TEMPLATE = "msg-template";
- private static final String OPTION_RCFILE = "rcfile";
- private static final String OPTION_SHOW_CHECKS = "show-checks";
- private static final String OPTION_COLUMN_OFFSET = "column-offset";
- private static final String OPTION_MAX_INFRACTIONS = "max-infractions";
- private static final String OPTION_UNTABIFY = "untabify";
- private static final String OPTION_DEBUG = "debug";
- private static final String OPTION_HELP = "help";
+ private static final Option OPTION_MSG_TEMPLATE = Option.builder()
+ .longOpt("msg-template")
+ .desc("Output pattern")
+ .hasArg()
+ .type(PatternOptionBuilder.STRING_VALUE)
+ .build();
+ private static final Option OPTION_RCFILE = Option.builder()
+ .longOpt("rcfile")
+ .desc("Configuration file")
+ .hasArg()
+ .type(PatternOptionBuilder.FILE_VALUE)
+ .build();
+ private static final Option OPTION_SHOW_CHECKS = Option.builder()
+ .longOpt("show-checks")
+ .desc("Show checks and quit")
+ .build();
+ private static final Option OPTION_COLUMN_OFFSET = Option.builder()
+ .longOpt("column-offset")
+ .desc("Set column offset, positive or negative")
+ .hasArg()
+ .type(PatternOptionBuilder.NUMBER_VALUE)
+ .build();
+ private static final Option OPTION_MAX_INFRACTIONS = Option.builder()
+ .longOpt("max-infractions")
+ .desc("Set max number of reporter infractions")
+ .hasArg()
+ .type(PatternOptionBuilder.NUMBER_VALUE)
+ .build();
+ private static final Option OPTION_UNTABIFY = Option.builder()
+ .longOpt("untabify")
+ .desc("Expand tabs to N spaces")
+ .hasArg()
+ .type(PatternOptionBuilder.NUMBER_VALUE)
+ .build();
+ private static final Option OPTION_DEBUG = Option.builder()
+ .longOpt("debug")
+ .desc("Enable showing of debug information")
+ .build();
+ private static final Option OPTION_HELP = Option.builder()
+ .longOpt("help")
+ .desc("Show this help")
+ .build();
static {
OPTIONS = new Options();
- OPTIONS.addOption(Option.builder()
- .longOpt(OPTION_HELP)
- .desc("Show this help")
- .build());
- OPTIONS.addOption(Option.builder()
- .longOpt(OPTION_MSG_TEMPLATE)
- .desc("Output pattern")
- .hasArg()
- .type(PatternOptionBuilder.STRING_VALUE)
- .build());
- OPTIONS.addOption(Option.builder()
- .longOpt(OPTION_RCFILE)
- .desc("Configuration file")
- .hasArg()
- .type(PatternOptionBuilder.FILE_VALUE)
- .build());
- OPTIONS.addOption(Option.builder()
- .longOpt(OPTION_SHOW_CHECKS)
- .desc("Show checks and quit")
- .build());
- OPTIONS.addOption(Option.builder()
- .longOpt(OPTION_UNTABIFY)
- .desc("Expand tabs to N spaces")
- .hasArg()
- .type(PatternOptionBuilder.NUMBER_VALUE)
- .build());
- OPTIONS.addOption(Option.builder()
- .longOpt(OPTION_COLUMN_OFFSET)
- .desc("Set column offset, positive or negative")
- .hasArg()
- .type(PatternOptionBuilder.NUMBER_VALUE)
- .build());
- OPTIONS.addOption(Option.builder()
- .longOpt(OPTION_MAX_INFRACTIONS)
- .desc("Set max number of reporter infractions")
- .hasArg()
- .type(PatternOptionBuilder.NUMBER_VALUE)
- .build());
- OPTIONS.addOption(Option.builder()
- .longOpt(OPTION_DEBUG)
- .desc("Enable showing of debug information")
- .build());
+ OPTIONS.addOption(OPTION_HELP);
+ OPTIONS.addOption(OPTION_MSG_TEMPLATE);
+ OPTIONS.addOption(OPTION_RCFILE);
+ OPTIONS.addOption(OPTION_SHOW_CHECKS);
+ OPTIONS.addOption(OPTION_UNTABIFY);
+ OPTIONS.addOption(OPTION_COLUMN_OFFSET);
+ OPTIONS.addOption(OPTION_MAX_INFRACTIONS);
+ OPTIONS.addOption(OPTION_DEBUG);
}
private static final Map SEVERITY_EXIT_CODE_MAPPING = Map.of(
@@ -124,12 +124,14 @@ private static void initDebugLogger() {
* @return Reporter.
*/
private static Reporter createReporter(final Configuration configuration) {
- final String template = configuration.hasProperty(OPTION_MSG_TEMPLATE)
- ? configuration.getPropertyString(OPTION_MSG_TEMPLATE)
+ final String msgTemplateOptName = OPTION_MSG_TEMPLATE.getLongOpt();
+ final String template = configuration.hasProperty(msgTemplateOptName)
+ ? configuration.getPropertyString(msgTemplateOptName)
: MessageFormatReporter.DEFAULT_FORMAT;
- final String columnOffsetStr = configuration.getPropertyString(OPTION_COLUMN_OFFSET);
- final Long columnOffset = configuration.hasProperty(OPTION_COLUMN_OFFSET)
+ final String columnOffsetOptName = OPTION_COLUMN_OFFSET.getLongOpt();
+ final String columnOffsetStr = configuration.getPropertyString(columnOffsetOptName);
+ final Long columnOffset = configuration.hasProperty(columnOffsetOptName)
? Long.parseLong(columnOffsetStr)
: null;
@@ -149,7 +151,7 @@ public static void main(final String[] args) throws ParseException, IOException,
try {
commandLine = Main.parseCommandline(args);
} catch (UnrecognizedOptionException exception) {
- System.out.println("Unrecognized option: " + exception.getMessage());
+ System.err.println("Unrecognized option: " + exception.getMessage());
System.exit(1);
return; // Keep inferer happy.
@@ -165,7 +167,7 @@ public static void main(final String[] args) throws ParseException, IOException,
final File rcfile = (File) commandLine.getParsedOptionValue(OPTION_RCFILE);
final Path path = rcfile.toPath();
if (!Files.exists(path)) {
- System.out.println("RC File does not exist: " + path);
+ System.err.println("RC File does not exist: " + path);
System.exit(1);
}
@@ -218,7 +220,8 @@ public static void main(final String[] args) throws ParseException, IOException,
private static void copyOptionToConfig(
final CommandLine commandLine,
final Configuration config,
- final String key) {
+ final Option option) {
+ final String key = option.getLongOpt();
if (commandLine.hasOption(key)) {
final String value = commandLine.getOptionValue(key);
config.setProperty(key, value);
diff --git a/magik-lint/src/test/java/nl/ramsolutions/sw/magik/lint/LintInstructionsHandlerTest.java b/magik-lint/src/test/java/nl/ramsolutions/sw/magik/lint/LintInstructionsHandlerTest.java
deleted file mode 100644
index a20e1cde..00000000
--- a/magik-lint/src/test/java/nl/ramsolutions/sw/magik/lint/LintInstructionsHandlerTest.java
+++ /dev/null
@@ -1,91 +0,0 @@
-package nl.ramsolutions.sw.magik.lint;
-
-import java.net.URI;
-import java.util.Map;
-import nl.ramsolutions.sw.magik.MagikFile;
-import org.junit.jupiter.api.Test;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-/**
- * Test LintInstructionsHandler.
- */
-@SuppressWarnings("checkstyle:MagicNumber")
- class LintInstructionsHandlerTest {
-
- private LintInstructionsHandler getInstructions(String code) {
- final URI uri = URI.create("tests://unittest");
- final MagikFile magikFile = new MagikFile(uri, code);
- return new LintInstructionsHandler(magikFile);
- }
-
- @Test
- void testReadGlobalScopeInstruction() {
- final String code = ""
- + "# mlint: a=test1\n"
- + "_method a.b\n"
- + " write(1)\n"
- + "_endmethod";
- final LintInstructionsHandler instructionsHandler = this.getInstructions(code);
-
- final Map instructionsGlobal = instructionsHandler.getInstructionsInScope(1, 0);
- assertThat(instructionsGlobal).containsExactly(Map.entry("a", "test1"));
-
- final Map instructionsMethod = instructionsHandler.getInstructionsInScope(3, 0);
- assertThat(instructionsMethod).containsExactly(Map.entry("a", "test1"));
- }
-
- @Test
- void testReadMethodScopeInstruction() {
- final String code = ""
- + "_method a.b\n"
- + " # mlint: b=test2\n"
- + " write(1)\n"
- + "_endmethod";
- final LintInstructionsHandler instructionsHandler = this.getInstructions(code);
-
- final Map instructionsGlobal = instructionsHandler.getInstructionsInScope(1, 0);
- assertThat(instructionsGlobal).isEmpty();
-
- final Map instructionsMethod = instructionsHandler.getInstructionsInScope(3, 0);
- assertThat(instructionsMethod).containsExactly(Map.entry("b", "test2"));
- }
-
- @Test
- void testReadCombinedScopeInstruction() {
- final String code = ""
- + "# mlint: a=test1\n"
- + "_method a.b\n"
- + " # mlint: b=test2\n"
- + " write(1)\n"
- + "_endmethod";
- final LintInstructionsHandler instructionsHandler = this.getInstructions(code);
-
- final Map instructionsGlobal = instructionsHandler.getInstructionsInScope(1, 0);
- assertThat(instructionsGlobal).containsExactly(Map.entry("a", "test1"));
-
- final Map instructionsMethod = instructionsHandler.getInstructionsInScope(4, 0);
- assertThat(instructionsMethod).containsExactly(
- Map.entry("a", "test1"),
- Map.entry("b", "test2"));
- }
-
- @Test
- void testReadLineInstruction() {
- final String code = ""
- + "_method a.b\n"
- + " write(1) # mlint: c=test3\n"
- + "_endmethod";
- final LintInstructionsHandler instructionsHandler = this.getInstructions(code);
-
- final Map instructionsGlobal = instructionsHandler.getInstructionsInScope(1, 0);
- assertThat(instructionsGlobal).isEmpty();
-
- final Map instructionsMethod = instructionsHandler.getInstructionsInScope(2, 0);
- assertThat(instructionsMethod).isEmpty();
-
- final Map instructions = instructionsHandler.getInstructionsAtLine(2);
- assertThat(instructions).containsExactly(Map.entry("c", "test3"));
- }
-
-}
diff --git a/magik-lint/src/test/java/nl/ramsolutions/sw/magik/lint/MagikFileScannerTest.java b/magik-lint/src/test/java/nl/ramsolutions/sw/magik/lint/MagikFileScannerTest.java
index ce9bf3dd..f34f1032 100644
--- a/magik-lint/src/test/java/nl/ramsolutions/sw/magik/lint/MagikFileScannerTest.java
+++ b/magik-lint/src/test/java/nl/ramsolutions/sw/magik/lint/MagikFileScannerTest.java
@@ -15,7 +15,7 @@ class MagikFileScannerTest {
/**
* VSCode runs from module directory, mvn runs from project directory.
*
- * @return Proper {{Path}} to use.
+ * @return Proper {@link Path} to use.
*/
private Path getPath(String relativePath) {
final Path path = Path.of(".").toAbsolutePath().getParent();
diff --git a/magik-squid/pom.xml b/magik-squid/pom.xml
index de6edf4e..3cfecc8b 100644
--- a/magik-squid/pom.xml
+++ b/magik-squid/pom.xml
@@ -5,7 +5,7 @@
nl.ramsolutions
magik-tools
- 0.5.5-SNAPSHOT
+ 0.6.0
magik-squid
diff --git a/magik-squid/src/main/java/com/sonar/sslr/api/GenericTokenType.java b/magik-squid/src/main/java/com/sonar/sslr/api/GenericTokenType.java
new file mode 100644
index 00000000..ea8d39cc
--- /dev/null
+++ b/magik-squid/src/main/java/com/sonar/sslr/api/GenericTokenType.java
@@ -0,0 +1,43 @@
+/*
+ * SonarSource Language Recognizer
+ * Copyright (C) 2010-2020 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package com.sonar.sslr.api;
+
+public enum GenericTokenType implements TokenType {
+ COMMENT, IDENTIFIER, LITERAL, CONSTANT, EOF, EOL, UNKNOWN_CHAR,
+
+ // Added:
+ WHITESPACE,
+ STATEMENT_SEPARATOR;
+
+ @Override
+ public String getName() {
+ return name();
+ }
+
+ @Override
+ public String getValue() {
+ return name();
+ }
+
+ @Override
+ public boolean hasToBeSkippedFromAst(AstNode node) {
+ return false;
+ }
+}
diff --git a/magik-language-server/src/main/java/nl/ramsolutions/sw/magik/languageserver/IgnoreHandler.java b/magik-squid/src/main/java/nl/ramsolutions/sw/IgnoreHandler.java
similarity index 97%
rename from magik-language-server/src/main/java/nl/ramsolutions/sw/magik/languageserver/IgnoreHandler.java
rename to magik-squid/src/main/java/nl/ramsolutions/sw/IgnoreHandler.java
index cd44a171..6b93cff9 100644
--- a/magik-language-server/src/main/java/nl/ramsolutions/sw/magik/languageserver/IgnoreHandler.java
+++ b/magik-squid/src/main/java/nl/ramsolutions/sw/IgnoreHandler.java
@@ -1,4 +1,4 @@
-package nl.ramsolutions.sw.magik.languageserver;
+package nl.ramsolutions.sw;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
@@ -78,7 +78,7 @@ public void removeIgnoreFile(final Path path) {
}
/**
- * Test if {{path}} is ignored.
+ * Test if {@code path} is ignored.
*
*
* A file is either ignored when:
diff --git a/magik-squid/src/main/java/nl/ramsolutions/sw/definitions/SwModule.java b/magik-squid/src/main/java/nl/ramsolutions/sw/definitions/SwModule.java
index f3355832..0a0125e8 100644
--- a/magik-squid/src/main/java/nl/ramsolutions/sw/definitions/SwModule.java
+++ b/magik-squid/src/main/java/nl/ramsolutions/sw/definitions/SwModule.java
@@ -21,7 +21,7 @@ public class SwModule {
/**
* Constructor.
* @param name Name of module.
- * @param path Path to {{@code module.def}} file.
+ * @param path Path to {@code module.def} file.
*/
public SwModule(final String name, final @Nullable Path path) {
this.name = name;
diff --git a/magik-squid/src/main/java/nl/ramsolutions/sw/definitions/SwModuleScanner.java b/magik-squid/src/main/java/nl/ramsolutions/sw/definitions/SwModuleScanner.java
index 4f563993..2a9f648b 100644
--- a/magik-squid/src/main/java/nl/ramsolutions/sw/definitions/SwModuleScanner.java
+++ b/magik-squid/src/main/java/nl/ramsolutions/sw/definitions/SwModuleScanner.java
@@ -110,7 +110,7 @@ public static SwModule moduleAtPath(final Path startPath) throws IOException {
/**
* Read module.def file.
- * @param path Path to {{@code module.def}} file.
+ * @param path Path to {@code module.def} file.
* @return Parsed module definition.
* @throws IOException -
*/
diff --git a/magik-squid/src/main/java/nl/ramsolutions/sw/definitions/SwProduct.java b/magik-squid/src/main/java/nl/ramsolutions/sw/definitions/SwProduct.java
index 9fa5ab54..ff6578dc 100644
--- a/magik-squid/src/main/java/nl/ramsolutions/sw/definitions/SwProduct.java
+++ b/magik-squid/src/main/java/nl/ramsolutions/sw/definitions/SwProduct.java
@@ -37,8 +37,8 @@ public String getName() {
}
/**
- * Get path to {{@code product.def}} file.
- * @return Path to {{@code product.def}} file.
+ * Get path to {@code product.def} file.
+ * @return Path to {@code product.def} file.
*/
@CheckForNull
public Path getPath() {
@@ -46,32 +46,32 @@ public Path getPath() {
}
/**
- * Get child {{@code SwProduct}}s of this product.
- * @return Child {{@code SwProduct}}s of this product.
+ * Get child {@link SwProduct}s of this product.
+ * @return Child {@link SwProduct}s of this product.
*/
public Set getChildren() {
return Collections.unmodifiableSet(this.children);
}
/**
- * Add a {{@code SwProduct}} to this product.
- * @param swProduct {{@code SwProduct}} to add.
+ * Add a {@link SwProduct} to this product.
+ * @param swProduct {@link SwProduct} to add.
*/
public void addChild(final SwProduct swProduct) {
this.children.add(swProduct);
}
/**
- * Get {{@code SwModule}}s in this product.
- * @return Collection of {{@code SwModule}}s in this product.
+ * Get {@link SwModule}s in this product.
+ * @return Collection of {@link SwModule}s in this product.
*/
public Set getModules() {
return Collections.unmodifiableSet(modules);
}
/**
- * Add a {{@code SwModule}} to this product.
- * @param swModule {{@code SwModule}} to add.
+ * Add a {@link SwModule} to this product.
+ * @param swModule {@link SwModule} to add.
*/
public void addModule(final SwModule swModule) {
this.modules.add(swModule);
diff --git a/magik-squid/src/main/java/nl/ramsolutions/sw/definitions/SwProductScanner.java b/magik-squid/src/main/java/nl/ramsolutions/sw/definitions/SwProductScanner.java
index a5c02a49..ee0b3a29 100644
--- a/magik-squid/src/main/java/nl/ramsolutions/sw/definitions/SwProductScanner.java
+++ b/magik-squid/src/main/java/nl/ramsolutions/sw/definitions/SwProductScanner.java
@@ -125,7 +125,7 @@ public static SwProduct productForPath(final Path startPath) throws IOException
/**
* Read product.def file.
- * @param path Path to {{@code product.def}} file.
+ * @param path Path to {@code product.def} file.
* @return Parsed product definition.
* @throws IOException -
*/
diff --git a/magik-squid/src/main/java/nl/ramsolutions/sw/magik/MagikFile.java b/magik-squid/src/main/java/nl/ramsolutions/sw/magik/MagikFile.java
index 79d5b73f..adaf9254 100644
--- a/magik-squid/src/main/java/nl/ramsolutions/sw/magik/MagikFile.java
+++ b/magik-squid/src/main/java/nl/ramsolutions/sw/magik/MagikFile.java
@@ -81,8 +81,8 @@ public String[] getSourceLines() {
}
/**
- * Parse the text for this file and return the top level {{AstNode}}.
- * @return Top level {{AstNode}}.
+ * Parse the text for this file and return the top level {@link AstNode}.
+ * @return Top level {@link AstNode}.
*/
public synchronized AstNode getTopNode() {
if (this.astNode == null) {
@@ -98,8 +98,8 @@ public synchronized AstNode getTopNode() {
}
/**
- * Get the {{GlobalScope}} for this file.
- * @return {{GlobalScope}} for this file.
+ * Get the {@link GlobalScope} for this file.
+ * @return {@link GlobalScope} for this file.
*/
public synchronized GlobalScope getGlobalScope() {
if (this.globalScope == null) {
@@ -113,8 +113,8 @@ public synchronized GlobalScope getGlobalScope() {
}
/**
- * Get {{Definition}}s in this file.
- * @return {{Definition}}s in this file.
+ * Get {@link Definition}s in this file.
+ * @return {@link Definition}s in this file.
*/
public synchronized List getDefinitions() {
if (this.definitions == null) {
diff --git a/magik-squid/src/main/java/nl/ramsolutions/sw/magik/MagikTypedFile.java b/magik-squid/src/main/java/nl/ramsolutions/sw/magik/MagikTypedFile.java
index 065c3fce..0aca72e4 100644
--- a/magik-squid/src/main/java/nl/ramsolutions/sw/magik/MagikTypedFile.java
+++ b/magik-squid/src/main/java/nl/ramsolutions/sw/magik/MagikTypedFile.java
@@ -1,6 +1,5 @@
package nl.ramsolutions.sw.magik;
-import com.sonar.sslr.api.AstNode;
import java.io.IOException;
import java.net.URI;
import java.nio.file.Path;
@@ -48,22 +47,20 @@ public MagikTypedFile(final Path path, final ITypeKeeper typeKeeper) throws IOEx
}
/**
- * Get the {{ITypeKeeper}} used for the {{LocalTypeReasoner}}.
+ * Get the {@link ITypeKeeper} used for the {@link LocalTypeReasoner}.
*/
public ITypeKeeper getTypeKeeper() {
return this.typeKeeper;
}
/**
- * Run the (cached) {{LocalTypeReasoner}} and return it.
- * @return The used LocalTypeReasoner.
+ * Run the (cached) {@link LocalTypeReasoner} and return it.
+ * @return The used {@link LocalTypeReasoner}.
*/
public synchronized LocalTypeReasoner getTypeReasoner() {
if (this.typeReasoner == null) {
- final AstNode node = this.getTopNode();
-
this.typeReasoner = new LocalTypeReasoner(this);
- this.typeReasoner.run(node);
+ this.typeReasoner.run();
}
return this.typeReasoner;
diff --git a/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/AstCompare.java b/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/AstCompare.java
index d989509e..3a0453aa 100644
--- a/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/AstCompare.java
+++ b/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/AstCompare.java
@@ -14,8 +14,7 @@ public final class AstCompare {
/**
* Flags to influence the compare functionality.
* Values:
- * {{IDENTIFIER_IGNORE_NAME}}: Ignore the name of the IDENTIFIER node.
- * {{ONLY_AST}}: Only compare AST, ignoring tokens such as '(' and ')'.
+ * {@code IDENTIFIER_IGNORE_NAME}: Ignore the name of the IDENTIFIER node.
*/
enum Flags {
IGNORE_IDENTIFIER_NAME,
diff --git a/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/AstQuery.java b/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/AstQuery.java
index a87bc14d..2cb2d2c7 100644
--- a/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/AstQuery.java
+++ b/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/AstQuery.java
@@ -20,10 +20,10 @@ private AstQuery() {
}
/**
- * Get the AstNodes which match a chain of {{AstNodeType}}s.
- * @param node {{AstNode}} to query
- * @param nodeTypes Chain of {{AstNodeType}}s
- * @return {{AstNode}}s which match query
+ * Get the AstNodes which match a chain of {@link AstNodeType}s.
+ * @param node {@link AstNode} to query
+ * @param nodeTypes Chain of {@link AstNodeType}s
+ * @return {@link AstNode}s which match query
*/
public static List getChildrenFromChain(final AstNode node, final AstNodeType... nodeTypes) {
List nodes = List.of(node);
@@ -43,11 +43,11 @@ public static List getChildrenFromChain(final AstNode node, final AstNo
}
/**
- * Get the first AstNode which matches a chain of {{AstNodeTypes}}s.
- * Tries to get {{getChildNode()}} on each node, for each {{nodeTypes}}.
- * @param node {{AstNode}} to query
- * @param nodeTypes Chain of {{AstNodeType}}s
- * @return {{AstNode}} which matches query, {{null}} if none is found.
+ * Get the first AstNode which matches a chain of {@link AstNodeTypes}s.
+ * Tries to get {@code getChildNode()} on each node, for each {@code nodeTypes}.
+ * @param node {@link AstNode} to query
+ * @param nodeTypes Chain of {@link AstNodeType}s
+ * @return {@link AstNode} which matches query, {@code null} if none is found.
*/
@CheckForNull
public static AstNode getFirstChildFromChain(final AstNode node, final AstNodeType... nodeTypes) {
@@ -62,10 +62,10 @@ public static AstNode getFirstChildFromChain(final AstNode node, final AstNodeTy
}
/**
- * Get a child {{AstNode}} from chain, but only if each node has one child and the type.
- * @param node {{AstNode}} to query
- * @param nodeTypes Chain of {{AstNodeType}}s
- * @return {{AstNode}} which matches query, {{null}} if not is found.
+ * Get a child {@link AstNode} from chain, but only if each node has one child and the type.
+ * @param node {@link AstNode} to query
+ * @param nodeTypes Chain of {@link AstNodeType}s
+ * @return {@link AstNode} which matches query, {@code null} if not is found.
*/
@CheckForNull
public static AstNode getOnlyFromChain(final AstNode node, final AstNodeType... nodeTypes) {
@@ -84,10 +84,10 @@ public static AstNode getOnlyFromChain(final AstNode node, final AstNodeType...
}
/**
- * Get a parent {{AstNode}} from chain, but only if each node matches the type.
- * @param node {{AstNode}} to query.
- * @param nodeTypes Chain of {{AstNodeType}}s.
- * @return {{AstNode}} which matches query, {{null}} if not found.
+ * Get a parent {@link AstNode} from chain, but only if each node matches the type.
+ * @param node {@link AstNode} to query.
+ * @param nodeTypes Chain of {@link AstNodeType}s.
+ * @return {@link AstNode} which matches query, {@code null} if not found.
*/
@CheckForNull
public static AstNode getParentFromChain(final AstNode node, final AstNodeType... nodeTypes) {
@@ -103,7 +103,7 @@ public static AstNode getParentFromChain(final AstNode node, final AstNodeType..
}
/**
- * Get the node in {{topNode}} before {{position}}.
+ * Get the node in {@code topNode} before {@code position}.
* @param topNode Top node.
* @param position Position for node.
* @return Token-Node before position.
@@ -125,7 +125,7 @@ public static AstNode nodeBefore(final AstNode topNode, final Position position)
}
/**
- * Get the node in {{topNode}} at {{position}}.
+ * Get the node in {@code topNode} at {@code position}.
* @param topNode Top node.
* @param position Position for node.
* @return Token-Node at position.
@@ -148,7 +148,7 @@ public static AstNode nodeAt(final AstNode topNode, final Position position) {
}
/**
- * Get the (token) node in {{node}} at {{position}} of a specific type.
+ * Get the (token) node in {@code node} at {@code position} of a specific type.
* @param topNode Top node.
* @param position Position for node.
* @param nodeTypes Node type to look for.
@@ -168,7 +168,7 @@ public static AstNode nodeAt(final AstNode topNode, final Position position, fin
}
/**
- * Get the node in {{topNode}} after {{position}}.
+ * Get the node in {@code topNode} after {@code position}.
* @param topNode Top node.
* @param position Position for node.
* @return Node after position.
@@ -190,7 +190,7 @@ public static AstNode nodeAfter(final AstNode topNode, final Position position)
}
/**
- * Get the {{AstNode}} surrounding {{position}}.
+ * Get the {@link AstNode} surrounding {@code position}.
* @param topNode Top node.
* @param position Position to look at.
* @return 'Finest' node containing position.
@@ -225,7 +225,7 @@ public static AstNode nodeSurrounding(final AstNode topNode, final Position posi
}
/**
- * Get the {{AstNode}} surrounding {{position}} of a specific type.
+ * Get the {@link AstNode} surrounding {@code position} of a specific type.
* @param topNode Top node.
* @param position Position to look at.
* @param nodeTypes Wanted node types.
diff --git a/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/AstWalker.java b/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/AstWalker.java
index 5054ddc6..ac08c016 100644
--- a/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/AstWalker.java
+++ b/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/AstWalker.java
@@ -7,8 +7,8 @@
import nl.ramsolutions.sw.magik.api.MagikGrammar;
/**
- * A {{AstNode}} tree walker with pre- and post-methods to iterate a parse tree.
- * Note that this is generated by the {{generate_ast_walker.py}} script,
+ * A {@link AstNode} tree walker with pre- and post-methods to iterate a parse tree.
+ * Note that this is generated by the {@code generate_ast_walker.py} script,
* do not edit this file manually!
*/
public abstract class AstWalker {
@@ -26,10 +26,10 @@ protected void walkChildren(final AstNode node) {
* Walk trivia and tokens of node.
*/
protected void walkTokens(final AstNode tokenNode) {
- tokenNode.getTokens().forEach(token -> {
- token.getTrivia().forEach(this::walkTrivia);
- this.walkToken(token);
- });
+ // Assume there can be only one token.
+ final Token token = tokenNode.getToken();
+ token.getTrivia().forEach(this::walkTrivia);
+ this.walkToken(token);
}
/**
diff --git a/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/KeyValueAtLineInstructionExtractor.java b/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/KeyValueAtLineInstructionExtractor.java
index 39d54464..4c539c8e 100644
--- a/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/KeyValueAtLineInstructionExtractor.java
+++ b/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/KeyValueAtLineInstructionExtractor.java
@@ -60,8 +60,8 @@ private Map> extractInstructions() {
}
/**
- * Get the instructions at line of {{AstNode}}.
- * @param searchNode Line of {{AstNode}} to get instructions from.
+ * Get the instructions at line of {@link AstNode}.
+ * @param searchNode Line of {@link AstNode} to get instructions from.
* @return Map with key/value instructions at node.
*/
public Map getInstructions(final AstNode searchNode) {
diff --git a/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/Location.java b/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/Location.java
index f79f8f87..ade0b9cd 100644
--- a/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/Location.java
+++ b/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/Location.java
@@ -50,9 +50,9 @@ public Location(final URI uri, final Range range) {
}
/**
- * Constructor from {{AstNode}}.
+ * Constructor from {@link AstNode}.
* @param uri Path to file.
- * @param node {{AstNode}} to create {{Location}} from.
+ * @param node {@link AstNode} to create {@link Location} from.
*/
public Location(final URI uri, final AstNode node) {
this.uri = uri;
@@ -60,9 +60,9 @@ public Location(final URI uri, final AstNode node) {
}
/**
- * Constructor from {{AstNode}}.
+ * Constructor from {@link AstNode}.
* @param uri Path to file.
- * @param token {{Token}} to create {{Location}} from.
+ * @param token {@link Token} to create {@link Location} from.
*/
public Location(final URI uri, final Token token) {
this.uri = uri;
diff --git a/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/Position.java b/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/Position.java
index fcfc3cea..b5e88fd2 100644
--- a/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/Position.java
+++ b/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/Position.java
@@ -12,7 +12,7 @@ public class Position implements Comparable {
private static final String NEWLINE_REGEXP = "(?:\\n|\\r\\n|\\r)";
/**
- * Comparator for {{Position}}s.
+ * Comparator for {@link Position}s.
*/
public class PositionComparator implements Comparator {
diff --git a/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/definitions/BinaryOperatorDefinition.java b/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/definitions/BinaryOperatorDefinition.java
index a89cd788..abca506a 100644
--- a/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/definitions/BinaryOperatorDefinition.java
+++ b/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/definitions/BinaryOperatorDefinition.java
@@ -1,6 +1,7 @@
package nl.ramsolutions.sw.magik.analysis.definitions;
import com.sonar.sslr.api.AstNode;
+import nl.ramsolutions.sw.magik.analysis.typing.types.TypeString;
/**
* Binary operator definition.
@@ -8,8 +9,8 @@
public class BinaryOperatorDefinition extends Definition {
private final String operator;
- private final String lhs;
- private final String rhs;
+ private final TypeString lhs;
+ private final TypeString rhs;
/**
* Constructor.
@@ -23,9 +24,18 @@ public BinaryOperatorDefinition(
final AstNode node,
final String pakkage,
final String operator,
- final String lhs,
- final String rhs) {
- super(node, pakkage, lhs + " " + operator + " " + rhs);
+ final TypeString lhs,
+ final TypeString rhs) {
+ super(node, TypeString.UNDEFINED);
+
+ if (!lhs.isSingle()) {
+ throw new IllegalStateException();
+ }
+
+ if (!rhs.isSingle()) {
+ throw new IllegalStateException();
+ }
+
this.operator = operator;
this.lhs = lhs;
this.rhs = rhs;
@@ -35,12 +45,17 @@ public String getOperator() {
return this.operator;
}
- public String getLhs() {
+ public TypeString getLhs() {
return this.lhs;
}
- public String getRhs() {
+ public TypeString getRhs() {
return this.rhs;
}
+ @Override
+ public String getName() {
+ return this.lhs.getFullString() + " " + this.operator + " " + this.rhs.getFullString();
+ }
+
}
diff --git a/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/definitions/ConditionDefinition.java b/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/definitions/ConditionDefinition.java
new file mode 100644
index 00000000..b8f83abd
--- /dev/null
+++ b/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/definitions/ConditionDefinition.java
@@ -0,0 +1,48 @@
+package nl.ramsolutions.sw.magik.analysis.definitions;
+
+import com.sonar.sslr.api.AstNode;
+import java.util.Collections;
+import java.util.List;
+import nl.ramsolutions.sw.magik.analysis.typing.types.TypeString;
+
+/**
+ * Condition definition.
+ */
+public class ConditionDefinition extends Definition {
+
+ private final String name;
+ private final String parent;
+ private final List dataNames;
+
+ /**
+ * Constructor.
+ * @param node Node.
+ * @param name Name.
+ * @param parent Parent.
+ * @param dataNames Data name list.
+ */
+ protected ConditionDefinition(
+ final AstNode node,
+ final String name,
+ final String parent,
+ final List dataNames) {
+ super(node, TypeString.UNDEFINED);
+ this.name = name;
+ this.parent = parent;
+ this.dataNames = dataNames;
+ }
+
+ @Override
+ public String getName() {
+ return this.name;
+ }
+
+ public String getParent() {
+ return this.parent;
+ }
+
+ public List getDataNames() {
+ return Collections.unmodifiableList(this.dataNames);
+ }
+
+}
diff --git a/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/definitions/DefConditionParser.java b/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/definitions/DefConditionParser.java
new file mode 100644
index 00000000..86d5dc10
--- /dev/null
+++ b/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/definitions/DefConditionParser.java
@@ -0,0 +1,122 @@
+package nl.ramsolutions.sw.magik.analysis.definitions;
+
+import com.sonar.sslr.api.AstNode;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+import nl.ramsolutions.sw.magik.analysis.helpers.ArgumentsNodeHelper;
+import nl.ramsolutions.sw.magik.analysis.helpers.ExpressionNodeHelper;
+import nl.ramsolutions.sw.magik.analysis.helpers.MethodInvocationNodeHelper;
+import nl.ramsolutions.sw.magik.api.MagikGrammar;
+
+/**
+ * Condition definition parser.
+ */
+public class DefConditionParser {
+
+ private static final String DEFINE_CONDITION = "define_condition()";
+ private static final String DEFINE_TOP_CONDITION = "define_top_condition()";
+ private static final String CONDITION = "condition";
+ private static final String SW_CONDITION = "sw:condition";
+
+ private final AstNode node;
+
+ /**
+ * Constructor.
+ * @param node Condition definition node.
+ */
+ public DefConditionParser(final AstNode node) {
+ if (node.isNot(MagikGrammar.METHOD_INVOCATION)) {
+ throw new IllegalArgumentException();
+ }
+
+ this.node = node;
+ }
+
+ /**
+ * Test if node is condition definition.
+ * @param node Node.
+ * @return True if is condition definition, false otherwise.
+ */
+ public static boolean isDefineCondition(final AstNode node) {
+ final MethodInvocationNodeHelper helper = new MethodInvocationNodeHelper(node);
+ if (!helper.isMethodInvocationOf(DEFINE_CONDITION)
+ && !helper.isMethodInvocationOf(DEFINE_TOP_CONDITION)) {
+ return false;
+ }
+
+ // Some sanity.
+ final AstNode parentNode = node.getParent();
+ final AstNode atomNode = parentNode.getFirstChild();
+ if (atomNode.isNot(MagikGrammar.ATOM)) {
+ return false;
+ }
+ final String exemplarName = atomNode.getTokenValue(); // Assume this is an exemplar.
+ if (!exemplarName.equalsIgnoreCase(CONDITION)
+ && !exemplarName.equalsIgnoreCase(SW_CONDITION)) {
+ return false;
+ }
+
+ final AstNode argumentsNode = node.getFirstChild(MagikGrammar.ARGUMENTS);
+ final ArgumentsNodeHelper argumentsHelper = new ArgumentsNodeHelper(argumentsNode);
+ final AstNode argument0Node = argumentsHelper.getArgument(0, MagikGrammar.SYMBOL);
+ final AstNode argument1Node = argumentsHelper.getArgument(1, MagikGrammar.SYMBOL);
+ final AstNode argument2Node = argumentsHelper.getArgument(2, MagikGrammar.SIMPLE_VECTOR);
+ return argument0Node != null
+ && argument1Node != null
+ && argument2Node != null;
+ }
+
+ /**
+ * Parse definition.
+ * @return List of {@link ConditionDefinition}s.
+ */
+ public List parseDefinitions() {
+ // Some sanity.
+ final AstNode parentNode = this.node.getParent();
+ final AstNode atomNode = parentNode.getFirstChild();
+ if (atomNode.isNot(MagikGrammar.ATOM)) {
+ throw new IllegalStateException();
+ }
+ final String exemplarName = atomNode.getTokenValue();
+ if (!exemplarName.equalsIgnoreCase(CONDITION)
+ && !exemplarName.equalsIgnoreCase(SW_CONDITION)) {
+ throw new IllegalStateException();
+ }
+
+ final AstNode argumentsNode = node.getFirstChild(MagikGrammar.ARGUMENTS);
+ final ArgumentsNodeHelper argumentsHelper = new ArgumentsNodeHelper(argumentsNode);
+ final AstNode argument0Node = argumentsHelper.getArgument(0, MagikGrammar.SYMBOL);
+ final AstNode argument1Node = argumentsHelper.getArgument(1, MagikGrammar.SYMBOL);
+ final AstNode argument2Node = argumentsHelper.getArgument(2, MagikGrammar.SIMPLE_VECTOR);
+ if (argument0Node == null
+ || argument1Node == null
+ || argument2Node == null) {
+ throw new IllegalStateException();
+ }
+
+ // Figure statement node.
+ final AstNode statementNode = node.getFirstAncestor(MagikGrammar.STATEMENT);
+
+ // Figure definition.
+ final String nameSymbol = argument0Node.getTokenValue();
+ final String name = nameSymbol.substring(1);
+ final String parentSymbol = argument1Node.getTokenValue();
+ final String parent = parentSymbol.substring(1);
+ final List