diff --git a/src/main/java/org/jastadd/tooling/aspect/AspectStructureViewElement.java b/src/main/java/org/jastadd/tooling/aspect/AspectStructureViewElement.java
new file mode 100644
index 0000000000000000000000000000000000000000..e4e40a1884863e6e00f55a2bc63e70b0664c88e9
--- /dev/null
+++ b/src/main/java/org/jastadd/tooling/aspect/AspectStructureViewElement.java
@@ -0,0 +1,93 @@
+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;
+  }
+
+}
diff --git a/src/main/java/org/jastadd/tooling/aspect/AspectStructureViewFactory.java b/src/main/java/org/jastadd/tooling/aspect/AspectStructureViewFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..c16c0338cc093b5008d3937ed16853f4b1988a69
--- /dev/null
+++ b/src/main/java/org/jastadd/tooling/aspect/AspectStructureViewFactory.java
@@ -0,0 +1,27 @@
+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);
+      }
+    };
+  }
+
+}
diff --git a/src/main/java/org/jastadd/tooling/aspect/AspectStructureViewModel.java b/src/main/java/org/jastadd/tooling/aspect/AspectStructureViewModel.java
new file mode 100644
index 0000000000000000000000000000000000000000..d2af7e5dbd0a7554b0cadec2980afb42477cf9b4
--- /dev/null
+++ b/src/main/java/org/jastadd/tooling/aspect/AspectStructureViewModel.java
@@ -0,0 +1,48 @@
+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()};
+  }
+}
diff --git a/src/main/java/org/jastadd/tooling/aspect/JastAddAspectAttributeFilter.java b/src/main/java/org/jastadd/tooling/aspect/JastAddAspectAttributeFilter.java
new file mode 100644
index 0000000000000000000000000000000000000000..974798ae8df3c14bc0dad827bb567e141052efc4
--- /dev/null
+++ b/src/main/java/org/jastadd/tooling/aspect/JastAddAspectAttributeFilter.java
@@ -0,0 +1,44 @@
+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;
+  }
+}
diff --git a/src/main/java/org/jastadd/tooling/aspect/psi/AspectElementFactory.java b/src/main/java/org/jastadd/tooling/aspect/psi/AspectElementFactory.java
index 575b08a8d95c3e09a06a0849ec22306d4825291b..1b0685db7fe1551e5dd02671ffe41a70d26005f7 100644
--- a/src/main/java/org/jastadd/tooling/aspect/psi/AspectElementFactory.java
+++ b/src/main/java/org/jastadd/tooling/aspect/psi/AspectElementFactory.java
@@ -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;
+  }
 }
diff --git a/src/main/java/org/jastadd/tooling/aspect/psi/JastAddAspectAttribute.java b/src/main/java/org/jastadd/tooling/aspect/psi/JastAddAspectAttribute.java
index 3641ed2542495ddb249c07539fc6864e8dea5a3b..e3e4c7b1d0e78a52d89086ecdd2222bb2aa8c4a4 100644
--- a/src/main/java/org/jastadd/tooling/aspect/psi/JastAddAspectAttribute.java
+++ b/src/main/java/org/jastadd/tooling/aspect/psi/JastAddAspectAttribute.java
@@ -1,6 +1,7 @@
 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 {
 }
diff --git a/src/main/java/org/jastadd/tooling/aspect/psi/impl/JastAddAspectAspectDeclarationImplExtension.java b/src/main/java/org/jastadd/tooling/aspect/psi/impl/JastAddAspectAspectDeclarationImplExtension.java
new file mode 100644
index 0000000000000000000000000000000000000000..a077cbc5ab79f8cb7f9aea825be891efba273532
--- /dev/null
+++ b/src/main/java/org/jastadd/tooling/aspect/psi/impl/JastAddAspectAspectDeclarationImplExtension.java
@@ -0,0 +1,67 @@
+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;
+      }
+    };
+  }
+}
diff --git a/src/main/java/org/jastadd/tooling/aspect/psi/impl/JastAddAspectAstTypeNameImplExtension.java b/src/main/java/org/jastadd/tooling/aspect/psi/impl/JastAddAspectAstTypeNameImplExtension.java
index c83758eea7659596e84856ed3d74ea7440cb1bde..50be78d826ecc6c22a0b7580f91e836e8712e8a2 100644
--- a/src/main/java/org/jastadd/tooling/aspect/psi/impl/JastAddAspectAstTypeNameImplExtension.java
+++ b/src/main/java/org/jastadd/tooling/aspect/psi/impl/JastAddAspectAstTypeNameImplExtension.java
@@ -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;
   }
