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
No related branches found
No related tags found
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