Skip to content

Commit

Permalink
Define structure: Initial stab at a completion contributor
Browse files Browse the repository at this point in the history
  • Loading branch information
pbroadbery committed Feb 14, 2018
1 parent c25580d commit 8e2ccfb
Show file tree
Hide file tree
Showing 27 changed files with 564 additions and 114 deletions.
39 changes: 22 additions & 17 deletions TODO.txt
Original file line number Diff line number Diff line change
@@ -1,27 +1,26 @@

Lexer:
Floating Point
Input file runner - use module to get SDK

Code Completion:
- Types in Type and "with" context
- Types where suitable as arguments to a fn
- names:
from lambdas
from commas (dependent types)

Extensions:
"Goto definition"
Improve structure view - should be prettier

Definition Trees & structure
Lexer: Floating Point

References:
- Use PsiPolyVariantReference for declaration/implementation stuff

Module building:
-- Idea here is that "initially", we'll support one type of module,
the checked out git repository.

Grammar:
Error recovery

PileMode
-- continuation lines

Use intellij log faciities

tests for rename: CodeInsightTestUtil.doInlineRename(new VariableInplaceRenameHandler(), newName, getEditor(), element);


Expand All @@ -31,17 +30,23 @@ Lexer:
DONE Comments
DONE Pre/Post Doc
DONE Bracket matching
DONE Definition Trees & structure

DONE Extensions: "Goto definition"

--------------------
SPAD differences:
Module building:
-- Idea here is that "initially", we'll support one type of module,
the checked out git repository.

++ instead of +++ (which will be annoying...)
')' system commands
Use intellij log faciities

select: Not a keyword(!)
--------------------
SPAD differences:

Quotes have lispish semantics/tokeniser rules
OK: ++ instead of +++ (which will be annoying...)
OK: ')' system commands
OK: select: Not a keyword(!)
OK: Quotes have lispish semantics/tokeniser rules

-----

Expand Down
10 changes: 9 additions & 1 deletion aldorparse.iml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" scope="TEST" name="org.mockito:mockito-all:1.10.19" level="project" />
<orderEntry type="library" name="lib" level="project" />
<orderEntry type="library" name="com.google.guava:guava:19.0" level="project" />
<orderEntry type="module-library">
<library>
Expand Down Expand Up @@ -74,5 +73,14 @@
<SOURCES />
</library>
</orderEntry>
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$MODULE_DIR$/lib/aldor-jps-plugin.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
</component>
</module>
2 changes: 1 addition & 1 deletion resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@

<configurationType implementation="aldor.spad.runconfiguration.SpadInputRunConfigurationType" />
<runConfigurationProducer implementation = "aldor.spad.runconfiguration.SpadInputRunConfigurationProducer"/>

<completion.contributor language="Aldor" implementationClass="aldor.editor.completion.AldorCompletionContributor"/>
<!-- Test language -->
<fileTypeFactory implementation="aldor.expression.ExpressionFileTypeFactory"/>
<lang.parserDefinition language="Expression" implementationClass="aldor.expression.ExpressionParserDefinition"/>
Expand Down
134 changes: 134 additions & 0 deletions src/main/java/aldor/editor/completion/AldorCompletionContributor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package aldor.editor.completion;

import aldor.language.SpadLanguage;
import aldor.psi.AldorId;
import aldor.psi.elements.AldorTypes;
import aldor.spad.SpadLibrary;
import aldor.spad.SpadLibraryManager;
import aldor.syntax.Syntax;
import aldor.syntax.SyntaxUtils;
import aldor.syntax.components.Apply;
import aldor.syntax.components.Id;
import aldor.ui.AldorIcons;
import com.google.common.annotations.VisibleForTesting;
import com.intellij.codeInsight.completion.AddSpaceInsertHandler;
import com.intellij.codeInsight.completion.CompletionContributor;
import com.intellij.codeInsight.completion.CompletionInitializationContext;
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.completion.util.ParenthesesInsertHandler;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupElementBuilder;
import com.intellij.patterns.ElementPattern;
import com.intellij.patterns.PatternCondition;
import com.intellij.patterns.StandardPatterns;
import com.intellij.psi.PsiElement;
import com.intellij.util.ProcessingContext;
import org.jetbrains.annotations.NotNull;

import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.intellij.patterns.PlatformPatterns.psiElement;
import static com.intellij.patterns.StandardPatterns.object;
import static com.intellij.patterns.StandardPatterns.or;

