package com.wix.rt.projectView; import com.intellij.ide.*; import com.intellij.ide.projectView.ProjectViewNode; import com.intellij.ide.projectView.SelectableTreeStructureProvider; import com.intellij.ide.projectView.ViewSettings; import com.intellij.ide.projectView.impl.nodes.BasePsiNode; import com.intellij.ide.util.DeleteHandler; import com.intellij.ide.util.treeView.AbstractTreeNode; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.actionSystem.PlatformDataKeys; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.util.PsiUtilCore; import com.intellij.util.containers.ContainerUtil; import com.wix.rt.RTProjectComponent; import com.wix.rt.build.RTFileUtil; import com.wix.rtk.projectView.RTFile; import com.wix.rtk.projectView.RTMergerTreeStructureProviderK2; import com.wix.rtk.projectView.RTNode; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.*; public class RTMergerTreeStructureProvider implements SelectableTreeStructureProvider, DumbAware { private final Project project; public RTMergerTreeStructureProvider(Project project) { this.project = project; } private static boolean hasRTFiles(@NotNull Collection<AbstractTreeNode> children) { for (AbstractTreeNode node : children) { if (node.getValue() instanceof PsiFile) { PsiFile file = (PsiFile) node.getValue(); if (file.getName().endsWith(".rt")) { // if (file.getFileType().equals(RTFileType.INSTANCE)) { return true; } } } return false; } private static boolean hasExt(VirtualFile file, String ext) { return file != null && file.getExtension() != null && file.getExtension().equals(ext); } private static boolean hasJsExt(VirtualFile file) { return hasExt(file, "js"); } private static boolean hasTsExt(VirtualFile file) { return hasExt(file, "ts"); } private static Map<String, ProjectViewNode> map(ProjectViewNode[] copy) { Map<String, ProjectViewNode> rtJsFiles = new HashMap<String, ProjectViewNode>(); for (ProjectViewNode element : copy) { if (RTFileUtil.isRTJSFile(element.getVirtualFile()) || hasJsExt(element.getVirtualFile()) || hasTsExt(element.getVirtualFile())) { rtJsFiles.put(element.getVirtualFile().getName(), element); } } return rtJsFiles; } private static boolean isGroupController(Project project) { RTProjectComponent comp = project.getComponent(RTProjectComponent.class); return comp.settings.groupController; } public static String getJSControllerName(VirtualFile rtFile) { return rtFile.getNameWithoutExtension() + ".js"; } public static String getTSControllerName(VirtualFile rtFile) { return rtFile.getNameWithoutExtension() + ".ts"; } @SuppressWarnings("rawtypes") @NotNull public Collection<AbstractTreeNode> modify(@NotNull AbstractTreeNode parent, @NotNull Collection<AbstractTreeNode> children, ViewSettings settings) { //ProjectViewNode[] copy = children.toArray(new ProjectViewNode[children.size()]); AbstractTreeNode[] arr2 = children.toArray(new AbstractTreeNode[children.size()]); //noinspection unchecked Collection<AbstractTreeNode<?>> r = RTMergerTreeStructureProviderK2.INSTANCE.modify22(project, parent, arr2, settings); return new LinkedHashSet<AbstractTreeNode>(r); } @NotNull public Collection<AbstractTreeNode> modify2(@NotNull AbstractTreeNode parent, @NotNull Collection<AbstractTreeNode> children, ViewSettings settings) { if (!RTProjectComponent.isEnabled(parent.getProject())) { return children; } if (parent.getValue() instanceof RTFile) return children; boolean groupController = isGroupController(project); RTProjectComponent comp = project.getComponent(RTProjectComponent.class); boolean groupOther = comp.settings.groupOther; // Optimization. Check if there are any RT files at all. boolean formsFound = hasRTFiles(children); if (!formsFound) return children; Collection<AbstractTreeNode> result = new LinkedHashSet<AbstractTreeNode>(children); ProjectViewNode[] copy = children.toArray(new ProjectViewNode[children.size()]); Map<String, ProjectViewNode> rtJsFiles = map(copy); for (ProjectViewNode<?> element : copy) { // if (isRTJSFile(element.getVirtualFile())) { // //skip // continue; // } PsiFile psiClass = null; if (element.getValue() instanceof PsiFile) { psiClass = (PsiFile) element.getValue(); // } else if (element.getValue() instanceof PsiClassOwner) { // final PsiClass[] psiClasses = ((PsiClassOwner) element.getValue()).getClasses(); // if (psiClasses.length == 1) { // psiClass = psiClasses[0]; // } } if (psiClass == null) continue; // String qName = psiClass.getQualifiedName(); // if (qName == null) continue; // List<PsiFile> forms = null; ProjectViewNode rtJsNode = null; try { // forms = FormClassIndex.findFormsBoundToClass(project, qName); String name = RTFileUtil.getJsRTFileName(element.getVirtualFile().getName()); // VirtualFile form = element.getVirtualFile().getParent().findChild(name); // if (form != null) { // forms = new ArrayList<PsiFile>(); if (rtJsFiles.containsKey(name)) { rtJsNode = rtJsFiles.get(name); } else { //try with a ts file name = RTFileUtil.getTsRTFileName(element.getVirtualFile().getName()); if (rtJsFiles.containsKey(name)) { rtJsNode = rtJsFiles.get(name); } } // forms.add(rtFile); // } } catch (ProcessCanceledException e) { continue; } if (rtJsNode != null) { Collection<BasePsiNode<? extends PsiFile>> subNodes = new ArrayList<BasePsiNode<? extends PsiFile>>(); //noinspection unchecked subNodes.add((BasePsiNode<? extends PsiFile>) element); //noinspection unchecked subNodes.add((BasePsiNode<? extends PsiFile>) rtJsNode); ProjectViewNode controllerNode = null; if (groupController) { String name = getJSControllerName(element.getVirtualFile()); if (rtJsFiles.containsKey(name)) { controllerNode = rtJsFiles.get(name); } else { //try with the TS extension name = getTSControllerName(element.getVirtualFile()); if (rtJsFiles.containsKey(name)) { controllerNode = rtJsFiles.get(name); } } if (controllerNode != null) { subNodes.add((BasePsiNode<? extends PsiFile>) controllerNode); result.remove(controllerNode); } } // if (groupOther) { // String name = getControllerName(element.getVirtualFile()); // if (rtJsFiles.containsKey(name)) { // controllerNode = rtJsFiles.get(name); // subNodes.add((BasePsiNode<? extends PsiFile>) controllerNode); // result.remove(controllerNode); // } // } PsiFile controller = controllerNode == null ? null : (PsiFile) controllerNode.getValue(); result.add(new RTNode(project, new RTFile(psiClass, (PsiFile) rtJsNode.getValue(), controller), settings, subNodes)); result.remove(element); result.remove(rtJsNode); } // Collection<BasePsiNode<? extends PsiElement>> formNodes = findFormsIn(children, forms); // if (!formNodes.isEmpty()) { // Collection<PsiFile> formFiles = convertToFiles(formNodes); // Collection<BasePsiNode<? extends PsiElement>> subNodes = new ArrayList<BasePsiNode<? extends PsiElement>>(); // //noinspection unchecked // subNodes.add((BasePsiNode<? extends PsiElement>) element); // subNodes.addAll(formNodes); // result.add(new FormNode(project, new RTNode(rtFile, psiClass), settings, subNodes)); // result.remove(element); // result.removeAll(formNodes); // } } // result.toArray() return result; } public Object getData(final Collection<AbstractTreeNode> selected, String dataId) { if (selected != null) { if (RTFile.DATA_KEY.is(dataId)) { List<RTFile> result = new ArrayList<RTFile>(); for (AbstractTreeNode node : selected) { if (node.getValue() instanceof RTFile) { result.add((RTFile) node.getValue()); } } if (!result.isEmpty()) { return result.toArray(new RTFile[result.size()]); } } else if (PlatformDataKeys.COPY_PROVIDER.is(dataId) || PlatformDataKeys.DELETE_ELEMENT_PROVIDER.is(dataId) || PlatformDataKeys.CUT_PROVIDER.is(dataId)) { for (AbstractTreeNode node : selected) { if (node.getValue() instanceof RTFile) { return new MyCopyProvider(selected); } } // } else if (PlatformDataKeys.PASTE_PROVIDER.is(dataId)) { } } return null; } private static PsiElement[] collectFormPsiElements(Collection<AbstractTreeNode> selected) { Set<PsiElement> result = new HashSet<PsiElement>(); for (AbstractTreeNode node : selected) { if (node.getValue() instanceof RTFile) { RTFile form = (RTFile) node.getValue(); result.add(form.getRtjsFile()); if (form.getController() != null) { result.add(form.getController()); } ContainerUtil.addAll(result, form.getRtFile()); } else if (node.getValue() instanceof PsiElement) { result.add((PsiElement) node.getValue()); } } return PsiUtilCore.toPsiElementArray(result); } private static Collection<PsiFile> convertToFiles(Collection<BasePsiNode<? extends PsiElement>> formNodes) { List<PsiFile> psiFiles = new ArrayList<PsiFile>(); for (AbstractTreeNode treeNode : formNodes) { psiFiles.add((PsiFile) treeNode.getValue()); } return psiFiles; } private static Collection<BasePsiNode<? extends PsiElement>> findFormsIn(Collection<AbstractTreeNode> children, List<PsiFile> forms) { if (children.isEmpty() || forms.isEmpty()) return Collections.emptyList(); List<BasePsiNode<? extends PsiElement>> result = new ArrayList<BasePsiNode<? extends PsiElement>>(); Set<PsiFile> psiFiles = new HashSet<PsiFile>(forms); for (final AbstractTreeNode child : children) { if (child instanceof BasePsiNode) { //noinspection unchecked BasePsiNode<? extends PsiElement> treeNode = (BasePsiNode<? extends PsiElement>) child; //noinspection SuspiciousMethodCalls if (psiFiles.contains(treeNode.getValue())) result.add(treeNode); } } return result; } @Nullable @Override public PsiElement getTopLevelElement(PsiElement psiElement) { return null; } private static class MyCopyProvider implements CopyProvider, PasteProvider, CutProvider, DeleteProvider { private final PsiElement[] myElements; public MyCopyProvider(final Collection<AbstractTreeNode> selected) { myElements = collectFormPsiElements(selected); } private static PsiElement[] collectFormPsiElements(Collection<AbstractTreeNode> selected) { Set<PsiElement> result = new HashSet<PsiElement>(); for (AbstractTreeNode node : selected) { if (node.getValue() instanceof RTFile) { RTFile form = (RTFile) node.getValue(); result.add(form.getRtjsFile()); if (form.getController() != null) { result.add(form.getController()); } ContainerUtil.addAll(result, form.getRtFile()); } else if (node.getValue() instanceof PsiElement) { result.add((PsiElement) node.getValue()); } } return PsiUtilCore.toPsiElementArray(result); } @Override public void performCopy(@NotNull DataContext dataContext) { // final SerializedComponentData data = new SerializedComponentData(serializeForCopy(myEditor, selectedComponents)); // final SimpleTransferable transferable = new SimpleTransferable<SerializedComponentData>(data, SerializedComponentData.class, ourDataFlavor); // CopyPasteManager.getInstance().setContents(); PsiCopyPasteManager.getInstance().setElements(myElements, true); } @Override public boolean isCopyEnabled(@NotNull DataContext dataContext) { return true; } @Override public boolean isCopyVisible(@NotNull DataContext dataContext) { return true; } @Override public void performPaste(@NotNull DataContext dataContext) { RTFile[] rtFiles = RTFile.DATA_KEY.getData(dataContext); if (rtFiles != null) { System.out.println(rtFiles.length); } } @Override public boolean isPastePossible(@NotNull DataContext dataContext) { return true; } @Override public boolean isPasteEnabled(@NotNull DataContext dataContext) { return true; } @Override public void performCut(@NotNull DataContext dataContext) { PsiCopyPasteManager.getInstance().setElements(myElements, true); } @Override public boolean isCutEnabled(@NotNull DataContext dataContext) { return true; } @Override public boolean isCutVisible(@NotNull DataContext dataContext) { return true; } public void deleteElement(@NotNull DataContext dataContext) { Project project = CommonDataKeys.PROJECT.getData(dataContext); DeleteHandler.deletePsiElement(myElements, project); } public boolean canDeleteElement(@NotNull DataContext dataContext) { return DeleteHandler.shouldEnableDeleteAction(myElements); } } }