diff options
author | Jon Bratseth <bratseth@gmail.com> | 2021-11-18 23:04:30 +0100 |
---|---|---|
committer | Jon Bratseth <bratseth@gmail.com> | 2021-11-18 23:04:30 +0100 |
commit | c978c9e29652b24b7f31ed545c1c0e48a17464ec (patch) | |
tree | c347e96d74bcb9d180346d90385a2dceb6fbcad5 /integration | |
parent | 28b80bf7669ff14f1af913ef7bcee8659ac555a2 (diff) |
Move and rename
Diffstat (limited to 'integration')
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 Binary files differnew file mode 100644 index 00000000000..c8dc4764e35 --- /dev/null +++ b/integration/intellij/src/main/resources/META-INF/pluginIcon.png diff --git a/integration/intellij/src/main/resources/META-INF/pluginIcon_dark.png b/integration/intellij/src/main/resources/META-INF/pluginIcon_dark.png Binary files differnew file mode 100644 index 00000000000..c8dc4764e35 --- /dev/null +++ b/integration/intellij/src/main/resources/META-INF/pluginIcon_dark.png diff --git a/integration/intellij/src/main/resources/icons/document_icon.png b/integration/intellij/src/main/resources/icons/document_icon.png Binary files differnew file mode 100644 index 00000000000..497c218ab91 --- /dev/null +++ b/integration/intellij/src/main/resources/icons/document_icon.png diff --git a/integration/intellij/src/main/resources/icons/document_summary_icon.png b/integration/intellij/src/main/resources/icons/document_summary_icon.png Binary files differnew file mode 100644 index 00000000000..72f42c68902 --- /dev/null +++ b/integration/intellij/src/main/resources/icons/document_summary_icon.png diff --git a/integration/intellij/src/main/resources/icons/first_phase_icon.png b/integration/intellij/src/main/resources/icons/first_phase_icon.png Binary files differnew file mode 100644 index 00000000000..18f24e9e462 --- /dev/null +++ b/integration/intellij/src/main/resources/icons/first_phase_icon.png diff --git a/integration/intellij/src/main/resources/icons/imported_field_icon.png b/integration/intellij/src/main/resources/icons/imported_field_icon.png Binary files differnew file mode 100644 index 00000000000..1ec9b1d6a96 --- /dev/null +++ b/integration/intellij/src/main/resources/icons/imported_field_icon.png diff --git a/integration/intellij/src/main/resources/icons/macro_icon.png b/integration/intellij/src/main/resources/icons/macro_icon.png Binary files differnew file mode 100644 index 00000000000..736d8168119 --- /dev/null +++ b/integration/intellij/src/main/resources/icons/macro_icon.png diff --git a/integration/intellij/src/main/resources/icons/override_macro_icon.png b/integration/intellij/src/main/resources/icons/override_macro_icon.png Binary files differnew file mode 100644 index 00000000000..024eb777eef --- /dev/null +++ b/integration/intellij/src/main/resources/icons/override_macro_icon.png diff --git a/integration/intellij/src/main/resources/icons/sd_icon.png b/integration/intellij/src/main/resources/icons/sd_icon.png Binary files differnew file mode 100644 index 00000000000..1c96aaee633 --- /dev/null +++ b/integration/intellij/src/main/resources/icons/sd_icon.png diff --git a/integration/intellij/src/main/resources/icons/struct_field_icon.png b/integration/intellij/src/main/resources/icons/struct_field_icon.png Binary files differnew file mode 100644 index 00000000000..7f5074630d4 --- /dev/null +++ b/integration/intellij/src/main/resources/icons/struct_field_icon.png diff --git a/integration/intellij/src/main/resources/icons/struct_icon.png b/integration/intellij/src/main/resources/icons/struct_icon.png Binary files differnew file mode 100644 index 00000000000..c2f6fdc8440 --- /dev/null +++ b/integration/intellij/src/main/resources/icons/struct_icon.png diff --git a/integration/intellij/src/main/resources/icons/summary_def_icon.png b/integration/intellij/src/main/resources/icons/summary_def_icon.png Binary files differnew file mode 100644 index 00000000000..a8faf12601f --- /dev/null +++ b/integration/intellij/src/main/resources/icons/summary_def_icon.png |