Skip to content

Commit

Permalink
Add completion contributor for import statements.
Browse files Browse the repository at this point in the history
Signed-off-by: Denys Dushyn <[email protected]>
  • Loading branch information
indegro committed Nov 18, 2023
1 parent e17a28b commit 45b7314
Show file tree
Hide file tree
Showing 9 changed files with 167 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public class ANTLRv4ASTFactory extends ASTFactory {
ruleElementTypeToPsiFactory.put(ANTLRv4TokenTypes.RULE_ELEMENT_TYPES.get(ANTLRv4Parser.RULE_modeSpec), ModeSpecNode.Factory.INSTANCE);
ruleElementTypeToPsiFactory.put(ANTLRv4TokenTypes.RULE_ELEMENT_TYPES.get(ANTLRv4Parser.RULE_action), AtAction.Factory.INSTANCE);
ruleElementTypeToPsiFactory.put(ANTLRv4TokenTypes.RULE_ELEMENT_TYPES.get(ANTLRv4Parser.RULE_identifier), TokenSpecNode.Factory.INSTANCE);
ruleElementTypeToPsiFactory.put(ANTLRv4TokenTypes.RULE_ELEMENT_TYPES.get(ANTLRv4Parser.RULE_delegateGrammar), DelegateGrammarNode.Factory.INSTANCE);
}

