package com.bitbakery.plugin.arc.psi; /* * Copyright (c) Kurt Christensen, 2009 * * Licensed under the Artistic 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.opensource.org/licenses/artistic-license-2.0.php * * 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.. */ import com.bitbakery.plugin.arc.ArcFileType; import com.bitbakery.plugin.arc.ArcFileTypeLoader; import com.bitbakery.plugin.arc.config.ArcSettings; import com.intellij.codeInsight.TailType; import com.intellij.codeInsight.lookup.LookupItem; import com.intellij.lang.ASTNode; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.openapi.vfs.VirtualFileSystem; import com.intellij.psi.*; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NotNull; import java.util.SortedSet; import java.util.TreeSet; /** * */ public class VariableReference extends ArcElement { private MyPsiReference reference; private VirtualFileSystem fs; public VariableReference(@NotNull final ASTNode node) { super(node); reference = new MyPsiReference(this); fs = VirtualFileManager.getInstance().getFileSystem("file"); } // todo - we need to handle multiple def/mac definitions public PsiReference getReference() { return reference; } private class MyPsiReference extends PsiReferenceBase<VariableReference> { public MyPsiReference(VariableReference element) { super(element); } public PsiElement handleElementRename(String newElementName) throws IncorrectOperationException { return super.handleElementRename(newElementName); // TODO - Do something! } public PsiElement resolve() { return walkTree(myElement.getParent()); } private PsiElement walkTree(PsiElement e) { if (e == null) { // First search for the element in other files within the project... VirtualFile[] roots = ProjectRootManager.getInstance(myElement.getProject()).getContentSourceRoots(); PsiElement el = search(roots, myElement.getProject()); if (el != null) { return el; } // TODO - This is pretty expensive... we pr'y need to index these files ahead of time; re-dexing whenever we change Arc home // ...if not there, then search through the standard Arc library files VirtualFile home = fs.findFileByPath(ArcSettings.getInstance().arcHome); return home == null ? null : search(home.getChildren(), myElement.getProject()); } else if (e instanceof PsiFile) { // TODO - Move this logic to the element class for (PsiElement def : e.getChildren()) { if (nameMatches(def)) { return def; } } } else if (e instanceof Def || e instanceof Mac || e instanceof Fn) { if (nameMatches(e)) { return e; } // TODO - Move this logic to the element class ParameterList params = PsiTreeUtil.getChildOfType(e, ParameterList.class); if (params != null) { for (PsiElement param : params.getChildren()) { if (nameMatches(param)) { return param; } } } } else if (e instanceof VariableAssignment) { if (nameMatches(e)) { return e; } } else if (e instanceof Let) { VariableDefinition var = PsiTreeUtil.getChildOfType(e, VariableDefinition.class); // TODO - Move this logic to the element class if (var != null) { if (nameMatches(var)) { return var; } } } else if (e instanceof With) { // TODO - Move this logic to the element class for (PsiElement el : e.getChildren()) { if (el instanceof VariableDefinition) { if (nameMatches(el)) { return el; } } } } return walkTree(e.getParent()); } private PsiElement search(VirtualFile[] children, Project p) { for (VirtualFile file : children) { PsiElement e = file.isDirectory() ? search(file.getChildren(), p) : search(file, p); if (e != null) return e; } return null; } private PsiElement search(VirtualFile file, Project project) { if (!ArcFileType.EXTENSION.equalsIgnoreCase(file.getExtension())) { return null; } PsiDocumentManager.getInstance(project).commitAllDocuments(); return search(PsiManager.getInstance(project).findFile(file).getChildren()); } private PsiElement search(PsiElement[] elements) { for (PsiElement e : elements) { if (nameMatches(e)) return e; PsiElement el = search(e.getChildren()); if (el != null) return el; } return null; } private boolean nameMatches(PsiElement e) { return e instanceof PsiNamedElement && ((PsiNamedElement) e).getName() != null && ((PsiNamedElement) e).getName().equals(myElement.getText()); } public TextRange getRangeInElement() { return new TextRange(0, myElement.getTextLength()); } public Object[] getVariants() { SortedSet<LookupItem> names = new TreeSet<LookupItem>(); // TODO - This is a hack, a first cut at trying out code completion. // TODO - We need two things: // TODO - (1) We need to gen a stub index, so that we can get this list speedy speedy // TODO - (2) We need to do a short walk up the tree to add any variables in scope (e.g., function params, or let/with variables) VirtualFile home = fs.findFileByPath(ArcSettings.getInstance().arcHome); for (VirtualFile file : home.getChildren()) { if (ArcFileTypeLoader.ARC.equals(file.getFileType())) { PsiElement[] elements = PsiManager.getInstance(myElement.getProject()).findFile(file).getChildren(); for (PsiElement e : elements) { if (e instanceof Definition) { LookupItem item = new LookupItem(e, ((PsiNamedElement) e).getName()); Definition d = (Definition) e; item.setTailType(TailType.SPACE); item.setAttribute(LookupItem.TAIL_TEXT_ATTR, " " + d.getParameterString()); item.setAttribute(LookupItem.TYPE_TEXT_ATTR, "[" + file.getName() + "]"); names.add(item); } else if (e instanceof VariableAssignment) { LookupItem item = new LookupItem(e, ((PsiNamedElement) e).getName()); item.setTailType(TailType.SPACE); item.setAttribute(LookupItem.TYPE_TEXT_ATTR, "[" + file.getName() + "]"); names.add(item); } } } } /* VirtualFile[] roots = ProjectRootManager.getInstance(myElement.getProject()).getContentSourceRoots(); for (VirtualFile file : roots) { System.out.println(file.toString()); } */ return names.toArray(); } private void prep(StringBuffer buf) { final int SPACE = 25; if (buf.length() < SPACE) { buf.append(" "); buf.setLength(SPACE); } else if (buf.length() >= SPACE) { buf.replace(SPACE - 3, SPACE - 1, "..."); buf.setLength(SPACE); } } } }