package com.intellij.lang.javascript.flex; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleUtilCore; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ModuleRootManager; import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiFileSystemItem; import com.intellij.psi.impl.source.resolve.reference.impl.providers.FileReference; import com.intellij.psi.impl.source.resolve.reference.impl.providers.FileReferenceSet; import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public class JSFlexFileReference extends FileReference { private final ReferenceSupport.RelativeToWhat myRelativeToWhat; public JSFlexFileReference(@NotNull final FileReferenceSet fileReferenceSet, TextRange range, int index, String text, final ReferenceSupport.RelativeToWhat relativeToWhat) { super(fileReferenceSet, range, index, text); myRelativeToWhat = relativeToWhat; } // - absolute paths remain absolute (i.e. not relative to project root) // - relative paths are kept relative to what they were relative to before refactoring @Override public PsiElement bindToElement(@NotNull final PsiElement element) throws IncorrectOperationException { if (!(element instanceof PsiFileSystemItem)) { throw new IncorrectOperationException("Cannot bind to element, should be instanceof PsiFileSystemItem: " + element); } final PsiFileSystemItem fileSystemItem = (PsiFileSystemItem)element; final VirtualFile destVFile = fileSystemItem.getVirtualFile(); if (destVFile == null) throw new IncorrectOperationException("Cannot bind to non-physical element:" + element); PsiFile currentPsiFile = getElement().getContainingFile(); final PsiElement contextPsiFile = currentPsiFile.getContext(); if (contextPsiFile != null) currentPsiFile = contextPsiFile.getContainingFile(); final VirtualFile currentVFile = currentPsiFile.getVirtualFile(); if (currentVFile == null) throw new IncorrectOperationException("Cannot bind from non-physical element:" + currentPsiFile); final Project project = element.getProject(); String newName = null; switch (myRelativeToWhat) { case Absolute: newName = destVFile.getPath(); break; case CurrentFile: newName = getRelativePath(currentVFile, destVFile, '/'); break; case ProjectRoot: final VirtualFile projectRoot = project.getBaseDir(); newName = projectRoot == null ? null : getRelativePath(projectRoot, destVFile, '/'); break; case SourceRoot: // first try to get source root that contains the file final VirtualFile sourceRootForFile = ProjectRootManager.getInstance(project).getFileIndex().getSourceRootForFile(destVFile); if (sourceRootForFile != null) { newName = getRelativePath(sourceRootForFile, destVFile, '/'); } else { final Module module = ModuleUtilCore.findModuleForFile(currentVFile, project); if (module != null) { final VirtualFile[] sourceRoots = ModuleRootManager.getInstance(module).getSourceRoots(); for (final VirtualFile sourceRoot : sourceRoots) { final String relativePath = getRelativePath(sourceRoot, destVFile, '/'); if (relativePath != null) { newName = relativePath; break; } } } } break; case Other: break; } if (newName != null && getFileReferenceSet().getPathString().startsWith("/") && !newName.startsWith("/")) { newName = "/" + newName; } return newName == null ? element : rename(newName); } // the difference from VfsUtil.getPath() is that if srcFile is a directory then one more "../" may be needed @Nullable private static String getRelativePath(@NotNull VirtualFile src, @NotNull VirtualFile dst, final char separatorChar) { final VirtualFile commonAncestor = VfsUtil.getCommonAncestor(src, dst); if (commonAncestor != null) { StringBuilder buffer = new StringBuilder(); if (!Comparing.equal(src, commonAncestor)) { if (src.isDirectory()) { buffer.append("..").append(separatorChar); // this line is the only difference from VfsUtil.getPath() } while (!Comparing.equal(src.getParent(), commonAncestor)) { buffer.append("..").append(separatorChar); src = src.getParent(); } } buffer.append(VfsUtilCore.getRelativePath(dst, commonAncestor, separatorChar)); return buffer.toString(); } return null; } }