aboutsummaryrefslogtreecommitdiffstats
path: root/integration
diff options
context:
space:
mode:
authorJon Bratseth <bratseth@gmail.com>2021-11-18 23:04:30 +0100
committerJon Bratseth <bratseth@gmail.com>2021-11-18 23:04:30 +0100
commitc978c9e29652b24b7f31ed545c1c0e48a17464ec (patch)
treec347e96d74bcb9d180346d90385a2dceb6fbcad5 /integration
parent28b80bf7669ff14f1af913ef7bcee8659ac555a2 (diff)
Move and rename
Diffstat (limited to 'integration')
-rw-r--r--integration/intellij/.gitignore15
-rw-r--r--integration/intellij/BACKLOG.md34
-rw-r--r--integration/intellij/README.md34
-rw-r--r--integration/intellij/build.gradle72
-rw-r--r--integration/intellij/settings.gradle5
-rw-r--r--integration/intellij/src/main/bnf/ai/vespa/intellij/schema/parser/sd.bnf356
-rw-r--r--integration/intellij/src/main/java/ai/vespa/intellij/schema/SdChooseByNameContributor.java58
-rw-r--r--integration/intellij/src/main/java/ai/vespa/intellij/schema/SdCodeStyleSettings.java18
-rw-r--r--integration/intellij/src/main/java/ai/vespa/intellij/schema/SdCodeStyleSettingsProvider.java50
-rw-r--r--integration/intellij/src/main/java/ai/vespa/intellij/schema/SdCommenter.java45
-rw-r--r--integration/intellij/src/main/java/ai/vespa/intellij/schema/SdCompletionContributor.java36
-rw-r--r--integration/intellij/src/main/java/ai/vespa/intellij/schema/SdFileType.java47
-rw-r--r--integration/intellij/src/main/java/ai/vespa/intellij/schema/SdIcons.java27
-rw-r--r--integration/intellij/src/main/java/ai/vespa/intellij/schema/SdLanguage.java24
-rw-r--r--integration/intellij/src/main/java/ai/vespa/intellij/schema/SdLanguageCodeStyleSettingsProvider.java39
-rw-r--r--integration/intellij/src/main/java/ai/vespa/intellij/schema/SdRefactoringSupportProvider.java22
-rw-r--r--integration/intellij/src/main/java/ai/vespa/intellij/schema/SdReference.java78
-rw-r--r--integration/intellij/src/main/java/ai/vespa/intellij/schema/SdSyntaxHighlighter.java212
-rw-r--r--integration/intellij/src/main/java/ai/vespa/intellij/schema/SdSyntaxHighlighterFactory.java23
-rw-r--r--integration/intellij/src/main/java/ai/vespa/intellij/schema/SdUtil.java221
-rw-r--r--integration/intellij/src/main/java/ai/vespa/intellij/schema/findUsages/SdDocumentSummaryGroupingRule.java38
-rw-r--r--integration/intellij/src/main/java/ai/vespa/intellij/schema/findUsages/SdDocumentSummaryGroupingRuleProvider.java21
-rw-r--r--integration/intellij/src/main/java/ai/vespa/intellij/schema/findUsages/SdFindUsagesHandler.java81
-rw-r--r--integration/intellij/src/main/java/ai/vespa/intellij/schema/findUsages/SdFindUsagesHandlerFactory.java28
-rw-r--r--integration/intellij/src/main/java/ai/vespa/intellij/schema/findUsages/SdFindUsagesProvider.java77
-rw-r--r--integration/intellij/src/main/java/ai/vespa/intellij/schema/findUsages/SdRankProfileGroupingRule.java39
-rw-r--r--integration/intellij/src/main/java/ai/vespa/intellij/schema/findUsages/SdRankProfileGroupingRuleProvider.java22
-rw-r--r--integration/intellij/src/main/java/ai/vespa/intellij/schema/findUsages/SdUsageGroup.java113
-rw-r--r--integration/intellij/src/main/java/ai/vespa/intellij/schema/hierarchy/SdCallHierarchyBrowser.java78
-rw-r--r--integration/intellij/src/main/java/ai/vespa/intellij/schema/hierarchy/SdCallHierarchyNodeDescriptor.java75
-rw-r--r--integration/intellij/src/main/java/ai/vespa/intellij/schema/hierarchy/SdCallHierarchyProvider.java65
-rw-r--r--integration/intellij/src/main/java/ai/vespa/intellij/schema/hierarchy/SdCallTreeStructure.java79
-rw-r--r--integration/intellij/src/main/java/ai/vespa/intellij/schema/hierarchy/SdCalleeTreeStructure.java73
-rw-r--r--integration/intellij/src/main/java/ai/vespa/intellij/schema/hierarchy/SdCallerTreeStructure.java69
-rw-r--r--integration/intellij/src/main/java/ai/vespa/intellij/schema/hierarchy/SdHierarchyUtil.java73
-rw-r--r--integration/intellij/src/main/java/ai/vespa/intellij/schema/lexer/SdLexerAdapter.java17
-rw-r--r--integration/intellij/src/main/java/ai/vespa/intellij/schema/parser/SdParserDefinition.java93
-rw-r--r--integration/intellij/src/main/java/ai/vespa/intellij/schema/psi/SdDeclaration.java12
-rw-r--r--integration/intellij/src/main/java/ai/vespa/intellij/schema/psi/SdDeclarationType.java37
-rw-r--r--integration/intellij/src/main/java/ai/vespa/intellij/schema/psi/SdElementDescriptionProvider.java35
-rw-r--r--integration/intellij/src/main/java/ai/vespa/intellij/schema/psi/SdElementFactory.java37
-rw-r--r--integration/intellij/src/main/java/ai/vespa/intellij/schema/psi/SdElementType.java20
-rw-r--r--integration/intellij/src/main/java/ai/vespa/intellij/schema/psi/SdFile.java33
-rw-r--r--integration/intellij/src/main/java/ai/vespa/intellij/schema/psi/SdFunctionDefinitionInterface.java31
-rw-r--r--integration/intellij/src/main/java/ai/vespa/intellij/schema/psi/SdIdentifier.java22
-rw-r--r--integration/intellij/src/main/java/ai/vespa/intellij/schema/psi/SdNamedElement.java14
-rw-r--r--integration/intellij/src/main/java/ai/vespa/intellij/schema/psi/SdTokenType.java25
-rw-r--r--integration/intellij/src/main/java/ai/vespa/intellij/schema/psi/impl/SdFirstPhaseDefinitionMixin.java61
-rw-r--r--integration/intellij/src/main/java/ai/vespa/intellij/schema/psi/impl/SdIdentifierMixin.java46
-rw-r--r--integration/intellij/src/main/java/ai/vespa/intellij/schema/psi/impl/SdIdentifierMixinImpl.java28
-rw-r--r--integration/intellij/src/main/java/ai/vespa/intellij/schema/psi/impl/SdNamedElementImpl.java204
-rw-r--r--integration/intellij/src/main/java/ai/vespa/intellij/schema/psi/impl/SdSummaryDefinitionMixin.java60
-rw-r--r--integration/intellij/src/main/java/ai/vespa/intellij/schema/structure/SdStructureViewElement.java96
-rw-r--r--integration/intellij/src/main/java/ai/vespa/intellij/schema/structure/SdStructureViewFactory.java33
-rw-r--r--integration/intellij/src/main/java/ai/vespa/intellij/schema/structure/SdStructureViewModel.java37
-rw-r--r--integration/intellij/src/main/jflex/ai/vespa/intellij/schema/lexer/sd.flex239
-rw-r--r--integration/intellij/src/main/resources/META-INF/plugin.xml41
-rw-r--r--integration/intellij/src/main/resources/META-INF/pluginIcon.pngbin0 -> 30317 bytes
-rw-r--r--integration/intellij/src/main/resources/META-INF/pluginIcon_dark.pngbin0 -> 30317 bytes
-rw-r--r--integration/intellij/src/main/resources/icons/document_icon.pngbin0 -> 436 bytes
-rw-r--r--integration/intellij/src/main/resources/icons/document_summary_icon.pngbin0 -> 426 bytes
-rw-r--r--integration/intellij/src/main/resources/icons/first_phase_icon.pngbin0 -> 424 bytes
-rw-r--r--integration/intellij/src/main/resources/icons/imported_field_icon.pngbin0 -> 538 bytes
-rw-r--r--integration/intellij/src/main/resources/icons/macro_icon.pngbin0 -> 579 bytes
-rw-r--r--integration/intellij/src/main/resources/icons/override_macro_icon.pngbin0 -> 703 bytes
-rw-r--r--integration/intellij/src/main/resources/icons/sd_icon.pngbin0 -> 682 bytes
-rw-r--r--integration/intellij/src/main/resources/icons/struct_field_icon.pngbin0 -> 627 bytes
-rw-r--r--integration/intellij/src/main/resources/icons/struct_icon.pngbin0 -> 279 bytes
-rw-r--r--integration/intellij/src/main/resources/icons/summary_def_icon.pngbin0 -> 686 bytes
69 files changed, 3568 insertions, 0 deletions
diff --git a/integration/intellij/.gitignore b/integration/intellij/.gitignore
new file mode 100644
index 00000000000..fa33885331d
--- /dev/null
+++ b/integration/intellij/.gitignore
@@ -0,0 +1,15 @@
+.gradle
+**/build/
+!src/**/build/
+
+# ignore generated files
+src/main/gen/
+
+# Ignore Gradle GUI config
+gradle-app.setting
+
+# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
+!gradle/wrapper/gradle-wrapper.jar
+
+# Cache of project
+.gradletasknamecache \ No newline at end of file
diff --git a/integration/intellij/BACKLOG.md b/integration/intellij/BACKLOG.md
new file mode 100644
index 00000000000..19e185dcc6b
--- /dev/null
+++ b/integration/intellij/BACKLOG.md
@@ -0,0 +1,34 @@
+### Open Issues
+
+1. In some cases, the grammar prefers not to enforce bad syntax, because if the parser encounters bad syntax it stops
+and can't build the PSI tree. That means none of the features will work for a file like that. For example, in cases
+where an element supposes to have zero-to-one occurrences, the grammar will treat it as zero-to-many.
+2. In order to enable the grammar recognize some keywords as identifiers (e.g. "filter" as a field's name), the
+identifier rule (named "IdentifierVal") wraps the regex (ID_REG) and the KeywordOrIdentifier rule (which contains all
+the keywords in the language).
+3. The implementation of the GoTo Declaration feature is not exactly the same as IntelliJ. In IntelliJ if a reference
+has several declarations, after clicking "Goto Declaration" there is a little window with all the declarations to choose
+from. It can be done by changing the method "multiResolve" in SdReference.java to return more than one declaration. The
+problem with that is that it causes the "Find Usages" feature to not work. For now, I decided to make the plugin
+"Goto Declaration" feature show only the most specific declaration by the right rank-profile scope.
+4. The "Find Usages" window can group usages only under rank-profiles and document-summaries. Other usages appear
+directly under the .sd file. In order to create another group type of usages' group, you'll need to create 2 classes:
+one for the extension "fileStructureGroupRuleProvider" (e.g. SdRankProfileGroupingRuleProvider.java), and one for the
+grouping rule itself (e.g. SdRankProfileGroupingRule.java).
+Another open problem is that the navigation isn't working in the current grouping rules. It means that when clicking on
+the group headline (e.g. some name of a rank-profile) the IDE doesn't "jump" to the matching declaration.
+5. Goto declaration doesn't work for document's inherits. e.g. if document A inherits from document B, B doesn't have a
+reference to its declaration.
+6. There aren't any tests for the plugin.
+
+### Some useful links:
+1. JetBrains official tutorials: https://plugins.jetbrains.com/docs/intellij/custom-language-support.html and
+https://plugins.jetbrains.com/docs/intellij/custom-language-support-tutorial.html
+2. Grammar-Kit HOWTO: Helps to understand the BNF syntax.
+ https://github.com/JetBrains/Grammar-Kit/blob/master/HOWTO.md
+3. How to deal with left-recursion in the grammar (in SD for example it happens in expressions). Last answer here:
+https://intellij-support.jetbrains.com/hc/en-us/community/posts/360001258300-What-s-the-alternative-to-left-recursion-in-GrammarKit-
+4. Great tutorial for a custom-language-plugin, but only for the basics (mainly the parser and lexer):
+ https://medium.com/@shan1024/custom-language-plugin-development-for-intellij-idea-part-01-d6a41ab96bc9
+5. Code of Dart (some custom language) plugin for IntelliJ:
+https://github.com/JetBrains/intellij-plugins/tree/0f07ca63355d5530b441ca566c98f17c560e77f8/Dart \ No newline at end of file
diff --git a/integration/intellij/README.md b/integration/intellij/README.md
new file mode 100644
index 00000000000..dbf31fccc6e
--- /dev/null
+++ b/integration/intellij/README.md
@@ -0,0 +1,34 @@
+<!-- Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -->
+
+# SD Reader
+
+IntelliJ plugin for working with Vespa application packages.
+
+## Using the plugin
+
+Download it from JetBrains Marketplace.
+
+## Using a local build
+
+Build (see below) and load it in IntelliJ by choosing
+Preferences -> Plugins -> Press the gear icon -> Install Plugin from Disk.
+
+## Building the plugin
+
+ ./gradlew
+
+This produces an installable plugin .zip in the directory build/distributions
+
+*Prerequisite*: gradle 7.
+
+Why gradle? Because it's what JetBrains supports for building plugins.
+However, gradle is configured with a maven directory layout.
+
+## Optional IntelliJ plugins for working with plugin development
+
+1. Plugin DevKit
+2. Grammar-Kit: For reading the .bnf file.
+3. PsiViewer: Helps testing the bnf grammar.
+
+With the first (?), you can run the gradle task "intellij/runIde" (or "./gradlew runIde" in the command line),
+open a project with some sd file and see how the plugin works on it.
diff --git a/integration/intellij/build.gradle b/integration/intellij/build.gradle
new file mode 100644
index 00000000000..5a4200d0062
--- /dev/null
+++ b/integration/intellij/build.gradle
@@ -0,0 +1,72 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+plugins {
+ id 'org.jetbrains.intellij' version '1.1.4'
+ id 'java'
+
+ id "org.jetbrains.grammarkit" version '2021.1.3'
+
+ id 'maven-publish' // to deploy the plugin into a Maven repo
+}
+
+defaultTasks 'buildPlugin'
+
+apply plugin: 'org.jetbrains.grammarkit'
+
+import org.jetbrains.grammarkit.tasks.GenerateLexer
+import org.jetbrains.grammarkit.tasks.GenerateParser
+
+task generateSdLexer(type: GenerateLexer) {
+ source 'src/main/jflex/ai/vespa/intellij/schema/lexer/sd.flex'
+ targetDir 'target/generated-sources/jflex/ai/vespa/intellij/schema/lexer/'
+ targetClass 'SdLexer'
+ purgeOldFiles true
+}
+
+task generateSdParser(type: GenerateParser) {
+ source 'src/main/bnf/ai/vespa/intellij/schema/parser/sd.bnf'
+ targetRoot 'target/generated-sources/bnf/'
+ pathToParser 'ai/vespa/intellij/schema/parser/SdParser.java'
+ pathToPsiRoot 'ai/vespa/intellij/schema/parser/psi/'
+ purgeOldFiles true
+}
+
+compileJava {
+ dependsOn generateSdLexer
+ dependsOn generateSdParser
+}
+
+group 'ai.vespa'
+version '1.0.3'
+
+sourceCompatibility = 11
+
+// This "noinspection" comment below is here to fix a warning
+// noinspection GroovyAssignabilityCheck
+repositories {
+ mavenCentral()
+}
+
+sourceSets.main.java.srcDirs = ['src/main/java', 'target/generated-sources/bnf', 'target/generated-sources/jflex']
+
+// See https://github.com/JetBrains/gradle-intellij-plugin/
+intellij {
+ version = '2021.2'
+ plugins = ['com.intellij.java']
+}
+
+buildSearchableOptions {
+ enabled = false
+}
+
+patchPluginXml {
+ version = project.version
+ sinceBuild = '203'
+ untilBuild = '212.*'
+ // in changeNotes you can add a description of the changes in this version (would appear in the plugin page in preferences\plugins)
+ changeNotes = """
+ <em></em>"""
+}
+
+test {
+ useJUnitPlatform()
+} \ No newline at end of file
diff --git a/integration/intellij/settings.gradle b/integration/intellij/settings.gradle
new file mode 100644
index 00000000000..02eff8ccdfa
--- /dev/null
+++ b/integration/intellij/settings.gradle
@@ -0,0 +1,5 @@
+// Copyright 2000-2020 JetBrains s.r.o. and other contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
+
+// This sets the name of the generated zip file uploadable to Intellij Marketplace
+// (But not the actual *name* of the plugin, which is the name in plugin.xml)
+rootProject.name = 'vespa'
diff --git a/integration/intellij/src/main/bnf/ai/vespa/intellij/schema/parser/sd.bnf b/integration/intellij/src/main/bnf/ai/vespa/intellij/schema/parser/sd.bnf
new file mode 100644
index 00000000000..f01ce41dd64
--- /dev/null
+++ b/integration/intellij/src/main/bnf/ai/vespa/intellij/schema/parser/sd.bnf
@@ -0,0 +1,356 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+/**
+ * Vespa schema file grammar.
+ * NOTE: This grammar does not enforce zero-or-one occurrences of elements (treats it like zero-to-many)
+ *
+ * @author: Shahar Ariel
+ */
+
+{
+ parserClass="ai.vespa.intellij.schema.parser.SdParser" // Name and the location of the parser which will be generated.
+
+ extends="com.intellij.extapi.psi.ASTWrapperPsiElement" // All nodes will extend this class. Wraps AST node to a PSI node.
+
+ // Prefix and suffix for all generated classes
+ psiClassPrefix="Sd"
+ psiImplClassSuffix="Impl"
+
+ psiPackage="ai.vespa.intellij.schema.psi" // Location to be used when generating PSI classes.
+ psiImplPackage="ai.vespa.intellij.schema.psi.impl" // Location to be used when generating PSI implementation classes.
+
+ elementTypeHolderClass="ai.vespa.intellij.schema.psi.SdTypes" // Element type holder class name.
+
+ elementTypeClass="ai.vespa.intellij.schema.psi.SdElementType" // Class which will be used to create internal nodes.
+ tokenTypeClass="ai.vespa.intellij.schema.psi.SdTokenType" // Class which will be used to create leaf nodes.
+
+ extends(".*Expr")=RankingExpression // Here to deal with left-recursion that happens in expressions
+
+ tokens = [
+ ID_REG = 'regexp:[a-zA-Z_][a-zA-Z0-9_]*'
+ ID_WITH_DASH_REG = 'regexp:[a-zA-Z_][a-zA-Z0-9_-]*'
+ WHITE_SPACE = 'regexp:\s+'
+ COMMENT = 'regexp:#.*'
+ SYMBOL = 'regexp:[!$|:{}(),.\[\]]'
+ COMPARISON_OPERATOR = 'regexp:[<>]|(==)|(<=)|(>=)|(~=)'
+ ARITHMETIC_OPERATOR = 'regexp:[\-+*/]'
+ INTEGER_REG = 'regexp:[0-9]+'
+ FLOAT_REG = 'regexp:[0-9]+[.][0-9]+[e]?'
+ STRING_REG = 'regexp:\"([^\"\\]*(\\.[^\"\\]*)*)\"'
+ WORD_REG = 'regexp:\w+'
+ ]
+}
+
+SdFile ::= SchemaDefinition | DocumentDefinition
+SchemaDefinition ::= (search | schema) IdentifierVal? (inherits IdentifierVal)? '{' SchemaBody '}'
+SchemaBody ::= SchemaBodyOptions* DocumentDefinition SchemaBodyOptions* // Does not support zero-or-one occurrences
+private SchemaBodyOptions ::= SchemaFieldDefinition | ImportFieldDefinition | DocumentSummaryDefinition |
+ RankProfileDefinition | IndexDefinition |
+ FieldSetDefinition | ConstantDefinition | OnnxModelDefinition | StemmingDefinition |
+ raw-as-base64-in-summary | SchemaAnnotationDefinition
+
+
+SchemaFieldDefinition ::= field IdentifierVal type FieldTypeName '{' SchemaFieldBody '}'
+ { mixin="ai.vespa.intellij.schema.psi.impl.SdNamedElementImpl"
+ implements=["ai.vespa.intellij.schema.psi.SdDeclaration" "ai.vespa.intellij.schema.psi.SdNamedElement"]
+ }
+
+FieldTypeName ::= ("array" '<' (FieldTypeName | IdentifierVal) '>') | ("weightedset" '<' SingleValueFieldTypeName '>') |
+ ("map" '<' (FieldTypeName | IdentifierVal) ',' (FieldTypeName | IdentifierVal) '>') | TensorType |
+ (SingleValueFieldTypeName '[' ']') | SingleValueFieldTypeName
+private SingleValueFieldTypeName ::= "string" | "int" | "long" | "bool" | "byte" | "float" | "double" | "position" | "predicate" | "raw" | "uri" |
+ "reference" '<' IdentifierVal '>' | "annotationreference" '<' IdentifierVal '>' | IdentifierVal
+private TensorType ::= "tensor" ('<' ("float" | "double" | "int8" | "bfloat16") '>')? '(' TensorDimension (',' TensorDimension)* ')'
+private TensorDimension ::= WordWrapper (('{' '}') | ('[' INTEGER_REG ']'))
+
+SchemaFieldBody ::= DocumentFieldBodyOptions* // Fields of schemas and documents defined the same way here
+
+DocumentSummaryDefinition ::= document-summary IdentifierWithDashVal (inherits IdentifierWithDashVal)? '{' DocumentSummaryBody '}'
+ { mixin="ai.vespa.intellij.schema.psi.impl.SdNamedElementImpl"
+ implements=["ai.vespa.intellij.schema.psi.SdDeclaration" "ai.vespa.intellij.schema.psi.SdNamedElement"]
+ }
+DocumentSummaryBody ::= DocumentSummaryBodyOptions* // Does not support zero-or-one occurrences
+private DocumentSummaryBodyOptions ::= SummaryDefinition | omit-summary-features | from-disk
+
+ImportFieldDefinition ::= import field IdentifierVal '.' IdentifierVal as IdentifierVal '{''}'
+ { mixin="ai.vespa.intellij.schema.psi.impl.SdNamedElementImpl"
+ implements=["ai.vespa.intellij.schema.psi.SdDeclaration" "ai.vespa.intellij.schema.psi.SdNamedElement"]
+ }
+
+FieldSetDefinition ::= fieldset IdentifierVal '{' FieldSetBody '}'
+FieldSetBody ::= FieldSetBodyOptions*
+private FieldSetBodyOptions ::= (fields ':' IdentifierVal (',' IdentifierVal)*) | QueryCommandDefinition | MatchDefinition
+
+ConstantDefinition ::= constant IdentifierVal '{' ConstantBody '}'
+ConstantBody ::= ConstantBodyOptions*
+private ConstantBodyOptions ::= (file ':' FilePath) | (uri ':' UriPath) | (type ':' TensorType)
+private FilePath ::= WordWrapper (('.' | '/') WordWrapper)+
+private UriPath ::= ('H'|'h') ('T'|'t') ('T'|'t') ('P'|'p') ('S'|'s')? ':' ('//')? (IdentifierWithDashVal | '.' | '/' | ':')+
+
+
+OnnxModelDefinition ::= onnx-model IdentifierVal '{' OnnxModelBody '}'
+OnnxModelBody ::= OnnxModelBodyOptions*
+private OnnxModelBodyOptions ::= (file ':' FilePath) | (uri ':' UriPath) |
+ ((input | output) (IdentifierVal | STRING_REG) ':' ('.' | '/' | '(' | ')' | IdentifierWithDashVal | WORD_REG))
+
+SchemaAnnotationDefinition ::= AnnotationDefinition
+ { mixin="ai.vespa.intellij.schema.psi.impl.SdNamedElementImpl"
+ implements=["ai.vespa.intellij.schema.psi.SdDeclaration" "ai.vespa.intellij.schema.psi.SdNamedElement"]
+ }
+
+private AnnotationDefinition ::= annotation IdentifierVal (inherits IdentifierVal)? '{' AnnotationFieldDefinition* '}'
+AnnotationFieldDefinition ::= field IdentifierVal type FieldTypeName '{' '}'
+ { mixin="ai.vespa.intellij.schema.psi.impl.SdNamedElementImpl"
+ implements=["ai.vespa.intellij.schema.psi.SdDeclaration" "ai.vespa.intellij.schema.psi.SdNamedElement"]
+ }
+
+//-------------------------
+//--- Expressions rules ---
+//-------------------------
+RankingExpression ::= FilePathExpr | ParenthesisedExpr | BooleanExpr | ArithmeticExpr | IfFunctionExpr |
+ QueryDefinitionExpr | FunctionCallExpr | InListRankingExpr | PrimitiveExpr
+
+FilePathExpr ::= file ':' (FilePath | WordWrapper)
+
+IfFunctionExpr ::= "if" '(' (InListRankingExpr | RankingExpression) ',' RankingExpression ',' RankingExpression ')'
+InListRankingExpr ::= RankingExpression "in" '[' RankingExpression (',' RankingExpression)* ']'
+
+BooleanExpr ::= RankingExpression COMPARISON_OPERATOR RankingExpression
+
+ArithmeticExpr ::= RankingExpression ARITHMETIC_OPERATOR RankingExpression
+
+QueryDefinitionExpr ::= QueryDefinition | ItemRawScoreDefinition
+
+FunctionCallExpr ::= IdentifierWithDashVal '(' RankingExpression (',' RankingExpression)* ')' ('.' IdentifierWithDashVal)?
+
+ParenthesisedExpr ::= '(' RankingExpression ')'
+
+PrimitiveExpr ::= (('-')? INTEGER_REG) | (('-')? FLOAT_REG) | IdentifierVal | RankFeature | STRING_REG
+
+//-------------------------
+//-- Rank Profile rules ---
+//-------------------------
+RankProfileDefinition ::= (rank-profile | model) IdentifierWithDashVal (inherits IdentifierWithDashVal)? '{' RankProfileBody '}'
+ { mixin="ai.vespa.intellij.schema.psi.impl.SdNamedElementImpl"
+ implements=["ai.vespa.intellij.schema.psi.SdDeclaration"]
+ }
+private RankProfileBody ::= RankProfileBodyOptions* // Does not support zero-or-one occurrences
+private RankProfileBodyOptions ::= MatchPhaseDefinition | NumThreadsDefinition | FunctionDefinition | TermwiseLimitDefinition |
+ ignore-default-rank-features | RankPropertiesDefinition | FirstPhaseDefinition |
+ SummaryFeaturesDefinition | MatchFeaturesDefinition | RankFeaturesDefinition |
+ SecondPhaseDefinition | ConstantsDefinition | RankDefinition | RankTypeDefinition |
+ MinHitsDefinition | NumSearchPartitionDefinition | FieldWeightDefinition
+
+MatchPhaseDefinition ::= match-phase '{' MatchPhaseBody '}'
+MatchPhaseBody ::= MatchPhaseBodyOptions+
+MatchPhaseBodyOptions ::= (attribute ':' IdentifierVal) | (order ':' (ascending | descending)) | (max-hits ':' ('-')? INTEGER_REG)
+ | DiversityDefinition | (evaluation-point ':' ('-')? FLOAT_REG) |
+ (pre-post-filter-tipping-point ':' ('-')? FLOAT_REG) // Does not support zero-or-one occurrences
+DiversityDefinition ::= diversity '{' DiversityBody '}'
+DiversityBody ::= DiversityBodyOptions*
+private DiversityBodyOptions ::= (attribute ':' IdentifierVal) | (min-groups ':' ('-')? INTEGER_REG) | (cutoff-factor ':' ('-')? FLOAT_REG) |
+ (cutoff-strategy ':' (strict | loose))
+
+private NumThreadsDefinition ::= num-threads-per-search ':' INTEGER_REG
+private TermwiseLimitDefinition ::= termwise-limit ':' ('-')? (FLOAT_REG | INTEGER_REG)
+private MinHitsDefinition ::= min-hits-per-thread ':' ('-')? INTEGER_REG
+private NumSearchPartitionDefinition ::= num-search-partition ':' INTEGER_REG
+FieldWeightDefinition ::= weight IdentifierVal ':' INTEGER_REG
+FirstPhaseDefinition ::= first-phase '{' FirstPhaseBody '}' { mixin="ai.vespa.intellij.schema.psi.impl.SdFirstPhaseDefinitionMixin" }
+FirstPhaseBody ::= FirstPhaseBodyOptions* // Does not support zero-or-one occurrences
+private FirstPhaseBodyOptions ::= (keep-rank-count ':' INTEGER_REG) | (rank-score-drop-limit ':' ('-')? (FLOAT_REG | INTEGER_REG)) | ExpressionDefinition
+
+ExpressionDefinition ::= expression ((':' RankingExpression) | ('{' RankingExpression* '}'))
+
+SecondPhaseDefinition ::= second-phase '{' SecondPhaseBody '}'
+SecondPhaseBody ::= SecondPhaseBodyOptions*
+private SecondPhaseBodyOptions ::= (rerank-count ':' INTEGER_REG) | ExpressionDefinition
+
+RankPropertiesDefinition ::= rank-properties '{' RankPropertiesBody '}'
+RankPropertiesBody ::= (RankPropertiesKey ':' RankPropertiesValue)+
+RankPropertiesKey ::= (IdentifierWithDashVal | STRING_REG | '(' | ')' | '.' | ',' | '$' | INTEGER_REG)+
+RankPropertiesValue ::= (('-')? INTEGER_REG) | (('-')? FLOAT_REG) | WORD_REG | IdentifierVal | STRING_REG
+
+FunctionDefinition ::= (function | macro) inline? IdentifierVal '(' (ArgumentDefinition (',' ArgumentDefinition)*)? ')'
+ '{' ExpressionDefinition '}'
+ { mixin="ai.vespa.intellij.schema.psi.impl.SdNamedElementImpl"
+ implements=["ai.vespa.intellij.schema.psi.SdFunctionDefinitionInterface" "ai.vespa.intellij.schema.psi.SdNamedElement"]
+ }
+ArgumentDefinition ::= IdentifierVal
+ { mixin="ai.vespa.intellij.schema.psi.impl.SdNamedElementImpl"
+ implements=["ai.vespa.intellij.schema.psi.SdDeclaration" "ai.vespa.intellij.schema.psi.SdNamedElement"]
+ }
+
+SummaryFeaturesDefinition ::= summary-features ((':' RankFeature+) | ((inherits IdentifierVal)? '{' RankFeature* '}'))
+
+MatchFeaturesDefinition ::= match-features ((':' RankFeature+) | ('{' RankFeature* '}'))
+
+RankFeaturesDefinition ::= rank-features ((':' RankFeature+) | ('{' RankFeature* '}'))
+
+ConstantsDefinition ::= constants '{' (IdentifierVal ':' RankPropertiesValue)* '}'
+
+RankFeature ::= QueryDefinition | ItemRawScoreDefinition | FunctionCallExpr | (IdentifierWithDashVal ('.' IdentifierWithDashVal)* )
+QueryDefinition ::= "query" '(' IdentifierWithDashVal ')'
+ { mixin="ai.vespa.intellij.schema.psi.impl.SdNamedElementImpl"
+ implements=["ai.vespa.intellij.schema.psi.SdDeclaration" "ai.vespa.intellij.schema.psi.SdNamedElement"]
+ }
+ItemRawScoreDefinition ::= "itemRawScore" '(' IdentifierVal ')'
+ { mixin="ai.vespa.intellij.schema.psi.impl.SdNamedElementImpl"
+ implements=["ai.vespa.intellij.schema.psi.SdDeclaration" "ai.vespa.intellij.schema.psi.SdNamedElement"]
+ }
+
+//-------------------------
+//---- Document rules -----
+//-------------------------
+DocumentDefinition ::= document (IdentifierVal (inherits IdentifierVal (',' IdentifierVal)*)?)? '{' DocumentBody '}'
+ { mixin="ai.vespa.intellij.schema.psi.impl.SdNamedElementImpl"
+ implements=["ai.vespa.intellij.schema.psi.SdDeclaration" "ai.vespa.intellij.schema.psi.SdNamedElement"]
+ }
+
+DocumentBody ::= DocumentBodyOptions*
+DocumentBodyOptions ::= DocumentStructDefinition | DocumentFieldDefinition | DocumentAnnotationDefinition
+
+DocumentAnnotationDefinition ::= AnnotationDefinition
+ { mixin="ai.vespa.intellij.schema.psi.impl.SdNamedElementImpl"
+ implements=["ai.vespa.intellij.schema.psi.SdDeclaration" "ai.vespa.intellij.schema.psi.SdNamedElement"]
+ }
+
+DocumentStructDefinition ::= struct IdentifierVal '{' DocumentStructBody '}'
+ { mixin="ai.vespa.intellij.schema.psi.impl.SdNamedElementImpl"
+ implements=["ai.vespa.intellij.schema.psi.SdDeclaration" "ai.vespa.intellij.schema.psi.SdNamedElement"]
+ }
+
+DocumentStructBody ::= DocumentStructFieldDefinition*
+DocumentStructFieldDefinition ::= field IdentifierVal type FieldTypeName '{' DocumentStructFieldBody '}'
+ { mixin="ai.vespa.intellij.schema.psi.impl.SdNamedElementImpl"
+ implements=["ai.vespa.intellij.schema.psi.SdDeclaration" "ai.vespa.intellij.schema.psi.SdNamedElement"]
+ }
+
+DocumentStructFieldBody ::= MatchDefinition?
+
+DocumentFieldDefinition ::= field IdentifierVal type FieldTypeName '{' DocumentFieldBody '}'
+ { mixin="ai.vespa.intellij.schema.psi.impl.SdNamedElementImpl"
+ implements=["ai.vespa.intellij.schema.psi.SdDeclaration" "ai.vespa.intellij.schema.psi.SdNamedElement"]
+ }
+
+DocumentFieldBody ::= DocumentFieldBodyOptions* // Does not support zero-or-one occurrences
+private DocumentFieldBodyOptions ::= StructFieldDefinition | MatchDefinition | IndexingDefinition | AttributeDefinition |
+ AliasDefinition | RankDefinition | IndexingRewriteState | QueryCommandDefinition | SummaryDefinition |
+ BoldingDefinition | (id ':' INTEGER_REG) | IndexDefinition | (normalizing ':' IdentifierWithDashVal) |
+ SortingDefinition | StemmingDefinition | (weight ':' INTEGER_REG) | WeightedSetDefinition |
+ RankTypeDefinition | DictionaryDefinition | SummaryToDefinition | body
+//***** Field's body elements ******//
+// Struct
+StructFieldDefinition ::= struct-field IdentifierVal ('.' IdentifierVal)? '{' StructFieldBody '}'
+ { mixin="ai.vespa.intellij.schema.psi.impl.SdNamedElementImpl"
+ implements=["ai.vespa.intellij.schema.psi.SdDeclaration" "ai.vespa.intellij.schema.psi.SdNamedElement"]
+ }
+
+StructFieldBody ::= StructFieldBodyOptions* // Does not support zero-or-one occurrences
+StructFieldBodyOptions ::= IndexingDefinition | AttributeDefinition | MatchDefinition | QueryCommandDefinition |
+ StructFieldDefinition | SummaryDefinition
+// Match
+MatchDefinition ::= match ((':' MatchProperty) | ('{' MatchProperty+ '}'))
+MatchProperty ::= text | exact | (exact-terminator ':' STRING_REG) | word | prefix | cased | uncased | substring |
+ suffix | (max-length ':' INTEGER_REG) | gram | (gram-size ':' INTEGER_REG) | WordWrapper
+// Indexing
+IndexingDefinition ::= indexing ((':' IndexingStatement) | ('{' IndexingStatement+ '}'))
+IndexingStatement ::= IndexingStatementOptions ((('|' | ';') IndexingStatementOptions)*)
+ // Does not support zero-or-one occurrences
+IndexingStatementOptions ::= summary | attribute | index | set_language | lowercase | (input (IdentifierVal | IndexingStuff)+) |
+ ('{' IndexingStatementOptions '}') | IndexingStuff+
+private IndexingStuff ::= WordWrapper | INTEGER_REG | FLOAT_REG | STRING_REG | ('{' IndexingStatementOptions+ '}') |
+ ':' | ('|' IndexingStatementOptions) | ';' | '.' | '(' | ')' | ARITHMETIC_OPERATOR | COMPARISON_OPERATOR
+// Attribute
+AttributeDefinition ::= attribute ((':' SimpleAttributeProperty) | ('{' (ComplexAttributeProperty | SimpleAttributeProperty)+ '}'))
+SimpleAttributeProperty ::= fast-search | fast-access | paged | mutable | enable-bit-vectors | enable-only-bit-vector | WordWrapper // Does not support zero-or-one occurrences
+ComplexAttributeProperty ::= AliasDefinition | SortingDefinition | DistanceMetricDef // Does not support zero-or-one occurrences
+DistanceMetricDef ::= distance-metric ':' IdentifierWithDashVal
+// Alias
+AliasDefinition ::= alias (IdentifierWithDashVal ('.' IdentifierWithDashVal)*)? ':' IdentifierWithDashVal ('.' IdentifierWithDashVal)*
+// Stemming
+StemmingDefinition ::= stemming ':' IdentifierWithDashVal
+// Rank
+RankDefinition ::= rank ((IdentifierVal? ':' RankingSetting) | ('{' RankingSetting '}'))
+RankingSetting ::= filter | normal | literal | WordWrapper
+// Indexing Rewrite
+IndexingRewriteState ::= indexing-rewrite ':' none
+// Query Command
+QueryCommandDefinition ::= query-command ':' (IdentifierVal | STRING_REG | WordWrapper)
+// Summary
+SummaryDefinition ::= summary IdentifierWithDashVal? (type FieldTypeName)? ((':' SummaryBodyOptions) | ( '{' SummaryBody '}'))
+ { mixin="ai.vespa.intellij.schema.psi.impl.SdSummaryDefinitionMixin" }
+SummaryBody ::= SummaryBodyOptions* // Does not support zero-or-one occurrences
+SummaryBodyOptions ::= full | static | dynamic | (source ':' (IdentifierVal ('.' IdentifierVal)?) (',' IdentifierVal ('.' IdentifierVal)?)*) |
+ (to ':' IdentifierVal (',' IdentifierVal)*) | matched-elements-only | BoldingDefinition
+// Summary To
+SummaryToDefinition ::= summary-to ':' WordWrapper (',' WordWrapper)*
+// Bolding
+BoldingDefinition ::= bolding ':' (on | off | true | false)
+// Index
+IndexDefinition ::= index IdentifierVal? ((':' IndexProperty) | ('{' IndexProperty '}'))
+IndexProperty ::= IndexPropertyOptions*
+private IndexPropertyOptions ::= (alias ':' IdentifierWithDashVal) | StemmingDefinition | (arity ':' INTEGER_REG) |
+ (lower-bound ':' INTEGER_REG ('L')?) | (upper-bound ':' INTEGER_REG ('L')?) |
+ (dense-posting-list-threshold ':' FLOAT_REG) | enable-bm25 | prefix | HnswDefinition
+HnswDefinition ::= hnsw '{' HnswBody '}'
+HnswBody ::= HnswBodyOptions*
+private HnswBodyOptions ::= (max-links-per-node ':' INTEGER_REG) | (neighbors-to-explore-at-insert ':' INTEGER_REG) |
+ (multi-threaded-indexing ':' (on | off | true | false))
+// Sorting
+SortingDefinition ::= sorting ((':' SortingProperty) | ('{' SortingProperty* '}'))
+SortingProperty ::= ascending | descending | (function ':' SortingFunction) | (strength ':' SortingStrength) |
+ (locale ':' IdentifierWithDashVal)
+SortingFunction ::= uca | raw | lowercase
+SortingStrength ::= primary | secondary | tertiary | quaternary | identical
+// Rank Type
+RankTypeDefinition ::= rank-type IdentifierVal? ':' IdentifierVal
+// Weighted Set
+WeightedSetDefinition ::= weightedset ((':' WeightedSetProperty) | ('{' WeightedSetProperty* '}')) // Does not support
+ // zero-or-one occurrences
+WeightedSetProperty ::= create-if-nonexistent | remove-if-zero
+// Dictionary
+DictionaryDefinition ::= dictionary ((':' DictionarySetting) | ('{' DictionarySetting* '}'))
+DictionarySetting ::= hash | btree | cased | uncased
+//***** End of Field's body elements ******//
+
+//---------------------
+//---- Util rules -----
+//---------------------
+
+private WordWrapper ::= KeywordOrIdentifier | KeywordNotIdentifier | ID_REG | ID_WITH_DASH_REG | WORD_REG
+
+IdentifierVal ::= KeywordOrIdentifier | ID_REG { mixin="ai.vespa.intellij.schema.psi.impl.SdIdentifierMixin"
+ implements=["ai.vespa.intellij.schema.psi.SdIdentifier"]
+ }
+
+IdentifierWithDashVal ::= ID_WITH_DASH_REG | IdentifierVal { mixin="ai.vespa.intellij.schema.psi.impl.SdIdentifierMixin"
+ implements=["ai.vespa.intellij.schema.psi.SdIdentifier"]
+ }
+
+// Those lists of keywords (KeywordOrIdentifier and KeywordNotIdentifier) have to be synchronized with sd.flex file.
+// If you add a keyword here, you should add it to the sd.flex file as well.
+KeywordOrIdentifier ::= schema | search | document | struct | field | type | indexing | input | output | inherits |
+ import | as | raw | uri | file | annotationreference | array | weightedset | map |
+ order | ascending | descending | diversity | constants | expression | weight | match |
+ function | macro | inline | text | exact | word | prefix | cased | uncased | substring | suffix |
+ gram | paged | mutable | alias | sorting | strength | locale | uca | lowercase |
+ primary | secondary | tertiary | quaternary | identical | rank | filter | normal | literal |
+ none | full | dynamic | source | to | strict | loose |
+ bolding | on | off | true | false | id | normalizing | stemming | arity | hnsw | dictionary | hash | btree |
+ fieldset | fields | constant | annotation
+ | attribute | body | header | index | static |
+ reference | summary | set_language | model
+
+// Note- in this form, those keywords can't be use as identifier-with-dash!
+KeywordNotIdentifier ::= struct-field | document-summary | omit-summary-features | from-disk | rank-profile | rank-type |
+ num-threads-per-search | termwise-limit | ignore-default-rank-features | min-hits-per-thread |
+ num-search-partition | match-phase | max-hits | second-phase | rerank-count | min-groups |
+ first-phase | keep-rank-count | rank-score-drop-limit | rank-properties | summary-features |
+ match-features | rank-features |
+ exact-terminator | max-length | gram-size | fast-search | fast-access | distance-metric |
+ indexing-rewrite | query-command | matched-elements-only | lower-bound | upper-bound |
+ dense-posting-list-threshold | enable-bm25 | max-links-per-node | neighbors-to-explore-at-insert |
+ multi-threaded-indexing | create-if-nonexistent | remove-if-zero | raw-as-base64-in-summary |
+ onnx-model | cutoff-factor | cutoff-strategy | on-match | on-rank | on-summary | enable-bit-vectors |
+ enable-only-bit-vector | summary-to | evaluation-point | pre-post-filter-tipping-point
+ \ No newline at end of file
diff --git a/integration/intellij/src/main/java/ai/vespa/intellij/schema/SdChooseByNameContributor.java b/integration/intellij/src/main/java/ai/vespa/intellij/schema/SdChooseByNameContributor.java
new file mode 100644
index 00000000000..01992ef4a5b
--- /dev/null
+++ b/integration/intellij/src/main/java/ai/vespa/intellij/schema/SdChooseByNameContributor.java
@@ -0,0 +1,58 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.intellij.schema;
+
+import com.intellij.navigation.ChooseByNameContributor;
+import com.intellij.navigation.NavigationItem;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.PsiManager;
+import com.intellij.psi.search.FileTypeIndex;
+import com.intellij.psi.search.GlobalSearchScope;
+import ai.vespa.intellij.schema.psi.SdDeclaration;
+import ai.vespa.intellij.schema.psi.SdFile;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * This class is used for the extension (in plugin.xml) to enable "Go To Symbol" feature.
+ *
+ * @author Shahar Ariel
+ */
+public class SdChooseByNameContributor implements ChooseByNameContributor {
+
+ @Override
+ public String @NotNull [] getNames(Project project, boolean includeNonProjectItems) {
+ Collection<VirtualFile> virtualFiles = FileTypeIndex.getFiles(SdFileType.INSTANCE, GlobalSearchScope.allScope(project));
+
+ List<SdDeclaration> declarations = new ArrayList<>();
+
+ for (VirtualFile file : virtualFiles) {
+ SdFile sdFile = (SdFile) PsiManager.getInstance(project).findFile(file);
+ declarations.addAll(SdUtil.findDeclarations(sdFile));
+ }
+
+ List<String> names = new ArrayList<>(declarations.size());
+ for (SdDeclaration declaration : declarations) {
+ names.add(declaration.getName());
+ }
+ return names.toArray(new String[names.size()]);
+ }
+
+ @Override
+ public NavigationItem @NotNull [] getItemsByName(String name, String pattern, Project project, boolean includeNonProjectItems) {
+ Collection<VirtualFile> virtualFiles = FileTypeIndex.getFiles(SdFileType.INSTANCE, GlobalSearchScope.allScope(project));
+
+ List<SdDeclaration> declarations = new ArrayList<>();
+
+ for (VirtualFile file : virtualFiles) {
+ SdFile sdFile = (SdFile) PsiManager.getInstance(project).findFile(file);
+ declarations.addAll(SdUtil.findDeclarationsByName(sdFile, name));
+ }
+
+ return declarations.toArray(new NavigationItem[declarations.size()]);
+ }
+
+}
diff --git a/integration/intellij/src/main/java/ai/vespa/intellij/schema/SdCodeStyleSettings.java b/integration/intellij/src/main/java/ai/vespa/intellij/schema/SdCodeStyleSettings.java
new file mode 100644
index 00000000000..bdf1bf3eb7b
--- /dev/null
+++ b/integration/intellij/src/main/java/ai/vespa/intellij/schema/SdCodeStyleSettings.java
@@ -0,0 +1,18 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.intellij.schema;
+
+import com.intellij.psi.codeStyle.CodeStyleSettings;
+import com.intellij.psi.codeStyle.CustomCodeStyleSettings;
+
+/**
+ * This class represent a code style settings, and creates an option page in settings/preferences.
+ *
+ * @author Shahar Ariel
+ */
+public class SdCodeStyleSettings extends CustomCodeStyleSettings {
+
+ public SdCodeStyleSettings(CodeStyleSettings settings) {
+ super("SdCodeStyleSettings", settings);
+ }
+
+}
diff --git a/integration/intellij/src/main/java/ai/vespa/intellij/schema/SdCodeStyleSettingsProvider.java b/integration/intellij/src/main/java/ai/vespa/intellij/schema/SdCodeStyleSettingsProvider.java
new file mode 100644
index 00000000000..b02e8371b77
--- /dev/null
+++ b/integration/intellij/src/main/java/ai/vespa/intellij/schema/SdCodeStyleSettingsProvider.java
@@ -0,0 +1,50 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.intellij.schema;
+
+import com.intellij.application.options.CodeStyleAbstractConfigurable;
+import com.intellij.application.options.CodeStyleAbstractPanel;
+import com.intellij.application.options.TabbedLanguageCodeStylePanel;
+import com.intellij.psi.codeStyle.CodeStyleConfigurable;
+import com.intellij.psi.codeStyle.CodeStyleSettings;
+import com.intellij.psi.codeStyle.CodeStyleSettingsProvider;
+import com.intellij.psi.codeStyle.CustomCodeStyleSettings;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * This class is used for the extension (in plugin.xml) to the class SdCodeStyleSettings.
+ *
+ * @author Shahar Ariel
+ */
+public class SdCodeStyleSettingsProvider extends CodeStyleSettingsProvider {
+
+ @Override
+ public CustomCodeStyleSettings createCustomSettings(CodeStyleSettings settings) {
+ return new SdCodeStyleSettings(settings);
+ }
+
+ @Nullable
+ @Override
+ public String getConfigurableDisplayName() {
+ return "Sd";
+ }
+
+ @NotNull
+ public CodeStyleConfigurable createConfigurable(@NotNull CodeStyleSettings settings, @NotNull CodeStyleSettings modelSettings) {
+ return new CodeStyleAbstractConfigurable(settings, modelSettings, this.getConfigurableDisplayName()) {
+ @Override
+ protected CodeStyleAbstractPanel createPanel(CodeStyleSettings settings) {
+ return new SdCodeStyleMainPanel(getCurrentSettings(), settings);
+ }
+ };
+ }
+
+ private static class SdCodeStyleMainPanel extends TabbedLanguageCodeStylePanel {
+
+ public SdCodeStyleMainPanel(CodeStyleSettings currentSettings, CodeStyleSettings settings) {
+ super(SdLanguage.INSTANCE, currentSettings, settings);
+ }
+
+ }
+
+}
diff --git a/integration/intellij/src/main/java/ai/vespa/intellij/schema/SdCommenter.java b/integration/intellij/src/main/java/ai/vespa/intellij/schema/SdCommenter.java
new file mode 100644
index 00000000000..a8872f71b63
--- /dev/null
+++ b/integration/intellij/src/main/java/ai/vespa/intellij/schema/SdCommenter.java
@@ -0,0 +1,45 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.intellij.schema;
+
+import com.intellij.lang.Commenter;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * This class is used for the extension (in plugin.xml), to enable turning a line into a comment with
+ * "Code -> Comment with line comment".
+ *
+ * @author Shahar Ariel
+ */
+public class SdCommenter implements Commenter {
+
+ @Nullable
+ @Override
+ public String getLineCommentPrefix() {
+ return "#";
+ }
+
+ @Nullable
+ @Override
+ public String getBlockCommentPrefix() {
+ return "";
+ }
+
+ @Nullable
+ @Override
+ public String getBlockCommentSuffix() {
+ return null;
+ }
+
+ @Nullable
+ @Override
+ public String getCommentedBlockCommentPrefix() {
+ return null;
+ }
+
+ @Nullable
+ @Override
+ public String getCommentedBlockCommentSuffix() {
+ return null;
+ }
+
+}
diff --git a/integration/intellij/src/main/java/ai/vespa/intellij/schema/SdCompletionContributor.java b/integration/intellij/src/main/java/ai/vespa/intellij/schema/SdCompletionContributor.java
new file mode 100644
index 00000000000..3e5b53c87ea
--- /dev/null
+++ b/integration/intellij/src/main/java/ai/vespa/intellij/schema/SdCompletionContributor.java
@@ -0,0 +1,36 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.intellij.schema;
+
+import com.intellij.codeInsight.completion.CompletionContributor;
+import com.intellij.codeInsight.completion.CompletionParameters;
+import com.intellij.codeInsight.completion.CompletionProvider;
+import com.intellij.codeInsight.completion.CompletionResultSet;
+import com.intellij.codeInsight.completion.CompletionType;
+import com.intellij.codeInsight.lookup.LookupElementBuilder;
+import com.intellij.patterns.PlatformPatterns;
+import com.intellij.util.ProcessingContext;
+import ai.vespa.intellij.schema.psi.SdTypes;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * This class is used for the extension (in plugin.xml) to enables Auto-Complete. Partially works for now.
+ *
+ * @author Shahar Ariel
+ */
+public class SdCompletionContributor extends CompletionContributor {
+
+
+ public SdCompletionContributor() {
+ extend(CompletionType.BASIC,
+ PlatformPatterns.psiElement(SdTypes.IDENTIFIER_VAL),
+ new CompletionProvider<>() {
+ public void addCompletions(@NotNull CompletionParameters parameters, //completion parameters contain details of the cursor position
+ @NotNull ProcessingContext context,
+ @NotNull CompletionResultSet resultSet) { //result set contains completion details to suggest
+ resultSet.addElement(LookupElementBuilder.create(""));
+ }
+ }
+ );
+ }
+
+}
diff --git a/integration/intellij/src/main/java/ai/vespa/intellij/schema/SdFileType.java b/integration/intellij/src/main/java/ai/vespa/intellij/schema/SdFileType.java
new file mode 100644
index 00000000000..b469d0c0ebb
--- /dev/null
+++ b/integration/intellij/src/main/java/ai/vespa/intellij/schema/SdFileType.java
@@ -0,0 +1,47 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.intellij.schema;
+
+import com.intellij.openapi.fileTypes.LanguageFileType;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.Icon;
+
+/**
+ * This class is used for the extension (in plugin.xml), to define SD as a file's type.
+ *
+ * @author Shahar Ariel
+ */
+public class SdFileType extends LanguageFileType {
+
+ public static final SdFileType INSTANCE = new SdFileType();
+
+ private SdFileType() {
+ super(SdLanguage.INSTANCE);
+ }
+
+ @NotNull
+ @Override
+ public String getName() {
+ return "Sd File";
+ }
+
+ @NotNull
+ @Override
+ public String getDescription() {
+ return "Sd language file";
+ }
+
+ @NotNull
+ @Override
+ public String getDefaultExtension() {
+ return "sd";
+ }
+
+ @Nullable
+ @Override
+ public Icon getIcon() {
+ return SdIcons.FILE;
+ }
+
+}
diff --git a/integration/intellij/src/main/java/ai/vespa/intellij/schema/SdIcons.java b/integration/intellij/src/main/java/ai/vespa/intellij/schema/SdIcons.java
new file mode 100644
index 00000000000..96f8428b223
--- /dev/null
+++ b/integration/intellij/src/main/java/ai/vespa/intellij/schema/SdIcons.java
@@ -0,0 +1,27 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.intellij.schema;
+
+import com.intellij.openapi.util.IconLoader;
+
+import javax.swing.Icon;
+
+/**
+ * This class is used for defining Icons for the IDE.
+ *
+ * @author Shahar Ariel
+ */
+public class SdIcons {
+
+ public static final Icon FILE = IconLoader.getIcon("icons/sd_icon.png", SdIcons.class);
+ public static final Icon STRUCT_FIELD = IconLoader.getIcon("icons/struct_field_icon.png", SdIcons.class);
+ public static final Icon IMPORTED_FIELD = IconLoader.getIcon("icons/imported_field_icon.png", SdIcons.class);
+ public static final Icon DOCUMENT_SUMMARY = IconLoader.getIcon("icons/document_summary_icon.png", SdIcons.class);
+ public static final Icon STRUCT = IconLoader.getIcon("icons/struct_icon.png", SdIcons.class);
+ public static final Icon DOCUMENT = IconLoader.getIcon("icons/document_icon.png", SdIcons.class);
+ public static final Icon SUMMARY = IconLoader.getIcon("icons/summary_def_icon.png", SdIcons.class);
+ public static final Icon MACRO = IconLoader.getIcon("icons/macro_icon.png", SdIcons.class);
+ public static final Icon OVERRIDE_MACRO = IconLoader.getIcon("icons/override_macro_icon.png", SdIcons.class);
+ public static final Icon FIRST_PHASE = IconLoader.getIcon("icons/first_phase_icon.png", SdIcons.class);
+
+}
+
diff --git a/integration/intellij/src/main/java/ai/vespa/intellij/schema/SdLanguage.java b/integration/intellij/src/main/java/ai/vespa/intellij/schema/SdLanguage.java
new file mode 100644
index 00000000000..c4d471ee737
--- /dev/null
+++ b/integration/intellij/src/main/java/ai/vespa/intellij/schema/SdLanguage.java
@@ -0,0 +1,24 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.intellij.schema;
+
+import com.intellij.lang.Language;
+
+/**
+ * This class defines SD as a language.
+ *
+ * @author Shahar Ariel
+ */
+public class SdLanguage extends Language {
+
+ public static final SdLanguage INSTANCE = new SdLanguage();
+
+ private SdLanguage() {
+ super("Sd");
+ }
+
+ @Override
+ public boolean isCaseSensitive() {
+ return true;
+ }
+
+}
diff --git a/integration/intellij/src/main/java/ai/vespa/intellij/schema/SdLanguageCodeStyleSettingsProvider.java b/integration/intellij/src/main/java/ai/vespa/intellij/schema/SdLanguageCodeStyleSettingsProvider.java
new file mode 100644
index 00000000000..5f618a62d6e
--- /dev/null
+++ b/integration/intellij/src/main/java/ai/vespa/intellij/schema/SdLanguageCodeStyleSettingsProvider.java
@@ -0,0 +1,39 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.intellij.schema;
+
+import com.intellij.lang.Language;
+import com.intellij.psi.codeStyle.CodeStyleSettingsCustomizable;
+import com.intellij.psi.codeStyle.LanguageCodeStyleSettingsProvider;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * This class is used for the extension (in plugin.xml), to make the IDE use our plugin's code for coding style.
+ *
+ * @author Shahar Ariel
+ */
+public class SdLanguageCodeStyleSettingsProvider extends LanguageCodeStyleSettingsProvider {
+
+ @NotNull
+ @Override
+ public Language getLanguage() {
+ return SdLanguage.INSTANCE;
+ }
+
+ @Override
+ public void customizeSettings(@NotNull CodeStyleSettingsCustomizable consumer, @NotNull SettingsType settingsType) {
+ if (settingsType == SettingsType.SPACING_SETTINGS) {
+ consumer.showStandardOptions("SPACE_AFTER_COLON");
+// consumer.renameStandardOption("SPACE_AROUND_ASSIGNMENT_OPERATORS", "Separator");
+ } else if (settingsType == SettingsType.BLANK_LINES_SETTINGS) {
+ consumer.showStandardOptions("KEEP_BLANK_LINES_IN_CODE");
+// } else if (settingsType == SettingsType.INDENT_SETTINGS) {
+// consumer.showStandardOptions("USE_RELATIVE_INDENTS");
+ }
+ }
+
+ @Override
+ public String getCodeSample(@NotNull SettingsType settingsType) {
+ return "field myField type int {\n indexing: summary\n}";
+ }
+
+}
diff --git a/integration/intellij/src/main/java/ai/vespa/intellij/schema/SdRefactoringSupportProvider.java b/integration/intellij/src/main/java/ai/vespa/intellij/schema/SdRefactoringSupportProvider.java
new file mode 100644
index 00000000000..24de7bb6253
--- /dev/null
+++ b/integration/intellij/src/main/java/ai/vespa/intellij/schema/SdRefactoringSupportProvider.java
@@ -0,0 +1,22 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.intellij.schema;
+
+import com.intellij.lang.refactoring.RefactoringSupportProvider;
+import com.intellij.psi.PsiElement;
+import ai.vespa.intellij.schema.psi.SdIdentifier;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * This class is used for the extension (in plugin.xml), to enable refactoring.
+ *
+ * @author Shahar Ariel
+ */
+public class SdRefactoringSupportProvider extends RefactoringSupportProvider {
+
+ @Override
+ public boolean isMemberInplaceRenameAvailable(@NotNull PsiElement elementToRename, @Nullable PsiElement context) {
+ return (elementToRename instanceof SdIdentifier);
+ }
+
+}
diff --git a/integration/intellij/src/main/java/ai/vespa/intellij/schema/SdReference.java b/integration/intellij/src/main/java/ai/vespa/intellij/schema/SdReference.java
new file mode 100644
index 00000000000..6404a738e59
--- /dev/null
+++ b/integration/intellij/src/main/java/ai/vespa/intellij/schema/SdReference.java
@@ -0,0 +1,78 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.intellij.schema;
+
+import com.intellij.codeInsight.lookup.LookupElement;
+import com.intellij.codeInsight.lookup.LookupElementBuilder;
+import com.intellij.openapi.util.TextRange;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiElementResolveResult;
+import com.intellij.psi.PsiNamedElement;
+import com.intellij.psi.PsiPolyVariantReference;
+import com.intellij.psi.PsiReferenceBase;
+import com.intellij.psi.ResolveResult;
+import com.intellij.util.IncorrectOperationException;
+import ai.vespa.intellij.schema.psi.SdDeclaration;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A reference to a Psi Element.
+ *
+ * @author Shahar Ariel
+ */
+public class SdReference extends PsiReferenceBase<PsiElement> implements PsiPolyVariantReference {
+
+ private final String elementName;
+
+ public SdReference(@NotNull PsiElement element, TextRange textRange) {
+ super(element, textRange);
+ elementName = element.getText().substring(textRange.getStartOffset(), textRange.getEndOffset());
+ }
+
+ @NotNull
+ @Override
+ public ResolveResult @NotNull [] multiResolve(boolean incompleteCode) {
+
+ PsiElement file = myElement.getContainingFile();
+
+ final List<SdDeclaration> declarations = SdUtil.findDeclarationsByScope(file, myElement, elementName);
+
+ List<ResolveResult> results = new ArrayList<>();
+
+ for (SdDeclaration declaration : declarations) {
+ results.add(new PsiElementResolveResult(declaration));
+ }
+ return results.toArray(new ResolveResult[results.size()]);
+ }
+
+ @Nullable
+ @Override
+ public PsiElement resolve() {
+ ResolveResult[] resolveResults = multiResolve(false);
+ return resolveResults.length == 1 ? resolveResults[0].getElement() : null;
+ }
+
+ @Override
+ public Object @NotNull [] getVariants() {
+ List<SdDeclaration> declarations = SdUtil.findDeclarations(myElement.getContainingFile());
+ List<LookupElement> variants = new ArrayList<>();
+ for (final SdDeclaration element : declarations) {
+ if (element.getName() != null && element.getName().length() > 0) {
+ variants.add(LookupElementBuilder
+ .create(element).withIcon(SdIcons.FILE)
+ .withTypeText(element.getContainingFile().getName())
+ );
+ }
+ }
+ return variants.toArray();
+ }
+
+ @Override
+ public PsiElement handleElementRename(@NotNull String newElementName) throws IncorrectOperationException {
+ return ((PsiNamedElement) myElement).setName(newElementName);
+ }
+
+}
diff --git a/integration/intellij/src/main/java/ai/vespa/intellij/schema/SdSyntaxHighlighter.java b/integration/intellij/src/main/java/ai/vespa/intellij/schema/SdSyntaxHighlighter.java
new file mode 100644
index 00000000000..df3da81e9fc
--- /dev/null
+++ b/integration/intellij/src/main/java/ai/vespa/intellij/schema/SdSyntaxHighlighter.java
@@ -0,0 +1,212 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.intellij.schema;
+
+import static com.intellij.openapi.editor.colors.TextAttributesKey.createTextAttributesKey;
+
+import com.intellij.lexer.Lexer;
+import com.intellij.openapi.editor.DefaultLanguageHighlighterColors;
+import com.intellij.openapi.editor.HighlighterColors;
+import com.intellij.openapi.editor.colors.TextAttributesKey;
+import com.intellij.openapi.fileTypes.SyntaxHighlighterBase;
+import com.intellij.psi.TokenType;
+import com.intellij.psi.tree.IElementType;
+import ai.vespa.intellij.schema.lexer.SdLexerAdapter;
+import ai.vespa.intellij.schema.psi.SdTypes;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.HashSet;
+
+/**
+ * Defines the syntax highlighting of an SD file.
+ *
+ * @author Shahar Ariel
+ */
+public class SdSyntaxHighlighter extends SyntaxHighlighterBase {
+
+ private static final HashSet<IElementType> keyWordsSet = initKeyWordsSet();
+ private static final HashSet<IElementType> constantsSet = initConstantsSet();
+
+ public static final TextAttributesKey IDENTIFIER =
+ createTextAttributesKey("SD_IDENTIFIER", DefaultLanguageHighlighterColors.IDENTIFIER);
+ public static final TextAttributesKey CONSTANT =
+ createTextAttributesKey("SD_CONSTANT", DefaultLanguageHighlighterColors.CONSTANT);
+ public static final TextAttributesKey KEY =
+ createTextAttributesKey("SD_KEY", DefaultLanguageHighlighterColors.KEYWORD);
+ public static final TextAttributesKey SYMBOL =
+ createTextAttributesKey("SD_SYMBOL", DefaultLanguageHighlighterColors.BRACKETS);
+ public static final TextAttributesKey STRING =
+ createTextAttributesKey("SD_STRING", DefaultLanguageHighlighterColors.STRING);
+ public static final TextAttributesKey NUMBER =
+ createTextAttributesKey("SD_NUMBER", DefaultLanguageHighlighterColors.NUMBER);
+ public static final TextAttributesKey COMMENT =
+ createTextAttributesKey("SD_COMMENT", DefaultLanguageHighlighterColors.LINE_COMMENT);
+ public static final TextAttributesKey BAD_CHARACTER =
+ createTextAttributesKey("Sd_BAD_CHARACTER", HighlighterColors.BAD_CHARACTER);
+
+
+ private static final TextAttributesKey[] BAD_CHAR_KEYS = new TextAttributesKey[]{BAD_CHARACTER};
+ private static final TextAttributesKey[] IDENTIFIER_KEYS = new TextAttributesKey[]{IDENTIFIER};
+ private static final TextAttributesKey[] CONSTANT_KEYS = new TextAttributesKey[]{CONSTANT};
+ private static final TextAttributesKey[] KEY_KEYS = new TextAttributesKey[]{KEY};
+ private static final TextAttributesKey[] SYMBOL_KEYS = new TextAttributesKey[]{SYMBOL};
+ private static final TextAttributesKey[] STRING_KEYS = new TextAttributesKey[]{STRING};
+ private static final TextAttributesKey[] NUMBER_KEYS = new TextAttributesKey[]{NUMBER};
+ private static final TextAttributesKey[] COMMENT_KEYS = new TextAttributesKey[]{COMMENT};
+ private static final TextAttributesKey[] EMPTY_KEYS = new TextAttributesKey[0];
+
+ @NotNull
+ @Override
+ public Lexer getHighlightingLexer() {
+ return new SdLexerAdapter();
+ }
+
+ @Override
+ public TextAttributesKey @NotNull [] getTokenHighlights(IElementType tokenType) {
+ if (tokenType.equals(SdTypes.IDENTIFIER_VAL) || tokenType.equals(SdTypes.IDENTIFIER_WITH_DASH_VAL)) {
+ return IDENTIFIER_KEYS;
+ } else if (keyWordsSet.contains(tokenType)) {
+ return KEY_KEYS;
+ } else if (tokenType.equals(SdTypes.SYMBOL)) {
+ return SYMBOL_KEYS;
+ } else if (tokenType.equals(SdTypes.STRING_REG)) {
+ return STRING_KEYS;
+ } else if (tokenType.equals(SdTypes.INTEGER_REG) || tokenType.equals(SdTypes.FLOAT_REG)) {
+ return NUMBER_KEYS;
+ } else if (tokenType.equals(SdTypes.COMMENT)) {
+ return COMMENT_KEYS;
+ } else if (constantsSet.contains(tokenType)) {
+ return CONSTANT_KEYS;
+ } else if (tokenType.equals(TokenType.BAD_CHARACTER)) {
+ return BAD_CHAR_KEYS;
+ } else {
+ return EMPTY_KEYS;
+ }
+ }
+
+ private static HashSet<IElementType> initKeyWordsSet() {
+ HashSet<IElementType> keyWords = new HashSet<>();
+ keyWords.add(SdTypes.SEARCH);
+ keyWords.add(SdTypes.SCHEMA);
+ keyWords.add(SdTypes.FIELD);
+ keyWords.add(SdTypes.TYPE);
+ keyWords.add(SdTypes.INDEXING);
+ keyWords.add(SdTypes.INPUT);
+ keyWords.add(SdTypes.DOCUMENT_SUMMARY);
+ keyWords.add(SdTypes.INHERITS);
+ keyWords.add(SdTypes.IMPORT);
+ keyWords.add(SdTypes.AS);
+ keyWords.add(SdTypes.FIELDSET);
+ keyWords.add(SdTypes.FIELDS);
+ keyWords.add(SdTypes.CONSTANT);
+ keyWords.add(SdTypes.FILE);
+ keyWords.add(SdTypes.URI);
+ keyWords.add(SdTypes.OUTPUT);
+ keyWords.add(SdTypes.ONNX_MODEL);
+ keyWords.add(SdTypes.ANNOTATION);
+ keyWords.add(SdTypes.RANK_PROFILE);
+ keyWords.add(SdTypes.MATCH_PHASE);
+ keyWords.add(SdTypes.FIRST_PHASE);
+ keyWords.add(SdTypes.EXPRESSION);
+ keyWords.add(SdTypes.SECOND_PHASE);
+ keyWords.add(SdTypes.RANK_PROPERTIES);
+ keyWords.add(SdTypes.MACRO);
+ keyWords.add(SdTypes.FUNCTION);
+ keyWords.add(SdTypes.INLINE);
+ keyWords.add(SdTypes.SUMMARY_FEATURES);
+ keyWords.add(SdTypes.MATCH_FEATURES);
+ keyWords.add(SdTypes.RANK_FEATURES);
+ keyWords.add(SdTypes.CONSTANTS);
+ keyWords.add(SdTypes.DOCUMENT);
+ keyWords.add(SdTypes.STRUCT);
+ keyWords.add(SdTypes.STRUCT_FIELD);
+ keyWords.add(SdTypes.MATCH);
+ keyWords.add(SdTypes.DISTANCE_METRIC);
+ keyWords.add(SdTypes.ALIAS);
+ keyWords.add(SdTypes.STEMMING);
+ keyWords.add(SdTypes.RANK);
+ keyWords.add(SdTypes.INDEXING_REWRITE);
+ keyWords.add(SdTypes.QUERY_COMMAND);
+ keyWords.add(SdTypes.BOLDING);
+ keyWords.add(SdTypes.HNSW);
+ keyWords.add(SdTypes.SORTING);
+ keyWords.add(SdTypes.RANK_TYPE);
+ keyWords.add(SdTypes.WEIGHTEDSET);
+ keyWords.add(SdTypes.DICTIONARY);
+ keyWords.add(SdTypes.ID);
+ keyWords.add(SdTypes.NORMALIZING);
+ keyWords.add(SdTypes.WEIGHT);
+
+ return keyWords;
+ }
+
+ private static HashSet<IElementType> initConstantsSet() {
+ HashSet<IElementType> constants = new HashSet<>();
+ constants.add(SdTypes.RAW_AS_BASE64_IN_SUMMARY);
+ constants.add(SdTypes.OMIT_SUMMARY_FEATURES);
+ constants.add(SdTypes.FROM_DISK);
+ constants.add(SdTypes.IGNORE_DEFAULT_RANK_FEATURES);
+ constants.add(SdTypes.ATTRIBUTE);
+ constants.add(SdTypes.ORDER);
+ constants.add(SdTypes.MAX_HITS);
+ constants.add(SdTypes.DIVERSITY);
+ constants.add(SdTypes.MIN_GROUPS);
+ constants.add(SdTypes.CUTOFF_FACTOR);
+ constants.add(SdTypes.CUTOFF_STRATEGY);
+ constants.add(SdTypes.NUM_THREADS_PER_SEARCH);
+ constants.add(SdTypes.TERMWISE_LIMIT);
+ constants.add(SdTypes.MIN_HITS_PER_THREAD);
+ constants.add(SdTypes.NUM_SEARCH_PARTITION);
+ constants.add(SdTypes.KEEP_RANK_COUNT);
+ constants.add(SdTypes.RANK_SCORE_DROP_LIMIT);
+ constants.add(SdTypes.RERANK_COUNT);
+ constants.add(SdTypes.TEXT);
+ constants.add(SdTypes.EXACT);
+ constants.add(SdTypes.EXACT_TERMINATOR);
+ constants.add(SdTypes.WORD);
+ constants.add(SdTypes.PREFIX);
+ constants.add(SdTypes.CASED);
+ constants.add(SdTypes.UNCASED);
+ constants.add(SdTypes.SUBSTRING);
+ constants.add(SdTypes.SUFFIX);
+ constants.add(SdTypes.MAX_LENGTH);
+ constants.add(SdTypes.GRAM);
+ constants.add(SdTypes.GRAM_SIZE);
+ constants.add(SdTypes.SUMMARY);
+ constants.add(SdTypes.INDEX);
+ constants.add(SdTypes.SET_LANGUAGE);
+ constants.add(SdTypes.LOWERCASE);
+ constants.add(SdTypes.FAST_SEARCH);
+ constants.add(SdTypes.FAST_ACCESS);
+ constants.add(SdTypes.PAGED);
+ constants.add(SdTypes.MUTABLE);
+ constants.add(SdTypes.FILTER);
+ constants.add(SdTypes.NORMAL);
+ constants.add(SdTypes.NONE);
+ constants.add(SdTypes.FULL);
+ constants.add(SdTypes.DYNAMIC);
+ constants.add(SdTypes.SOURCE);
+ constants.add(SdTypes.TO);
+ constants.add(SdTypes.MATCHED_ELEMENTS_ONLY);
+ constants.add(SdTypes.ON);
+ constants.add(SdTypes.OFF);
+ constants.add(SdTypes.TRUE);
+ constants.add(SdTypes.FALSE);
+ constants.add(SdTypes.ARITY);
+ constants.add(SdTypes.LOWER_BOUND);
+ constants.add(SdTypes.UPPER_BOUND);
+ constants.add(SdTypes.DENSE_POSTING_LIST_THRESHOLD);
+ constants.add(SdTypes.ENABLE_BM25);
+ constants.add(SdTypes.MAX_LINKS_PER_NODE);
+ constants.add(SdTypes.NEIGHBORS_TO_EXPLORE_AT_INSERT);
+ constants.add(SdTypes.MULTI_THREADED_INDEXING);
+ constants.add(SdTypes.ASCENDING);
+ constants.add(SdTypes.DESCENDING);
+ constants.add(SdTypes.STRENGTH);
+ constants.add(SdTypes.LOCALE);
+ constants.add(SdTypes.CREATE_IF_NONEXISTENT);
+ constants.add(SdTypes.REMOVE_IF_ZERO);
+ return constants;
+ }
+
+}
+
diff --git a/integration/intellij/src/main/java/ai/vespa/intellij/schema/SdSyntaxHighlighterFactory.java b/integration/intellij/src/main/java/ai/vespa/intellij/schema/SdSyntaxHighlighterFactory.java
new file mode 100644
index 00000000000..6cf30ecb54e
--- /dev/null
+++ b/integration/intellij/src/main/java/ai/vespa/intellij/schema/SdSyntaxHighlighterFactory.java
@@ -0,0 +1,23 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.intellij.schema;
+
+import com.intellij.openapi.fileTypes.SyntaxHighlighter;
+import com.intellij.openapi.fileTypes.SyntaxHighlighterFactory;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * This class is used for the extension (in plugin.xml) to the class SdSyntaxHighlighter.
+ *
+ * @author Shahar Ariel
+ */
+public class SdSyntaxHighlighterFactory extends SyntaxHighlighterFactory {
+
+ @NotNull
+ @Override
+ public SyntaxHighlighter getSyntaxHighlighter(Project project, VirtualFile virtualFile) {
+ return new SdSyntaxHighlighter();
+ }
+
+}
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
new file mode 100644
index 00000000000..f3f949d645f
--- /dev/null
+++ b/integration/intellij/src/main/java/ai/vespa/intellij/schema/SdUtil.java
@@ -0,0 +1,221 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.intellij.schema;
+
+import com.intellij.lang.ASTNode;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiManager;
+import com.intellij.psi.PsiNamedElement;
+import com.intellij.psi.PsiReference;
+import com.intellij.psi.search.FileTypeIndex;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.psi.util.PsiTreeUtil;
+import ai.vespa.intellij.schema.psi.SdAnnotationFieldDefinition;
+import ai.vespa.intellij.schema.psi.SdArgumentDefinition;
+import ai.vespa.intellij.schema.psi.SdDeclaration;
+import ai.vespa.intellij.schema.psi.SdDocumentAnnotationDefinition;
+import ai.vespa.intellij.schema.psi.SdDocumentDefinition;
+import ai.vespa.intellij.schema.psi.SdDocumentFieldDefinition;
+import ai.vespa.intellij.schema.psi.SdDocumentStructDefinition;
+import ai.vespa.intellij.schema.psi.SdDocumentStructFieldDefinition;
+import ai.vespa.intellij.schema.psi.SdDocumentSummaryDefinition;
+import ai.vespa.intellij.schema.psi.SdFieldTypeName;
+import ai.vespa.intellij.schema.psi.SdFile;
+import ai.vespa.intellij.schema.psi.SdFunctionDefinition;
+import ai.vespa.intellij.schema.psi.SdIdentifier;
+import ai.vespa.intellij.schema.psi.SdImportFieldDefinition;
+import ai.vespa.intellij.schema.psi.SdRankProfileDefinition;
+import ai.vespa.intellij.schema.psi.SdSchemaAnnotationDefinition;
+import ai.vespa.intellij.schema.psi.SdSchemaFieldDefinition;
+import ai.vespa.intellij.schema.psi.SdSummaryDefinition;
+import ai.vespa.intellij.schema.psi.SdTypes;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * Util class for the plugin's code.
+ *
+ * @author Shahar Ariel
+ */
+public class SdUtil {
+
+ public static @NotNull HashMap<String, List<PsiElement>> createMacrosMap(SdFile file) {
+ HashMap<String, List<PsiElement>> macrosMap = new HashMap<>();
+ for (SdRankProfileDefinition rankProfile : PsiTreeUtil
+ .findChildrenOfType(file, SdRankProfileDefinition.class)) {
+ for (SdFunctionDefinition macro : PsiTreeUtil.findChildrenOfType(rankProfile, SdFunctionDefinition.class)) {
+ macrosMap.computeIfAbsent(macro.getName(), k -> new ArrayList<>()).add(macro);
+ }
+ }
+ return macrosMap;
+ }
+
+ /**
+ * @param baseRankProfile the rank-profile node to find its parent
+ * @return the rank-profile that the baseRankProfile inherits from, or null if it doesn't exist
+ */
+ public static PsiElement getRankProfileParent(SdRankProfileDefinition baseRankProfile) {
+ if (baseRankProfile == null) {
+ return null;
+ }
+ ASTNode inheritsNode = baseRankProfile.getNode().findChildByType(SdTypes.INHERITS);
+ if (inheritsNode == null) {
+ return null;
+ }
+ ASTNode ancestorAST = baseRankProfile.getNode().findChildByType(SdTypes.IDENTIFIER_VAL, inheritsNode);
+ if (ancestorAST == null) {
+ ancestorAST = baseRankProfile.getNode().findChildByType(SdTypes.IDENTIFIER_WITH_DASH_VAL, inheritsNode);
+ if (ancestorAST == null) {
+ return null;
+ }
+ }
+ SdIdentifier ancestorIdentifier = (SdIdentifier) ancestorAST.getPsi();
+ PsiReference ref = ancestorIdentifier.getReference();
+ return ref != null ? ref.resolve() : null;
+ }
+
+ public static String createFunctionDescription(SdFunctionDefinition macro) {
+ SdRankProfileDefinition rankProfile = PsiTreeUtil.getParentOfType(macro, SdRankProfileDefinition.class);
+ String rankProfileName;
+ if (rankProfile != null) {
+ rankProfileName = rankProfile.getName();
+ List<SdArgumentDefinition> args = macro.getArgumentDefinitionList();
+ StringBuilder text = new StringBuilder(rankProfileName + "." + macro.getName() + "(");
+ for (int i = 0; i < args.size(); i++) {
+ text.append(args.get(i).getName());
+ if (i < args.size() - 1) {
+ text.append(", ");
+ }
+ }
+ text.append(")");
+ return text.toString();
+ } else {
+ return macro.getName();
+ }
+ }
+
+ public static List<SdDeclaration> findDeclarationsByName(PsiElement file, String name) {
+ List<SdDeclaration> result = new ArrayList<>();
+
+ for (SdDeclaration declaration : PsiTreeUtil.collectElementsOfType(file, SdDeclaration.class)) {
+ if (name.equals(declaration.getName())) {
+ result.add(declaration);
+ }
+ }
+ return result;
+ }
+
+
+ public static List<SdDeclaration> findDeclarationsByScope(PsiElement file, PsiElement element, String name) {
+ List<SdDeclaration> result = new ArrayList<>();
+
+ // If element is a field declared in another file (to be imported), return the declaration from the other file
+ // if found, else return an empty result list
+ if (element.getParent() instanceof SdImportFieldDefinition &&
+ element.getNextSibling().getNextSibling().getText().equals("as")) {
+ Project project = file.getProject();
+
+ PsiReference docFieldRef = element.getPrevSibling().getPrevSibling().getReference();
+ PsiElement docField = docFieldRef != null ? docFieldRef.resolve() : null;
+ SdFieldTypeName fieldType = docField != null ? PsiTreeUtil.findChildOfType(docField, SdFieldTypeName.class) : null;
+ SdIdentifier docIdentifier = fieldType != null ? PsiTreeUtil.findChildOfType(fieldType, SdIdentifier.class) : null;
+ String docName = docIdentifier != null ? ((PsiNamedElement) docIdentifier).getName() : null;
+ if (docName == null) {
+ return result;
+ }
+
+ Collection<VirtualFile> virtualFiles = FileTypeIndex.getFiles(SdFileType.INSTANCE, GlobalSearchScope.allScope(project));
+
+ for (VirtualFile vfile : virtualFiles) {
+ SdFile sdFile = (SdFile) PsiManager.getInstance(project).findFile(vfile);
+ if (sdFile != null && !sdFile.getName().equals(docName + ".sd")) {
+ continue;
+ }
+ result.addAll(SdUtil.findDeclarationsByName(sdFile, name));
+ }
+ return result;
+ }
+
+ // If element is the macro's name in the macro definition, return the macro definition
+ if (element.getParent() instanceof SdFunctionDefinition) {
+ result.add((SdDeclaration) element.getParent());
+ return result;
+ }
+
+ // Check if element is inside a macro body
+ SdFunctionDefinition macroParent = PsiTreeUtil.getParentOfType(element, SdFunctionDefinition.class);
+ if (macroParent != null) {
+ for (SdArgumentDefinition arg : PsiTreeUtil.findChildrenOfType(macroParent, SdArgumentDefinition.class)) {
+ if (name.equals(arg.getName())) { // if the element was declared as an argument of the macro
+ result.add(arg);
+ return result;
+ }
+ }
+ }
+
+ // If element is a macro's name, return the most specific declaration of the macro
+ if (((SdIdentifier) element).isFunctionName(file, name)) {
+ PsiElement curRankProfile = PsiTreeUtil.getParentOfType(element, SdRankProfileDefinition.class);
+ while (curRankProfile != null) {
+ for (SdFunctionDefinition macro : PsiTreeUtil.collectElementsOfType(curRankProfile, SdFunctionDefinition.class)) {
+ if (macro.getName() != null && macro.getName().equals(name)) {
+ result.add(macro);
+ return result;
+ }
+ }
+ curRankProfile = getRankProfileParent((SdRankProfileDefinition) curRankProfile);
+ }
+ }
+
+ for (PsiElement declaration : PsiTreeUtil.collectElements(file, psiElement ->
+ psiElement instanceof SdDeclaration && !(psiElement instanceof SdArgumentDefinition))) {
+ if (name.equals(((SdDeclaration) declaration).getName())) {
+ result.add((SdDeclaration) declaration);
+ break;
+ }
+ }
+
+ return result;
+ }
+
+ public static List<SdDeclaration> findDeclarations(PsiElement element) {
+ return new ArrayList<>(PsiTreeUtil.collectElementsOfType(element, SdDeclaration.class));
+ }
+
+ public static List<PsiElement> findSchemaChildren(PsiElement element) {
+ return new ArrayList<>(PsiTreeUtil.collectElementsOfType(element, new Class[]{SdDocumentDefinition.class,
+ SdSchemaFieldDefinition.class,
+ SdImportFieldDefinition.class,
+ SdSchemaAnnotationDefinition.class,
+ SdDocumentSummaryDefinition.class,
+ SdRankProfileDefinition.class}));
+ }
+
+ public static List<PsiElement> findAnnotationChildren(PsiElement element) {
+ return new ArrayList<>(PsiTreeUtil.collectElementsOfType(element, SdAnnotationFieldDefinition.class));
+ }
+
+ public static List<PsiElement> findDocumentChildren(PsiElement element) {
+ return new ArrayList<>(PsiTreeUtil.collectElementsOfType(element, new Class[]{SdDocumentStructDefinition.class,
+ SdDocumentAnnotationDefinition.class,
+ SdDocumentFieldDefinition.class}));
+ }
+
+ public static List<PsiElement> findDocumentStructChildren(PsiElement element) {
+ return new ArrayList<>(PsiTreeUtil.collectElementsOfType(element, SdDocumentStructFieldDefinition.class));
+ }
+
+ public static List<PsiElement> findRankProfileChildren(PsiElement element) {
+ return new ArrayList<>(PsiTreeUtil.collectElementsOfType(element, SdFunctionDefinition.class));
+ }
+
+ public static List<PsiElement> findDocumentSummaryChildren(PsiElement element) {
+ return new ArrayList<>(PsiTreeUtil.collectElementsOfType(element, SdSummaryDefinition.class));
+ }
+
+}
diff --git a/integration/intellij/src/main/java/ai/vespa/intellij/schema/findUsages/SdDocumentSummaryGroupingRule.java b/integration/intellij/src/main/java/ai/vespa/intellij/schema/findUsages/SdDocumentSummaryGroupingRule.java
new file mode 100644
index 00000000000..ab463562355
--- /dev/null
+++ b/integration/intellij/src/main/java/ai/vespa/intellij/schema/findUsages/SdDocumentSummaryGroupingRule.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.findUsages;
+
+import com.intellij.openapi.project.DumbAware;
+import com.intellij.psi.PsiElement;
+import com.intellij.usages.Usage;
+import com.intellij.usages.UsageGroup;
+import com.intellij.usages.UsageTarget;
+import com.intellij.usages.rules.PsiElementUsage;
+import com.intellij.usages.rules.SingleParentUsageGroupingRule;
+import ai.vespa.intellij.schema.SdLanguage;
+import ai.vespa.intellij.schema.psi.SdDocumentSummaryDefinition;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * A Document Summary that groups elements in the "Find Usages" window.
+ *
+ * @author Shahar Ariel
+ */
+public class SdDocumentSummaryGroupingRule extends SingleParentUsageGroupingRule implements DumbAware {
+
+ @Override
+ protected @Nullable UsageGroup getParentGroupFor(@NotNull Usage usage, UsageTarget @NotNull [] targets) {
+ PsiElement psiElement = usage instanceof PsiElementUsage ? ((PsiElementUsage)usage).getElement() : null;
+ if (psiElement == null || psiElement.getLanguage() != SdLanguage.INSTANCE) return null;
+
+ while (psiElement != null) {
+ if (psiElement instanceof SdDocumentSummaryDefinition) {
+ final SdDocumentSummaryDefinition componentElement = (SdDocumentSummaryDefinition) psiElement;
+ return new SdUsageGroup(componentElement);
+ }
+ psiElement = psiElement.getParent();
+ }
+
+ return null;
+ }
+}
diff --git a/integration/intellij/src/main/java/ai/vespa/intellij/schema/findUsages/SdDocumentSummaryGroupingRuleProvider.java b/integration/intellij/src/main/java/ai/vespa/intellij/schema/findUsages/SdDocumentSummaryGroupingRuleProvider.java
new file mode 100644
index 00000000000..76b82d7c857
--- /dev/null
+++ b/integration/intellij/src/main/java/ai/vespa/intellij/schema/findUsages/SdDocumentSummaryGroupingRuleProvider.java
@@ -0,0 +1,21 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.intellij.schema.findUsages;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.usages.impl.FileStructureGroupRuleProvider;
+import com.intellij.usages.rules.UsageGroupingRule;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * This class is used for the extension (in plugin.xml) to the class SdDocumentSummaryGroupingRule.
+ *
+ * @author Shahar Ariel
+ */
+public class SdDocumentSummaryGroupingRuleProvider implements FileStructureGroupRuleProvider {
+
+ @Override
+ public @Nullable UsageGroupingRule getUsageGroupingRule(@NotNull Project project) {
+ return new SdDocumentSummaryGroupingRule();
+ }
+}
diff --git a/integration/intellij/src/main/java/ai/vespa/intellij/schema/findUsages/SdFindUsagesHandler.java b/integration/intellij/src/main/java/ai/vespa/intellij/schema/findUsages/SdFindUsagesHandler.java
new file mode 100644
index 00000000000..4609f2e7fbb
--- /dev/null
+++ b/integration/intellij/src/main/java/ai/vespa/intellij/schema/findUsages/SdFindUsagesHandler.java
@@ -0,0 +1,81 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.intellij.schema.findUsages;
+
+import com.intellij.find.findUsages.FindUsagesHandler;
+import com.intellij.find.findUsages.FindUsagesOptions;
+import com.intellij.openapi.application.ReadAction;
+import com.intellij.openapi.application.ReadActionProcessor;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.PsiReference;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.psi.search.SearchScope;
+import com.intellij.psi.search.searches.ReferencesSearch;
+import com.intellij.usageView.UsageInfo;
+import com.intellij.util.Processor;
+import ai.vespa.intellij.schema.SdUtil;
+import ai.vespa.intellij.schema.psi.SdFile;
+import ai.vespa.intellij.schema.psi.SdFunctionDefinition;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * This class handles creating the "Find Usages" window.
+ *
+ * @author Shahar Ariel
+ */
+public class SdFindUsagesHandler extends FindUsagesHandler {
+
+ protected HashMap<String, List<PsiElement>> macrosMap;
+
+ protected SdFindUsagesHandler(@NotNull PsiElement psiElement) {
+ super(psiElement);
+ PsiFile file = psiElement.getContainingFile();
+ macrosMap = file instanceof SdFile ? SdUtil.createMacrosMap((SdFile) psiElement.getContainingFile()) : new HashMap<>();
+ }
+
+ @Override
+ public boolean processElementUsages(@NotNull final PsiElement elementToSearch,
+ @NotNull final Processor<? super UsageInfo> processor,
+ @NotNull final FindUsagesOptions options) {
+ final ReadActionProcessor<PsiReference> refProcessor = new ReadActionProcessor<>() {
+ @Override
+ public boolean processInReadAction(final PsiReference ref) {
+ return processor.process(new UsageInfo(ref));
+ }
+ };
+
+ final SearchScope scope = options.searchScope;
+
+ final boolean searchText = options.isSearchForTextOccurrences && scope instanceof GlobalSearchScope;
+
+ if (options.isUsages) {
+ if (!(elementToSearch instanceof SdFunctionDefinition)) {
+ boolean success =
+ ReferencesSearch.search(createSearchParameters(elementToSearch, scope, options)).forEach(refProcessor);
+ if (!success) return false;
+ } else {
+ String macroName = ReadAction.compute( ((SdFunctionDefinition) elementToSearch)::getName);
+
+ for (PsiElement macroImpl : macrosMap.get(macroName)) {
+ boolean success =
+ ReferencesSearch.search(createSearchParameters(macroImpl, scope, options)).forEach(refProcessor);
+ if (!success) return false;
+ }
+ }
+ }
+ if (searchText) {
+ if (options.fastTrack != null) {
+ options.fastTrack.searchCustom(consumer -> processUsagesInText(elementToSearch, processor, (GlobalSearchScope)scope));
+ }
+ else {
+ return processUsagesInText(elementToSearch, processor, (GlobalSearchScope)scope);
+ }
+ }
+
+ return true;
+ }
+
+}
diff --git a/integration/intellij/src/main/java/ai/vespa/intellij/schema/findUsages/SdFindUsagesHandlerFactory.java b/integration/intellij/src/main/java/ai/vespa/intellij/schema/findUsages/SdFindUsagesHandlerFactory.java
new file mode 100644
index 00000000000..98be15778e7
--- /dev/null
+++ b/integration/intellij/src/main/java/ai/vespa/intellij/schema/findUsages/SdFindUsagesHandlerFactory.java
@@ -0,0 +1,28 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.intellij.schema.findUsages;
+
+import com.intellij.find.findUsages.FindUsagesHandler;
+import com.intellij.find.findUsages.FindUsagesHandlerFactory;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiNamedElement;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * This class is used for the extension (in plugin.xml) to the class SdFindUsagesHandler.
+ *
+ * @author Shahar Ariel
+ */
+public class SdFindUsagesHandlerFactory extends FindUsagesHandlerFactory {
+
+ @Override
+ public boolean canFindUsages(@NotNull PsiElement element) {
+ return element instanceof PsiNamedElement;
+ }
+
+ @Override
+ public @Nullable FindUsagesHandler createFindUsagesHandler(@NotNull PsiElement element,
+ boolean forHighlightUsages) {
+ return new SdFindUsagesHandler(element);
+ }
+}
diff --git a/integration/intellij/src/main/java/ai/vespa/intellij/schema/findUsages/SdFindUsagesProvider.java b/integration/intellij/src/main/java/ai/vespa/intellij/schema/findUsages/SdFindUsagesProvider.java
new file mode 100644
index 00000000000..a86fb2b058b
--- /dev/null
+++ b/integration/intellij/src/main/java/ai/vespa/intellij/schema/findUsages/SdFindUsagesProvider.java
@@ -0,0 +1,77 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.intellij.schema.findUsages;
+
+import com.intellij.lang.cacheBuilder.DefaultWordsScanner;
+import com.intellij.lang.cacheBuilder.WordsScanner;
+import com.intellij.lang.findUsages.FindUsagesProvider;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiNamedElement;
+import com.intellij.psi.tree.TokenSet;
+import ai.vespa.intellij.schema.lexer.SdLexerAdapter;
+import ai.vespa.intellij.schema.psi.SdDeclaration;
+import ai.vespa.intellij.schema.psi.SdIdentifierVal;
+import ai.vespa.intellij.schema.psi.SdIdentifierWithDashVal;
+import ai.vespa.intellij.schema.psi.SdTypes;
+import ai.vespa.intellij.schema.psi.impl.SdNamedElementImpl;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * This class is used for the extension (in plugin.xml), to enable "find Usages" window using the plugin code.
+ *
+ * @author Shahar Ariel
+ */
+public class SdFindUsagesProvider implements FindUsagesProvider {
+
+ @Nullable
+ @Override
+ public WordsScanner getWordsScanner() {
+ return new DefaultWordsScanner(new SdLexerAdapter(),
+ TokenSet.create(SdTypes.ID_REG, SdTypes.ID_WITH_DASH_REG, SdTypes.IDENTIFIER_VAL,
+ SdTypes.IDENTIFIER_WITH_DASH_VAL),
+ TokenSet.create(SdTypes.COMMENT),
+ TokenSet.create(SdTypes.STRING_REG, SdTypes.INTEGER_REG, SdTypes.FLOAT_REG));
+ }
+
+ @Override
+ public boolean canFindUsagesFor(@NotNull PsiElement psiElement) {
+ return psiElement instanceof PsiNamedElement;
+ }
+
+ @Nullable
+ @Override
+ public String getHelpId(@NotNull PsiElement psiElement) {
+ return null;
+ }
+
+ @NotNull
+ @Override
+ public String getType(@NotNull PsiElement element) {
+ if (element instanceof SdDeclaration) {
+ return ((SdNamedElementImpl) element).getTypeName();
+ } else {
+ return "";
+ }
+ }
+
+ @NotNull
+ @Override
+ public String getDescriptiveName(@NotNull PsiElement element) {
+ return "";
+ }
+
+ @NotNull
+ @Override
+ public String getNodeText(@NotNull PsiElement element, boolean useFullName) {
+ if (element instanceof SdIdentifierVal || element instanceof SdIdentifierWithDashVal) {
+ String name = ((PsiNamedElement) element).getName();
+ return name != null ? name : "";
+ } else if (element instanceof SdDeclaration) {
+ String fullText = element.getNode().getText();
+ return fullText.substring(0, fullText.indexOf('{'));
+ } else {
+ return "";
+ }
+ }
+
+}
diff --git a/integration/intellij/src/main/java/ai/vespa/intellij/schema/findUsages/SdRankProfileGroupingRule.java b/integration/intellij/src/main/java/ai/vespa/intellij/schema/findUsages/SdRankProfileGroupingRule.java
new file mode 100644
index 00000000000..fd3073b6016
--- /dev/null
+++ b/integration/intellij/src/main/java/ai/vespa/intellij/schema/findUsages/SdRankProfileGroupingRule.java
@@ -0,0 +1,39 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.intellij.schema.findUsages;
+
+import com.intellij.openapi.project.DumbAware;
+import com.intellij.psi.PsiElement;
+import com.intellij.usages.Usage;
+import com.intellij.usages.UsageGroup;
+import com.intellij.usages.UsageTarget;
+import com.intellij.usages.rules.PsiElementUsage;
+import com.intellij.usages.rules.SingleParentUsageGroupingRule;
+import ai.vespa.intellij.schema.SdLanguage;
+import ai.vespa.intellij.schema.psi.SdRankProfileDefinition;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * A Rank Profile that groups elements in the "Find Usages" window.
+ *
+ * @author Shahar Ariel
+ */
+public class SdRankProfileGroupingRule extends SingleParentUsageGroupingRule implements DumbAware {
+
+ @Override
+ protected @Nullable UsageGroup getParentGroupFor(@NotNull Usage usage, UsageTarget @NotNull [] targets) {
+ PsiElement psiElement = usage instanceof PsiElementUsage ? ((PsiElementUsage)usage).getElement() : null;
+ if (psiElement == null || psiElement.getLanguage() != SdLanguage.INSTANCE) return null;
+
+ while (psiElement != null) {
+ if (psiElement instanceof SdRankProfileDefinition) {
+ final SdRankProfileDefinition componentElement = (SdRankProfileDefinition) psiElement;
+ return new SdUsageGroup(componentElement);
+ }
+ psiElement = psiElement.getParent();
+ }
+
+ return null;
+ }
+
+}
diff --git a/integration/intellij/src/main/java/ai/vespa/intellij/schema/findUsages/SdRankProfileGroupingRuleProvider.java b/integration/intellij/src/main/java/ai/vespa/intellij/schema/findUsages/SdRankProfileGroupingRuleProvider.java
new file mode 100644
index 00000000000..0726e103943
--- /dev/null
+++ b/integration/intellij/src/main/java/ai/vespa/intellij/schema/findUsages/SdRankProfileGroupingRuleProvider.java
@@ -0,0 +1,22 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.intellij.schema.findUsages;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.usages.impl.FileStructureGroupRuleProvider;
+import com.intellij.usages.rules.UsageGroupingRule;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * This class is used for the extension (in plugin.xml) to the class SdRankProfileGroupingRule.
+ *
+ * @author Shahar Ariel
+ */
+public class SdRankProfileGroupingRuleProvider implements FileStructureGroupRuleProvider {
+
+ @Override
+ public @Nullable UsageGroupingRule getUsageGroupingRule(@NotNull Project project) {
+ return new SdRankProfileGroupingRule();
+ }
+
+}
diff --git a/integration/intellij/src/main/java/ai/vespa/intellij/schema/findUsages/SdUsageGroup.java b/integration/intellij/src/main/java/ai/vespa/intellij/schema/findUsages/SdUsageGroup.java
new file mode 100644
index 00000000000..6f24ce27476
--- /dev/null
+++ b/integration/intellij/src/main/java/ai/vespa/intellij/schema/findUsages/SdUsageGroup.java
@@ -0,0 +1,113 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.intellij.schema.findUsages;
+
+import com.intellij.navigation.ItemPresentation;
+import com.intellij.navigation.NavigationItemFileStatus;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vcs.FileStatus;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.SmartPointerManager;
+import com.intellij.psi.SmartPsiElementPointer;
+import com.intellij.usages.UsageGroup;
+import com.intellij.usages.UsageView;
+import ai.vespa.intellij.schema.psi.SdDeclaration;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+
+/**
+ * A group of elements in the "Find Usages" window.
+ *
+ * @author Shahar Ariel
+ */
+public class SdUsageGroup implements UsageGroup {
+
+ private final VirtualFile myFile;
+ private final SmartPsiElementPointer<SdDeclaration> myElementPointer;
+ private final String myText;
+ private final Icon myIcon;
+
+ public SdUsageGroup(SdDeclaration element) {
+ myFile = element.getContainingFile().getVirtualFile();
+ myText = StringUtil.notNullize(element.getName());
+ myElementPointer = SmartPointerManager.getInstance(element.getProject()).createSmartPsiElementPointer(element);
+ ItemPresentation presentation = element.getPresentation();
+ myIcon = presentation != null ? presentation.getIcon(true) : null;
+ }
+
+ @Override
+ public boolean isValid() {
+ SdDeclaration element = myElementPointer.getElement();
+ return element != null && element.isValid();
+ }
+
+ @Override
+ public void navigate(boolean requestFocus) {
+ final SdDeclaration nameElement = myElementPointer.getElement();
+ if (nameElement != null) {
+ nameElement.navigate(requestFocus);
+ }
+ }
+
+ @Override
+ public boolean canNavigate() {
+ return isValid();
+ }
+
+ @Override
+ public boolean canNavigateToSource() {
+ return canNavigate();
+ }
+
+ @Override
+ public int compareTo(@NotNull UsageGroup usageGroup) {
+// return getPresentableGroupText().compareToIgnoreCase(usageGroup.getPresentableGroupText());
+ return getText(null).compareTo(usageGroup.getText(null));
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ if (object instanceof SdUsageGroup) {
+ final SdUsageGroup other = (SdUsageGroup) object;
+ return myFile.equals(other.myFile) && myText.equals(other.myText);
+ }
+ return false;
+ }
+
+ @Override
+ public FileStatus getFileStatus() {
+ return isValid() ? NavigationItemFileStatus.get(myElementPointer.getElement()) : null;
+ }
+
+ @Override
+ public int hashCode() {
+ return myText.hashCode();
+ }
+
+ @Override
+ public @NotNull String getPresentableGroupText() {
+ return myText;
+ }
+
+ @Override
+ public @Nullable Icon getIcon() {
+ return myIcon;
+ }
+
+ // here because JetBrains asked:
+
+ @SuppressWarnings("deprecation")
+ public Icon getIcon(boolean isOpen) {
+ return myIcon;
+ }
+
+ @SuppressWarnings("deprecation")
+ public String getText(UsageView view) {
+ return myText;
+ }
+
+ @Override
+ public void update() {}
+
+}
diff --git a/integration/intellij/src/main/java/ai/vespa/intellij/schema/hierarchy/SdCallHierarchyBrowser.java b/integration/intellij/src/main/java/ai/vespa/intellij/schema/hierarchy/SdCallHierarchyBrowser.java
new file mode 100644
index 00000000000..38817e15553
--- /dev/null
+++ b/integration/intellij/src/main/java/ai/vespa/intellij/schema/hierarchy/SdCallHierarchyBrowser.java
@@ -0,0 +1,78 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.intellij.schema.hierarchy;
+
+import com.intellij.ide.hierarchy.CallHierarchyBrowserBase;
+import com.intellij.ide.hierarchy.HierarchyNodeDescriptor;
+import com.intellij.ide.hierarchy.HierarchyTreeStructure;
+import com.intellij.ide.util.treeView.NodeDescriptor;
+import com.intellij.openapi.actionSystem.ActionGroup;
+import com.intellij.openapi.actionSystem.ActionManager;
+import com.intellij.openapi.actionSystem.IdeActions;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.PsiElement;
+import com.intellij.util.ObjectUtils;
+import ai.vespa.intellij.schema.psi.SdFunctionDefinition;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Comparator;
+import java.util.Map;
+
+import javax.swing.JTree;
+
+/**
+ * A browser for the "Call Hierarchy" window.
+ *
+ * @author Shahar Ariel
+ */
+public class SdCallHierarchyBrowser extends CallHierarchyBrowserBase {
+
+ public SdCallHierarchyBrowser(@NotNull Project project,
+ @NotNull PsiElement macro) {
+ super(project, macro);
+ }
+
+ @Nullable
+ @Override
+ protected PsiElement getElementFromDescriptor(@NotNull HierarchyNodeDescriptor descriptor) {
+ return ObjectUtils.tryCast(descriptor.getPsiElement(), PsiElement.class);
+ }
+
+ @Override
+ protected void createTrees(@NotNull Map<? super String, ? super JTree> type2TreeMap) {
+ ActionGroup group = (ActionGroup) ActionManager.getInstance().getAction(IdeActions.GROUP_CALL_HIERARCHY_POPUP);
+ type2TreeMap.put(getCallerType(), createHierarchyTree(group));
+ type2TreeMap.put(getCalleeType(), createHierarchyTree(group));
+ }
+
+ private @NotNull JTree createHierarchyTree(ActionGroup group) {
+ return createTree(false);
+ }
+
+ @Override
+ protected boolean isApplicableElement(@NotNull PsiElement element) {
+ return element instanceof SdFunctionDefinition;
+ }
+
+
+ @Nullable
+ @Override
+ protected HierarchyTreeStructure createHierarchyTreeStructure(@NotNull String typeName, @NotNull PsiElement psiElement) {
+ if (getCallerType().equals(typeName)) {
+ return new SdCallerTreeStructure(myProject, psiElement, getCurrentScopeType());
+ }
+ else if (getCalleeType().equals(typeName)) {
+ return new SdCalleeTreeStructure(myProject, psiElement, getCurrentScopeType());
+ }
+ else {
+ return null;
+ }
+ }
+
+ @Nullable
+ @Override
+ protected Comparator<NodeDescriptor<?>> getComparator() {
+ return SdHierarchyUtil.getComparator(myProject);
+ }
+
+}
diff --git a/integration/intellij/src/main/java/ai/vespa/intellij/schema/hierarchy/SdCallHierarchyNodeDescriptor.java b/integration/intellij/src/main/java/ai/vespa/intellij/schema/hierarchy/SdCallHierarchyNodeDescriptor.java
new file mode 100644
index 00000000000..f99a82dc81a
--- /dev/null
+++ b/integration/intellij/src/main/java/ai/vespa/intellij/schema/hierarchy/SdCallHierarchyNodeDescriptor.java
@@ -0,0 +1,75 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.intellij.schema.hierarchy;
+
+import com.intellij.ide.hierarchy.HierarchyNodeDescriptor;
+import com.intellij.ide.util.treeView.NodeDescriptor;
+import com.intellij.navigation.ItemPresentation;
+import com.intellij.openapi.roots.ui.util.CompositeAppearance;
+import com.intellij.openapi.util.Comparing;
+import com.intellij.psi.NavigatablePsiElement;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+
+import ai.vespa.intellij.schema.SdIcons;
+import ai.vespa.intellij.schema.SdUtil;
+import ai.vespa.intellij.schema.psi.SdFirstPhaseDefinition;
+import ai.vespa.intellij.schema.psi.impl.SdFirstPhaseDefinitionMixin;
+import ai.vespa.intellij.schema.psi.SdFunctionDefinition;
+
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.Icon;
+
+/**
+ * A node descriptor to a node in a tree in the "Call Hierarchy" window.
+ *
+ * @author Shahar Ariel
+ */
+public class SdCallHierarchyNodeDescriptor extends HierarchyNodeDescriptor {
+
+ public SdCallHierarchyNodeDescriptor(final NodeDescriptor parentDescriptor, @NotNull final PsiElement element, final boolean isBase) {
+ super(element.getProject(), parentDescriptor, element, isBase);
+ CompositeAppearance.DequeEnd beginning = myHighlightedText.getBeginning();
+ if (element instanceof SdFunctionDefinition) {
+ beginning.addText(SdUtil.createFunctionDescription((SdFunctionDefinition) element));
+ } else if (element instanceof SdFirstPhaseDefinition) {
+ beginning.addText(((SdFirstPhaseDefinitionMixin) element).getName());
+ } else {
+ beginning.addText(element.getText());
+ }
+ }
+
+ @Override
+ public boolean update() {
+ boolean changes = super.update();
+ final CompositeAppearance oldText = myHighlightedText;
+ myHighlightedText = new CompositeAppearance();
+ NavigatablePsiElement element = (NavigatablePsiElement)getPsiElement();
+ if (element == null) {
+ return invalidElement();
+ }
+
+ final ItemPresentation presentation = element.getPresentation();
+ if (presentation != null) {
+ myHighlightedText.getEnding().addText(presentation.getPresentableText());
+ PsiFile file = element.getContainingFile();
+ if (file != null) { // adds the file's name
+ myHighlightedText.getEnding().addText(" (" + file.getName() + ")", HierarchyNodeDescriptor.getPackageNameAttributes());
+ }
+ Icon icon = SdIcons.FILE;
+ if (element instanceof SdFunctionDefinition) {
+ icon = ((SdFunctionDefinition) element).isOverride() ? SdIcons.OVERRIDE_MACRO : SdIcons.MACRO;
+ } else if (element instanceof SdFirstPhaseDefinition) {
+ icon = SdIcons.FIRST_PHASE;
+ }
+ installIcon(icon, changes);
+ }
+
+ myName = myHighlightedText.getText();
+ if (!Comparing.equal(myHighlightedText, oldText)) {
+ changes = true;
+ }
+ return changes;
+ }
+
+}
diff --git a/integration/intellij/src/main/java/ai/vespa/intellij/schema/hierarchy/SdCallHierarchyProvider.java b/integration/intellij/src/main/java/ai/vespa/intellij/schema/hierarchy/SdCallHierarchyProvider.java
new file mode 100644
index 00000000000..f7f243356b9
--- /dev/null
+++ b/integration/intellij/src/main/java/ai/vespa/intellij/schema/hierarchy/SdCallHierarchyProvider.java
@@ -0,0 +1,65 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.intellij.schema.hierarchy;
+
+import com.intellij.ide.hierarchy.CallHierarchyBrowserBase;
+import com.intellij.ide.hierarchy.HierarchyBrowser;
+import com.intellij.ide.hierarchy.HierarchyProvider;
+import com.intellij.openapi.actionSystem.DataContext;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.psi.PsiDocumentManager;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.PsiReference;
+import ai.vespa.intellij.schema.psi.SdFunctionDefinition;
+import ai.vespa.intellij.schema.psi.SdIdentifierVal;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import com.intellij.openapi.actionSystem.CommonDataKeys;
+import com.intellij.openapi.project.Project;
+
+/**
+ * This class is used for the extension (in plugin.xml), to enable "Call Hierarchy" window using the plugin code.
+ *
+ * @author Shahar Ariel
+ */
+public class SdCallHierarchyProvider implements HierarchyProvider {
+
+ @Override
+ public @Nullable PsiElement getTarget(@NotNull DataContext dataContext) {
+ final Project project = CommonDataKeys.PROJECT.getData(dataContext);
+ final Editor editor = CommonDataKeys.EDITOR.getData(dataContext);
+ if (project == null || editor == null) return null;
+
+ final PsiFile file = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument());
+ if (file == null) {
+ return null;
+ }
+ final PsiElement element = file.findElementAt(editor.getCaretModel().getOffset());
+ if (element == null) {
+ return null;
+ }
+
+ if (element instanceof SdIdentifierVal || element.getParent() instanceof SdIdentifierVal) {
+ PsiReference ref = element instanceof SdIdentifierVal ? element.getReference() : element.getParent().getReference();
+ if (ref == null) {
+ return null;
+ }
+ PsiElement resolvedRef = ref.resolve();
+ if (resolvedRef instanceof SdFunctionDefinition) {
+ return resolvedRef;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public @NotNull HierarchyBrowser createHierarchyBrowser(@NotNull PsiElement target) {
+ return new SdCallHierarchyBrowser(target.getProject(), target);
+ }
+
+ @Override
+ public void browserActivated(@NotNull HierarchyBrowser hierarchyBrowser) {
+ ((SdCallHierarchyBrowser) hierarchyBrowser).changeView(CallHierarchyBrowserBase.getCallerType());
+ }
+
+}
diff --git a/integration/intellij/src/main/java/ai/vespa/intellij/schema/hierarchy/SdCallTreeStructure.java b/integration/intellij/src/main/java/ai/vespa/intellij/schema/hierarchy/SdCallTreeStructure.java
new file mode 100644
index 00000000000..053e7bd20f9
--- /dev/null
+++ b/integration/intellij/src/main/java/ai/vespa/intellij/schema/hierarchy/SdCallTreeStructure.java
@@ -0,0 +1,79 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.intellij.schema.hierarchy;
+
+import com.intellij.ide.hierarchy.HierarchyNodeDescriptor;
+import com.intellij.ide.hierarchy.HierarchyTreeStructure;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.util.PsiTreeUtil;
+import com.intellij.util.ArrayUtil;
+import com.intellij.util.ArrayUtilRt;
+import ai.vespa.intellij.schema.SdUtil;
+import ai.vespa.intellij.schema.psi.SdFile;
+import ai.vespa.intellij.schema.psi.SdFunctionDefinition;
+import ai.vespa.intellij.schema.psi.SdRankProfileDefinition;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+
+/**
+ * A general tree in the "Call Hierarchy" window.
+ *
+ * @author Shahar Ariel
+ */
+public abstract class SdCallTreeStructure extends HierarchyTreeStructure {
+
+ protected final String myScopeType;
+ protected final SdFile myFile;
+ protected HashMap<String, List<PsiElement>> macrosMap;
+ protected HashMap<String, HashSet<SdRankProfileDefinition>> ranksHeritageMap;
+
+ public SdCallTreeStructure(Project project, PsiElement element, String currentScopeType) {
+ super(project, new SdCallHierarchyNodeDescriptor(null, element, true));
+ myScopeType = currentScopeType;
+ myFile = (SdFile) element.getContainingFile();
+ macrosMap = SdUtil.createMacrosMap(myFile);
+ ranksHeritageMap = new HashMap<>();
+ }
+
+ @NotNull
+ protected abstract HashSet<PsiElement> getChildren(@NotNull SdFunctionDefinition element);
+
+ @Override
+ protected Object @NotNull [] buildChildren(@NotNull HierarchyNodeDescriptor descriptor) {
+ final List<SdCallHierarchyNodeDescriptor> descriptors = new ArrayList<>();
+
+ if (descriptor instanceof SdCallHierarchyNodeDescriptor) {
+ PsiElement element = descriptor.getPsiElement();
+ if (element == null) {
+ return ArrayUtilRt.EMPTY_OBJECT_ARRAY;
+ }
+ boolean isCallable = SdHierarchyUtil.isExecutable(element);
+ HierarchyNodeDescriptor nodeDescriptor = getBaseDescriptor();
+ if (!isCallable || nodeDescriptor == null) {
+ return ArrayUtilRt.EMPTY_OBJECT_ARRAY;
+ }
+
+ HashSet<PsiElement> children = getChildren((SdFunctionDefinition) element);
+
+ final HashMap<PsiElement, SdCallHierarchyNodeDescriptor> callerToDescriptorMap = new HashMap<>();
+ PsiElement baseClass = PsiTreeUtil.getParentOfType(element, SdRankProfileDefinition.class);
+
+ for (PsiElement caller : children) {
+ if (isInScope(baseClass, caller, myScopeType)) {
+ SdCallHierarchyNodeDescriptor callerDescriptor = callerToDescriptorMap.get(caller);
+ if (callerDescriptor == null) {
+ callerDescriptor = new SdCallHierarchyNodeDescriptor(descriptor, caller, false);
+ callerToDescriptorMap.put(caller, callerDescriptor);
+ descriptors.add(callerDescriptor);
+ }
+ }
+ }
+ }
+ return ArrayUtil.toObjectArray(descriptors);
+ }
+
+}
diff --git a/integration/intellij/src/main/java/ai/vespa/intellij/schema/hierarchy/SdCalleeTreeStructure.java b/integration/intellij/src/main/java/ai/vespa/intellij/schema/hierarchy/SdCalleeTreeStructure.java
new file mode 100644
index 00000000000..37fd336b615
--- /dev/null
+++ b/integration/intellij/src/main/java/ai/vespa/intellij/schema/hierarchy/SdCalleeTreeStructure.java
@@ -0,0 +1,73 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.intellij.schema.hierarchy;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiNamedElement;
+import com.intellij.psi.PsiReference;
+import com.intellij.psi.util.PsiTreeUtil;
+import ai.vespa.intellij.schema.psi.SdExpressionDefinition;
+import ai.vespa.intellij.schema.psi.SdFunctionDefinition;
+import ai.vespa.intellij.schema.psi.SdIdentifier;
+import ai.vespa.intellij.schema.psi.SdRankProfileDefinition;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+
+/**
+ * A Callee tree in the "Call Hierarchy" window.
+ *
+ * @author Shahar Ariel
+ */
+public class SdCalleeTreeStructure extends SdCallTreeStructure {
+
+ public SdCalleeTreeStructure(Project project, PsiElement element, String currentScopeType) {
+ super(project, element, currentScopeType);
+ }
+
+ @NotNull
+ @Override
+ protected HashSet<PsiElement> getChildren(@NotNull SdFunctionDefinition element) {
+ return getCallees(element, macrosMap);
+ }
+
+ private HashSet<PsiElement> getCallees(@NotNull SdFunctionDefinition macro, HashMap<String, List<PsiElement>> macrosMap) {
+ final HashSet<PsiElement> results = new HashSet<>();
+ SdExpressionDefinition expression = PsiTreeUtil.findChildOfType(macro, SdExpressionDefinition.class);
+ if (expression == null) {
+ return results;
+ }
+ for (SdIdentifier identifier : PsiTreeUtil.collectElementsOfType(expression, SdIdentifier.class)) {
+ if (macrosMap.containsKey(((PsiNamedElement) identifier).getName())) {
+ PsiReference identifierRef = identifier.getReference();
+ if (identifierRef != null) {
+ results.add(identifierRef.resolve());
+ }
+ }
+ }
+
+ SdRankProfileDefinition rankProfile = PsiTreeUtil.getParentOfType(macro, SdRankProfileDefinition.class);
+ if (rankProfile == null) {
+ return results;
+ }
+ String rankProfileName = rankProfile.getName();
+ if (!ranksHeritageMap.containsKey(rankProfileName)) {
+ ranksHeritageMap.put(rankProfileName, SdHierarchyUtil.getRankProfileChildren(myFile, rankProfile));
+ }
+
+ HashSet<SdRankProfileDefinition> inheritedRanks = ranksHeritageMap.get(rankProfileName);
+
+ for (PsiElement macroImpl : macrosMap.get(macro.getName())) {
+ if (inheritedRanks.contains(PsiTreeUtil.getParentOfType(macroImpl, SdRankProfileDefinition.class))) {
+ results.add(macroImpl);
+ }
+
+ }
+
+ return results;
+ }
+
+
+}
diff --git a/integration/intellij/src/main/java/ai/vespa/intellij/schema/hierarchy/SdCallerTreeStructure.java b/integration/intellij/src/main/java/ai/vespa/intellij/schema/hierarchy/SdCallerTreeStructure.java
new file mode 100644
index 00000000000..444e71978c9
--- /dev/null
+++ b/integration/intellij/src/main/java/ai/vespa/intellij/schema/hierarchy/SdCallerTreeStructure.java
@@ -0,0 +1,69 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.intellij.schema.hierarchy;
+
+import com.intellij.openapi.progress.ProgressManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiReference;
+import com.intellij.psi.search.SearchScope;
+import com.intellij.psi.search.searches.ReferencesSearch;
+import com.intellij.psi.util.PsiTreeUtil;
+import com.intellij.util.containers.ContainerUtil;
+import ai.vespa.intellij.schema.psi.SdFirstPhaseDefinition;
+import ai.vespa.intellij.schema.psi.SdFunctionDefinition;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.function.Consumer;
+
+/**
+ * A Caller tree in the "Call Hierarchy" window.
+ *
+ * @author Shahar Ariel
+ */
+public class SdCallerTreeStructure extends SdCallTreeStructure {
+
+ private HashMap<String, HashSet<PsiElement>> macroTreeChildren;
+
+ public SdCallerTreeStructure(Project project, PsiElement element, String currentScopeType) {
+ super(project, element, currentScopeType);
+ macroTreeChildren = new HashMap<>();
+ }
+
+ @NotNull
+ @Override
+ protected HashSet<PsiElement> getChildren(@NotNull SdFunctionDefinition element) {
+ return getCallers(element, macrosMap);
+ }
+
+ private HashSet<PsiElement> getCallers(@NotNull SdFunctionDefinition macro, @NotNull HashMap<String, List<PsiElement>> macrosMap) {
+ String macroName = macro.getName();
+
+ if (macroTreeChildren.containsKey(macroName)) {
+ return macroTreeChildren.get(macroName);
+ }
+
+ HashSet<PsiElement> results = new HashSet<>();
+
+ for (PsiElement macroImpl : macrosMap.get(macroName)) {
+ SearchScope searchScope = getSearchScope(myScopeType, macroImpl);
+ ReferencesSearch.search(macroImpl, searchScope).forEach((Consumer<? super PsiReference>) r -> {
+ ProgressManager.checkCanceled();
+ PsiElement psiElement = r.getElement();
+ SdFunctionDefinition f = PsiTreeUtil.getParentOfType(psiElement, SdFunctionDefinition.class, false);
+ if (f != null && f.getName() != null && !f.getName().equals(macroName)) {
+ ContainerUtil.addIfNotNull(results, f);
+ } else {
+ SdFirstPhaseDefinition fp = PsiTreeUtil.getParentOfType(psiElement, SdFirstPhaseDefinition.class, false);
+ ContainerUtil.addIfNotNull(results, fp);
+ }
+ });
+ }
+
+ macroTreeChildren.put(macroName, results);
+ return results;
+ }
+
+}
diff --git a/integration/intellij/src/main/java/ai/vespa/intellij/schema/hierarchy/SdHierarchyUtil.java b/integration/intellij/src/main/java/ai/vespa/intellij/schema/hierarchy/SdHierarchyUtil.java
new file mode 100644
index 00000000000..dc925f0f369
--- /dev/null
+++ b/integration/intellij/src/main/java/ai/vespa/intellij/schema/hierarchy/SdHierarchyUtil.java
@@ -0,0 +1,73 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.intellij.schema.hierarchy;
+
+import com.intellij.ide.hierarchy.HierarchyBrowserManager;
+import com.intellij.ide.util.treeView.AlphaComparator;
+import com.intellij.ide.util.treeView.NodeDescriptor;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.*;
+import com.intellij.psi.util.PsiTreeUtil;
+
+import ai.vespa.intellij.schema.SdUtil;
+import ai.vespa.intellij.schema.psi.SdFile;
+import ai.vespa.intellij.schema.psi.SdFunctionDefinition;
+import ai.vespa.intellij.schema.psi.SdRankProfileDefinition;
+
+import java.util.Comparator;
+import java.util.HashSet;
+
+/**
+ * Call Hierarchy feature utilities.
+ *
+ * @author Shahar Ariel
+ */
+public class SdHierarchyUtil {
+
+ private static final Comparator<NodeDescriptor<?>> NODE_DESCRIPTOR_COMPARATOR = Comparator.comparingInt(NodeDescriptor::getIndex);
+
+ private SdHierarchyUtil() {
+ }
+
+ public static boolean isExecutable(PsiElement component) {
+ return component instanceof SdFunctionDefinition;
+ }
+
+ public static HashSet<SdRankProfileDefinition> getRankProfileChildren(SdFile file, SdRankProfileDefinition rankProfileTarget) {
+ HashSet<SdRankProfileDefinition> result = new HashSet<>();
+ HashSet<SdRankProfileDefinition> notResult = new HashSet<>();
+
+ for (SdRankProfileDefinition rank : PsiTreeUtil.collectElementsOfType(file, SdRankProfileDefinition.class)) {
+ if (notResult.contains(rank)) {
+ continue;
+ }
+ HashSet<SdRankProfileDefinition> tempRanks = new HashSet<>();
+ SdRankProfileDefinition curRank = rank;
+ while (curRank != null) {
+ String curRankName = curRank.getName();
+ if (curRankName != null && curRankName.equals(rankProfileTarget.getName())) {
+ result.addAll(tempRanks);
+ break;
+ }
+ tempRanks.add(curRank);
+ PsiElement temp = SdUtil.getRankProfileParent(curRank);
+ curRank = temp != null ? (SdRankProfileDefinition) temp : null;
+ }
+ if (curRank == null) {
+ notResult.addAll(tempRanks);
+ }
+ }
+ return result;
+ }
+
+
+ public static Comparator<NodeDescriptor<?>> getComparator(Project project) {
+ final HierarchyBrowserManager.State state = HierarchyBrowserManager.getInstance(project).getState();
+ if (state != null && state.SORT_ALPHABETICALLY) {
+ return AlphaComparator.INSTANCE;
+ }
+ else {
+ return NODE_DESCRIPTOR_COMPARATOR;
+ }
+ }
+
+}
diff --git a/integration/intellij/src/main/java/ai/vespa/intellij/schema/lexer/SdLexerAdapter.java b/integration/intellij/src/main/java/ai/vespa/intellij/schema/lexer/SdLexerAdapter.java
new file mode 100644
index 00000000000..8b474dcff69
--- /dev/null
+++ b/integration/intellij/src/main/java/ai/vespa/intellij/schema/lexer/SdLexerAdapter.java
@@ -0,0 +1,17 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.intellij.schema.lexer;
+
+import com.intellij.lexer.FlexAdapter;
+
+/**
+ * Adapter of the JFlex lexer to the IntelliJ Platform Lexer API.
+ *
+ * @author Shahar Ariel
+ */
+public class SdLexerAdapter extends FlexAdapter {
+
+ public SdLexerAdapter() {
+ super(new SdLexer(null));
+ }
+
+}
diff --git a/integration/intellij/src/main/java/ai/vespa/intellij/schema/parser/SdParserDefinition.java b/integration/intellij/src/main/java/ai/vespa/intellij/schema/parser/SdParserDefinition.java
new file mode 100644
index 00000000000..d5b76c039d0
--- /dev/null
+++ b/integration/intellij/src/main/java/ai/vespa/intellij/schema/parser/SdParserDefinition.java
@@ -0,0 +1,93 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.intellij.schema.parser;
+
+import com.intellij.lang.ASTNode;
+import com.intellij.lang.ParserDefinition;
+import com.intellij.lang.PsiParser;
+import com.intellij.lexer.Lexer;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.FileViewProvider;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.TokenType;
+import com.intellij.psi.tree.IFileElementType;
+import com.intellij.psi.tree.TokenSet;
+import ai.vespa.intellij.schema.SdLanguage;
+import ai.vespa.intellij.schema.lexer.SdLexerAdapter;
+import ai.vespa.intellij.schema.psi.SdFile;
+import ai.vespa.intellij.schema.psi.SdTypes;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * This class is used for the extension (in plugin.xml), to make the parsing process use the plugin code.
+ *
+ * @author Shahar Ariel
+ */
+public class SdParserDefinition implements ParserDefinition {
+
+ public static final TokenSet WHITE_SPACES = TokenSet.create(TokenType.WHITE_SPACE);
+ public static final TokenSet COMMENTS = TokenSet.create(SdTypes.COMMENT);
+ public static final TokenSet STRINGS = TokenSet.create(SdTypes.STRING_REG);
+
+ public static final IFileElementType FILE = new IFileElementType(SdLanguage.INSTANCE);
+
+ @NotNull
+ @Override
+ public Lexer createLexer(Project project) {
+ return new SdLexerAdapter();
+ }
+
+ @NotNull
+ @Override
+ public PsiParser createParser(final Project project) {
+ return new SdParser();
+ }
+
+ @NotNull
+ @Override
+ public TokenSet getWhitespaceTokens() {
+ return WHITE_SPACES;
+ }
+
+ @NotNull
+ @Override
+ public TokenSet getCommentTokens() {
+ return COMMENTS;
+ }
+
+ @NotNull
+ @Override
+ public TokenSet getStringLiteralElements() {
+ return STRINGS;
+ }
+
+ @NotNull
+ @Override
+ public IFileElementType getFileNodeType() {
+ return FILE;
+ }
+
+ @NotNull
+ @Override
+ public PsiFile createFile(@NotNull FileViewProvider viewProvider) {
+ return new SdFile(viewProvider);
+ }
+
+ @NotNull
+ @Override
+ public SpaceRequirements spaceExistenceTypeBetweenTokens(ASTNode left, ASTNode right) {
+ return SpaceRequirements.MAY;
+ }
+
+ @NotNull
+ @Override
+ public PsiElement createElement(ASTNode node) {
+ return SdTypes.Factory.createElement(node);
+ }
+
+}
+
+
+
+
+
diff --git a/integration/intellij/src/main/java/ai/vespa/intellij/schema/psi/SdDeclaration.java b/integration/intellij/src/main/java/ai/vespa/intellij/schema/psi/SdDeclaration.java
new file mode 100644
index 00000000000..c2880181d66
--- /dev/null
+++ b/integration/intellij/src/main/java/ai/vespa/intellij/schema/psi/SdDeclaration.java
@@ -0,0 +1,12 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.intellij.schema.psi;
+
+import com.intellij.navigation.NavigationItem;
+import com.intellij.psi.PsiElement;
+
+/**
+ * A declaration in the SD language.
+ *
+ * @author Shahar Ariel
+ */
+public interface SdDeclaration extends PsiElement, NavigationItem, SdNamedElement {}
diff --git a/integration/intellij/src/main/java/ai/vespa/intellij/schema/psi/SdDeclarationType.java b/integration/intellij/src/main/java/ai/vespa/intellij/schema/psi/SdDeclarationType.java
new file mode 100644
index 00000000000..129a32153f0
--- /dev/null
+++ b/integration/intellij/src/main/java/ai/vespa/intellij/schema/psi/SdDeclarationType.java
@@ -0,0 +1,37 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.intellij.schema.psi;
+
+/**
+ * This Enum describes the different declarations' types and their names.
+ *
+ * @author Shahar Ariel
+ */
+public enum SdDeclarationType {
+
+ DOCUMENT("Document"),
+ STRUCT("Struct"),
+ ANNOTATION("Annotation"),
+ SCHEMA_FIELD("Field (in Schema)"),
+ DOCUMENT_FIELD("Field (in Document)"),
+ STRUCT_FIELD("Struct-Field"),
+ ANNOTATION_FIELD("Field (in Annotation)"),
+ DOCUMENT_STRUCT_FIELD("Field (in Struct)"),
+ IMPORTED_FIELD("Imported Field"),
+ DOCUMENT_SUMMARY("Document-Summary"),
+ RANK_PROFILE("Rank Profile"),
+ MACRO("Macro"),
+ MACRO_ARGUMENT("Macro's Argument"),
+ QUERY("Query (first use in file)"),
+ ITEM_RAW_SCORE("ItemRawScore (first use in file)");
+
+ private final String typeName;
+ SdDeclarationType(String name) {
+ this.typeName = name;
+ }
+
+ @Override
+ public String toString() {
+ return typeName;
+ }
+
+}
diff --git a/integration/intellij/src/main/java/ai/vespa/intellij/schema/psi/SdElementDescriptionProvider.java b/integration/intellij/src/main/java/ai/vespa/intellij/schema/psi/SdElementDescriptionProvider.java
new file mode 100644
index 00000000000..7cd2d7f9331
--- /dev/null
+++ b/integration/intellij/src/main/java/ai/vespa/intellij/schema/psi/SdElementDescriptionProvider.java
@@ -0,0 +1,35 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.intellij.schema.psi;
+
+import com.intellij.psi.ElementDescriptionLocation;
+import com.intellij.psi.ElementDescriptionProvider;
+import com.intellij.psi.PsiElement;
+import ai.vespa.intellij.schema.psi.impl.SdNamedElementImpl;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * This class is used for the extension (in plugin.xml), to enable "find Usages" window take the element description from
+ * here. Used only for the "target" element.
+ *
+ * @author Shahar Ariel
+ */
+public class SdElementDescriptionProvider implements ElementDescriptionProvider {
+
+ /**
+ * Controls the headline of the element in the "Find Usages" window.
+ *
+ * @param psiElement the element to describe
+ * @return a string with the description to write in the headline
+ */
+ @Nullable
+ @Override
+ public String getElementDescription(@NotNull PsiElement psiElement, @NotNull ElementDescriptionLocation elementDescriptionLocation) {
+ if (psiElement instanceof SdDeclaration) {
+ return ((SdNamedElementImpl) psiElement).getTypeName();
+ } else {
+ return "";
+ }
+ }
+
+}
diff --git a/integration/intellij/src/main/java/ai/vespa/intellij/schema/psi/SdElementFactory.java b/integration/intellij/src/main/java/ai/vespa/intellij/schema/psi/SdElementFactory.java
new file mode 100644
index 00000000000..d6cbcece6d5
--- /dev/null
+++ b/integration/intellij/src/main/java/ai/vespa/intellij/schema/psi/SdElementFactory.java
@@ -0,0 +1,37 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.intellij.schema.psi;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.PsiFileFactory;
+
+import com.intellij.psi.util.PsiTreeUtil;
+import ai.vespa.intellij.schema.SdFileType;
+
+/**
+ * This class is a factory of psi elements in the SD PSI tree.
+ *
+ * @author Shahar Ariel
+ */
+public class SdElementFactory {
+
+ private static final String GENERAL_FILE_TEXT = "search {document %s {} rank-profile %s {}}";
+
+ public static SdIdentifierVal createIdentifierVal(Project project, String name) {
+ String fileText = String.format(GENERAL_FILE_TEXT, name, name);
+ final SdFile file = createFile(project, fileText);
+ return PsiTreeUtil.findChildOfType(file, SdIdentifierVal.class);
+ }
+
+ public static SdIdentifierWithDashVal createIdentifierWithDashVal(Project project, String name) {
+ String fileText = String.format(GENERAL_FILE_TEXT, name, name);
+ final SdFile file = createFile(project, fileText);
+ return PsiTreeUtil.findChildOfType(file, SdIdentifierWithDashVal.class);
+ }
+
+ public static SdFile createFile(Project project, String text) {
+ String name = "dummy.sd";
+ return (SdFile) PsiFileFactory.getInstance(project).
+ createFileFromText(name, SdFileType.INSTANCE, text);
+ }
+
+}
diff --git a/integration/intellij/src/main/java/ai/vespa/intellij/schema/psi/SdElementType.java b/integration/intellij/src/main/java/ai/vespa/intellij/schema/psi/SdElementType.java
new file mode 100644
index 00000000000..e8154bce8ad
--- /dev/null
+++ b/integration/intellij/src/main/java/ai/vespa/intellij/schema/psi/SdElementType.java
@@ -0,0 +1,20 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.intellij.schema.psi;
+
+import com.intellij.psi.tree.IElementType;
+import ai.vespa.intellij.schema.SdLanguage;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * An SdElementType.
+ *
+ * @author Shahar Ariel
+ */
+public class SdElementType extends IElementType {
+
+ public SdElementType(@NotNull @NonNls String debugName) {
+ super(debugName, SdLanguage.INSTANCE);
+ }
+
+}
diff --git a/integration/intellij/src/main/java/ai/vespa/intellij/schema/psi/SdFile.java b/integration/intellij/src/main/java/ai/vespa/intellij/schema/psi/SdFile.java
new file mode 100644
index 00000000000..1260883c96f
--- /dev/null
+++ b/integration/intellij/src/main/java/ai/vespa/intellij/schema/psi/SdFile.java
@@ -0,0 +1,33 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.intellij.schema.psi;
+
+import com.intellij.extapi.psi.PsiFileBase;
+import com.intellij.openapi.fileTypes.FileType;
+import com.intellij.psi.FileViewProvider;
+import ai.vespa.intellij.schema.SdFileType;
+import ai.vespa.intellij.schema.SdLanguage;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * An SD file.
+ *
+ * @author Shahar Ariel
+ */
+public class SdFile extends PsiFileBase {
+
+ public SdFile(@NotNull FileViewProvider viewProvider) {
+ super(viewProvider, SdLanguage.INSTANCE);
+ }
+
+ @NotNull
+ @Override
+ public FileType getFileType() {
+ return SdFileType.INSTANCE;
+ }
+
+ @Override
+ public String toString() {
+ return "Sd File";
+ }
+
+}
diff --git a/integration/intellij/src/main/java/ai/vespa/intellij/schema/psi/SdFunctionDefinitionInterface.java b/integration/intellij/src/main/java/ai/vespa/intellij/schema/psi/SdFunctionDefinitionInterface.java
new file mode 100644
index 00000000000..e7fa94cf81c
--- /dev/null
+++ b/integration/intellij/src/main/java/ai/vespa/intellij/schema/psi/SdFunctionDefinitionInterface.java
@@ -0,0 +1,31 @@
+package ai.vespa.intellij.schema.psi;
+
+import com.intellij.psi.util.PsiTreeUtil;
+import ai.vespa.intellij.schema.SdUtil;
+
+/**
+ * A function's declaration in the SD language.
+ *
+ * @author Shahar Ariel
+ */
+public interface SdFunctionDefinitionInterface extends SdDeclaration {
+
+ default boolean isOverride() {
+ String macroName = this.getName();
+
+ SdRankProfileDefinition curRankProfile = PsiTreeUtil.getParentOfType(this, SdRankProfileDefinition.class);
+ if (curRankProfile != null) {
+ curRankProfile = (SdRankProfileDefinition) SdUtil.getRankProfileParent(curRankProfile);
+ }
+ while (curRankProfile != null) {
+ for (SdFunctionDefinition macro : PsiTreeUtil.collectElementsOfType(curRankProfile, SdFunctionDefinition.class)) {
+ if (macro.getName() != null && macro.getName().equals(macroName)) {
+ return true;
+ }
+ }
+ curRankProfile = (SdRankProfileDefinition) SdUtil.getRankProfileParent(curRankProfile);
+ }
+ return false;
+ }
+
+}
diff --git a/integration/intellij/src/main/java/ai/vespa/intellij/schema/psi/SdIdentifier.java b/integration/intellij/src/main/java/ai/vespa/intellij/schema/psi/SdIdentifier.java
new file mode 100644
index 00000000000..1ac88cb3acd
--- /dev/null
+++ b/integration/intellij/src/main/java/ai/vespa/intellij/schema/psi/SdIdentifier.java
@@ -0,0 +1,22 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.intellij.schema.psi;
+
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.util.PsiTreeUtil;
+
+/**
+ * This interface represents an identifier in the SD language (regular identifiers and identifiers with dash).
+ * @author Shahar Ariel
+ */
+public interface SdIdentifier extends PsiElement {
+
+ default boolean isFunctionName(PsiElement file, String name) {
+ for (SdFunctionDefinition macro : PsiTreeUtil.collectElementsOfType(file, SdFunctionDefinition.class)) {
+ if (name.equals(macro.getName())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+}
diff --git a/integration/intellij/src/main/java/ai/vespa/intellij/schema/psi/SdNamedElement.java b/integration/intellij/src/main/java/ai/vespa/intellij/schema/psi/SdNamedElement.java
new file mode 100644
index 00000000000..0cd7cedc967
--- /dev/null
+++ b/integration/intellij/src/main/java/ai/vespa/intellij/schema/psi/SdNamedElement.java
@@ -0,0 +1,14 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.intellij.schema.psi;
+
+import com.intellij.psi.PsiNameIdentifierOwner;
+
+/**
+ * This interface is used to wrap a Psi Element with SdNamedElement interface, which enables the element to be a
+ * "name owner" (like an identifier). It allows the element to take a part in references, find usages and more.
+ *
+ * @author Shahar Ariel
+ */
+public interface SdNamedElement extends PsiNameIdentifierOwner {
+
+}
diff --git a/integration/intellij/src/main/java/ai/vespa/intellij/schema/psi/SdTokenType.java b/integration/intellij/src/main/java/ai/vespa/intellij/schema/psi/SdTokenType.java
new file mode 100644
index 00000000000..e42906530b0
--- /dev/null
+++ b/integration/intellij/src/main/java/ai/vespa/intellij/schema/psi/SdTokenType.java
@@ -0,0 +1,25 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.intellij.schema.psi;
+
+import com.intellij.psi.tree.IElementType;
+import ai.vespa.intellij.schema.SdLanguage;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * An SdTokenType.
+ *
+ * @author Shahar Ariel
+ */
+public class SdTokenType extends IElementType {
+
+ public SdTokenType(@NotNull @NonNls String debugName) {
+ super(debugName, SdLanguage.INSTANCE);
+ }
+
+ @Override
+ public String toString() {
+ return "SdTokenType." + super.toString();
+ }
+
+}
diff --git a/integration/intellij/src/main/java/ai/vespa/intellij/schema/psi/impl/SdFirstPhaseDefinitionMixin.java b/integration/intellij/src/main/java/ai/vespa/intellij/schema/psi/impl/SdFirstPhaseDefinitionMixin.java
new file mode 100644
index 00000000000..43c02ea4fcf
--- /dev/null
+++ b/integration/intellij/src/main/java/ai/vespa/intellij/schema/psi/impl/SdFirstPhaseDefinitionMixin.java
@@ -0,0 +1,61 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.intellij.schema.psi.impl;
+
+import com.intellij.extapi.psi.ASTWrapperPsiElement;
+import com.intellij.lang.ASTNode;
+import com.intellij.navigation.ItemPresentation;
+import com.intellij.psi.util.PsiTreeUtil;
+import ai.vespa.intellij.schema.SdIcons;
+import ai.vespa.intellij.schema.psi.SdRankProfileDefinition;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.Icon;
+
+/**
+ * This class is used for methods' implementations for SdFirstPhaseDefinition. Connected with "mixin" to
+ * FirstPhaseDefinition rule in sd.bnf
+ *
+ * @author Shahar Ariel
+ */
+public class SdFirstPhaseDefinitionMixin extends ASTWrapperPsiElement {
+
+ public SdFirstPhaseDefinitionMixin(@NotNull ASTNode node) {
+ super(node);
+ }
+
+ @NotNull
+ public String getName() {
+ SdRankProfileDefinition rankProfile = PsiTreeUtil.getParentOfType(this, SdRankProfileDefinition.class);
+ if (rankProfile == null) {
+ return "";
+ }
+ return "first-phase of " + rankProfile.getName();
+ }
+
+ public ItemPresentation getPresentation() {
+ final SdFirstPhaseDefinitionMixin element = this;
+ return new ItemPresentation() {
+ @Override
+ public String getPresentableText() {
+ SdRankProfileDefinition rankProfile = PsiTreeUtil.getParentOfType(element, SdRankProfileDefinition.class);
+ if (rankProfile == null) {
+ return "";
+ }
+ return "first-phase of " + rankProfile.getName();
+ }
+
+ @Nullable
+ @Override
+ public String getLocationString() {
+ return element.getContainingFile() != null ? element.getContainingFile().getName() : null;
+ }
+
+ @Override
+ public Icon getIcon(boolean unused) {
+ return SdIcons.FIRST_PHASE;
+ }
+ };
+ }
+}
+
diff --git a/integration/intellij/src/main/java/ai/vespa/intellij/schema/psi/impl/SdIdentifierMixin.java b/integration/intellij/src/main/java/ai/vespa/intellij/schema/psi/impl/SdIdentifierMixin.java
new file mode 100644
index 00000000000..897217c082c
--- /dev/null
+++ b/integration/intellij/src/main/java/ai/vespa/intellij/schema/psi/impl/SdIdentifierMixin.java
@@ -0,0 +1,46 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.intellij.schema.psi.impl;
+
+import com.intellij.lang.ASTNode;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiNamedElement;
+import ai.vespa.intellij.schema.psi.SdElementFactory;
+import ai.vespa.intellij.schema.psi.SdIdentifier;
+import ai.vespa.intellij.schema.psi.SdIdentifierVal;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * This abstract class is used for methods' implementations for SdIdentifier. Connected with "mixin" to IdentifierVal and
+ * IdentifierWithDashVal rules in sd.bnf
+ *
+ * @author Shahar Ariel
+ */
+public abstract class SdIdentifierMixin extends SdIdentifierMixinImpl implements PsiNamedElement {
+
+ public SdIdentifierMixin(@NotNull ASTNode node) {
+ super(node);
+ }
+
+ @NotNull
+ public String getName() {
+ // IMPORTANT: Convert embedded escaped spaces to simple spaces
+ return this.getText().replaceAll("\\\\ ", " ");
+ }
+
+ @NotNull
+ public PsiElement setName(@NotNull String newName) {
+ ASTNode node = this.getNode().getFirstChildNode();
+ if (node != null) {
+ SdIdentifier elementName;
+ if (this instanceof SdIdentifierVal) {
+ elementName = SdElementFactory.createIdentifierVal(this.getProject(), newName);
+ } else { // this instanceof SdIdentifierWithDashVal
+ elementName = SdElementFactory.createIdentifierWithDashVal(this.getProject(), newName);
+ }
+ ASTNode newNode = elementName.getFirstChild().getNode();
+ this.getNode().replaceChild(node, newNode);
+ }
+ return this;
+ }
+
+}
diff --git a/integration/intellij/src/main/java/ai/vespa/intellij/schema/psi/impl/SdIdentifierMixinImpl.java b/integration/intellij/src/main/java/ai/vespa/intellij/schema/psi/impl/SdIdentifierMixinImpl.java
new file mode 100644
index 00000000000..3f69952d0b6
--- /dev/null
+++ b/integration/intellij/src/main/java/ai/vespa/intellij/schema/psi/impl/SdIdentifierMixinImpl.java
@@ -0,0 +1,28 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.intellij.schema.psi.impl;
+
+import com.intellij.extapi.psi.ASTWrapperPsiElement;
+import com.intellij.lang.ASTNode;
+import com.intellij.openapi.util.TextRange;
+import com.intellij.psi.PsiReference;
+import ai.vespa.intellij.schema.SdReference;
+import ai.vespa.intellij.schema.psi.SdIdentifier;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * This class is used for methods' implementations for SdIdentifier. The abstract class SdIdentifierMixin extents it.
+ *
+ * @author Shahar Ariel
+ */
+public class SdIdentifierMixinImpl extends ASTWrapperPsiElement implements SdIdentifier {
+
+ public SdIdentifierMixinImpl(@NotNull ASTNode node) {
+ super(node);
+ }
+
+ @NotNull
+ public PsiReference getReference() {
+ return new SdReference(this, new TextRange(0, getNode().getText().length()));
+ }
+
+}
diff --git a/integration/intellij/src/main/java/ai/vespa/intellij/schema/psi/impl/SdNamedElementImpl.java b/integration/intellij/src/main/java/ai/vespa/intellij/schema/psi/impl/SdNamedElementImpl.java
new file mode 100644
index 00000000000..ee328f43075
--- /dev/null
+++ b/integration/intellij/src/main/java/ai/vespa/intellij/schema/psi/impl/SdNamedElementImpl.java
@@ -0,0 +1,204 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.intellij.schema.psi.impl;
+
+import com.intellij.extapi.psi.ASTWrapperPsiElement;
+import com.intellij.icons.AllIcons;
+import com.intellij.lang.ASTNode;
+import com.intellij.navigation.ItemPresentation;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.util.PsiTreeUtil;
+import ai.vespa.intellij.schema.SdIcons;
+import ai.vespa.intellij.schema.SdUtil;
+import ai.vespa.intellij.schema.psi.SdAnnotationFieldDefinition;
+import ai.vespa.intellij.schema.psi.SdArgumentDefinition;
+import ai.vespa.intellij.schema.psi.SdDeclarationType;
+import ai.vespa.intellij.schema.psi.SdDocumentAnnotationDefinition;
+import ai.vespa.intellij.schema.psi.SdDocumentDefinition;
+import ai.vespa.intellij.schema.psi.SdDocumentFieldDefinition;
+import ai.vespa.intellij.schema.psi.SdDocumentStructDefinition;
+import ai.vespa.intellij.schema.psi.SdDocumentStructFieldDefinition;
+import ai.vespa.intellij.schema.psi.SdDocumentSummaryDefinition;
+import ai.vespa.intellij.schema.psi.SdElementFactory;
+import ai.vespa.intellij.schema.psi.SdFunctionDefinition;
+import ai.vespa.intellij.schema.psi.SdIdentifier;
+import ai.vespa.intellij.schema.psi.SdImportFieldDefinition;
+import ai.vespa.intellij.schema.psi.SdItemRawScoreDefinition;
+import ai.vespa.intellij.schema.psi.SdNamedElement;
+import ai.vespa.intellij.schema.psi.SdQueryDefinition;
+import ai.vespa.intellij.schema.psi.SdRankProfileDefinition;
+import ai.vespa.intellij.schema.psi.SdSchemaAnnotationDefinition;
+import ai.vespa.intellij.schema.psi.SdSchemaFieldDefinition;
+import ai.vespa.intellij.schema.psi.SdStructFieldDefinition;
+import ai.vespa.intellij.schema.psi.SdTypes;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.Icon;
+
+/**
+ * This abstract class is used to wrap a Psi Element with SdNamedElement interface, which enables the element to be a
+ * "name owner" (like an identifier). It allows the element to take a part in references, find usages and more.
+ * @author Shahar Ariel
+ */
+public abstract class SdNamedElementImpl extends ASTWrapperPsiElement implements SdNamedElement {
+
+ public SdNamedElementImpl(@NotNull ASTNode node) {
+ super(node);
+ }
+
+ @NotNull
+ public String getName() {
+ ASTNode node;
+ if (this instanceof SdImportFieldDefinition) {
+ ASTNode asNode = this.getNode().findChildByType(SdTypes.AS);
+ node = this.getNode().findChildByType(SdTypes.IDENTIFIER_VAL, asNode);
+ } else if (this instanceof SdRankProfileDefinition || this instanceof SdDocumentSummaryDefinition
+ || this instanceof SdQueryDefinition) {
+ node = this.getNode().findChildByType(SdTypes.IDENTIFIER_WITH_DASH_VAL);
+ } else {
+ node = this.getNode().findChildByType(SdTypes.IDENTIFIER_VAL);
+ }
+ if (node != null) {
+ return node.getText();
+ } else {
+ return "";
+ }
+ }
+
+ public String getTypeName() {
+ return getType().toString();
+ }
+
+ public SdDeclarationType getType() {
+ if (this instanceof SdSchemaFieldDefinition) {
+ return SdDeclarationType.SCHEMA_FIELD;
+ } else if (this instanceof SdDocumentSummaryDefinition) {
+ return SdDeclarationType.DOCUMENT_SUMMARY;
+ } else if (this instanceof SdImportFieldDefinition) {
+ return SdDeclarationType.IMPORTED_FIELD;
+ } else if (this instanceof SdRankProfileDefinition) {
+ return SdDeclarationType.RANK_PROFILE;
+ } else if (this instanceof SdFunctionDefinition) {
+ return SdDeclarationType.MACRO;
+ } else if (this instanceof SdArgumentDefinition) {
+ return SdDeclarationType.MACRO_ARGUMENT;
+ } else if (this instanceof SdDocumentDefinition) {
+ return SdDeclarationType.DOCUMENT;
+ } else if (this instanceof SdDocumentStructDefinition) {
+ return SdDeclarationType.STRUCT;
+ } else if (this instanceof SdSchemaAnnotationDefinition ||
+ this instanceof SdDocumentAnnotationDefinition) {
+ return SdDeclarationType.ANNOTATION;
+ } else if (this instanceof SdDocumentStructFieldDefinition) {
+ return SdDeclarationType.DOCUMENT_STRUCT_FIELD;
+ } else if (this instanceof SdDocumentFieldDefinition) {
+ return SdDeclarationType.DOCUMENT_FIELD;
+ } else if (this instanceof SdStructFieldDefinition) {
+ return SdDeclarationType.STRUCT_FIELD;
+ } else if (this instanceof SdAnnotationFieldDefinition) {
+ return SdDeclarationType.ANNOTATION_FIELD;
+ } else if (this instanceof SdQueryDefinition) {
+ return SdDeclarationType.QUERY;
+ } else if (this instanceof SdItemRawScoreDefinition) {
+ return SdDeclarationType.ITEM_RAW_SCORE;
+ } else {
+ return null;
+ }
+ }
+
+ @NotNull
+ public PsiElement setName(@NotNull String newName) {
+ ASTNode node;
+ if (this instanceof SdImportFieldDefinition) {
+ ASTNode asNode = this.getNode().findChildByType(SdTypes.AS);
+ node = this.getNode().findChildByType(SdTypes.IDENTIFIER_VAL, asNode);
+ } else {
+ node = this.getNode().findChildByType(SdTypes.IDENTIFIER_VAL);
+ }
+ SdIdentifier elementName = null;
+ if (node != null) {
+ elementName = SdElementFactory.createIdentifierVal(this.getProject(), newName);
+ } else {
+ node = this.getNode().findChildByType(SdTypes.IDENTIFIER_WITH_DASH_VAL);
+ }
+ if (node != null) {
+ elementName = SdElementFactory.createIdentifierWithDashVal(this.getProject(), newName);
+ }
+ if (elementName != null) {
+ ASTNode newNode = elementName.getFirstChild().getNode();
+ this.getNode().replaceChild(node, newNode);
+ }
+ return this;
+ }
+
+ public PsiElement getNameIdentifier() {
+ ASTNode keyNode = this.getNode().findChildByType(SdTypes.ID);
+ if (keyNode != null) {
+ return keyNode.getPsi();
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public ItemPresentation getPresentation() {
+ final SdNamedElement element = this;
+ return new ItemPresentation() {
+ @Nullable
+ @Override
+ public String getPresentableText() {
+ if (element instanceof SdFunctionDefinition) {
+ return SdUtil.createFunctionDescription((SdFunctionDefinition) element);
+ }
+ SdRankProfileDefinition rankProfileParent = PsiTreeUtil.getParentOfType(element, SdRankProfileDefinition.class);
+ if (rankProfileParent != null) {
+ if (element instanceof SdQueryDefinition || element instanceof SdItemRawScoreDefinition) {
+ return element.getName() + " in " + rankProfileParent.getName();
+ }
+ return rankProfileParent.getName() + "." + element.getName();
+ }
+ return element.getName();
+ }
+
+ @Nullable
+ @Override
+ public String getLocationString() {
+ return element.getContainingFile() != null ? element.getContainingFile().getName() : null;
+ }
+
+ @Nullable
+ @Override
+ public Icon getIcon(boolean unused) {
+ if (element instanceof SdSchemaFieldDefinition || element instanceof SdDocumentFieldDefinition ||
+ element instanceof SdAnnotationFieldDefinition || element instanceof SdQueryDefinition ||
+ element instanceof SdItemRawScoreDefinition) {
+ return AllIcons.Nodes.Field;
+ } else if (element instanceof SdStructFieldDefinition ||
+ element instanceof SdDocumentStructFieldDefinition) {
+ return SdIcons.STRUCT_FIELD;
+ } else if (element instanceof SdImportFieldDefinition) {
+ return SdIcons.IMPORTED_FIELD;
+ } else if (element instanceof SdFunctionDefinition) {
+ return AllIcons.Nodes.Method;
+ // Didn't use isOverride() here because it causes the Structure View to load too slow
+ // return ((SdFunctionDefinition) element).isOverride() ? SdIcons.OVERRIDE_MACRO : AllIcons.Nodes.Method;
+ } else if (element instanceof SdDocumentStructDefinition) {
+ return SdIcons.STRUCT;
+ } else if (element instanceof SdRankProfileDefinition) {
+ return AllIcons.Nodes.Record;
+ } else if (element instanceof SdDocumentSummaryDefinition) {
+ return SdIcons.DOCUMENT_SUMMARY;
+ } else if (element instanceof SdDocumentDefinition) {
+ return SdIcons.DOCUMENT;
+ } else if (element instanceof SdSchemaAnnotationDefinition ||
+ element instanceof SdDocumentAnnotationDefinition) {
+ return AllIcons.Nodes.ObjectTypeAttribute;
+ }
+ else {
+ return null;
+ }
+ }
+ };
+ }
+
+}
diff --git a/integration/intellij/src/main/java/ai/vespa/intellij/schema/psi/impl/SdSummaryDefinitionMixin.java b/integration/intellij/src/main/java/ai/vespa/intellij/schema/psi/impl/SdSummaryDefinitionMixin.java
new file mode 100644
index 00000000000..71d93641939
--- /dev/null
+++ b/integration/intellij/src/main/java/ai/vespa/intellij/schema/psi/impl/SdSummaryDefinitionMixin.java
@@ -0,0 +1,60 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.intellij.schema.psi.impl;
+
+import com.intellij.extapi.psi.ASTWrapperPsiElement;
+import com.intellij.lang.ASTNode;
+import com.intellij.navigation.ItemPresentation;
+import ai.vespa.intellij.schema.SdIcons;
+import ai.vespa.intellij.schema.psi.SdTypes;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.Icon;
+
+/**
+ * This class is used for methods' implementations for SdSummaryDefinition. Connected with "mixin" to SummaryDefinition
+ * rule in sd.bnf
+ *
+ * @author Shahar Ariel
+ */
+public abstract class SdSummaryDefinitionMixin extends ASTWrapperPsiElement {
+
+ public SdSummaryDefinitionMixin(@NotNull ASTNode node) {
+ super(node);
+ }
+
+ @NotNull
+ public String getName() {
+ ASTNode node;
+ node = this.getNode().findChildByType(SdTypes.IDENTIFIER_WITH_DASH_VAL);
+ if (node != null) {
+ return node.getText();
+ } else {
+ return "";
+ }
+ }
+
+ @Override
+ public ItemPresentation getPresentation() {
+ final SdSummaryDefinitionMixin element = this;
+ return new ItemPresentation() {
+
+ @Override
+ public String getPresentableText() {
+ return getName();
+ }
+
+ @Nullable
+ @Override
+ public String getLocationString() {
+ return element.getContainingFile() != null ? element.getContainingFile().getName() : null;
+ }
+
+ @Override
+ public Icon getIcon(boolean unused) {
+ return SdIcons.SUMMARY;
+ }
+ };
+ }
+
+}
diff --git a/integration/intellij/src/main/java/ai/vespa/intellij/schema/structure/SdStructureViewElement.java b/integration/intellij/src/main/java/ai/vespa/intellij/schema/structure/SdStructureViewElement.java
new file mode 100644
index 00000000000..76662c5cd31
--- /dev/null
+++ b/integration/intellij/src/main/java/ai/vespa/intellij/schema/structure/SdStructureViewElement.java
@@ -0,0 +1,96 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.intellij.schema.structure;
+
+import com.intellij.ide.projectView.PresentationData;
+import com.intellij.ide.structureView.StructureViewTreeElement;
+import com.intellij.ide.util.treeView.smartTree.SortableTreeElement;
+import com.intellij.ide.util.treeView.smartTree.TreeElement;
+import com.intellij.navigation.ItemPresentation;
+import com.intellij.psi.NavigatablePsiElement;
+import com.intellij.psi.PsiElement;
+import ai.vespa.intellij.schema.SdUtil;
+import ai.vespa.intellij.schema.psi.SdDocumentAnnotationDefinition;
+import ai.vespa.intellij.schema.psi.SdDocumentDefinition;
+import ai.vespa.intellij.schema.psi.SdDocumentStructDefinition;
+import ai.vespa.intellij.schema.psi.SdDocumentSummaryDefinition;
+import ai.vespa.intellij.schema.psi.SdFile;
+import ai.vespa.intellij.schema.psi.SdRankProfileDefinition;
+import ai.vespa.intellij.schema.psi.SdSchemaAnnotationDefinition;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class is used for the presentation of an element in the "Structure View" window.
+ *
+ * @author Shahar Ariel
+ */
+public class SdStructureViewElement implements StructureViewTreeElement, SortableTreeElement {
+
+ private final NavigatablePsiElement myElement;
+
+ public SdStructureViewElement(NavigatablePsiElement element) {
+ this.myElement = element;
+ }
+
+ @Override
+ public Object getValue() {
+ return myElement;
+ }
+
+ @Override
+ public void navigate(boolean requestFocus) {
+ myElement.navigate(requestFocus);
+ }
+
+ @Override
+ public boolean canNavigate() {
+ return myElement.canNavigate();
+ }
+
+ @Override
+ public boolean canNavigateToSource() {
+ return myElement.canNavigateToSource();
+ }
+
+ @NotNull
+ @Override
+ public String getAlphaSortKey() {
+ String name = myElement.getName();
+ return name != null ? name : "";
+ }
+
+ @NotNull
+ @Override
+ public ItemPresentation getPresentation() {
+ ItemPresentation presentation = myElement.getPresentation();
+ return presentation != null ? presentation : new PresentationData();
+ }
+
+ @Override
+ public TreeElement @NotNull [] getChildren() {
+ List<PsiElement> children = new ArrayList<>();
+ if (myElement instanceof SdFile) {
+ children = SdUtil.findSchemaChildren(myElement);
+ } else if (myElement instanceof SdDocumentDefinition) {
+ children = SdUtil.findDocumentChildren(myElement);
+ } else if (myElement instanceof SdDocumentStructDefinition) {
+ children = SdUtil.findDocumentStructChildren(myElement);
+ } else if (myElement instanceof SdRankProfileDefinition) {
+ children = SdUtil.findRankProfileChildren(myElement);
+ } else if (myElement instanceof SdDocumentSummaryDefinition) {
+ children = SdUtil.findDocumentSummaryChildren(myElement);
+ } else if (myElement instanceof SdSchemaAnnotationDefinition ||
+ myElement instanceof SdDocumentAnnotationDefinition) {
+ children = SdUtil.findAnnotationChildren(myElement);
+ }
+
+ List<TreeElement> treeElements = new ArrayList<>(children.size());
+ for (PsiElement child : children) {
+ treeElements.add(new SdStructureViewElement((NavigatablePsiElement) child));
+ }
+ return treeElements.toArray(new TreeElement[0]);
+ }
+
+}
diff --git a/integration/intellij/src/main/java/ai/vespa/intellij/schema/structure/SdStructureViewFactory.java b/integration/intellij/src/main/java/ai/vespa/intellij/schema/structure/SdStructureViewFactory.java
new file mode 100644
index 00000000000..3d7b3a1a275
--- /dev/null
+++ b/integration/intellij/src/main/java/ai/vespa/intellij/schema/structure/SdStructureViewFactory.java
@@ -0,0 +1,33 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.intellij.schema.structure;
+
+import com.intellij.ide.structureView.StructureViewBuilder;
+import com.intellij.ide.structureView.StructureViewModel;
+import com.intellij.ide.structureView.TreeBasedStructureViewBuilder;
+import com.intellij.lang.PsiStructureViewFactory;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.psi.PsiFile;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * This class is used for the extension (in plugin.xml) to the class SdStructureViewModel. It make the IDE use our
+ * plugin's code when creating the "Structure View" window.
+ *
+ * @author Shahar Ariel
+ */
+public class SdStructureViewFactory implements PsiStructureViewFactory {
+
+ @Nullable
+ @Override
+ public StructureViewBuilder getStructureViewBuilder(@NotNull final PsiFile psiFile) {
+ return new TreeBasedStructureViewBuilder() {
+ @NotNull
+ @Override
+ public StructureViewModel createStructureViewModel(@Nullable Editor editor) {
+ return new SdStructureViewModel(psiFile);
+ }
+ };
+ }
+
+}
diff --git a/integration/intellij/src/main/java/ai/vespa/intellij/schema/structure/SdStructureViewModel.java b/integration/intellij/src/main/java/ai/vespa/intellij/schema/structure/SdStructureViewModel.java
new file mode 100644
index 00000000000..1f1c271dcf6
--- /dev/null
+++ b/integration/intellij/src/main/java/ai/vespa/intellij/schema/structure/SdStructureViewModel.java
@@ -0,0 +1,37 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.intellij.schema.structure;
+
+import com.intellij.ide.structureView.StructureViewModel;
+import com.intellij.ide.structureView.StructureViewModelBase;
+import com.intellij.ide.structureView.StructureViewTreeElement;
+import com.intellij.ide.util.treeView.smartTree.Sorter;
+import com.intellij.psi.PsiFile;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * This class represents the "Structure View" window.
+ *
+ * @author Shahar Ariel
+ */
+public class SdStructureViewModel extends StructureViewModelBase implements StructureViewModel.ElementInfoProvider {
+ public SdStructureViewModel(PsiFile psiFile) {
+ super(psiFile, new SdStructureViewElement(psiFile));
+ }
+
+
+ public Sorter @NotNull [] getSorters() {
+ return new Sorter[]{Sorter.ALPHA_SORTER};
+ }
+
+
+ @Override
+ public boolean isAlwaysShowsPlus(StructureViewTreeElement element) {
+ return false;
+ }
+
+ @Override
+ public boolean isAlwaysLeaf(StructureViewTreeElement element) {
+ return false;
+ }
+
+}
diff --git a/integration/intellij/src/main/jflex/ai/vespa/intellij/schema/lexer/sd.flex b/integration/intellij/src/main/jflex/ai/vespa/intellij/schema/lexer/sd.flex
new file mode 100644
index 00000000000..b4491acc717
--- /dev/null
+++ b/integration/intellij/src/main/jflex/ai/vespa/intellij/schema/lexer/sd.flex
@@ -0,0 +1,239 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.intellij.schema.lexer;
+
+import com.intellij.lexer.FlexLexer;
+import com.intellij.psi.tree.IElementType;
+import com.intellij.ui.components.MultiColumnList;
+import ai.vespa.intellij.schema.psi.SdTokenType;
+
+import static ai.vespa.intellij.schema.psi.SdTypes.*; // That is the class which is specified as `elementTypeHolderClass` in bnf
+ // grammar file. This will contain all other tokens which we will use.
+import static com.intellij.psi.TokenType.BAD_CHARACTER; // Pre-defined bad character token.
+import static com.intellij.psi.TokenType.WHITE_SPACE; // Pre-defined whitespace character token.
+
+/*
+ * Vespa schema parser lexer
+ *
+ * @author Shahar Ariel
+ */
+
+%%
+
+%public
+%class SdLexer
+%implements FlexLexer
+%function advance
+%type IElementType
+%unicode
+
+//**--------- REGEXES ---------**//
+// If some character sequence is matched to this regex, it will be treated as an IDENTIFIER.
+ID=[a-zA-Z_][a-zA-Z0-9_]*
+ID_WITH_DASH = [a-zA-Z_][a-zA-Z0-9_-]*
+// If some character sequence is matched to this regex, it will be treated as a WHITE_SPACE.
+WHITE_SPACE=[ \t\n\x0B\f\r]+
+
+COMMENT=#.*
+SYMBOL= [!$|:{}(),.\[\]]
+INTEGER = [0-9]+
+FLOAT = {INTEGER}[.][0-9]+[e]?
+COMPARISON_OPERATOR = [<>]|(==)|(<=)|(>=)|(\~=)
+ARITHMETIC_OPERATOR = [\-+*/]
+STRING = \"([^\"\\]*(\\.[^\"\\]*)*)\"
+WORD = \w+
+
+
+%%
+
+<YYINITIAL> {
+ /**
+ In here, we match keywords. So if a keyword is found, this returns a token which corresponds to that keyword.
+ These tokens are generated using the 'sd.bnf' file and located in the SdTypes class.
+ These tokens are Parsed uses these return values to match token squence to a parser rule.
+ */
+
+ /**
+ This list of keywords has to be synchronized with sd.bnf file. If you add a keyword here, you should add it to the
+ sd.bnf file as well (to the rule KeywordOrIdentifier / KeywordNotIdentifier).
+ */
+
+ "search" { return SEARCH; }
+ "schema" { return SCHEMA; }
+ "document" { return DOCUMENT; }
+ "inherits" { return INHERITS; }
+ "struct" { return STRUCT; }
+ "field" { return FIELD; }
+ "type" { return TYPE; }
+ "struct-field" { return STRUCT_FIELD; }
+ "match" { return MATCH; }
+
+ "indexing" { return INDEXING; }
+ "summary" { return SUMMARY; }
+ "attribute" { return ATTRIBUTE; }
+ "set_language" { return SET_LANGUAGE; }
+
+ "array" { return ARRAY; }
+ "raw" { return RAW; }
+ "uri" { return URI; }
+ "reference" { return REFERENCE; }
+ "annotationreference" { return ANNOTATIONREFERENCE; }
+ "weightedset" { return WEIGHTEDSET; }
+ "map" { return MAP; }
+
+ "text" { return TEXT; }
+ "exact" { return EXACT; }
+ "exact-terminator" { return EXACT_TERMINATOR; }
+ "word" { return WORD; }
+ "prefix" { return PREFIX; }
+ "cased" { return CASED; }
+ "uncased" { return UNCASED; }
+ "substring" { return SUBSTRING; }
+ "suffix" { return SUFFIX; }
+ "max-length" { return MAX_LENGTH; }
+ "gram" { return GRAM; }
+ "gram-size" { return GRAM_SIZE; }
+
+ "fast-search" { return FAST_SEARCH; }
+ "fast-access" { return FAST_ACCESS; }
+ "alias" { return ALIAS; }
+ "sorting" { return SORTING; }
+ "uca" { return UCA; }
+ "lowercase" { return LOWERCASE; }
+ "paged" { return PAGED; }
+ "strength" { return STRENGTH; }
+ "primary" { return PRIMARY; }
+ "secondary" { return SECONDARY; }
+ "tertiary" { return TERTIARY; }
+ "quaternary" { return QUATERNARY; }
+ "identical" { return IDENTICAL; }
+ "distance-metric" { return DISTANCE_METRIC; }
+
+ "rank" { return RANK; }
+ "filter" { return FILTER; }
+ "normal" { return NORMAL; }
+ "literal" { return LITERAL; }
+ "indexing-rewrite" { return INDEXING_REWRITE; }
+ "none" { return NONE; }
+ "query-command" { return QUERY_COMMAND; }
+ "full" { return FULL; }
+ "static" { return STATIC; }
+ "dynamic" { return DYNAMIC; }
+ "source" { return SOURCE; }
+ "to" { return TO; }
+ "matched-elements-only" { return MATCHED_ELEMENTS_ONLY; }
+
+ "input" { return INPUT; }
+ "mutable" { return MUTABLE; }
+ "enable-bit-vectors" { return ENABLE_BIT_VECTORS; }
+ "enable-only-bit-vector" { return ENABLE_ONLY_BIT_VECTOR; }
+ "document-summary" { return DOCUMENT_SUMMARY; }
+ "from-disk" { return FROM_DISK; }
+ "omit-summary-features" { return OMIT_SUMMARY_FEATURES; }
+ "import" { return IMPORT; }
+ "as" { return AS; }
+
+ "rank-profile" { return RANK_PROFILE; }
+ "model" { return MODEL; }
+ "match-phase" { return MATCH_PHASE; }
+ "order" { return ORDER; }
+ "ascending" { return ASCENDING; }
+ "descending" { return DESCENDING; }
+ "locale" { return LOCALE; }
+ "max-hits" { return MAX_HITS; }
+ "diversity" { return DIVERSITY; }
+ "min-groups" { return MIN_GROUPS; }
+ "cutoff-factor" { return CUTOFF_FACTOR; }
+ "cutoff-strategy" { return CUTOFF_STRATEGY; }
+ "loose" { return LOOSE; }
+ "strict" { return STRICT; }
+ "rank-properties" { return RANK_PROPERTIES; }
+
+ "first-phase" { return FIRST_PHASE; }
+ "keep-rank-count" { return KEEP_RANK_COUNT; }
+ "rank-score-drop-limit" { return RANK_SCORE_DROP_LIMIT; }
+ "expression" { return EXPRESSION; }
+ "file" { return FILE; }
+ "expression" { return EXPRESSION; }
+ "num-threads-per-search" { return NUM_THREADS_PER_SEARCH; }
+ "termwise-limit" { return TERMWISE_LIMIT; }
+ "ignore-default-rank-features" { return IGNORE_DEFAULT_RANK_FEATURES; }
+ "min-hits-per-thread" { return MIN_HITS_PER_THREAD; }
+ "num-search-partition" { return NUM_SEARCH_PARTITION; }
+ "constants" { return CONSTANTS; }
+ "second-phase" { return SECOND_PHASE; }
+ "rerank-count" { return RERANK_COUNT; }
+ "rank-features" { return RANK_FEATURES; }
+
+ "weight" { return WEIGHT; }
+ "index" { return INDEX; }
+ "bolding" { return BOLDING; }
+ "on" { return ON; }
+ "off" { return OFF; }
+ "true" { return TRUE; }
+ "false" { return FALSE; }
+ "id" { return ID; }
+ "normalizing" { return NORMALIZING; }
+ "stemming" { return STEMMING; }
+ "arity" { return ARITY; }
+ "lower-bound" { return LOWER_BOUND; }
+ "upper-bound" { return UPPER_BOUND; }
+ "dense-posting-list-threshold" {return DENSE_POSTING_LIST_THRESHOLD; }
+ "enable-bm25" { return ENABLE_BM25; }
+ "hnsw" { return HNSW; }
+ "max-links-per-node" { return MAX_LINKS_PER_NODE; }
+ "neighbors-to-explore-at-insert" { return NEIGHBORS_TO_EXPLORE_AT_INSERT; }
+ "multi-threaded-indexing" { return MULTI_THREADED_INDEXING; }
+ "create-if-nonexistent" { return CREATE_IF_NONEXISTENT; }
+ "remove-if-zero" { return REMOVE_IF_ZERO; }
+ "dictionary" { return DICTIONARY; }
+ "hash" { return HASH; }
+ "btree" { return BTREE; }
+
+ "fieldset" { return FIELDSET; }
+ "fields" { return FIELDS; }
+ "constant" { return CONSTANT; }
+ "output" { return OUTPUT; }
+
+ "annotation" { return ANNOTATION; }
+ "rank-type" { return RANK_TYPE; }
+ "onnx-model" { return ONNX_MODEL; }
+ "raw-as-base64-in-summary" { return RAW_AS_BASE64_IN_SUMMARY; }
+ "on-match" { return ON_MATCH; }
+ "on-rank" { return ON_RANK; }
+ "on-summary" { return ON_SUMMARY; }
+
+ "function" { return FUNCTION; }
+ "macro" { return MACRO; }
+ "inline" { return INLINE; }
+
+ "summary-features" { return SUMMARY_FEATURES; }
+ "match-features" { return MATCH_FEATURES; }
+ "rank-features" { return RANK_FEATURES; }
+
+ "body" { return BODY; }
+ "header" { return HEADER; }
+ "summary-to" { return SUMMARY_TO; }
+
+ "evaluation-point" { return EVALUATION_POINT; }
+ "pre-post-filter-tipping-point" { return PRE_POST_FILTER_TIPPING_POINT; }
+
+ // In here, we check for character sequences which matches regular expressions defined above.
+ {ID} { return ID_REG; }
+ {ID_WITH_DASH} { return ID_WITH_DASH_REG; }
+
+ {WHITE_SPACE} { return WHITE_SPACE; }
+
+ {COMMENT} { return COMMENT; }
+ {SYMBOL} { return SYMBOL; }
+ {INTEGER} { return INTEGER_REG; }
+ {FLOAT} { return FLOAT_REG; }
+ {ARITHMETIC_OPERATOR} { return ARITHMETIC_OPERATOR; }
+ {COMPARISON_OPERATOR} { return COMPARISON_OPERATOR; }
+ {WORD} { return WORD_REG; }
+ {STRING} { return STRING_REG; }
+
+}
+
+// If the character sequence does not match any of the above rules, we return BAD_CHARACTER which indicates that
+// there is an error in the character sequence. This is used to highlight errors.
+[^] { return BAD_CHARACTER; } \ No newline at end of file
diff --git a/integration/intellij/src/main/resources/META-INF/plugin.xml b/integration/intellij/src/main/resources/META-INF/plugin.xml
new file mode 100644
index 00000000000..49db6c59b3e
--- /dev/null
+++ b/integration/intellij/src/main/resources/META-INF/plugin.xml
@@ -0,0 +1,41 @@
+<!-- Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -->
+<idea-plugin>
+ <id>ai.vespa</id>
+ <name>Vespa</name>
+
+ <!-- Text to display as company information on Preferences/Settings | Plugin page -->
+ <vendor>vespa.ai</vendor>
+
+ <!-- Product and plugin compatibility requirements -->
+ <depends>com.intellij.java</depends>
+ <depends>com.intellij.modules.platform</depends>
+
+ <!-- Text to display as description on Preferences/Settings | Plugin page -->
+ <description><![CDATA[
+ <p>Vespa.ai schema file support</p>
+ ]]></description>
+
+ <!-- Extension points defined by the plugin -->
+ <extensions defaultExtensionNs="com.intellij">
+ <fileType name="Sd File" implementationClass="ai.vespa.intellij.schema.SdFileType" fieldName="INSTANCE"
+ language="Sd" extensions="sd"/>
+ <lang.parserDefinition language="Sd" implementationClass="ai.vespa.intellij.schema.parser.SdParserDefinition"/>
+ <lang.syntaxHighlighterFactory language="Sd" implementationClass="ai.vespa.intellij.schema.SdSyntaxHighlighterFactory"/>
+ <completion.contributor language="Sd" implementationClass="ai.vespa.intellij.schema.SdCompletionContributor"/>
+
+ <lang.findUsagesProvider language="Sd" implementationClass="ai.vespa.intellij.schema.findUsages.SdFindUsagesProvider"/>
+ <findUsagesHandlerFactory implementation="ai.vespa.intellij.schema.findUsages.SdFindUsagesHandlerFactory"/>
+ <fileStructureGroupRuleProvider implementation="ai.vespa.intellij.schema.findUsages.SdRankProfileGroupingRuleProvider"/>
+ <fileStructureGroupRuleProvider implementation="ai.vespa.intellij.schema.findUsages.SdDocumentSummaryGroupingRuleProvider"/>
+ <elementDescriptionProvider implementation="ai.vespa.intellij.schema.psi.SdElementDescriptionProvider"/>
+
+ <lang.psiStructureViewFactory language="Sd" implementationClass="ai.vespa.intellij.schema.structure.SdStructureViewFactory"/>
+ <codeStyleSettingsProvider implementation="ai.vespa.intellij.schema.SdCodeStyleSettingsProvider"/>
+ <langCodeStyleSettingsProvider implementation="ai.vespa.intellij.schema.SdLanguageCodeStyleSettingsProvider"/>
+ <lang.commenter language="Sd" implementationClass="ai.vespa.intellij.schema.SdCommenter"/>
+ <lang.refactoringSupport language="Sd" implementationClass="ai.vespa.intellij.schema.SdRefactoringSupportProvider"/>
+ <gotoSymbolContributor implementation="ai.vespa.intellij.schema.SdChooseByNameContributor"/>
+ <callHierarchyProvider language="Sd" implementationClass="ai.vespa.intellij.schema.hierarchy.SdCallHierarchyProvider"/>
+ </extensions>
+
+</idea-plugin> \ No newline at end of file
diff --git a/integration/intellij/src/main/resources/META-INF/pluginIcon.png b/integration/intellij/src/main/resources/META-INF/pluginIcon.png
new file mode 100644
index 00000000000..c8dc4764e35
--- /dev/null
+++ b/integration/intellij/src/main/resources/META-INF/pluginIcon.png
Binary files differ
diff --git a/integration/intellij/src/main/resources/META-INF/pluginIcon_dark.png b/integration/intellij/src/main/resources/META-INF/pluginIcon_dark.png
new file mode 100644
index 00000000000..c8dc4764e35
--- /dev/null
+++ b/integration/intellij/src/main/resources/META-INF/pluginIcon_dark.png
Binary files differ
diff --git a/integration/intellij/src/main/resources/icons/document_icon.png b/integration/intellij/src/main/resources/icons/document_icon.png
new file mode 100644
index 00000000000..497c218ab91
--- /dev/null
+++ b/integration/intellij/src/main/resources/icons/document_icon.png
Binary files differ
diff --git a/integration/intellij/src/main/resources/icons/document_summary_icon.png b/integration/intellij/src/main/resources/icons/document_summary_icon.png
new file mode 100644
index 00000000000..72f42c68902
--- /dev/null
+++ b/integration/intellij/src/main/resources/icons/document_summary_icon.png
Binary files differ
diff --git a/integration/intellij/src/main/resources/icons/first_phase_icon.png b/integration/intellij/src/main/resources/icons/first_phase_icon.png
new file mode 100644
index 00000000000..18f24e9e462
--- /dev/null
+++ b/integration/intellij/src/main/resources/icons/first_phase_icon.png
Binary files differ
diff --git a/integration/intellij/src/main/resources/icons/imported_field_icon.png b/integration/intellij/src/main/resources/icons/imported_field_icon.png
new file mode 100644
index 00000000000..1ec9b1d6a96
--- /dev/null
+++ b/integration/intellij/src/main/resources/icons/imported_field_icon.png
Binary files differ
diff --git a/integration/intellij/src/main/resources/icons/macro_icon.png b/integration/intellij/src/main/resources/icons/macro_icon.png
new file mode 100644
index 00000000000..736d8168119
--- /dev/null
+++ b/integration/intellij/src/main/resources/icons/macro_icon.png
Binary files differ
diff --git a/integration/intellij/src/main/resources/icons/override_macro_icon.png b/integration/intellij/src/main/resources/icons/override_macro_icon.png
new file mode 100644
index 00000000000..024eb777eef
--- /dev/null
+++ b/integration/intellij/src/main/resources/icons/override_macro_icon.png
Binary files differ
diff --git a/integration/intellij/src/main/resources/icons/sd_icon.png b/integration/intellij/src/main/resources/icons/sd_icon.png
new file mode 100644
index 00000000000..1c96aaee633
--- /dev/null
+++ b/integration/intellij/src/main/resources/icons/sd_icon.png
Binary files differ
diff --git a/integration/intellij/src/main/resources/icons/struct_field_icon.png b/integration/intellij/src/main/resources/icons/struct_field_icon.png
new file mode 100644
index 00000000000..7f5074630d4
--- /dev/null
+++ b/integration/intellij/src/main/resources/icons/struct_field_icon.png
Binary files differ
diff --git a/integration/intellij/src/main/resources/icons/struct_icon.png b/integration/intellij/src/main/resources/icons/struct_icon.png
new file mode 100644
index 00000000000..c2f6fdc8440
--- /dev/null
+++ b/integration/intellij/src/main/resources/icons/struct_icon.png
Binary files differ
diff --git a/integration/intellij/src/main/resources/icons/summary_def_icon.png b/integration/intellij/src/main/resources/icons/summary_def_icon.png
new file mode 100644
index 00000000000..a8faf12601f
--- /dev/null
+++ b/integration/intellij/src/main/resources/icons/summary_def_icon.png
Binary files differ