blob: be947169b8168ae67b1b4495fb6d902cdfbe8916 [file] [log] [blame]
/*
* Copyright 2016 The Bazel Authors. All rights reserved.
*
* 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.google.idea.blaze.java.lang.build;
import com.google.idea.blaze.base.lang.buildfile.references.GlobReference;
import com.google.idea.blaze.base.lang.buildfile.search.BlazePackage;
import com.google.idea.blaze.base.lang.buildfile.search.ResolveUtil;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFileSystemItem;
import com.intellij.refactoring.safeDelete.JavaSafeDeleteProcessor;
import com.intellij.refactoring.safeDelete.NonCodeUsageSearchInfo;
import com.intellij.refactoring.safeDelete.usageInfo.SafeDeleteUsageInfo;
import com.intellij.usageView.UsageInfo;
import com.intellij.util.IncorrectOperationException;
import java.util.Collection;
import java.util.List;
import javax.annotation.Nullable;
import org.jetbrains.annotations.NotNull;
/**
* Removes glob references which don't refer directly to the item(s) being deleted (b/28979434)
* (e.g. indirect references from glob([*.java])).
*
* <p>Runs before JavaSafeDeleteProcessor, and delegates to it, removing indirect glob references.
* Only the first valid SafeDeleteProcessorDelegate is used in almost all cases*, so this class
* effectively replaces JavaSafeDeleteProcessor (*in the situations where all processors are used,
* this class has no effect).
*/
public class BuildFileSafeDeleteProcessor extends JavaSafeDeleteProcessor {
/**
* Delegates to JavaSafeDeleteProcessor, then removes indirect glob references which we don't want
* to block safe delete.
*/
@Nullable
@Override
public NonCodeUsageSearchInfo findUsages(
@NotNull PsiElement element,
@NotNull PsiElement[] allElementsToDelete,
@NotNull List<UsageInfo> result) {
NonCodeUsageSearchInfo superResult = super.findUsages(element, allElementsToDelete, result);
result.removeIf(BuildFileSafeDeleteProcessor::ignoreUsage);
return superResult;
}
/**
* We keep globs which reference the file directly (i.e. without wildcards), and remove all
* indirect references for the purposes of the 'safe delete' action.
*/
private static boolean ignoreUsage(UsageInfo usage) {
if (usage.getReference() instanceof GlobReference && usage instanceof SafeDeleteUsageInfo) {
PsiElement referencedElement = ((SafeDeleteUsageInfo) usage).getReferencedElement();
PsiFileSystemItem file = ResolveUtil.asFileSystemItemSearch(referencedElement);
String relativePath = getBlazePackageRelativePathToFile(file);
if (relativePath == null) {
return false;
}
return !((GlobReference) usage.getReference())
.matchesDirectly(relativePath, file.isDirectory());
}
return false;
}
@Nullable
private static String getBlazePackageRelativePathToFile(@Nullable PsiFileSystemItem file) {
if (file == null) {
return null;
}
BlazePackage containingPackage = BlazePackage.getContainingPackage(file);
if (containingPackage == null) {
return null;
}
return containingPackage.getRelativePathToChild(file.getVirtualFile());
}
@Override
public boolean handlesElement(PsiElement element) {
return super.handlesElement(element);
}
@Nullable
@Override
public Collection<? extends PsiElement> getElementsToSearch(
@NotNull PsiElement element,
@Nullable Module module,
@NotNull Collection<PsiElement> allElementsToDelete) {
return super.getElementsToSearch(element, module, allElementsToDelete);
}
@Nullable
@Override
public Collection<PsiElement> getAdditionalElementsToDelete(
@NotNull PsiElement element,
@NotNull Collection<PsiElement> allElementsToDelete,
boolean askUser) {
return super.getAdditionalElementsToDelete(element, allElementsToDelete, askUser);
}
@Nullable
@Override
public Collection<String> findConflicts(
@NotNull PsiElement element, @NotNull PsiElement[] allElementsToDelete) {
return super.findConflicts(element, allElementsToDelete);
}
@Nullable
@Override
public UsageInfo[] preprocessUsages(Project project, UsageInfo[] usages) {
return usages;
}
@Override
public void prepareForDeletion(PsiElement element) throws IncorrectOperationException {}
@Override
public boolean isToSearchInComments(PsiElement element) {
return super.isToSearchInComments(element);
}
@Override
public void setToSearchInComments(PsiElement element, boolean enabled) {
super.setToSearchInComments(element, enabled);
}
@Override
public boolean isToSearchForTextOccurrences(PsiElement element) {
return super.isToSearchForTextOccurrences(element);
}
@Override
public void setToSearchForTextOccurrences(PsiElement element, boolean enabled) {
super.setToSearchForTextOccurrences(element, enabled);
}
}