feat(zon): Code completion

This commit is contained in:
FalsePattern 2023-08-10 11:07:18 +02:00 committed by FalsePattern
parent 211f2c8065
commit b0b7a2cc7b
Signed by: falsepattern
GPG key ID: FDF7126A9E124447
6 changed files with 171 additions and 20 deletions

View file

@ -25,6 +25,7 @@ Changelog structure reference:
- Basic syntax highlighting - Basic syntax highlighting
- Color settings page - Color settings page
- Brace matching - Brace matching
- Code completion
## [0.4.0] ## [0.4.0]

View file

@ -0,0 +1,86 @@
/*
* Copyright 2023 FalsePattern
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.falsepattern.zigbrains.zon.completion;
import com.falsepattern.zigbrains.zon.parser.ZonFile;
import com.falsepattern.zigbrains.zon.psi.ZonProperty;
import com.falsepattern.zigbrains.zon.psi.ZonPropertyPlaceholder;
import com.falsepattern.zigbrains.zon.psi.ZonStruct;
import com.falsepattern.zigbrains.zon.psi.ZonTypes;
import com.falsepattern.zigbrains.zon.psi.impl.ZonPsiImplUtil;
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 org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.Set;
public class ZonCompletionContributor extends CompletionContributor {
private static final List<String> ZON_ROOT_KEYS = List.of("name", "version", "dependencies");
private static final List<String> ZON_DEP_KEYS = List.of("url", "hash");
public ZonCompletionContributor() {
extend(CompletionType.BASIC,
PlatformPatterns.psiElement()
.withParent(PlatformPatterns.psiElement(ZonTypes.PROPERTY_PLACEHOLDER))
.withSuperParent(3, PlatformPatterns.psiElement(ZonFile.class)),
new CompletionProvider<>() {
@Override
protected void addCompletions(@NotNull CompletionParameters parameters, @NotNull ProcessingContext context, @NotNull CompletionResultSet result) {
var placeholder = (ZonPropertyPlaceholder) parameters.getPosition().getParent();
var zonStruct = (ZonStruct) placeholder.getParent();
var keys = ZonPsiImplUtil.getKeys(zonStruct);
doAddCompletions(placeholder.getText().startsWith("."), keys, ZON_ROOT_KEYS, result);
}
});
extend(CompletionType.BASIC,
PlatformPatterns.psiElement()
.withParent(PlatformPatterns.psiElement(ZonTypes.PROPERTY_PLACEHOLDER))
.withSuperParent(3, PlatformPatterns.psiElement(ZonTypes.VALUE))
.withSuperParent(6, PlatformPatterns.psiElement(ZonTypes.VALUE))
.withSuperParent(9, PlatformPatterns.psiElement(ZonFile.class)),
new CompletionProvider<>() {
@Override
protected void addCompletions(@NotNull CompletionParameters parameters, @NotNull ProcessingContext context, @NotNull CompletionResultSet result) {
var placeholder = (ZonPropertyPlaceholder) parameters.getPosition().getParent();
var depStruct = (ZonStruct) placeholder.getParent();
var parentProperty = (ZonProperty) depStruct.getParent()
.getParent()
.getParent()
.getParent()
.getParent();
if (!"dependencies".equals(ZonPsiImplUtil.getText(parentProperty.getIdentifier()))) {
return;
}
doAddCompletions(placeholder.getText().startsWith("."), ZonPsiImplUtil.getKeys(depStruct), ZON_DEP_KEYS, result);
}
});
}
private static void doAddCompletions(boolean hasDot, Set<String> current, List<String> expected, CompletionResultSet result) {
for (var key: expected) {
if (current.contains(key)) {
continue;
}
result.addElement(LookupElementBuilder.create(hasDot ? key : "." + key));
}
}
}

View file

@ -34,27 +34,42 @@ WHITE_SPACE=[\s]+
LINE_COMMENT="//" [^\n]* | "////" [^\n]* LINE_COMMENT="//" [^\n]* | "////" [^\n]*
COMMENT="///".* COMMENT="///".*
ID=[A-Za-z_][A-Za-z0-9_]* | "@\"" {STRING_CHAR}* \" ID=[A-Za-z_][A-Za-z0-9_]*
STRING_CHAR=( [^\\\"] | \\[^] ) hex=[0-9a-fA-F]
STRING_LITERAL_SINGLE=\"{STRING_CHAR}*\" char_escape
LINE_STRING=(\\\\ [^\n]* [ \n]*)+ = "\\x" {hex} {hex}
| "\\u{" {hex}+ "}"
| "\\" [nr\\t'\"]
string_char
= {char_escape}
| [^\\\"\n]
LINE_STRING=("\\\\" [^\n]* [ \n]*)+
%state STRING_LITERAL
%state ID_STRING
%% %%
<YYINITIAL> {
{WHITE_SPACE} { return WHITE_SPACE; }
"." { return DOT; }
"{" { return LBRACE; }
"}" { return RBRACE; }
"=" { return EQ; }
"," { return COMMA; }
{COMMENT} { return COMMENT; }
{LINE_COMMENT} { return COMMENT; }
{ID} { return ID; } <YYINITIAL> {WHITE_SPACE} { return WHITE_SPACE; }
{STRING_LITERAL_SINGLE} { return STRING_LITERAL_SINGLE; } <YYINITIAL> "." { return DOT; }
{LINE_STRING} { return LINE_STRING; } <YYINITIAL> "IntellijIdeaRulezzz" { return INTELLIJ_COMPLETION_DUMMY; }
} <YYINITIAL> "{" { return LBRACE; }
<YYINITIAL> "}" { return RBRACE; }
<YYINITIAL> "=" { return EQ; }
<YYINITIAL> "," { return COMMA; }
<YYINITIAL> {COMMENT} { return COMMENT; }
<YYINITIAL> {LINE_COMMENT} { return COMMENT; }
[^] { return BAD_CHARACTER; } <YYINITIAL> {ID} { return ID; }
<YYINITIAL> "@\"" { yybegin(ID_STRING); }
<ID_STRING> {string_char}*"\"" { yybegin(YYINITIAL); return ID; }
<YYINITIAL> "\"" { yybegin(STRING_LITERAL); }
<STRING_LITERAL> {string_char}*"\"" { yybegin(YYINITIAL); return STRING_LITERAL_SINGLE; }
<YYINITIAL> {LINE_STRING} { return LINE_STRING; }
[^] { yybegin(YYINITIAL); return BAD_CHARACTER; }

View file

@ -42,9 +42,13 @@
zonFile ::= struct zonFile ::= struct
struct ::= DOT LBRACE (property (COMMA property)* COMMA?)? RBRACE struct ::= DOT LBRACE (property COMMA | property_placeholder COMMA?)* property? RBRACE
property ::= DOT ID EQ value property ::= DOT identifier EQ value
identifier ::= ID
property_placeholder ::= DOT? INTELLIJ_COMPLETION_DUMMY
value ::= struct | STRING_LITERAL value ::= struct | STRING_LITERAL

View file

@ -0,0 +1,42 @@
/*
* Copyright 2023 FalsePattern
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.falsepattern.zigbrains.zon.psi.impl;
import com.falsepattern.zigbrains.zon.psi.ZonIdentifier;
import com.falsepattern.zigbrains.zon.psi.ZonStruct;
import java.util.HashSet;
import java.util.Set;
public class ZonPsiImplUtil {
public static Set<String> getKeys(ZonStruct struct) {
var result = new HashSet<String>();
for (var property: struct.getPropertyList()) {
result.add(getText(property.getIdentifier()));
}
return result;
}
public static String getText(ZonIdentifier identifier) {
var idStr = identifier.getText();
if (idStr.startsWith("@")) {
return idStr.substring(2, idStr.length() - 2);
} else {
return idStr;
}
}
}

View file

@ -96,6 +96,9 @@
<lang.braceMatcher language="Zon" <lang.braceMatcher language="Zon"
implementationClass="com.falsepattern.zigbrains.zon.braces.ZonBraceMatcher"/> implementationClass="com.falsepattern.zigbrains.zon.braces.ZonBraceMatcher"/>
<completion.contributor language="Zon"
implementationClass="com.falsepattern.zigbrains.zon.completion.ZonCompletionContributor"/>
<!-- endregion Zon --> <!-- endregion Zon -->
</extensions> </extensions>