don't leak dom navigation targets from gutter icons (IDEA-103010)

This commit is contained in:
peter
2014-01-20 18:56:17 +01:00
parent 04425c9f60
commit 8adfd344eb
2 changed files with 86 additions and 37 deletions
@@ -37,4 +37,14 @@ public abstract class NotNullLazyValue<T> {
}
return myValue;
}
public static <T> NotNullLazyValue<T> createConstantValue(@NotNull final T value) {
return new NotNullLazyValue<T>() {
@NotNull
@Override
protected T compute() {
return value;
}
};
}
}
@@ -26,7 +26,9 @@ import com.intellij.navigation.GotoRelatedItem;
import com.intellij.openapi.editor.markup.GutterIconRenderer;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Factory;
import com.intellij.openapi.util.NotNullLazyValue;
import com.intellij.openapi.util.Ref;
import com.intellij.psi.PsiElement;
import com.intellij.psi.SmartPointerManager;
import com.intellij.psi.SmartPsiElementPointer;
@@ -64,7 +66,7 @@ public class NavigationGutterIconBuilder<T> {
private final Icon myIcon;
private final NotNullFunction<T,Collection<? extends PsiElement>> myConverter;
private NotNullLazyValue<Collection<? extends T>> myTargets;
private NotNullLazyValue<Collection<T>> myTargets;
private boolean myLazy;
private String myTooltipText;
private String myPopupTitle;
@@ -134,18 +136,15 @@ public class NavigationGutterIconBuilder<T> {
}
public NavigationGutterIconBuilder<T> setTargets(@NotNull final NotNullLazyValue<Collection<? extends T>> targets) {
myTargets = targets;
//noinspection unchecked
myTargets = (NotNullLazyValue)targets;
myLazy = true;
return this;
}
public NavigationGutterIconBuilder<T> setTargets(@NotNull final Collection<? extends T> targets) {
myTargets = new NotNullLazyValue<Collection<? extends T>>() {
@NotNull
public Collection<? extends T> compute() {
return targets;
}
};
//noinspection unchecked
myTargets = NotNullLazyValue.createConstantValue((Collection<T>)targets);
return this;
}
@@ -206,46 +205,55 @@ public class NavigationGutterIconBuilder<T> {
public RelatedItemLineMarkerInfo<PsiElement> createLineMarkerInfo(@NotNull PsiElement element) {
final MyNavigationGutterIconRenderer renderer = createGutterIconRenderer(element.getProject());
final String tooltip = renderer.getTooltipText();
NotNullLazyValue<Collection<? extends GotoRelatedItem>> gotoTargets = new NotNullLazyValue<Collection<? extends GotoRelatedItem>>() {
@NotNull
@Override
protected Collection<? extends GotoRelatedItem> compute() {
if (myGotoRelatedItemProvider != null) {
return ContainerUtil.concat(myTargets.getValue(), myGotoRelatedItemProvider);
}
return Collections.emptyList();
}
};
NotNullLazyValue<Collection<? extends GotoRelatedItem>> gotoTargets = createGotoTargetsThunk(myLazy, myGotoRelatedItemProvider,
evaluateAndForget(myTargets));
return new RelatedItemLineMarkerInfo<PsiElement>(element, element.getTextRange(), renderer.getIcon(), Pass.UPDATE_OVERRIDEN_MARKERS,
tooltip == null ? null : new ConstantFunction<PsiElement, String>(tooltip),
renderer.isNavigateAction() ? renderer : null, renderer.getAlignment(),
gotoTargets);
}
private static <T> NotNullLazyValue<Collection<? extends GotoRelatedItem>> createGotoTargetsThunk(boolean lazy,
final NotNullFunction<T, Collection<? extends GotoRelatedItem>> gotoRelatedItemProvider,
final Factory<Collection<T>> factory) {
if (gotoRelatedItemProvider == null) {
return NotNullLazyValue.<Collection<? extends GotoRelatedItem>>createConstantValue(Collections.<GotoRelatedItem>emptyList());
}
if (lazy) {
return new NotNullLazyValue<Collection<? extends GotoRelatedItem>>() {
@NotNull
@Override
protected Collection<? extends GotoRelatedItem> compute() {
return ContainerUtil.concat(factory.create(), gotoRelatedItemProvider);
}
};
}
Collection<GotoRelatedItem> concat = ContainerUtil.concat(factory.create(), gotoRelatedItemProvider);
return NotNullLazyValue.<Collection<? extends GotoRelatedItem>>createConstantValue(concat);
}
private void checkBuilt() {
assert myTargets != null : "Must have called .setTargets() before calling create()";
}
private MyNavigationGutterIconRenderer createGutterIconRenderer(@NotNull Project project) {
checkBuilt();
final SmartPointerManager manager = SmartPointerManager.getInstance(project);
NotNullLazyValue<List<SmartPsiElementPointer>> pointers = new NotNullLazyValue<List<SmartPsiElementPointer>>() {
@NotNull
public List<SmartPsiElementPointer> compute() {
Set<PsiElement> elements = new THashSet<PsiElement>();
Collection<? extends T> targets = myTargets.getValue();
final List<SmartPsiElementPointer> list = new ArrayList<SmartPsiElementPointer>(targets.size());
for (final T target : targets) {
for (final PsiElement psiElement : myConverter.fun(target)) {
if (elements.add(psiElement) && psiElement.isValid()) {
list.add(manager.createSmartPsiElementPointer(psiElement));
}
}
}
return list;
private static <T> Factory<T> evaluateAndForget(NotNullLazyValue<T> lazyValue) {
final Ref<NotNullLazyValue<T>> ref = Ref.create(lazyValue);
return new Factory<T>() {
@Override
public T create() {
T result = ref.get().getValue();
ref.set(null);
return result;
}
};
}
private MyNavigationGutterIconRenderer createGutterIconRenderer(@NotNull final Project project) {
checkBuilt();
NotNullLazyValue<List<SmartPsiElementPointer>> pointers = createPointersThunk(myLazy, project, evaluateAndForget(myTargets),
myConverter);
final boolean empty = isEmpty();
@@ -278,6 +286,37 @@ public class NavigationGutterIconBuilder<T> {
return new MyNavigationGutterIconRenderer(this, myAlignment, myIcon, myTooltipText, pointers, renderer, empty);
}
private static <T> NotNullLazyValue<List<SmartPsiElementPointer>> createPointersThunk(boolean lazy,
final Project project,
final Factory<Collection<T>> targets,
final NotNullFunction<T, Collection<? extends PsiElement>> converter) {
if (!lazy) {
return NotNullLazyValue.createConstantValue(calcPsiTargets(project, targets.create(), converter));
}
return new NotNullLazyValue<List<SmartPsiElementPointer>>() {
@NotNull
public List<SmartPsiElementPointer> compute() {
return calcPsiTargets(project, targets.create(), converter);
}
};
}
private static <T> List<SmartPsiElementPointer> calcPsiTargets(Project project, Collection<? extends T> targets,
NotNullFunction<T, Collection<? extends PsiElement>> converter) {
SmartPointerManager manager = SmartPointerManager.getInstance(project);
Set<PsiElement> elements = new THashSet<PsiElement>();
final List<SmartPsiElementPointer> list = new ArrayList<SmartPsiElementPointer>(targets.size());
for (final T target : targets) {
for (final PsiElement psiElement : converter.fun(target)) {
if (elements.add(psiElement) && psiElement.isValid()) {
list.add(manager.createSmartPsiElementPointer(psiElement));
}
}
}
return list;
}
private boolean isEmpty() {
if (myLazy) {
return false;