public class AldorCompletionContributor extends CompletionContributor {

public AldorCompletionContributor() {

ElementPattern<PsiElement> typeElement = psiElement().withElementType(or(object(AldorTypes.TYPE), object(AldorTypes.TYPE_E_12)));
ElementPattern<PsiElement> typeElementStopPattern = psiElement().withParent(StandardPatterns.not(AldorPatterns.isFirstChild()));

ElementPattern<? extends PsiElement> insideTypeElementPattern = psiElement(AldorId.class).withLanguage(SpadLanguage.INSTANCE).inside(true, typeElement, typeElementStopPattern);

extend(CompletionType.BASIC, psiElement(), idCompletion());
}

private CompletionProvider<CompletionParameters> idCompletion() {
return new CompletionProvider<CompletionParameters>() {
@Override
protected void addCompletions(@NotNull CompletionParameters parameters,
ProcessingContext context,
@NotNull CompletionResultSet result) {
List<LookupElement> element = allTypes(parameters);
result.addAllElements(element);
}
};
}

public List<LookupElement> allTypes(CompletionParameters parameters) {
PsiElement elt = parameters.getPosition();
SpadLibrary spadLibrary = SpadLibraryManager.instance().spadLibraryForElement(elt);
return allTypes(spadLibrary);
}

@VisibleForTesting
public static List<LookupElement> allTypes(SpadLibrary spadLibrary) {
List<Syntax> allTypes = spadLibrary.allTypes();
return allTypes.stream()
.flatMap(e -> createLookupElement(spadLibrary, e).map(Stream::of).orElse(Stream.empty()))
.collect(Collectors.toList());
}

private static Optional<LookupElement> createLookupElement(SpadLibrary spadLibrary, Syntax syntax) {
Optional<Id> id = SyntaxUtils.leadingId(syntax).maybeAs(Id.class);
if (!id.isPresent()) {
return Optional.empty();
}
else {
Optional<Apply> isApply = syntax.maybeAs(Apply.class);
if (isApply.filter(apply -> apply.arguments().size() > 1).isPresent()) {
return Optional.of(LookupElementBuilder.create(id.get().symbol())
.withInsertHandler(ParenthesesInsertHandler.WITH_PARAMETERS)
.withIcon(AldorIcons.IDENTIFIER)
.withTailText(tailTextForElement(spadLibrary, id.get()), true)
);
}
if (isApply.filter(apply -> apply.arguments().size() == 1).isPresent()) {
return Optional.of(LookupElementBuilder.create(id.get().symbol())
.withInsertHandler(AddSpaceInsertHandler.INSTANCE_WITH_AUTO_POPUP));
}
else {
return Optional.of(LookupElementBuilder.create(id.get().symbol())
.withTailText(tailTextForElement(spadLibrary, id.get()), true)
);
}
}
}

private static String tailTextForElement(SpadLibrary spadLibrary, Id symbol) {
return " (" + spadLibrary.definingFile(symbol) + ")";
}

@Override
public void beforeCompletion(@NotNull CompletionInitializationContext context) {
super.beforeCompletion(context);
}

@Override
public void duringCompletion(@NotNull CompletionInitializationContext context) {
super.duringCompletion(context);
}

@Override
public void fillCompletionVariants(@NotNull CompletionParameters parameters, @NotNull CompletionResultSet result) {
super.fillCompletionVariants(parameters, result);
}

private static final class AldorPatterns {
static ElementPattern<PsiElement> isFirstChild() {
//noinspection InnerClassTooDeeplyNested
return psiElement().with(new PatternCondition<PsiElement>("firstChild") {
@Override
public boolean accepts(@NotNull PsiElement psiElement, ProcessingContext context) {
return (psiElement.getParent() != null) && psiElement.getParent().getFirstChild().equals(psiElement);
}
});
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import aldor.syntax.SyntaxPrinter;
import aldor.syntax.SyntaxPsiParser;
import aldor.syntax.SyntaxUtils;
import aldor.syntax.components.Apply;
import com.google.common.collect.Sets;
import com.intellij.ide.hierarchy.HierarchyNodeDescriptor;
import com.intellij.ide.hierarchy.HierarchyTreeStructure;
Expand All @@ -16,20 +15,20 @@
import com.intellij.psi.SmartPointerManager;
import com.intellij.psi.SmartPsiElementPointer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.List;
import java.util.Set;
import java.util.stream.Stream;

import static aldor.syntax.SyntaxPsiParser.SurroundType.Leading;
import static aldor.syntax.SyntaxUtils.psiElementFromSyntax;

public final class AldorParentCategoryHierarchyTreeStructure extends HierarchyTreeStructure {
private static final Object[] EMPTY_ARRAY = new Object[0];
private final SmartPsiElementPointer<PsiElement> smartPointer;

private AldorParentCategoryHierarchyTreeStructure(Project project, @NotNull Syntax syntax) {
super(project, createBaseNodeDescriptor(project, null, syntax));
super(project, createBaseNodeDescriptor(project, syntax));
this.smartPointer = SmartPointerManager.getInstance(project).createSmartPsiElementPointer(psiElementFromSyntax(syntax));
}

Expand All @@ -45,8 +44,8 @@ public static HierarchyTreeStructure createRootTreeStructure(Project project, @N
return new AldorParentCategoryHierarchyTreeStructure(project, syntax);
}

private static HierarchyNodeDescriptor createBaseNodeDescriptor(Project project, NodeDescriptor<PsiElement> parentDescriptor, Syntax syntax) {
return new AldorHierarchyNodeDescriptor(project, parentDescriptor, psiElementFromSyntax(syntax), syntax, true);
private static HierarchyNodeDescriptor createBaseNodeDescriptor(Project project, @NotNull Syntax syntax) {
return new AldorHierarchyNodeDescriptor(project, null, psiElementFromSyntax(syntax), syntax, true);
}

private static HierarchyNodeDescriptor createNodeDescriptor(Project project, NodeDescriptor<PsiElement> parentDescriptor, Syntax syntax) {
Expand Down Expand Up @@ -95,20 +94,4 @@ protected Object[] buildChildren(@NotNull HierarchyNodeDescriptor descriptor) {

return Stream.concat(parentNodes, operationNodes).toArray();
}

@Nullable
private static PsiElement psiElementFromSyntax(Syntax syntax) {
Syntax syntax1 = syntax;
while (true) {
if (syntax1.psiElement() != null) {
return syntax1.psiElement();
}
if (syntax1.is(Apply.class)) {
syntax1 = syntax1.as(Apply.class).operator();
}
else {
return null;
}
}
}
}
3 changes: 2 additions & 1 deletion src/main/java/aldor/psi/AldorDeclare.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
import aldor.psi.stub.AldorDeclareStub;
import com.intellij.psi.NavigatablePsiElement;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiNamedElement;
import com.intellij.psi.StubBasedPsiElement;
import org.jetbrains.annotations.Nullable;

public interface AldorDeclare extends StubBasedPsiElement<AldorDeclareStub>, NavigatablePsiElement {
public interface AldorDeclare extends StubBasedPsiElement<AldorDeclareStub>, NavigatablePsiElement, PsiNamedElement {

@Nullable
AldorDeclareStub getGreenStub();
Expand Down
2 changes: 0 additions & 2 deletions src/main/java/aldor/psi/elements/AldorElementTypeFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -83,14 +83,12 @@ public static final class FileStubElementType extends IStubFileElementType<PsiFi
private FileStubElementType(Language language, int stubVersion) {
super("aldorFile", language);
this.stubVersion = stubVersion;

}

@Override
public int getStubVersion() {
return stubVersion;
}

}

}
15 changes: 15 additions & 0 deletions src/main/java/aldor/psi/impl/AldorColonExprMixin.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@
import aldor.psi.stub.AldorDeclareStub;
import aldor.syntax.Syntax;
import aldor.syntax.SyntaxPsiParser;
import aldor.syntax.SyntaxUtils;
import aldor.syntax.components.Id;
import com.intellij.extapi.psi.StubBasedPsiElementBase;
import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElement;
import com.intellij.psi.ResolveState;
import com.intellij.psi.scope.PsiScopeProcessor;
import com.intellij.psi.stubs.IStubElementType;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;

public abstract class AldorColonExprMixin extends StubBasedPsiElementBase<AldorDeclareStub> implements AldorColonExpr, AldorDeclare {
Expand Down Expand Up @@ -45,6 +48,12 @@ public boolean processDeclarations(@NotNull PsiScopeProcessor processor, @NotNul
return true;
}

@Override
public String getName() {
return SyntaxUtils.leadingId(SyntaxPsiParser.parse(lhs())).maybeAs(Id.class).map(Id::symbol).orElse(null);

}

@Override
public PsiElement lhs() {
return this.getExprList().get(0);
Expand All @@ -54,4 +63,10 @@ public PsiElement lhs() {
public PsiElement rhs() {
return this.getExprList().get(1);
}

@SuppressWarnings("ThrowsRuntimeException")
@Override
public PsiElement setName(@NotNull String name) throws IncorrectOperationException {
throw new IncorrectOperationException("no rename for definitions (yet)");
}
}
Loading

0 comments on commit 8e2ccfb

Please sign in to comment.