aboutsummaryrefslogtreecommitdiffstats
path: root/integration
diff options
context:
space:
mode:
authorJon Bratseth <bratseth@gmail.com>2022-02-12 18:56:04 +0100
committerJon Bratseth <bratseth@gmail.com>2022-02-12 18:56:04 +0100
commitd4bf9c688f38b3bbb4a6aa881f54dba96a12071a (patch)
tree499b193e224a19eb67bc8f23526523ea0e4acd00 /integration
parentc4f7ab5658060eb9700276c646c237d433a17708 (diff)
Resolve inherited profiles
Diffstat (limited to 'integration')
-rw-r--r--integration/intellij/src/main/java/ai/vespa/intellij/schema/SdUtil.java79
-rw-r--r--integration/intellij/src/main/java/ai/vespa/intellij/schema/model/RankProfile.java34
-rw-r--r--integration/intellij/src/main/java/ai/vespa/intellij/schema/model/Schema.java24
-rw-r--r--integration/intellij/src/main/java/ai/vespa/intellij/schema/utils/AST.java38
-rw-r--r--integration/intellij/src/main/java/ai/vespa/intellij/schema/utils/Tokens.java112
-rw-r--r--integration/intellij/src/test/java/ai/vespa/intellij/model/SchemaTest.java2
6 files changed, 177 insertions, 112 deletions
diff --git a/integration/intellij/src/main/java/ai/vespa/intellij/schema/SdUtil.java b/integration/intellij/src/main/java/ai/vespa/intellij/schema/SdUtil.java
index cc214f7320c..546f2030002 100644
--- a/integration/intellij/src/main/java/ai/vespa/intellij/schema/SdUtil.java
+++ b/integration/intellij/src/main/java/ai/vespa/intellij/schema/SdUtil.java
@@ -225,83 +225,4 @@ public class SdUtil {
return new ArrayList<>(PsiTreeUtil.collectElementsOfType(element, SdSummaryDefinition.class));
}
- /** A collection of tokens with a current index. */
- public static class Tokens {
-
- private final ASTNode[] nodes;
- private int i = 0;
-
- private Tokens(PsiElement element) {
- nodes = element.getNode().getChildren(null);
- }
-
- /**
- * Advances to the next token, if it is of the given type.
- *
- * @return true if the current token was of the given type and we advanced it, false
- * if it was not and nothing was changed
- */
- public boolean skip(IElementType ... tokenTypes) {
- boolean is = is(tokenTypes);
- if (is)
- i++;
- return is;
- }
-
- /**
- * Advances beyond the next token, if it is whitespace.
- *
- * @return true if the current token was of the given type and we advanced it, false
- * if it was not and nothing was changed
- */
- public boolean skipWhitespace() {
- boolean is = isWhitespace();
- if (is)
- i++;
- return is;
- }
-
- /** Returns whether the current token is of the given type */
- public boolean is(IElementType ... tokenTypes) {
- for (IElementType type : tokenTypes)
- if (current().getElementType() == type) return true;
- return false;
- }
-
- /** Returns whether the current token is whitespace */
- public boolean isWhitespace() {
- return current().getPsi() instanceof PsiWhiteSpace;
- }
-
- /** Returns the current token if it is of the required type and throws otherwise. */
- public ASTNode require(IElementType ... tokenTypes) {
- if ( ! is(tokenTypes) )
- throw new IllegalArgumentException("Expected " + toString(tokenTypes) + " but got " + current());
- ASTNode current = current();
- i++;
- return current;
- }
-
- public void requireWhitespace() {
- if ( ! isWhitespace() )
- throw new IllegalArgumentException("Expected whitespace, but got " + current());
- i++;
- }
-
- /** Returns the current token (AST node), or null if we have reached the end. */
- public ASTNode current() {
- if (i >= nodes.length) return null;
- return nodes[i];
- }
-
- private String toString(IElementType[] tokens) {
- return Arrays.stream(tokens).map(token -> token.getDebugName()).collect(Collectors.joining(", "));
- }
-
- public static Tokens of(PsiElement element) {
- return new Tokens(element);
- }
-
- }
-
}
diff --git a/integration/intellij/src/main/java/ai/vespa/intellij/schema/model/RankProfile.java b/integration/intellij/src/main/java/ai/vespa/intellij/schema/model/RankProfile.java
index 3b559cecd27..c66c9d4ca99 100644
--- a/integration/intellij/src/main/java/ai/vespa/intellij/schema/model/RankProfile.java
+++ b/integration/intellij/src/main/java/ai/vespa/intellij/schema/model/RankProfile.java
@@ -4,6 +4,7 @@ package ai.vespa.intellij.schema.model;
import ai.vespa.intellij.schema.SdUtil;
import ai.vespa.intellij.schema.psi.SdRankProfileDefinition;
import ai.vespa.intellij.schema.psi.SdTypes;
+import ai.vespa.intellij.schema.utils.AST;
import com.intellij.lang.ASTNode;
import java.util.ArrayList;
@@ -37,33 +38,12 @@ public class RankProfile {
* @return the profiles this inherits from, empty if none
*/
public List<RankProfile> findInherited() {
- ASTNode inheritsNode = definition.getNode().findChildByType(SdTypes.INHERITS);
- if (inheritsNode == null) return List.of();
- return inherits().stream()
- .map(parentIdentifierAST -> parentIdentifierAST.getPsi().getReference())
- .filter(reference -> reference != null)
- .map(reference -> owner.rankProfile(reference.getCanonicalText()))
- .flatMap(r -> r.stream())
- .collect(Collectors.toList());
- }
-
- /** Returns the nodes following "inherits" in the definition of this */
- private List<ASTNode> inherits() {
- SdUtil.Tokens tokens = SdUtil.Tokens.of(definition);
- tokens.require(SdTypes.RANK_PROFILE);
- tokens.requireWhitespace();
- tokens.require(SdTypes.IDENTIFIER_VAL, SdTypes.IDENTIFIER_WITH_DASH_VAL);
- if ( ! tokens.skipWhitespace()) return List.of();
- if ( ! tokens.skip(SdTypes.INHERITS)) return List.of();
- tokens.requireWhitespace();
- List<ASTNode> inherited = new ArrayList<>();
- do {
- inherited.add(tokens.require(SdTypes.IDENTIFIER_VAL, SdTypes.IDENTIFIER_WITH_DASH_VAL));
- tokens.skipWhitespace();
- if ( ! tokens.skip(SdTypes.COMMA)) break;
- tokens.skipWhitespace();
- } while (true);
- return inherited;
+ return AST.inherits(definition).stream()
+ .map(parentIdentifierAST -> parentIdentifierAST.getPsi().getReference())
+ .filter(reference -> reference != null)
+ .map(reference -> owner.rankProfile(reference.getCanonicalText()))
+ .flatMap(r -> r.stream())
+ .collect(Collectors.toList());
}
}
diff --git a/integration/intellij/src/main/java/ai/vespa/intellij/schema/model/Schema.java b/integration/intellij/src/main/java/ai/vespa/intellij/schema/model/Schema.java
index 3670d095be2..7ad91907b22 100644
--- a/integration/intellij/src/main/java/ai/vespa/intellij/schema/model/Schema.java
+++ b/integration/intellij/src/main/java/ai/vespa/intellij/schema/model/Schema.java
@@ -3,13 +3,12 @@ package ai.vespa.intellij.schema.model;
import ai.vespa.intellij.schema.psi.SdFile;
import ai.vespa.intellij.schema.psi.SdRankProfileDefinition;
+import ai.vespa.intellij.schema.psi.SdSchemaDefinition;
+import ai.vespa.intellij.schema.utils.AST;
import ai.vespa.intellij.schema.utils.Files;
import ai.vespa.intellij.schema.utils.Path;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiFile;
-import com.intellij.psi.PsiFileSystemItem;
-import com.intellij.psi.search.FilenameIndex;
-import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.PsiTreeUtil;
import java.util.Optional;
@@ -29,6 +28,9 @@ public class Schema {
/** The project this is part of */
private final Project project;
+ /** The schema this inherits, or empty if none. Resolved lazily. */
+ private Optional<Schema> inherited = null;
+
public Schema(SdFile definition, Path path, Project project) {
this.definition = definition;
this.path = path;
@@ -39,14 +41,26 @@ public class Schema {
public SdFile definition() { return definition; }
+ public Optional<Schema> inherited() {
+ if (inherited != null) return inherited;
+ System.out.println("----------- starting schema inherits");
+ return inherited = AST.inherits(PsiTreeUtil.collectElementsOfType(definition, SdSchemaDefinition.class).stream().findFirst().get())
+ .stream()
+ .findFirst() // Only one schema can be inherited; ignore any following
+ .map(inheritedNode -> fromProjectFile(project, path.getParentPath().append(inheritedNode.getText() + ".sd")));
+ }
+
/** Returns a rank profile belonging to this, defined either inside it or in a separate .profile file */
public Optional<RankProfile> rankProfile(String name) {
- var definition = findProfileElement(name, this.definition);
- if (definition.isEmpty()) { // Defined in a separate file schema-name/profile-name.profile
+ var definition = findProfileElement(name, this.definition); // Look up in this
+ if (definition.isEmpty()) { // Look up in a separate file schema-name/profile-name.profile
Optional<PsiFile> file = Files.open(path.getParentPath().append(name()).append(name + ".profile"), project);
if (file.isPresent())
definition = findProfileElement(name, file.get());
}
+ if (definition.isEmpty() && inherited().isPresent()) { // Look up in parent
+ return inherited().get().rankProfile(name);
+ }
return definition.map(d -> new RankProfile(d, this));
}
diff --git a/integration/intellij/src/main/java/ai/vespa/intellij/schema/utils/AST.java b/integration/intellij/src/main/java/ai/vespa/intellij/schema/utils/AST.java
new file mode 100644
index 00000000000..0edf7d1f93c
--- /dev/null
+++ b/integration/intellij/src/main/java/ai/vespa/intellij/schema/utils/AST.java
@@ -0,0 +1,38 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.intellij.schema.utils;
+
+import ai.vespa.intellij.schema.psi.SdTypes;
+import com.intellij.lang.ASTNode;
+import com.intellij.psi.PsiElement;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * AST navigation tools.
+ *
+ * @author bratseth
+ */
+public class AST {
+
+ /** Returns the nodes following "inherits" in the given element. */
+ public static List<ASTNode> inherits(PsiElement element) {
+ Tokens tokens = Tokens.of(element);
+ tokens.dump();
+ tokens.requireElement();
+ tokens.requireWhitespace();
+ tokens.require(SdTypes.IDENTIFIER_VAL, SdTypes.IDENTIFIER_WITH_DASH_VAL);
+ if ( ! tokens.skipWhitespace()) return List.of();
+ if ( ! tokens.skip(SdTypes.INHERITS)) return List.of();
+ tokens.requireWhitespace();
+ List<ASTNode> inherited = new ArrayList<>();
+ do {
+ inherited.add(tokens.require(SdTypes.IDENTIFIER_VAL, SdTypes.IDENTIFIER_WITH_DASH_VAL));
+ tokens.skipWhitespace();
+ if ( ! tokens.skip(SdTypes.COMMA)) break;
+ tokens.skipWhitespace();
+ } while (true);
+ return inherited;
+ }
+
+}
diff --git a/integration/intellij/src/main/java/ai/vespa/intellij/schema/utils/Tokens.java b/integration/intellij/src/main/java/ai/vespa/intellij/schema/utils/Tokens.java
new file mode 100644
index 00000000000..f5f80baf854
--- /dev/null
+++ b/integration/intellij/src/main/java/ai/vespa/intellij/schema/utils/Tokens.java
@@ -0,0 +1,112 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.intellij.schema.utils;
+
+import com.intellij.lang.ASTNode;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiWhiteSpace;
+import com.intellij.psi.impl.source.tree.CompositeElement;
+import com.intellij.psi.impl.source.tree.LeafPsiElement;
+import com.intellij.psi.impl.source.tree.TreeElement;
+import com.intellij.psi.tree.IElementType;
+
+import java.util.Arrays;
+import java.util.stream.Collectors;
+
+/** A collection of tokens with a current index. */
+public class Tokens {
+
+ private final ASTNode[] nodes;
+ private int i = 0;
+
+ private Tokens(PsiElement element) {
+ nodes = element.getNode().getChildren(null);
+ }
+
+ /**
+ * Advances to the next token, if it is of the given type.
+ *
+ * @return true if the current token was of the given type and we advanced it, false
+ * if it was not and nothing was changed
+ */
+ public boolean skip(IElementType... tokenTypes) {
+ boolean is = is(tokenTypes);
+ if (is)
+ i++;
+ return is;
+ }
+
+ /**
+ * Advances beyond the next token, if it is whitespace.
+ *
+ * @return true if the current token was of the given type and we advanced it, false
+ * if it was not and nothing was changed
+ */
+ public boolean skipWhitespace() {
+ boolean is = isWhitespace();
+ if (is)
+ i++;
+ return is;
+ }
+
+ /** Returns whether the current token is of the given type */
+ public boolean is(IElementType... tokenTypes) {
+ for (IElementType type : tokenTypes)
+ if (current().getElementType() == type) return true;
+ return false;
+ }
+
+ /** Returns whether the current token is whitespace */
+ public boolean isWhitespace() {
+ return current().getPsi() instanceof PsiWhiteSpace;
+ }
+
+ /** Returns whether the current token is an element */
+ public boolean isElement() {
+ return current() instanceof TreeElement;
+ }
+
+ /** Returns the current token if it is of the required type and throws otherwise. */
+ public ASTNode require(IElementType... tokenTypes) {
+ if (!is(tokenTypes))
+ throw new IllegalArgumentException("Expected " + toString(tokenTypes) + " but got " + current());
+ ASTNode current = current();
+ i++;
+ return current;
+ }
+
+ /** Returns the current token if it is any element and throws otherwise. */
+ public ASTNode requireElement() {
+ if (!isElement())
+ throw new IllegalArgumentException("Expected an element but got " + current().getClass());
+ ASTNode current = current();
+ i++;
+ return current;
+ }
+
+ public void requireWhitespace() {
+ if (!isWhitespace())
+ throw new IllegalArgumentException("Expected whitespace, but got " + current());
+ i++;
+ }
+
+ /** Returns the current token (AST node), or null if we have reached the end. */
+ public ASTNode current() {
+ if (i >= nodes.length) return null;
+ return nodes[i];
+ }
+
+ private String toString(IElementType[] tokens) {
+ return Arrays.stream(tokens).map(token -> token.getDebugName()).collect(Collectors.joining(", "));
+ }
+
+ public static Tokens of(PsiElement element) {
+ return new Tokens(element);
+ }
+
+ /** For debugging: Prints the remaining to standard out. */
+ public void dump() {
+ for (int j = i; j < nodes.length; j++)
+ System.out.println(nodes[j]);
+ }
+
+}
diff --git a/integration/intellij/src/test/java/ai/vespa/intellij/model/SchemaTest.java b/integration/intellij/src/test/java/ai/vespa/intellij/model/SchemaTest.java
index 1a33b17d8d3..6ce93d3fe28 100644
--- a/integration/intellij/src/test/java/ai/vespa/intellij/model/SchemaTest.java
+++ b/integration/intellij/src/test/java/ai/vespa/intellij/model/SchemaTest.java
@@ -52,7 +52,7 @@ public class SchemaTest extends LightJavaCodeInsightFixtureTestCase {
List<RankProfile> parents = profile.findInherited();
assertEquals(2, parents.size());
assertEquals("other_child_profile", parents.get(0).name());
- assertEquals("parent-profile", parents.get(1).name());
+ assertEquals("parent_profile", parents.get(1).name());
}
@Test