Skip to content
Snippets Groups Projects
Commit 9e03fb94 authored by Johannes Mey's avatar Johannes Mey
Browse files

work on the structure view.

parent 9c1f1ddd
Branches
Tags
1 merge request!5Resolve "Structure View Support"
Pipeline #12000 passed
Showing
with 509 additions and 30 deletions
package org.jastadd.tooling.aspect;
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.util.PsiTreeUtil;
import org.jastadd.tooling.aspect.psi.AspectFile;
import org.jastadd.tooling.aspect.psi.JastAddAspectAspectBodyDeclaration;
import org.jastadd.tooling.aspect.psi.JastAddAspectAspectDeclaration;
import org.jastadd.tooling.aspect.psi.JastAddAspectTypeDeclaration;
import org.jastadd.tooling.aspect.psi.impl.JastAddAspectAspectDeclarationImpl;
import org.jastadd.tooling.aspect.psi.impl.JastAddAspectAspectInhAttributeDeclarationImpl;
import org.jastadd.tooling.aspect.psi.impl.JastAddAspectAspectSynAttributeDeclarationImpl;
import org.jastadd.tooling.aspect.psi.impl.JastAddAspectCollectionAttributeImpl;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
public class AspectStructureViewElement implements StructureViewTreeElement, SortableTreeElement {
private final NavigatablePsiElement myElement;
public AspectStructureViewElement(NavigatablePsiElement element) {
this.myElement = element;
}
@Override
public NavigatablePsiElement 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() {
if (myElement instanceof AspectFile) {
return PsiTreeUtil.getChildrenOfTypeAsList(myElement, JastAddAspectTypeDeclaration.class)
.stream()
.map(JastAddAspectTypeDeclaration::getAspectDeclaration)
.filter(Objects::nonNull)
.map(decl -> new AspectStructureViewElement((JastAddAspectAspectDeclarationImpl) decl))
.toArray(TreeElement[]::new);
} else if (myElement instanceof JastAddAspectAspectDeclaration) {
return PsiTreeUtil.getChildrenOfTypeAsList(((JastAddAspectAspectDeclaration) myElement).getAspectBody(), JastAddAspectAspectBodyDeclaration.class)
.stream()
.map(decl -> {
if (decl.getAspectSynAttributeDeclaration() != null) {
return new AspectStructureViewElement((JastAddAspectAspectSynAttributeDeclarationImpl) decl.getAspectSynAttributeDeclaration());
} else if (decl.getAspectInhAttributeDeclaration() != null) {
return new AspectStructureViewElement((JastAddAspectAspectInhAttributeDeclarationImpl) decl.getAspectInhAttributeDeclaration());
} else if (decl.getCollectionAttribute() != null) {
return new AspectStructureViewElement((JastAddAspectCollectionAttributeImpl) decl.getCollectionAttribute());
}
return null;
})
.filter(Objects::nonNull)
.toArray(TreeElement[]::new);
}
return EMPTY_ARRAY;
}
}
package org.jastadd.tooling.aspect;
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;
public class AspectStructureViewFactory implements PsiStructureViewFactory {
@Nullable
@Override
public StructureViewBuilder getStructureViewBuilder(@NotNull final PsiFile psiFile) {
return new TreeBasedStructureViewBuilder() {
@NotNull
@Override
public StructureViewModel createStructureViewModel(@Nullable Editor editor) {
return new AspectStructureViewModel(psiFile);
}
};
}
}
package org.jastadd.tooling.aspect;
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.Filter;
import com.intellij.ide.util.treeView.smartTree.Grouper;
import com.intellij.ide.util.treeView.smartTree.Sorter;
import com.intellij.psi.PsiFile;
import org.jastadd.tooling.aspect.psi.JastAddAspectAspectDeclaration;
import org.jastadd.tooling.aspect.psi.JastAddAspectAttribute;
import org.jetbrains.annotations.NotNull;
public class AspectStructureViewModel extends StructureViewModelBase implements
StructureViewModel.ElementInfoProvider {
public AspectStructureViewModel(PsiFile psiFile) {
super(psiFile, new AspectStructureViewElement(psiFile));
}
@NotNull
public Sorter @NotNull [] getSorters() {
return new Sorter[]{Sorter.ALPHA_SORTER};
}
@Override
public boolean isAlwaysShowsPlus(StructureViewTreeElement element) {
return element.getValue() instanceof JastAddAspectAspectDeclaration;
}
@Override
public boolean isAlwaysLeaf(StructureViewTreeElement element) {
return element.getValue() instanceof JastAddAspectAttribute;
}
@Override
public Grouper @NotNull [] getGroupers() {
// TODO group by member type
return new Grouper[]{};
}
@Override
public Filter @NotNull [] getFilters() {
return new Filter[]{new JastAddAspectAttributeFilter()};
}
}
package org.jastadd.tooling.aspect;
import com.intellij.ide.util.treeView.smartTree.ActionPresentation;
import com.intellij.ide.util.treeView.smartTree.ActionPresentationData;
import com.intellij.ide.util.treeView.smartTree.Filter;
import com.intellij.ide.util.treeView.smartTree.TreeElement;
import org.jastadd.tooling.aspect.psi.JastAddAspectAspectDeclaration;
import org.jastadd.tooling.aspect.psi.JastAddAspectAttribute;
import org.jastadd.tooling.util.JastAddIcons;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
public class JastAddAspectAttributeFilter implements Filter {
@NonNls
public static final String ID = "HIDE_ATTRIBUTE";
@Override
public boolean isVisible(TreeElement treeNode) {
if (treeNode instanceof AspectStructureViewElement) {
return !(((AspectStructureViewElement) treeNode).getValue() instanceof JastAddAspectAttribute);
}
else {
return true;
}
}
@Override
@NotNull
public ActionPresentation getPresentation() {
// TODO use i18n and string bundle like JavaStructureViewBundle
return new ActionPresentationData("Hide Attributes", null, JastAddIcons.ATTRIBUTE);
}
@Override
@NotNull
public String getName() {
return ID;
}
@Override
public boolean isReverted() {
return false;
}
}
......@@ -34,4 +34,13 @@ public class AspectElementFactory {
return (AspectFile) PsiFileFactory.getInstance(project).
createFileFromText(name, AspectFileType.INSTANCE, text);
}
public static JastAddAspectAspectDeclaration createAspectDeclaration(Project project, String name) {
final AspectFile file = createFile(project, "aspect " + name + "{}");
PsiElement result = file.getFirstChild().findElementAt(20);
if (result != null) {
return (JastAddAspectAspectDeclaration) result.getParent().getParent();
}
return null;
}
}
package org.jastadd.tooling.aspect.psi;
import com.intellij.psi.PsiElement;
import org.jastadd.tooling.grammar.psi.GrammarNamedElement;
public interface JastAddAspectAttribute extends PsiElement {
public interface JastAddAspectAttribute extends PsiElement, GrammarNamedElement {
}
package org.jastadd.tooling.aspect.psi.impl;
import com.intellij.lang.ASTNode;
import com.intellij.navigation.ItemPresentation;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.psi.PsiElement;
import org.jastadd.tooling.aspect.psi.AspectElementFactory;
import org.jastadd.tooling.aspect.psi.JastAddAspectAspectDeclaration;
import org.jastadd.tooling.aspect.psi.JastAddAspectAspectName;
import org.jastadd.tooling.grammar.psi.GrammarNamedElement;
import org.jastadd.tooling.grammar.psi.impl.GrammarNamedElementImpl;
import org.jastadd.tooling.util.JastAddIcons;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
public class JastAddAspectAspectDeclarationImplExtension extends GrammarNamedElementImpl implements GrammarNamedElement {
public JastAddAspectAspectDeclarationImplExtension(@NotNull ASTNode node) {
super(node);
}
public String getName() {
return getNameIdentifier().getText();
}
public PsiElement setName(@NotNull String newName) {
ASTNode identifierNode = getNameIdentifier().getNode().getFirstChildNode();
if (identifierNode != null) {
JastAddAspectAspectDeclaration name = AspectElementFactory.createAspectDeclaration(getProject(), newName);
assert name != null; // we know the name is not null because we always create the same one
ASTNode newNameIdentifierNode = name.getAspectName().getNode().getFirstChildNode();
getNameIdentifier().getNode().replaceChild(identifierNode, newNameIdentifierNode);
}
return this;
}
@Override
@NotNull
public JastAddAspectAspectName getNameIdentifier() {
return ((JastAddAspectAspectDeclaration) getNode().getPsi()).getAspectName();
}
@Override
public ItemPresentation getPresentation() {
return new ItemPresentation() {
@Nullable
@Override
public String getPresentableText() {
return "aspect " + getName();
}
@Override
public String getLocationString() {
Document document = FileDocumentManager.getInstance().getDocument(getNode().getPsi().getContainingFile().getVirtualFile());
return document != null ? "l." + document.getLineNumber(getTextRange().getStartOffset() + 1) : "";
}
@Override
public Icon getIcon(boolean unused) {
return JastAddIcons.FILE;
}
};
}
}
......@@ -3,6 +3,7 @@ package org.jastadd.tooling.aspect.psi.impl;
import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElement;
import org.jastadd.tooling.aspect.psi.AspectElementFactory;
import org.jastadd.tooling.aspect.psi.AspectTypes;
import org.jastadd.tooling.aspect.psi.JastAddAspectAstTypeName;
import org.jastadd.tooling.grammar.psi.GrammarNamedElement;
import org.jastadd.tooling.grammar.psi.impl.GrammarNamedElementImpl;
......@@ -15,18 +16,21 @@ public class JastAddAspectAstTypeNameImplExtension extends GrammarNamedElementIm
}
public String getName() {
// this finds the *first* ID, which is what we want
return getNode().getText();
ASTNode nameNode = getNode().findChildByType(AspectTypes.AST_TYPE_NAME);
if (nameNode != null) {
return nameNode.getText();
} else {
return null;
}
}
public PsiElement setName(@NotNull String newName) {
// FIXME this can break the grammar when the type is used in an unnamed component (and in many other cases probably)
ASTNode keyNode = getNode().getFirstChildNode();
if (keyNode != null) {
ASTNode nameNode = getNode().findChildByType(AspectTypes.AST_TYPE_NAME);
if (nameNode != null) {
JastAddAspectAstTypeName name = AspectElementFactory.createAstTypeName(getProject(), newName);
assert name != null; // we know the name is not null because we always create the same one
ASTNode newKeyNode = name.getNode().getFirstChildNode();
getNode().replaceChild(keyNode, newKeyNode);
ASTNode newNameNode = name.getNode().getFirstChildNode();
getNode().replaceChild(nameNode, newNameNode);
}
return this;
}
......
......@@ -3,6 +3,7 @@ package org.jastadd.tooling.aspect.psi.impl;
import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElement;
import org.jastadd.tooling.aspect.psi.AspectElementFactory;
import org.jastadd.tooling.aspect.psi.AspectTypes;
import org.jastadd.tooling.aspect.psi.JastAddAspectClassOrInterfaceType;
import org.jastadd.tooling.grammar.psi.GrammarNamedElement;
import org.jastadd.tooling.grammar.psi.impl.GrammarNamedElementImpl;
......@@ -16,22 +17,28 @@ public class JastAddAspectClassOrInterfaceTypeImplExtension extends GrammarNamed
public String getName() {
// this finds the *first* ID, which is what we want
return getNode().getText();
ASTNode nameNode = getNode().findChildByType(AspectTypes.JAVA_IDENTIFIER);
if (nameNode != null) {
return nameNode.getText();
} else {
return null;
}
}
public PsiElement setName(@NotNull String newName) {
// FIXME this can break the grammar when the type is used in an unnamed component (and in many other cases probably)
ASTNode keyNode = getNode().getFirstChildNode();
if (keyNode != null) {
ASTNode nameNode = getNode().findChildByType(AspectTypes.JAVA_IDENTIFIER);
if (nameNode != null) {
JastAddAspectClassOrInterfaceType name = AspectElementFactory.createClassOrInterfaceType(getProject(), newName);
assert name != null; // we know the name is not null because we always create the same one
ASTNode newKeyNode = name.getNode().getFirstChildNode();
getNode().replaceChild(keyNode, newKeyNode);
assert !name.getJavaIdentifierList().isEmpty(); // we know there is always one name - the class name
ASTNode newKeyNode = name.getJavaIdentifierList().get(0).getNode().getFirstChildNode();
getNode().replaceChild(nameNode, newKeyNode);
}
return this;
}
public PsiElement getNameIdentifier() {
// the entire thing is the identifier
return getNode().getPsi();
}
......
package org.jastadd.tooling.aspect.psi.impl;
import com.intellij.extapi.psi.ASTWrapperPsiElement;
import com.intellij.lang.ASTNode;
import com.intellij.navigation.ItemPresentation;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.util.NlsSafe;
import com.intellij.psi.PsiElement;
import com.intellij.util.IncorrectOperationException;
import org.jastadd.tooling.aspect.psi.AspectTypes;
import org.jastadd.tooling.aspect.psi.JastAddAspectAttribute;
import org.jastadd.tooling.aspect.psi.JastAddAspectCollectionAttribute;
import org.jastadd.tooling.grammar.psi.impl.GrammarNamedElementImpl;
import org.jastadd.tooling.util.JastAddIcons;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public abstract class JastAddAspectCollAttributeImpl extends ASTWrapperPsiElement implements JastAddAspectAttribute {
import javax.swing.*;
public abstract class JastAddAspectCollAttributeImpl extends GrammarNamedElementImpl implements JastAddAspectAttribute {
public JastAddAspectCollAttributeImpl(@NotNull ASTNode node) {
super(node);
}
public String getName() {
// this finds the *first* ID, which is what we want
ASTNode nameNode = getNode().findChildByType(AspectTypes.ATTRIBUTE_NAME);
if (nameNode != null) {
return nameNode.getText();
} else {
return null;
}
}
@Override
public @Nullable PsiElement getNameIdentifier() {
return ((JastAddAspectCollectionAttribute) this).getAttributeName();
}
@Override
public PsiElement setName(@NlsSafe @NotNull String name) throws IncorrectOperationException {
throw new IncorrectOperationException("Renaming collection attributes is not supported.");
}
@Override
public @Nullable PsiElement getIdentifyingElement() {
return this;
}
@Override
public ItemPresentation getPresentation() {
return new ItemPresentation() {
@Nullable
@Override
public String getPresentableText() {
return "coll " + getName();
}
@Override
public String getLocationString() {
Document document = FileDocumentManager.getInstance().getDocument(getNode().getPsi().getContainingFile().getVirtualFile());
return document != null ? "l." + document.getLineNumber(getTextRange().getStartOffset() + 1) : "";
}
@Override
public Icon getIcon(boolean unused) {
return JastAddIcons.COL_ATTRIBUTE;
}
};
}
}
package org.jastadd.tooling.aspect.psi.impl;
import com.intellij.extapi.psi.ASTWrapperPsiElement;
import com.intellij.lang.ASTNode;
import com.intellij.navigation.ItemPresentation;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.util.NlsSafe;
import com.intellij.psi.PsiElement;
import com.intellij.util.IncorrectOperationException;
import org.jastadd.tooling.aspect.psi.AspectTypes;
import org.jastadd.tooling.aspect.psi.JastAddAspectAspectInhAttributeDeclaration;
import org.jastadd.tooling.aspect.psi.JastAddAspectAttribute;
import org.jastadd.tooling.grammar.psi.impl.GrammarNamedElementImpl;
import org.jastadd.tooling.util.JastAddIcons;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public abstract class JastAddAspectInhAttributeImpl extends ASTWrapperPsiElement implements JastAddAspectAttribute {
import javax.swing.*;
public abstract class JastAddAspectInhAttributeImpl extends GrammarNamedElementImpl implements JastAddAspectAttribute {
public JastAddAspectInhAttributeImpl(@NotNull ASTNode node) {
super(node);
}
public String getName() {
// this finds the *first* ID, which is what we want
ASTNode nameNode = getNode().findChildByType(AspectTypes.ATTRIBUTE_NAME);
if (nameNode != null) {
return nameNode.getText();
} else {
return null;
}
}
@Override
public @Nullable PsiElement getNameIdentifier() {
return ((JastAddAspectAspectInhAttributeDeclaration) this).getAttributeName();
}
@Override
public PsiElement setName(@NlsSafe @NotNull String name) throws IncorrectOperationException {
throw new IncorrectOperationException("Renaming collection attributes is not supported.");
}
@Override
public @Nullable PsiElement getIdentifyingElement() {
return this;
}
@Override
public ItemPresentation getPresentation() {
return new ItemPresentation() {
@Nullable
@Override
public String getPresentableText() {
return "inh " + getName();
}
@Override
public String getLocationString() {
Document document = FileDocumentManager.getInstance().getDocument(getNode().getPsi().getContainingFile().getVirtualFile());
return document != null ? "l." + document.getLineNumber(getTextRange().getStartOffset() + 1) : "";
}
@Override
public Icon getIcon(boolean unused) {
return JastAddIcons.INH_ATTRIBUTE;
}
};
}
}
package org.jastadd.tooling.aspect.psi.impl;
import com.intellij.extapi.psi.ASTWrapperPsiElement;
import com.intellij.lang.ASTNode;
import com.intellij.navigation.ItemPresentation;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.util.NlsSafe;
import com.intellij.psi.PsiElement;
import com.intellij.util.IncorrectOperationException;
import org.jastadd.tooling.aspect.psi.AspectTypes;
import org.jastadd.tooling.aspect.psi.JastAddAspectAspectSynAttributeDeclaration;
import org.jastadd.tooling.aspect.psi.JastAddAspectAttribute;
import org.jastadd.tooling.grammar.psi.impl.GrammarNamedElementImpl;
import org.jastadd.tooling.util.JastAddIcons;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public abstract class JastAddAspectSynAttributeImpl extends ASTWrapperPsiElement implements JastAddAspectAttribute {
import javax.swing.*;
public abstract class JastAddAspectSynAttributeImpl extends GrammarNamedElementImpl implements JastAddAspectAttribute {
public JastAddAspectSynAttributeImpl(@NotNull ASTNode node) {
super(node);
}
public String getName() {
// this finds the *first* ID, which is what we want
ASTNode nameNode = getNode().findChildByType(AspectTypes.ATTRIBUTE_NAME);
if (nameNode != null) {
return nameNode.getText();
} else {
return null;
}
}
@Override
public @Nullable PsiElement getNameIdentifier() {
return ((JastAddAspectAspectSynAttributeDeclaration) this).getAttributeName();
}
@Override
public PsiElement setName(@NlsSafe @NotNull String name) throws IncorrectOperationException {
throw new IncorrectOperationException("Renaming collection attributes is not supported.");
}
@Override
public @Nullable PsiElement getIdentifyingElement() {
return this;
}
@Override
public ItemPresentation getPresentation() {
return new ItemPresentation() {
@Nullable
@Override
public String getPresentableText() {
return "syn " + getName();
}
@Override
public String getLocationString() {
Document document = FileDocumentManager.getInstance().getDocument(getNode().getPsi().getContainingFile().getVirtualFile());
return document != null ? "l." + document.getLineNumber(getTextRange().getStartOffset() + 1) : "";
}
@Override
public Icon getIcon(boolean unused) {
return JastAddIcons.SYN_ATTRIBUTE;
}
};
}
}
......@@ -16,28 +16,28 @@ public class GrammarTypeDeclImplExtension extends GrammarNamedElementImpl implem
public String getName() {
// this finds the *first* ID, which is what we want
ASTNode keyNode = getNode().findChildByType(GrammarTypes.TYPE_NAME);
if (keyNode != null) {
return keyNode.getText();
ASTNode nameNode = getNode().findChildByType(GrammarTypes.TYPE_NAME);
if (nameNode != null) {
return nameNode.getText();
} else {
return null;
}
}
public PsiElement setName(@NotNull String newName) {
ASTNode keyNode = getNode().findChildByType(GrammarTypes.TYPE_NAME);
if (keyNode != null) {
ASTNode nameNode = getNode().findChildByType(GrammarTypes.TYPE_NAME);
if (nameNode != null) {
GrammarTypeName name = GrammarElementFactory.createTypeName(getProject(), newName);
ASTNode newKeyNode = name.getNode();
getNode().replaceChild(keyNode, newKeyNode);
getNode().replaceChild(nameNode, newKeyNode);
}
return this;
}
public PsiElement getNameIdentifier() {
ASTNode keyNode = getNode().findChildByType(GrammarTypes.TYPE_NAME);
if (keyNode != null) {
return keyNode.getPsi();
ASTNode nameNode = getNode().findChildByType(GrammarTypes.TYPE_NAME);
if (nameNode != null) {
return nameNode.getPsi();
} else {
return null;
}
......
......@@ -91,7 +91,8 @@
<lang.formatter language="JastAddAspect" implementationClass="org.jastadd.tooling.aspect.AspectFormattingModelBuilder"/>
<lang.foldingBuilder language="JastAddAspect" implementationClass="org.jastadd.tooling.aspect.AspectFoldingBuilder"/>
FoldingBuilder"/>
<lang.psiStructureViewFactory language="JastAddAspect" implementationClass="org.jastadd.tooling.aspect.AspectStructureViewFactory"/>
</extensions>
<actions>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment