re-fix "comparator breaks contract" error (e.g. in ea.jetbrains.com/browser/ea_reports/9957034): introduce Usage.getNavigationOffset() and use it to sort children nodes

GitOrigin-RevId: 830f5693452c6a2b4ac5c33bd94d06280fdce1a3
This commit is contained in:
Alexey Kudravtsev
2022-07-01 12:48:56 +00:00
committed by intellij-monorepo-bot
parent ea1533a948
commit a19bab9414
3 changed files with 37 additions and 22 deletions
@@ -28,6 +28,7 @@ import com.intellij.openapi.util.*;
import com.intellij.openapi.vcs.FileStatusListener;
import com.intellij.openapi.vcs.FileStatusManager;
import com.intellij.openapi.vfs.ReadonlyStatusHandler;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.pom.Navigatable;
import com.intellij.psi.*;
@@ -125,29 +126,23 @@ public class UsageViewImpl implements UsageViewEx {
public static final Comparator<Usage> USAGE_COMPARATOR_BY_FILE_AND_OFFSET = (o1, o2) -> {
if (o1 == o2) return 0;
if (o1 == NullUsage.INSTANCE) return -1;
if (o2 == NullUsage.INSTANCE) return 1;
if (o1 instanceof Comparable && o2 instanceof Comparable && o1.getClass() == o2.getClass()) {
//noinspection unchecked
int selfcompared = ((Comparable<Usage>)o1).compareTo(o2);
if (selfcompared != 0) return selfcompared;
if (o1 instanceof UsageInFile && o2 instanceof UsageInFile) {
UsageInFile u1 = (UsageInFile)o1;
UsageInFile u2 = (UsageInFile)o2;
VirtualFile f1 = u1.getFile();
VirtualFile f2 = u2.getFile();
if (f1 != null && f1.isValid() && f2 != null && f2.isValid()) {
return f1.getPresentableUrl().compareTo(f2.getPresentableUrl());
}
}
return 0;
}
if (o1 == NullUsage.INSTANCE || o1 == null) return -1;
if (o2 == NullUsage.INSTANCE || o2 == null) return 1;
int c = compareByFileAndOffset(o1, o2);
if (c != 0) return c;
return o1.toString().compareTo(o2.toString());
};
private static int compareByFileAndOffset(@NotNull Usage o1, @NotNull Usage o2) {
VirtualFile file1 = o1 instanceof UsageInFile ? ((UsageInFile)o1).getFile() : null;
VirtualFile file2 = o2 instanceof UsageInFile ? ((UsageInFile)o2).getFile() : null;
if (file1 == null || file2 == null) return 0;
if (file1.equals(file2)) {
return Integer.compare(o1.getNavigationOffset(), o2.getNavigationOffset());
}
return VfsUtilCore.compareByPath(file1, file2);
}
@NonNls public static final String HELP_ID = "ideaInterface.find";
private UsageContextPanel myCurrentUsageContextPanel; // accessed in EDT only
private final List<UsageContextPanel> myAllUsageContextPanels = new ArrayList<>(); // accessed in EDT only
@@ -1,7 +1,10 @@
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.usages;
import com.intellij.openapi.editor.LogicalPosition;
import com.intellij.openapi.fileEditor.FileEditorLocation;
import com.intellij.openapi.fileEditor.TextEditor;
import com.intellij.openapi.fileEditor.TextEditorLocation;
import com.intellij.pom.Navigatable;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -23,4 +26,21 @@ public interface Usage extends Navigatable {
void selectInEditor();
void highlightInEditor();
/**
* @return offset of this usage in its containing file to which the corresponding "Go to Source" action should navigate,
* or {@code -1} if the offset can't be computed for some reason.
* This offset is used in "Find Usages" tool window tree to group usages by containing file and then by their offsets.
* Please consider overriding this method if you implement {@link Usage} from scratch and can compute its offset efficiently.
* The already existing implementations in the core, like {@link UsageInfo2UsageAdapter} implement this method efficiently enough, so there's no need to override them.
* Also, please make your {@link Usage} implementation extend {@link com.intellij.usages.rules.UsageInFile} to be able to group usages in "Find Usages" tool window.
*/
default int getNavigationOffset() {
FileEditorLocation location = getLocation();
if (location instanceof TextEditorLocation) {
LogicalPosition position = ((TextEditorLocation)location).getPosition();
return ((TextEditor)location.getEditor()).getEditor().logicalPositionToOffset(position);
}
return -1;
}
}
@@ -41,7 +41,7 @@ import java.util.concurrent.CompletableFuture;
public class UsageInfo2UsageAdapter implements UsageInModule, UsageInfoAdapter,
UsageInLibrary, UsageInFile, PsiElementUsage,
MergeableUsage, Comparable<UsageInfo2UsageAdapter>,
MergeableUsage,
RenameableUsage, DataProvider, UsagePresentation {
public static final NotNullFunction<UsageInfo, Usage> CONVERTER = UsageInfo2UsageAdapter::new;
private static final Comparator<UsageInfo> BY_NAVIGATION_OFFSET = Comparator.comparingInt(UsageInfo::getNavigationOffset);