/** Create a FileElement for root or a parse tree CompositeElement (not
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package org.antlr.intellij.plugin.completion;

import com.intellij.codeInsight.completion.CompletionContributor;
import com.intellij.codeInsight.completion.CompletionType;
import com.intellij.patterns.PlatformPatterns;
import com.intellij.patterns.PsiElementPattern;
import org.antlr.intellij.plugin.psi.DelegateGrammarNode;
import org.antlr.intellij.plugin.psi.LexerRuleRefNode;
import org.antlr.intellij.plugin.psi.ParserRuleRefNode;

public class ANTLRv4CompletionContributor extends CompletionContributor {
// a user can type in an upper case letter
private final static PsiElementPattern.Capture<LexerRuleRefNode> LEXER_RULE_GRAMMAR_IMPORT_PATTERN =
PlatformPatterns
.psiElement(LexerRuleRefNode.class)
.withSuperParent(2, DelegateGrammarNode.class);

// a user can type in a lower case letter
private final static PsiElementPattern.Capture<ParserRuleRefNode> PARSE_RULE_GRAMMAR_IMPORT_PATTERN =
PlatformPatterns
.psiElement(ParserRuleRefNode.class)
.withSuperParent(2, DelegateGrammarNode.class);

public ANTLRv4CompletionContributor() {
extend(CompletionType.BASIC, LEXER_RULE_GRAMMAR_IMPORT_PATTERN, new ANTLRv4ImportCompletionProvider());
extend(CompletionType.BASIC, PARSE_RULE_GRAMMAR_IMPORT_PATTERN, new ANTLRv4ImportCompletionProvider());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package org.antlr.intellij.plugin.completion;

import com.intellij.codeInsight.completion.CompletionParameters;
import com.intellij.codeInsight.completion.CompletionProvider;
import com.intellij.codeInsight.completion.CompletionResultSet;
import com.intellij.codeInsight.lookup.LookupElementBuilder;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.impl.source.PsiFileImpl;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.ProcessingContext;
import org.antlr.intellij.plugin.ANTLRv4FileRoot;
import org.antlr.intellij.plugin.psi.DelegateGrammarNode;
import org.jetbrains.annotations.NotNull;

import java.util.Arrays;
import java.util.Objects;

public class ANTLRv4ImportCompletionProvider extends CompletionProvider<CompletionParameters> {
@Override
protected void addCompletions(@NotNull CompletionParameters completionParameters, @NotNull ProcessingContext processingContext, @NotNull CompletionResultSet completionResultSet) {
PsiElement position = completionParameters.getPosition();
DelegateGrammarNode delegateGrammarNode = PsiTreeUtil.getParentOfType(position, DelegateGrammarNode.class);
if (delegateGrammarNode == null) return;

PsiFile containingFile = completionParameters.getOriginalFile();
PsiDirectory containingDirectory = containingFile.getContainingDirectory();
if (containingDirectory == null) return; // scratch file case

CompletionResultSet resultSet = completionResultSet.caseInsensitive();
Arrays
.stream(containingDirectory.getChildren())
.map(psiElement -> {
if (psiElement instanceof ANTLRv4FileRoot) {
return (ANTLRv4FileRoot) psiElement;
} else {
return null;
}
})
.filter(Objects::nonNull)
.map(PsiFileImpl::getName)
.filter(name -> !name.equals(containingFile.getName()))
.map(name -> name.replace(".g4", ""))
.forEach(name -> resultSet.addElement(LookupElementBuilder.create(name)));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.antlr.intellij.plugin.psi;

import com.intellij.extapi.psi.ASTWrapperPsiElement;
import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElement;
import org.antlr.intellij.adaptor.parser.PsiElementFactory;
import org.jetbrains.annotations.NotNull;

public class DelegateGrammarNode extends ASTWrapperPsiElement {
public DelegateGrammarNode(@NotNull ASTNode node) {
super(node);
}

public static class Factory implements PsiElementFactory {
public static Factory INSTANCE = new Factory();

@Override
public PsiElement createElement(ASTNode node) {
return new DelegateGrammarNode(node);
}
}
}
2 changes: 2 additions & 0 deletions src/main/resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -173,5 +173,7 @@ For really big files and slow grammars, there is an appreciable delay when displ
instance="org.antlr.intellij.plugin.configdialogs.ANTLRv4ProjectSettings"/>
<projectService serviceImplementation="org.antlr.intellij.plugin.configdialogs.ANTLRv4GrammarPropertiesComponent"/>
<lang.refactoringSupport language="ANTLRv4" implementationClass="org.antlr.intellij.plugin.refactor.ANTLRv4RefactoringSupport"/>
<completion.contributor language="ANTLRv4"
implementationClass="org.antlr.intellij.plugin.completion.ANTLRv4CompletionContributor"/>
</extensions>
</idea-plugin>
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package org.antlr.intellij.plugin.completion;

import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.testFramework.fixtures.BasePlatformTestCase;
import org.antlr.intellij.plugin.ANTLRv4FileType;
import org.antlr.intellij.plugin.TestUtils;

import java.util.Arrays;
import java.util.List;

public class ANTLRv4CompletionContributorTest extends BasePlatformTestCase {
public void test_import_completion() {
myFixture.copyDirectoryToProject("/import_parser_rules", "");
myFixture.configureByText(
ANTLRv4FileType.INSTANCE,
"""
parser grammar ANTLRv4Parser;
import <caret>
""");
List<String> completions = Arrays.stream(myFixture.completeBasic()).map(LookupElement::getLookupString).toList();
assertNotNull(completions);
assertSize(3, completions);
assertTrue(completions.contains("Grammar"));
assertTrue(completions.contains("GrammarExpr"));
assertTrue(completions.contains("CommonLexerRules"));

}

public void test_import_completion_is_case_insensitive_lower_case() {
myFixture.copyDirectoryToProject("/import_parser_rules", "");
myFixture.configureByText(
ANTLRv4FileType.INSTANCE,
"""
parser grammar ANTLRv4Parser;
import g<caret>
""");
List<String> completions = Arrays.stream(myFixture.completeBasic()).map(LookupElement::getLookupString).toList();
assertNotNull(completions);
assertSize(2, completions);
assertTrue(completions.contains("Grammar"));
assertTrue(completions.contains("GrammarExpr"));
}

public void test_import_completion_is_case_insensitive_upper_case() {
myFixture.copyDirectoryToProject("/import_parser_rules", "");
myFixture.configureByText(
ANTLRv4FileType.INSTANCE,
"""
parser grammar ANTLRv4Parser;
import G<caret>
""");
List<String> completions = Arrays.stream(myFixture.completeBasic()).map(LookupElement::getLookupString).toList();
assertNotNull(completions);
assertSize(2, completions);
assertTrue(completions.contains("Grammar"));
assertTrue(completions.contains("GrammarExpr"));
}

protected String getTestDataPath() {
return "src/test/resources/completion";
}

@Override
protected void tearDown() throws Exception {
TestUtils.tearDownIgnoringObjectNotDisposedException(() -> super.tearDown());
}
}
Empty file.
Empty file.
Empty file.

0 comments on commit 45b7314

Please sign in to comment.