package com.jetbrains.lang.dart.ide.hierarchy; import com.intellij.ide.hierarchy.HierarchyBrowserManager; import com.intellij.ide.util.treeView.AlphaComparator; import com.intellij.ide.util.treeView.NodeDescriptor; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; import com.intellij.psi.util.PsiTreeUtil; import com.jetbrains.lang.dart.DartComponentType; import com.jetbrains.lang.dart.analyzer.DartAnalysisServerService; import com.jetbrains.lang.dart.psi.*; import com.jetbrains.lang.dart.util.DartResolveUtil; import org.dartlang.analysis.server.protocol.Element; import org.dartlang.analysis.server.protocol.Location; import org.dartlang.analysis.server.protocol.TypeHierarchyItem; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Collections; import java.util.Comparator; import java.util.List; import static com.jetbrains.lang.dart.DartTokenTypes.*; public class DartHierarchyUtil { private static final Comparator<NodeDescriptor> NODE_DESCRIPTOR_COMPARATOR = Comparator.comparingInt(NodeDescriptor::getIndex); private DartHierarchyUtil() { } @Nullable public static DartClass findDartClass(@NotNull final Project project, @NotNull final TypeHierarchyItem item) { final Element classElement = item.getClassElement(); final Location location = classElement.getLocation(); final DartComponent component = findDartComponent(project, location); return component instanceof DartClass ? (DartClass)component : null; } public static Comparator<NodeDescriptor> getComparator(Project project) { final HierarchyBrowserManager.State state = HierarchyBrowserManager.getInstance(project).getState(); if (state != null && state.SORT_ALPHABETICALLY) { return AlphaComparator.INSTANCE; } else { return NODE_DESCRIPTOR_COMPARATOR; } } @NotNull public static List<TypeHierarchyItem> getTypeHierarchyItems(@NotNull DartClass dartClass) { final VirtualFile file = dartClass.getContainingFile().getVirtualFile(); final DartComponentName name = dartClass.getComponentName(); if (name == null) return Collections.emptyList(); return DartAnalysisServerService.getInstance(dartClass.getProject()) .search_getTypeHierarchy(file, name.getTextRange().getStartOffset(), false); } public static boolean isExecutable(@NotNull PsiElement component) { if (component instanceof DartComponentName) return false; final DartComponentType componentType = DartComponentType.typeOf(component); if (componentType == null) return false; switch (componentType) { case CONSTRUCTOR: case FUNCTION: case METHOD: case OPERATOR: return true; default: return false; } } public static PsiElement getResolvedElementAtCursor(DataContext dataContext) { final Project project = CommonDataKeys.PROJECT.getData(dataContext); final Editor editor = CommonDataKeys.EDITOR.getData(dataContext); if (project == null || editor == null) return null; final PsiFile file = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument()); final PsiElement psiElement = file == null ? null : file.findElementAt(editor.getCaretModel().getOffset()); DartReference dartReference = PsiTreeUtil.getParentOfType(psiElement, DartReference.class); if (dartReference != null) { if (dartReference.getTokenType() == NEW_EXPRESSION) { DartComponent cons = DartResolveUtil.findConstructorDeclaration((DartNewExpression)dartReference); if (cons != null && cons.getTokenType() == METHOD_DECLARATION) { return cons; } else { return null; // Class with no constructor. } } if (dartReference.getTokenType() == CALL_EXPRESSION) { dartReference = getRightmostReference(dartReference.getFirstChild()); } DartComponent comp = DartResolveUtil.findReferenceAndComponentTarget(dartReference); return comp != null && isExecutable(comp) ? comp : null; } else { if (psiElement == null) return null; if (isExecutable(psiElement)) return psiElement; DartComponentName name = PsiTreeUtil.getParentOfType(psiElement, DartComponentName.class); if (name == null) { // Cursor may be between identifier and left paren of function definition. if (psiElement instanceof PsiWhiteSpace) { name = PsiTreeUtil.getPrevSiblingOfType(psiElement, DartComponentName.class); } else if ("(".equals(psiElement.getText())) { name = PsiTreeUtil.getPrevSiblingOfType(psiElement.getParent(), DartComponentName.class); } } if (name != null) { PsiElement def = name.getParent(); return def != null && isExecutable(def) ? def : null; } return null; } } private static DartReference getRightmostReference(PsiElement element) { PsiElement last = PsiTreeUtil.getDeepestLast(element); return PsiTreeUtil.getParentOfType(last, DartReference.class); } @Nullable public static DartComponent findDartComponent(@NotNull final Project project, @NotNull final Location location) { String filePath = location.getFile(); if (filePath == null) { return null; } filePath = FileUtil.toSystemIndependentName(filePath); final VirtualFile virtualFile = LocalFileSystem.getInstance().findFileByPath(filePath); if (virtualFile == null) { return null; } final PsiFile psiFile = PsiManager.getInstance(project).findFile(virtualFile); if (psiFile == null) { return null; } final int offset = DartAnalysisServerService.getInstance(project).getConvertedOffset(virtualFile, location.getOffset()); final PsiElement elementAtOffset = psiFile.findElementAt(offset); return PsiTreeUtil.getParentOfType(elementAtOffset, DartComponent.class); } }