/*
* Copyright 2013-2016 Sergey Ignatov, Alexander Zolotov, Florin Patan
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.goide.tree;
import com.goide.GoIcons;
import com.goide.psi.*;
import com.goide.psi.impl.GoPsiImplUtil;
import com.goide.sdk.GoPackageUtil;
import com.intellij.ide.structureView.*;
import com.intellij.ide.structureView.impl.common.PsiTreeElementBase;
import com.intellij.ide.util.ActionShortcutProvider;
import com.intellij.ide.util.FileStructureNodeProvider;
import com.intellij.ide.util.treeView.smartTree.*;
import com.intellij.lang.PsiStructureViewFactory;
import com.intellij.openapi.actionSystem.Shortcut;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.IndexNotReadyException;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.StubBasedPsiElement;
import com.intellij.psi.SyntaxTraverser;
import com.intellij.psi.stubs.StubElement;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
public class GoStructureViewFactory implements PsiStructureViewFactory {
@Nullable
@Override
public StructureViewBuilder getStructureViewBuilder(@NotNull PsiFile psiFile) {
return new TreeBasedStructureViewBuilder() {
@NotNull
@Override
public StructureViewModel createStructureViewModel(@Nullable Editor editor) {
return new Model(psiFile, editor);
}
@Override
public boolean isRootNodeShown() {
return false;
}
};
}
public static class Model extends StructureViewModelBase implements StructureViewModel.ElementInfoProvider {
private static final List<NodeProvider> PROVIDERS =
ContainerUtil.newSmartList(new TreeElementFileStructureNodeProvider());
Model(@NotNull PsiFile file, @Nullable Editor editor) {
super(file, editor, new Element(file));
withSuitableClasses(GoFile.class, GoNamedElement.class)
.withSorters(ExportabilitySorter.INSTANCE, Sorter.ALPHA_SORTER);
}
@NotNull
@Override
public Filter[] getFilters() {
return new Filter[]{new GoPrivateMembersFilter()};
}
@Override
public boolean isAlwaysShowsPlus(StructureViewTreeElement structureViewTreeElement) {
return false;
}
@Override
public boolean isAlwaysLeaf(StructureViewTreeElement structureViewTreeElement) {
return false;
}
@NotNull
@Override
public Collection<NodeProvider> getNodeProviders() {
return PROVIDERS;
}
private static class TreeElementFileStructureNodeProvider implements FileStructureNodeProvider<TreeElement>, ActionShortcutProvider {
public static final String ID = "Show package structure";
@NotNull
@Override
public ActionPresentation getPresentation() {
return new ActionPresentationData(ID, null, GoIcons.PACKAGE);
}
@NotNull
@Override
public String getName() {
return ID;
}
@NotNull
@Override
public Collection<TreeElement> provideNodes(@NotNull TreeElement node) {
PsiElement psi = node instanceof Element ? ((Element)node).getElement() : null;
if (psi instanceof GoFile) {
GoFile orig = (GoFile)psi;
List<TreeElement> result = ContainerUtil.newSmartList();
for (GoFile f : GoPackageUtil.getAllPackageFiles(orig)) {
if (f != orig) {
ContainerUtil.addAll(result, new Element(f).getChildren());
}
}
return result;
}
return Collections.emptyList();
}
@NotNull
@Override
public String getCheckBoxText() {
return ID;
}
@NotNull
@Override
public Shortcut[] getShortcut() {
throw new IncorrectOperationException("see getActionIdForShortcut()");
}
@NotNull
@Override
public String getActionIdForShortcut() {
return "FileStructurePopup";
}
}
}
public static class Element extends PsiTreeElementBase<PsiElement> {
public Element(@NotNull PsiElement e) {
super(e);
}
@NotNull
@Override
public Collection<StructureViewTreeElement> getChildrenBase() {
List<StructureViewTreeElement> result = ContainerUtil.newArrayList();
PsiElement element = getElement();
if (element instanceof GoFile) {
for (GoTypeSpec o : ((GoFile)element).getTypes()) result.add(new Element(o));
for (GoConstDefinition o : ((GoFile)element).getConstants()) result.add(new Element(o));
for (GoVarDefinition o : ((GoFile)element).getVars()) result.add(new Element(o));
for (GoFunctionDeclaration o : ((GoFile)element).getFunctions()) result.add(new Element(o));
for (GoMethodDeclaration o : ((GoFile)element).getMethods()) {
GoType type = o.getReceiverType();
GoTypeReferenceExpression e = GoPsiImplUtil.getTypeReference(type);
PsiElement resolve = e != null ? e.resolve() : null;
if (resolve == null) {
result.add(new Element(o));
}
}
}
else if (element instanceof GoTypeSpec) {
GoTypeSpec typeSpec = (GoTypeSpec)element;
GoType type = typeSpec.getSpecType().getType();
for (GoMethodDeclaration m : typeSpec.getMethods()) result.add(new Element(m));
if (type instanceof GoStructType) {
for (GoFieldDeclaration field : ((GoStructType)type).getFieldDeclarationList()) {
for (GoFieldDefinition definition : field.getFieldDefinitionList()) result.add(new Element(definition));
GoAnonymousFieldDefinition anon = field.getAnonymousFieldDefinition();
if (anon != null) result.add(new Element(anon));
}
}
else if (type instanceof GoInterfaceType) {
for (GoMethodSpec m : ((GoInterfaceType)type).getMethodSpecList()) result.add(new Element(m));
}
}
else if (element instanceof GoFunctionOrMethodDeclaration) {
StubElement<?> stub = ((StubBasedPsiElement)element).getStub();
Iterable<GoTypeSpec> list =
stub != null
? GoPsiTreeUtil.getStubChildrenOfTypeAsList(element, GoTypeSpec.class)
: SyntaxTraverser.psiTraverser(((GoFunctionOrMethodDeclaration)element).getBlock()).filter(GoTypeSpec.class);
for (GoTypeSpec s : list) {
result.add(new Element(s));
}
}
return result;
}
@Nullable
@Override
public String getPresentableText() {
String textInner = getPresentationTextInner();
return textInner != null ? textInner.replaceAll("\\(\\n", "(").replaceAll("\\n\\)", ")") : null;
}
@Nullable
private String getPresentationTextInner() {
PsiElement element = getElement();
if (element == null) {
return null;
}
String separator = ": ";
if (element instanceof GoFile) {
return ((GoFile)element).getName();
}
if (element instanceof GoNamedSignatureOwner) {
GoSignature signature = ((GoNamedSignatureOwner)element).getSignature();
String signatureText = signature != null ? signature.getText() : "";
String name = ((GoNamedSignatureOwner)element).getName();
return StringUtil.notNullize(name) + signatureText;
}
if (element instanceof GoTypeSpec) {
GoType type = ((GoTypeSpec)element).getSpecType().getType();
String appendix = type instanceof GoStructType || type instanceof GoInterfaceType ?
"" :
separator + GoPsiImplUtil.getText(type);
return ((GoTypeSpec)element).getName() + appendix;
}
if (element instanceof GoNamedElement) {
GoType type = null;
try {
type = ((GoNamedElement)element).getGoType(null);
}
catch (IndexNotReadyException ignored) {
}
String typeText = type == null || element instanceof GoAnonymousFieldDefinition ? "" : separator + GoPsiImplUtil.getText(type);
return ((GoNamedElement)element).getName() + typeText;
}
Logger.getInstance(GoStructureViewFactory.class).error("Cannot get presentation for " + element.getClass().getName());
return null;
}
}
}