/* * Copyright 2000-2015 JetBrains s.r.o. * * 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.jetbrains.lang.dart.psi.impl; import com.intellij.openapi.util.TextRange; 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.impl.source.resolve.ResolveCache; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.IncorrectOperationException; import com.jetbrains.lang.dart.DartLanguage; import com.jetbrains.lang.dart.analyzer.DartAnalysisServerService; import com.jetbrains.lang.dart.analyzer.DartServerData.DartNavigationRegion; import com.jetbrains.lang.dart.analyzer.DartServerData.DartNavigationTarget; import com.jetbrains.lang.dart.psi.DartFile; import com.jetbrains.lang.dart.psi.DartImportStatement; import com.jetbrains.lang.dart.psi.DartUriElement; import com.jetbrains.lang.dart.resolve.DartResolver; import com.jetbrains.lang.dart.util.DartResolveUtil; import com.jetbrains.lang.dart.util.DartUrlResolver; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.List; /** * Reference to a file in an import, export or part directive. */ public class DartFileReference implements PsiPolyVariantReference { private static final Resolver RESOLVER = new Resolver(); @NotNull private final DartUriElement myUriElement; @NotNull private final String myUri; @NotNull private final TextRange myRange; public DartFileReference(@NotNull final DartUriElement uriElement, @NotNull final String uri) { final int offset = uriElement.getText().indexOf(uri); assert offset >= 0 : uriElement.getText() + " doesn't contain " + uri; myUriElement = uriElement; myUri = uri; myRange = TextRange.create(offset, offset + uri.length()); } @NotNull @Override public PsiElement getElement() { return myUriElement; } @Override public TextRange getRangeInElement() { return myRange; } @NotNull @Override public ResolveResult[] multiResolve(boolean incompleteCode) { return ResolveCache.getInstance(myUriElement.getProject()).resolveWithCaching(this, RESOLVER, true, incompleteCode); } @Nullable @Override public PsiElement resolve() { final ResolveResult[] resolveResults = multiResolve(false); return resolveResults.length == 0 || resolveResults.length > 1 || !resolveResults[0].isValidResult() ? null : resolveResults[0].getElement(); } @NotNull @Override public String getCanonicalText() { return myUri; } @Override public PsiElement handleElementRename(final String newFileName) throws IncorrectOperationException { final int index = Math.max(myUri.lastIndexOf('/'), myUri.lastIndexOf("\\\\")); final String newUri = index < 0 ? newFileName : myUri.substring(0, index) + "/" + newFileName; return updateUri(newUri); } @Override public PsiElement bindToElement(@NotNull final PsiElement element) throws IncorrectOperationException { if (element instanceof PsiFile) { final VirtualFile contextFile = DartResolveUtil.getRealVirtualFile(myUriElement.getContainingFile()); final VirtualFile targetFile = DartResolveUtil.getRealVirtualFile(((PsiFile)element)); if (contextFile != null && targetFile != null) { final String newUri = DartUrlResolver.getInstance(myUriElement.getProject(), contextFile).getDartUrlForFile(targetFile); if (newUri.startsWith(DartUrlResolver.PACKAGE_PREFIX)) { return updateUri(newUri); } else if (newUri.startsWith(DartUrlResolver.FILE_PREFIX)) { final String relativePath = FileUtil.getRelativePath(contextFile.getParent().getPath(), targetFile.getPath(), '/'); if (relativePath != null) { return updateUri(relativePath); } } } } return myUriElement; } private PsiElement updateUri(@NotNull final String newUri) { final String uriElementText = myUriElement.getText(); final String startQuote = uriElementText.substring(0, myRange.getStartOffset()); final String endQuote = uriElementText.substring(myRange.getEndOffset(), uriElementText.length()); final String text = "import " + startQuote + newUri + endQuote + ";"; final PsiFile fileFromText = PsiFileFactory.getInstance(myUriElement.getProject()).createFileFromText(DartLanguage.INSTANCE, text); final DartImportStatement importStatement = PsiTreeUtil.findChildOfType(fileFromText, DartImportStatement.class); assert importStatement != null : fileFromText.getText(); return myUriElement.replace(importStatement.getUriElement()); } @Override public boolean isReferenceTo(final PsiElement element) { return element instanceof DartFile && element.equals(resolve()); } @NotNull @Override public Object[] getVariants() { return EMPTY_ARRAY; } @Override public boolean isSoft() { return false; } private static class Resolver implements ResolveCache.PolyVariantResolver<DartFileReference> { @NotNull @Override public ResolveResult[] resolve(@NotNull final DartFileReference reference, final boolean incompleteCode) { final PsiFile refPsiFile = reference.getElement().getContainingFile(); final int refOffset = reference.getElement().getTextRange().getStartOffset(); final int refLength = reference.getElement().getTextRange().getLength(); DartNavigationRegion region = DartResolver.findRegion(refPsiFile, refOffset, refLength); if (region == null) { // file might be not open in editor, so we do not have navigation information for it final VirtualFile virtualFile = DartResolveUtil.getRealVirtualFile(refPsiFile); final DartAnalysisServerService das = DartAnalysisServerService.getInstance(refPsiFile.getProject()); if (virtualFile != null && das.getNavigation(virtualFile).isEmpty() && das.getHighlight(virtualFile).isEmpty()) { final PsiElement parent = reference.getElement().getParent(); final int parentOffset = parent.getTextRange().getStartOffset(); final int parentLength = parent.getTextRange().getLength(); final List<DartNavigationRegion> regions = das.analysis_getNavigation(virtualFile, parentOffset, parentLength); if (regions != null) { region = DartResolver.findRegion(regions, refOffset, refLength); } } } if (region != null) { final List<DartNavigationTarget> targets = region.getTargets(); if (!targets.isEmpty()) { final DartNavigationTarget target = targets.get(0); final String targetPath = target.getFile(); final VirtualFile targetVirtualFile = LocalFileSystem.getInstance().findFileByPath(targetPath); if (targetVirtualFile != null) { final PsiFile targetFile = reference.getElement().getManager().findFile(targetVirtualFile); if (targetFile != null) { return new ResolveResult[]{new PsiElementResolveResult(targetFile)}; } } } } return ResolveResult.EMPTY_ARRAY; } } }