Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add completion contributor for import statements. #676

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.