diff --git a/src/main/java/org/jastadd/tooling/aspect/psi/impl/JastAddAspectClassOrInterfaceTypeImplExtension.java b/src/main/java/org/jastadd/tooling/aspect/psi/impl/JastAddAspectClassOrInterfaceTypeImplExtension.java
index ca1542e8db7756b3ad81660546a929b38fb3278d..c483eff0fb41607f3aa3aa35f3863c547ddfa33a 100644
--- a/src/main/java/org/jastadd/tooling/aspect/psi/impl/JastAddAspectClassOrInterfaceTypeImplExtension.java
+++ b/src/main/java/org/jastadd/tooling/aspect/psi/impl/JastAddAspectClassOrInterfaceTypeImplExtension.java
@@ -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();
   }
 
diff --git a/src/main/java/org/jastadd/tooling/aspect/psi/impl/JastAddAspectCollAttributeImpl.java b/src/main/java/org/jastadd/tooling/aspect/psi/impl/JastAddAspectCollAttributeImpl.java
index 0381a57e809a9634499c5ec46b31445f8c8e5abd..931466e895e289596cfd9c53ae2c486e530511c6 100644
--- a/src/main/java/org/jastadd/tooling/aspect/psi/impl/JastAddAspectCollAttributeImpl.java
+++ b/src/main/java/org/jastadd/tooling/aspect/psi/impl/JastAddAspectCollAttributeImpl.java
@@ -1,12 +1,71 @@
 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;
+      }
+    };
+  }
 }
diff --git a/src/main/java/org/jastadd/tooling/aspect/psi/impl/JastAddAspectInhAttributeImpl.java b/src/main/java/org/jastadd/tooling/aspect/psi/impl/JastAddAspectInhAttributeImpl.java
index 6bd54d13cdaebb6413cf846e395df22a82685f8c..5b2692b74af86411604202bd14d08a2574653c9b 100644
--- a/src/main/java/org/jastadd/tooling/aspect/psi/impl/JastAddAspectInhAttributeImpl.java
+++ b/src/main/java/org/jastadd/tooling/aspect/psi/impl/JastAddAspectInhAttributeImpl.java
@@ -1,12 +1,71 @@
 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;
+      }
+    };
+  }
 }
diff --git a/src/main/java/org/jastadd/tooling/aspect/psi/impl/JastAddAspectSynAttributeImpl.java b/src/main/java/org/jastadd/tooling/aspect/psi/impl/JastAddAspectSynAttributeImpl.java
index 1b9443a44f50f29ddaa1ac17b7df717f12f4c78a..358e467052a897db4dbfedad710656846a190dcd 100644
--- a/src/main/java/org/jastadd/tooling/aspect/psi/impl/JastAddAspectSynAttributeImpl.java
+++ b/src/main/java/org/jastadd/tooling/aspect/psi/impl/JastAddAspectSynAttributeImpl.java
@@ -1,12 +1,72 @@
 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;
+      }
+    };
+  }
 }
diff --git a/src/main/java/org/jastadd/tooling/grammar/psi/impl/GrammarTypeDeclImplExtension.java b/src/main/java/org/jastadd/tooling/grammar/psi/impl/GrammarTypeDeclImplExtension.java
index 39bd24dbfd95d0f4f9989ec860c9de58a9e2b38a..7be798bc7a666dc7117385a630b7cec5c8498ec9 100644
--- a/src/main/java/org/jastadd/tooling/grammar/psi/impl/GrammarTypeDeclImplExtension.java
+++ b/src/main/java/org/jastadd/tooling/grammar/psi/impl/GrammarTypeDeclImplExtension.java
@@ -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;
     }
diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml
index a41624890b5ba72d39f91aa10a63e9fb19276af3..d84dd364f37c819a7929e7af627878c00d78fbf6 100644
--- a/src/main/resources/META-INF/plugin.xml
+++ b/src/main/resources/META-INF/plugin.xml
@@ -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>