diff --git a/plugins/yaml/resources/messages/YAMLBundle.properties b/plugins/yaml/resources/messages/YAMLBundle.properties
index 7f9e2137cec8..7560753c1d3d 100644
--- a/plugins/yaml/resources/messages/YAMLBundle.properties
+++ b/plugins/yaml/resources/messages/YAMLBundle.properties
@@ -40,6 +40,9 @@ YamlScalarType.error.scalar.value=Scalar value expected
YamlIntegerType.error.integer.value=Integer value expected
YamlNumberType.error.numeric.value=Numeric value expected
+YAMLStructureViewDocument.element.name=YAML document
+YAMLStructureViewSequenceItem.element.name=Sequence Item
+
YAMLParser.invalid.header.symbols=Invalid block scalar header
YAMLParser.multiple.anchors=Multiple anchors are not permitted
YAMLParser.multiple.tags=Multiple tags are not permitted
@@ -47,6 +50,9 @@ YAMLParser.multiple.tags=Multiple tags are not permitted
YAMLAnchorRenameProcessor.lost.alias=Alias will refer to another anchor
YAMLAnchorRenameProcessor.reuse=Anchor already has the same name
+YAMLAliasResolveNodeProvider.action.name=Show aliased sub-trees
+YAMLAliasResolveNodeProvider.action.description=Resolve aliases and show referenced values in the structure
+
inspections.group.name=YAML
inspections.unresolved.alias.name=Unresolved alias
inspections.unresolved.alias.message=Can''t resolve alias {0}
diff --git a/plugins/yaml/src/org/jetbrains/yaml/structureView/DuplicatedPsiTreeElementBase.java b/plugins/yaml/src/org/jetbrains/yaml/structureView/DuplicatedPsiTreeElementBase.java
new file mode 100644
index 000000000000..2798cf9051d0
--- /dev/null
+++ b/plugins/yaml/src/org/jetbrains/yaml/structureView/DuplicatedPsiTreeElementBase.java
@@ -0,0 +1,175 @@
+// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
+package org.jetbrains.yaml.structureView;
+
+import com.intellij.ide.structureView.StructureViewTreeElement;
+import com.intellij.ide.structureView.impl.common.PsiTreeElementBase;
+import com.intellij.ide.util.treeView.AbstractTreeUi;
+import com.intellij.ide.util.treeView.NodeDescriptorProvidingKey;
+import com.intellij.ide.util.treeView.TreeAnchorizer;
+import com.intellij.navigation.ItemPresentation;
+import com.intellij.pom.Navigatable;
+import com.intellij.psi.PsiElement;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Collection;
+import java.util.Objects;
+
+/**
+ * File structure popup work with unique values (see {@link StructureViewTreeElement#getValue()}).
+ * If it is needed to show the same PSI element several times then each occurrence of such psi should be wrapped to some unique object.
+ * This class perform this task.
+ *
+ * This class could be moved to some common platform package if it will be needed for somebody else.
+ * The implementation of this class is based on {@link PsiTreeElementBase} implementation (copy-pasted and modified).
+ */
+abstract class DuplicatedPsiTreeElementBase implements StructureViewTreeElement,
+ ItemPresentation,
+ NodeDescriptorProvidingKey {
+ @NotNull
+ private final StoredData myValue;
+
+ /** @param details is used to distinguish different occurrences of the same PSI element */
+ protected DuplicatedPsiTreeElementBase(@NotNull T psiElement, @NotNull String details) {
+ myValue = new StoredData<>(psiElement, details);
+ }
+
+ /** @return stored psi element */
+ @NotNull
+ public final T getElement() {
+ return getValue().getElement();
+ }
+
+ /** @return additional information which is used to distinguish different occurrences of the same PSI element */
+ @NotNull
+ public final String getDetails() {
+ return getValue().getDetails();
+ }
+
+ @NotNull
+ public abstract Collection getChildrenBase();
+
+ @NotNull
+ @Override
+ public ItemPresentation getPresentation() {
+ return this;
+ }
+
+ @NotNull
+ @Override
+ public Object getKey() {
+ return String.valueOf(getElement());
+ }
+
+ @Contract(pure = true)
+ @NotNull
+ @Override
+ public final StoredData getValue() {
+ return myValue;
+ }
+
+ public String toString() {
+ return getElement().toString();
+ }
+
+ @NotNull
+ @Override
+ public final StructureViewTreeElement[] getChildren() {
+ return AbstractTreeUi.calculateYieldingToWriteAction(this::doGetChildren);
+ }
+
+ @NotNull
+ private StructureViewTreeElement[] doGetChildren() {
+ return PsiTreeElementBase.mergeWithExtensions(getElement(), getChildrenBase());
+ }
+
+ @Override
+ public void navigate(boolean requestFocus) {
+ getElement().navigate(requestFocus);
+ }
+
+ @Override
+ public boolean canNavigate() {
+ return getElement().canNavigate();
+ }
+
+ @Override
+ public boolean canNavigateToSource() {
+ return canNavigate();
+ }
+
+ @Contract(value = "null -> false", pure = true)
+ public boolean equals(@Nullable Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ final DuplicatedPsiTreeElementBase that = (DuplicatedPsiTreeElementBase)o;
+
+ return getValue().equals(that.getValue());
+ }
+
+ public int hashCode() {
+ return getValue().hashCode();
+ }
+
+ public boolean isValid() {
+ return true;
+ }
+
+ /** This class is a wrapper over PSI element with additional information */
+ private static class StoredData implements Navigatable {
+ @NotNull
+ private final Object myAnchor;
+ @NotNull
+ private final String myDetails;
+
+ public StoredData(@NotNull T element, @NotNull String details) {
+ myAnchor = TreeAnchorizer.getService().createAnchor(element);
+ myDetails = details;
+ }
+
+ @NotNull
+ public T getElement() {
+ @SuppressWarnings("unchecked")
+ T element = (T)TreeAnchorizer.getService().retrieveElement(myAnchor);
+ assert element != null;
+ return element;
+ }
+
+ @NotNull
+ public String getDetails() {
+ return myDetails;
+ }
+
+ @Override
+ public void navigate(boolean requestFocus) {
+ getElement().navigate(requestFocus);
+ }
+
+ @Override
+ public boolean canNavigate() {
+ return getElement().canNavigate();
+ }
+
+ @Override
+ public boolean canNavigateToSource() {
+ return getElement().canNavigateToSource();
+ }
+
+ @Contract(value = "null -> false", pure = true)
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ StoredData> data = (StoredData>)o;
+ return Objects.equals(getElement(), data.getElement()) &&
+ Objects.equals(getDetails(), data.getDetails());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(getElement(), getDetails());
+ }
+ }
+}
diff --git a/plugins/yaml/src/org/jetbrains/yaml/structureView/YAMLAliasResolveNodeProvider.java b/plugins/yaml/src/org/jetbrains/yaml/structureView/YAMLAliasResolveNodeProvider.java
new file mode 100644
index 000000000000..ec11a5acbc62
--- /dev/null
+++ b/plugins/yaml/src/org/jetbrains/yaml/structureView/YAMLAliasResolveNodeProvider.java
@@ -0,0 +1,103 @@
+// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
+package org.jetbrains.yaml.structureView;
+
+import com.intellij.ide.structureView.StructureViewTreeElement;
+import com.intellij.ide.util.ActionShortcutProvider;
+import com.intellij.ide.util.FileStructureNodeProvider;
+import com.intellij.ide.util.treeView.smartTree.ActionPresentation;
+import com.intellij.ide.util.treeView.smartTree.ActionPresentationData;
+import com.intellij.ide.util.treeView.smartTree.TreeElement;
+import com.intellij.openapi.actionSystem.Shortcut;
+import com.intellij.openapi.util.Ref;
+import com.intellij.psi.PsiElement;
+import com.intellij.util.IncorrectOperationException;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.yaml.YAMLBundle;
+import org.jetbrains.yaml.psi.*;
+import org.jetbrains.yaml.resolve.YAMLAliasReference;
+
+import java.util.Collection;
+import java.util.Collections;
+
+public class YAMLAliasResolveNodeProvider implements FileStructureNodeProvider, ActionShortcutProvider {
+ @NonNls public static final String ID = "YAML_SHOW_RESOLVED_ALIAS_VALUES";
+
+ @NotNull
+ @Override
+ public String getCheckBoxText() {
+ return YAMLBundle.message("YAMLAliasResolveNodeProvider.action.name");
+ }
+
+ @NotNull
+ @Override
+ public Shortcut[] getShortcut() {
+ throw new IncorrectOperationException("see getActionIdForShortcut()");
+ }
+
+ @NotNull
+ @Override
+ public String getActionIdForShortcut() {
+ return "FileStructurePopup";
+ }
+
+ @NotNull
+ @Override
+ public Collection provideNodes(@NotNull TreeElement node) {
+ if (!(node instanceof DuplicatedPsiTreeElementBase)) {
+ return Collections.emptyList();
+ }
+ DuplicatedPsiTreeElementBase yamlNode = (DuplicatedPsiTreeElementBase)node;
+ PsiElement psiElem = yamlNode.getElement();
+ YAMLPsiElement yamlElem = psiElem instanceof YAMLPsiElement ? (YAMLPsiElement)psiElem : null;
+ YAMLValue value = getContainedValue(yamlElem);
+ if (!(value instanceof YAMLAlias)) {
+ return Collections.emptyList();
+ }
+ return YAMLStructureViewFactory.createChildrenViewTreeElements(resolveAlias((YAMLAlias)value), yamlNode.getDetails());
+ }
+
+ @NotNull
+ @Override
+ public ActionPresentation getPresentation() {
+ return new ActionPresentationData(YAMLBundle.message("YAMLAliasResolveNodeProvider.action.name"),
+ YAMLBundle.message("YAMLAliasResolveNodeProvider.action.description"),
+ YAMLStructureViewFactory.ALIAS_ICON);
+ }
+
+ @NotNull
+ @Override
+ public String getName() {
+ return ID;
+ }
+
+ @Nullable
+ private static YAMLValue resolveAlias(@NotNull YAMLAlias alias) {
+ YAMLAliasReference ref = alias.getReference();
+ YAMLAnchor anchor = ref == null ? null : ref.resolve();
+ return anchor != null ? anchor.getMarkedValue() : null;
+ }
+
+ @Nullable
+ @Contract("null -> null")
+ private static YAMLValue getContainedValue(@Nullable YAMLPsiElement element) {
+ if (element == null) {
+ return null;
+ }
+ Ref result = Ref.create();
+ element.accept(new YamlPsiElementVisitor() {
+ @Override
+ public void visitKeyValue(@NotNull YAMLKeyValue keyValue) {
+ result.set(keyValue.getValue());
+ }
+
+ @Override
+ public void visitSequenceItem(@NotNull YAMLSequenceItem sequenceItem) {
+ result.set(sequenceItem.getValue());
+ }
+ });
+ return result.get();
+ }
+}
diff --git a/plugins/yaml/src/org/jetbrains/yaml/structureView/YAMLStructureViewBuilder.java b/plugins/yaml/src/org/jetbrains/yaml/structureView/YAMLStructureViewBuilder.java
deleted file mode 100644
index 0373ae182c3d..000000000000
--- a/plugins/yaml/src/org/jetbrains/yaml/structureView/YAMLStructureViewBuilder.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package org.jetbrains.yaml.structureView;
-
-import com.intellij.ide.structureView.StructureViewModel;
-import com.intellij.ide.structureView.StructureViewModelBase;
-import com.intellij.ide.structureView.TreeBasedStructureViewBuilder;
-import com.intellij.ide.util.treeView.smartTree.Sorter;
-import com.intellij.openapi.editor.Editor;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-import org.jetbrains.yaml.psi.YAMLDocument;
-import org.jetbrains.yaml.psi.YAMLFile;
-import org.jetbrains.yaml.psi.YAMLKeyValue;
-
-/**
- * @author oleg
- */
-public class YAMLStructureViewBuilder extends TreeBasedStructureViewBuilder {
- private final YAMLFile myPsiFile;
-
- public YAMLStructureViewBuilder(@NotNull final YAMLFile psiFile) {
- myPsiFile = psiFile;
- }
-
- @Override
- @NotNull
- public StructureViewModel createStructureViewModel(@Nullable Editor editor) {
- return new StructureViewModelBase(myPsiFile, editor, new YAMLStructureViewElement(myPsiFile))
- .withSorters(Sorter.ALPHA_SORTER)
- .withSuitableClasses(YAMLFile.class, YAMLDocument.class, YAMLKeyValue.class);
- }
-}
\ No newline at end of file
diff --git a/plugins/yaml/src/org/jetbrains/yaml/structureView/YAMLStructureViewDocument.java b/plugins/yaml/src/org/jetbrains/yaml/structureView/YAMLStructureViewDocument.java
new file mode 100644
index 000000000000..6a1f0c9b1a53
--- /dev/null
+++ b/plugins/yaml/src/org/jetbrains/yaml/structureView/YAMLStructureViewDocument.java
@@ -0,0 +1,36 @@
+// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
+package org.jetbrains.yaml.structureView;
+
+import com.intellij.ide.structureView.StructureViewTreeElement;
+import com.intellij.ide.structureView.impl.common.PsiTreeElementBase;
+import com.intellij.util.PlatformIcons;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.yaml.YAMLBundle;
+import org.jetbrains.yaml.psi.YAMLDocument;
+
+import javax.swing.*;
+import java.util.Collection;
+import java.util.Objects;
+
+class YAMLStructureViewDocument extends PsiTreeElementBase {
+ YAMLStructureViewDocument(@NotNull YAMLDocument psiElement) {
+ super(psiElement);
+ }
+
+ @NotNull
+ @Override
+ public Collection getChildrenBase() {
+ YAMLDocument document = Objects.requireNonNull(getElement());
+ return YAMLStructureViewFactory.createChildrenViewTreeElements(document.getTopLevelValue(), "");
+ }
+
+ @NotNull
+ public String getPresentableText() {
+ return YAMLBundle.message("YAMLStructureViewDocument.element.name");
+ }
+
+ @NotNull
+ public Icon getIcon(boolean open) {
+ return PlatformIcons.XML_TAG_ICON;
+ }
+}
diff --git a/plugins/yaml/src/org/jetbrains/yaml/structureView/YAMLStructureViewElement.java b/plugins/yaml/src/org/jetbrains/yaml/structureView/YAMLStructureViewElement.java
deleted file mode 100644
index df7601aff403..000000000000
--- a/plugins/yaml/src/org/jetbrains/yaml/structureView/YAMLStructureViewElement.java
+++ /dev/null
@@ -1,150 +0,0 @@
-package org.jetbrains.yaml.structureView;
-
-import com.intellij.ide.structureView.StructureViewTreeElement;
-import com.intellij.navigation.ItemPresentation;
-import com.intellij.util.PlatformIcons;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-import org.jetbrains.yaml.psi.*;
-
-import javax.swing.*;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * @author oleg
- */
-public class YAMLStructureViewElement implements StructureViewTreeElement {
- private final YAMLPsiElement myElement;
-
- public YAMLStructureViewElement(final YAMLPsiElement element) {
- myElement = element;
- }
-
- @NotNull
- public StructureViewTreeElement[] getChildren() {
- final Collection extends YAMLPsiElement> children;
- if (myElement instanceof YAMLFile) {
- children = ((YAMLFile)myElement).getDocuments();
- }
- else if (myElement instanceof YAMLDocument) {
- children = getChildrenForValue(((YAMLDocument)myElement).getTopLevelValue());
- }
- else if (myElement instanceof YAMLSequenceItem) {
- children = getChildrenForValue(((YAMLSequenceItem)myElement).getValue());
- }
- else if (myElement instanceof YAMLKeyValue) {
- children = getChildrenForValue(((YAMLKeyValue)myElement).getValue());
- }
- else {
- children = Collections.emptyList();
- }
-
- final List structureElements = new ArrayList<>();
- for (YAMLPsiElement child : children) {
- structureElements.add(new YAMLStructureViewElement(child));
- }
- return structureElements.toArray(StructureViewTreeElement.EMPTY_ARRAY);
- }
-
- @NotNull
- private static Collection extends YAMLPsiElement> getChildrenForValue(@Nullable YAMLPsiElement element) {
- if (element instanceof YAMLMapping) {
- return ((YAMLMapping)element).getKeyValues();
- }
- if (element instanceof YAMLSequence) {
- return ((YAMLSequence)element).getItems();
- }
- return Collections.emptyList();
- }
-
-
-
- @NotNull
- public ItemPresentation getPresentation() {
- if (myElement instanceof YAMLKeyValue){
- final YAMLKeyValue kv = (YAMLKeyValue)myElement;
- return new ItemPresentation() {
- public String getPresentableText() {
- return kv.getKeyText();
- }
-
- public String getLocationString() {
- if (kv.getValue() instanceof YAMLScalar) {
- return kv.getValueText();
- }
- else {
- return null;
- }
- }
-
- public Icon getIcon(boolean open) {
- final YAMLValue value = kv.getValue();
- return value instanceof YAMLScalar ? kv.getIcon(0) : PlatformIcons.XML_TAG_ICON;
- }
- };
- }
- if (myElement instanceof YAMLDocument){
- return new ItemPresentation() {
- public String getPresentableText() {
- return "YAML document";
- }
-
- public String getLocationString() {
- return null;
- }
-
- public Icon getIcon(boolean open) {
- return PlatformIcons.XML_TAG_ICON;
- }
- };
- }
- if (myElement instanceof YAMLSequenceItem) {
- final YAMLSequenceItem item = ((YAMLSequenceItem)myElement);
- return new ItemPresentation() {
- @Nullable
- @Override
- public String getPresentableText() {
- if (item.getValue() instanceof YAMLScalar) {
- return ((YAMLScalar)item.getValue()).getTextValue();
- }
- else {
- return "Sequence Item";
- }
- }
-
- @Nullable
- @Override
- public String getLocationString() {
- return null;
- }
-
- @Nullable
- @Override
- public Icon getIcon(boolean unused) {
- return item.getValue() instanceof YAMLScalar ? PlatformIcons.PROPERTY_ICON : PlatformIcons.XML_TAG_ICON;
- }
- };
- }
- return myElement.getPresentation();
- }
-
- public YAMLPsiElement getValue() {
- return myElement;
- }
-
- public void navigate(boolean requestFocus) {
- myElement.navigate(requestFocus);
- }
-
- public boolean canNavigate() {
- return myElement.canNavigate();
- }
-
- public boolean canNavigateToSource() {
- return myElement.canNavigateToSource();
- }
-}
-
diff --git a/plugins/yaml/src/org/jetbrains/yaml/structureView/YAMLStructureViewFactory.java b/plugins/yaml/src/org/jetbrains/yaml/structureView/YAMLStructureViewFactory.java
index 9a7ae3db9689..04e52a927589 100644
--- a/plugins/yaml/src/org/jetbrains/yaml/structureView/YAMLStructureViewFactory.java
+++ b/plugins/yaml/src/org/jetbrains/yaml/structureView/YAMLStructureViewFactory.java
@@ -1,16 +1,34 @@
package org.jetbrains.yaml.structureView;
-import com.intellij.ide.structureView.StructureViewBuilder;
+import com.intellij.icons.AllIcons;
+import com.intellij.ide.structureView.*;
+import com.intellij.ide.util.treeView.smartTree.NodeProvider;
+import com.intellij.ide.util.treeView.smartTree.Sorter;
import com.intellij.lang.PsiStructureViewFactory;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.util.Ref;
import com.intellij.psi.PsiFile;
+import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
-import org.jetbrains.yaml.psi.YAMLFile;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.yaml.psi.*;
+
+import javax.swing.*;
+import java.util.Collection;
+import java.util.Collections;
/**
* @author oleg
*/
public class YAMLStructureViewFactory implements PsiStructureViewFactory {
+ static final Icon ALIAS_ICON = AllIcons.Nodes.ExpandNode;
+
+ @Nullable
public StructureViewBuilder getStructureViewBuilder(@NotNull final PsiFile psiFile) {
+ if (!(psiFile instanceof YAMLFile)) {
+ return null;
+ }
+
for (YAMLCustomStructureViewFactory extension : YAMLCustomStructureViewFactory.EP_NAME.getExtensions()) {
final StructureViewBuilder builder = extension.getStructureViewBuilder((YAMLFile)psiFile);
if (builder != null) {
@@ -18,6 +36,44 @@ public class YAMLStructureViewFactory implements PsiStructureViewFactory {
}
}
- return new YAMLStructureViewBuilder((YAMLFile) psiFile);
+ return new TreeBasedStructureViewBuilder() {
+ @Override
+ @NotNull
+ public StructureViewModel createStructureViewModel(@Nullable Editor editor) {
+ return new StructureViewModelBase(psiFile, editor, new YAMLStructureViewFile((YAMLFile)psiFile)){
+ @NotNull
+ @Override
+ public Collection getNodeProviders() {
+ return Collections.singleton(new YAMLAliasResolveNodeProvider());
+ }
+ }
+ .withSorters(Sorter.ALPHA_SORTER)
+ .withSuitableClasses(YAMLFile.class, YAMLDocument.class, YAMLKeyValue.class);
+ }
+ };
+ }
+
+ @NotNull
+ static String getAliasPresentableText(@NotNull YAMLAlias alias) {
+ return "*" + alias.getAliasName();
+ }
+
+ @NotNull
+ static Collection createChildrenViewTreeElements(@Nullable YAMLPsiElement element, String path) {
+ if (element == null) {
+ return Collections.emptyList();
+ }
+ Ref> result = Ref.create(Collections.emptyList());
+ element.accept(new YamlPsiElementVisitor() {
+ @Override
+ public void visitSequence(@NotNull YAMLSequence sequence) {
+ result.set(ContainerUtil.map(sequence.getItems(), i -> new YAMLStructureViewSequenceItem(i, path)));
+ }
+ @Override
+ public void visitMapping(@NotNull YAMLMapping mapping) {
+ result.set(ContainerUtil.map(mapping.getKeyValues(), kv -> new YAMLStructureViewKeyValue(kv, path)));
+ }
+ });
+ return result.get();
}
}
diff --git a/plugins/yaml/src/org/jetbrains/yaml/structureView/YAMLStructureViewFile.java b/plugins/yaml/src/org/jetbrains/yaml/structureView/YAMLStructureViewFile.java
new file mode 100644
index 000000000000..022815767719
--- /dev/null
+++ b/plugins/yaml/src/org/jetbrains/yaml/structureView/YAMLStructureViewFile.java
@@ -0,0 +1,49 @@
+// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
+package org.jetbrains.yaml.structureView;
+
+import com.intellij.ide.structureView.StructureViewTreeElement;
+import com.intellij.ide.structureView.impl.common.PsiTreeElementBase;
+import com.intellij.navigation.ItemPresentation;
+import com.intellij.util.containers.ContainerUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.yaml.psi.YAMLFile;
+
+import javax.swing.*;
+import java.util.Collection;
+import java.util.Objects;
+
+class YAMLStructureViewFile extends PsiTreeElementBase {
+ YAMLStructureViewFile(@NotNull YAMLFile psiElement) {
+ super(psiElement);
+ }
+
+ @NotNull
+ @Override
+ public Collection getChildrenBase() {
+ YAMLFile file = Objects.requireNonNull(getElement());
+ return ContainerUtil.map(file.getDocuments(), YAMLStructureViewDocument::new);
+ }
+
+ @Nullable
+ @Override
+ public String getPresentableText() {
+ return getFilePresentation().getPresentableText();
+ }
+
+ @Nullable
+ public String getLocationString() {
+ return getFilePresentation().getLocationString();
+ }
+
+ @Nullable
+ public Icon getIcon(boolean unused) {
+ return getFilePresentation().getIcon(unused);
+ }
+
+ @NotNull
+ private ItemPresentation getFilePresentation() {
+ YAMLFile file = Objects.requireNonNull(getElement());
+ return Objects.requireNonNull(file.getPresentation());
+ }
+}
diff --git a/plugins/yaml/src/org/jetbrains/yaml/structureView/YAMLStructureViewKeyValue.java b/plugins/yaml/src/org/jetbrains/yaml/structureView/YAMLStructureViewKeyValue.java
new file mode 100644
index 000000000000..10b94aa8fec1
--- /dev/null
+++ b/plugins/yaml/src/org/jetbrains/yaml/structureView/YAMLStructureViewKeyValue.java
@@ -0,0 +1,71 @@
+// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
+package org.jetbrains.yaml.structureView;
+
+import com.intellij.ide.structureView.StructureViewTreeElement;
+import com.intellij.util.PlatformIcons;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.yaml.psi.YAMLAlias;
+import org.jetbrains.yaml.psi.YAMLKeyValue;
+import org.jetbrains.yaml.psi.YAMLScalar;
+import org.jetbrains.yaml.psi.YAMLValue;
+
+import javax.swing.*;
+import java.util.Collection;
+import java.util.Objects;
+
+import static org.jetbrains.yaml.structureView.YAMLStructureViewFactory.ALIAS_ICON;
+
+class YAMLStructureViewKeyValue extends DuplicatedPsiTreeElementBase {
+ YAMLStructureViewKeyValue(@NotNull YAMLKeyValue kv, String path) {
+ super(kv, path + '.' + kv.getKeyText());
+ }
+
+ @NotNull
+ @Override
+ public Collection getChildrenBase() {
+ return YAMLStructureViewFactory.createChildrenViewTreeElements(getVal(), getDetails());
+ }
+
+ @NotNull
+ public String getPresentableText() {
+ return getKeyValue().getKeyText();
+ }
+
+ @Nullable
+ public String getLocationString() {
+ if (getVal() instanceof YAMLScalar) {
+ return getKeyValue().getValueText();
+ }
+ else if (getVal() instanceof YAMLAlias) {
+ return YAMLStructureViewFactory.getAliasPresentableText((YAMLAlias)getVal());
+ }
+ else {
+ return null;
+ }
+ }
+
+ @Nullable
+ public Icon getIcon(boolean open) {
+ if (getVal() instanceof YAMLScalar) {
+ return getKeyValue().getIcon(0);
+ }
+ else if (getVal() instanceof YAMLAlias) {
+ return ALIAS_ICON;
+ }
+ else {
+ return PlatformIcons.XML_TAG_ICON;
+ }
+ }
+
+ @NotNull
+ private YAMLKeyValue getKeyValue() {
+ return Objects.requireNonNull(getElement());
+ }
+
+ // Base class already has getValue() method with different semantic
+ @Nullable
+ private YAMLValue getVal() {
+ return getKeyValue().getValue();
+ }
+}
diff --git a/plugins/yaml/src/org/jetbrains/yaml/structureView/YAMLStructureViewSequenceItem.java b/plugins/yaml/src/org/jetbrains/yaml/structureView/YAMLStructureViewSequenceItem.java
new file mode 100644
index 000000000000..6fe920b13a1a
--- /dev/null
+++ b/plugins/yaml/src/org/jetbrains/yaml/structureView/YAMLStructureViewSequenceItem.java
@@ -0,0 +1,77 @@
+// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
+package org.jetbrains.yaml.structureView;
+
+import com.intellij.ide.structureView.StructureViewTreeElement;
+import com.intellij.lang.ASTNode;
+import com.intellij.util.PlatformIcons;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.yaml.YAMLBundle;
+import org.jetbrains.yaml.psi.YAMLAlias;
+import org.jetbrains.yaml.psi.YAMLScalar;
+import org.jetbrains.yaml.psi.YAMLSequenceItem;
+import org.jetbrains.yaml.psi.YAMLValue;
+
+import javax.swing.*;
+import java.util.Collection;
+
+import static org.jetbrains.yaml.structureView.YAMLStructureViewFactory.ALIAS_ICON;
+
+class YAMLStructureViewSequenceItem extends DuplicatedPsiTreeElementBase {
+ YAMLStructureViewSequenceItem(@NotNull YAMLSequenceItem item, String path) {
+ super(item, path + '-' + getItemNumber(item));
+ }
+
+ @NotNull
+ @Override
+ public Collection getChildrenBase() {
+ return YAMLStructureViewFactory.createChildrenViewTreeElements(getItemValue(), getDetails());
+ }
+
+ @NotNull
+ @Override
+ public String getPresentableText() {
+ if (getItemValue() instanceof YAMLScalar) {
+ return ((YAMLScalar)getItemValue()).getTextValue();
+ }
+ else if (getItemValue() instanceof YAMLAlias) {
+ return YAMLStructureViewFactory.getAliasPresentableText((YAMLAlias)getItemValue());
+ }
+ else {
+ return YAMLBundle.message("YAMLStructureViewSequenceItem.element.name");
+ }
+ }
+
+ @Nullable
+ @Override
+ public String getLocationString() {
+ return null;
+ }
+
+ @NotNull
+ @Override
+ public Icon getIcon(boolean unused) {
+ if (getItemValue() instanceof YAMLScalar) {
+ return PlatformIcons.PROPERTY_ICON;
+ }
+ else if (getItemValue() instanceof YAMLAlias) {
+ return ALIAS_ICON;
+ }
+ else {
+ return PlatformIcons.XML_TAG_ICON;
+ }
+ }
+
+ @Nullable
+ private YAMLValue getItemValue() {
+ return getElement().getValue();
+ }
+
+ private static int getItemNumber(@NotNull YAMLSequenceItem item) {
+ int num = 0;
+ for (ASTNode node = item.getNode().getTreePrev(); node != null; node = node.getTreePrev()) {
+ num++;
+ }
+ return num;
+ }
+}
diff --git a/plugins/yaml/testSrc/org/jetbrains/yaml/structure/YAMLStructureTest.java b/plugins/yaml/testSrc/org/jetbrains/yaml/structure/YAMLStructureTest.java
new file mode 100644
index 000000000000..7c85c2404d42
--- /dev/null
+++ b/plugins/yaml/testSrc/org/jetbrains/yaml/structure/YAMLStructureTest.java
@@ -0,0 +1,38 @@
+// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
+package org.jetbrains.yaml.structure;
+
+import com.intellij.openapi.application.ex.PathManagerEx;
+import com.intellij.testFramework.PlatformTestUtil;
+import com.intellij.testFramework.fixtures.LightPlatformCodeInsightFixtureTestCase;
+import com.intellij.util.ui.tree.TreeUtil;
+import org.jetbrains.yaml.structureView.YAMLAliasResolveNodeProvider;
+
+public class YAMLStructureTest extends LightPlatformCodeInsightFixtureTestCase {
+ @Override
+ protected String getTestDataPath() {
+ return PathManagerEx.getCommunityHomePath() + "/plugins/yaml/testSrc/org/jetbrains/yaml/structure/data/";
+ }
+
+
+ public void testAnchorsAndAliases() {
+ doTest();
+ }
+
+ private void doTest() {
+ String testName = getTestName(true);
+ myFixture.configureByFile(testName + ".yml");
+ doStructureTest(testName, false);
+ doStructureTest(testName, true);
+ }
+
+ private void doStructureTest(String testName, boolean resolveAliases) {
+ myFixture.testStructureView(svc -> {
+ svc.setActionActive(YAMLAliasResolveNodeProvider.ID, resolveAliases);
+ TreeUtil.expandAll(svc.getTree());
+ PlatformTestUtil.waitForPromise(svc.select(svc.getTreeModel().getCurrentEditorElement(), false));
+ String suffix = resolveAliases ? "resolved" : "unresolved";
+ assertSameLinesWithFile(getTestDataPath() + testName + "." + suffix + ".txt",
+ PlatformTestUtil.print(svc.getTree(), false));
+ });
+ }
+}
diff --git a/plugins/yaml/testSrc/org/jetbrains/yaml/structure/data/anchorsAndAliases.resolved.txt b/plugins/yaml/testSrc/org/jetbrains/yaml/structure/data/anchorsAndAliases.resolved.txt
new file mode 100644
index 000000000000..1dec56302a6d
--- /dev/null
+++ b/plugins/yaml/testSrc/org/jetbrains/yaml/structure/data/anchorsAndAliases.resolved.txt
@@ -0,0 +1,14 @@
+-anchorsAndAliases.yml
+ -YAML document
+ -First occurrence
+ -list
+ item1
+ item2
+ -Second occurrence
+ -list
+ item1
+ item2
+ -Override anchor
+ key
+ -Reuse anchor
+ key
\ No newline at end of file
diff --git a/plugins/yaml/testSrc/org/jetbrains/yaml/structure/data/anchorsAndAliases.unresolved.txt b/plugins/yaml/testSrc/org/jetbrains/yaml/structure/data/anchorsAndAliases.unresolved.txt
new file mode 100644
index 000000000000..6176e0923bb5
--- /dev/null
+++ b/plugins/yaml/testSrc/org/jetbrains/yaml/structure/data/anchorsAndAliases.unresolved.txt
@@ -0,0 +1,10 @@
+-anchorsAndAliases.yml
+ -YAML document
+ -First occurrence
+ -list
+ item1
+ item2
+ Second occurrence
+ -Override anchor
+ key
+ Reuse anchor
\ No newline at end of file
diff --git a/plugins/yaml/testSrc/org/jetbrains/yaml/structure/data/anchorsAndAliases.yml b/plugins/yaml/testSrc/org/jetbrains/yaml/structure/data/anchorsAndAliases.yml
new file mode 100644
index 000000000000..0218bf533e0a
--- /dev/null
+++ b/plugins/yaml/testSrc/org/jetbrains/yaml/structure/data/anchorsAndAliases.yml
@@ -0,0 +1,8 @@
+First occurrence: &xxx
+ list:
+ - item1
+ - item2
+Second occurrence: *xxx
+Override anchor: &xxx
+ key: val
+Reuse anchor: *xxx