(IJPL-61965) Make API for showing notifications above search results more generic

(IJPL-61965) Update API dump

(IJPL-61965) Set results notification callback when initializing SE UI

(IJPL-61965) Introduce interface for Search Everywhere contributor wrappers

(IJPL-61965) Make API for showing notifications above search results more generic

I also revoke access from contributors to SearchListener to make them isolated.


Merge-request: IJ-MR-137034
Merged-by: Evgeny Abramov <Evgeny.Abramov@jetbrains.com>

GitOrigin-RevId: cbb3fbe7c552ad661bc4ef5cd69e36f078008127
This commit is contained in:
Evgeny Abramov
2024-06-18 14:51:41 +00:00
committed by intellij-monorepo-bot
parent 513cb52fe0
commit d524487c52
22 changed files with 143 additions and 156 deletions

View File

@@ -907,7 +907,7 @@ advanced.setting.documentation.components.enable.highlighting.of.links=Syntax hi
advanced.setting.documentation.components.enable.code.background=Add background to inline and multiline code blocks
advanced.setting.documentation.auto.show.in.modal.dialogs=Show QuickDoc automatically during code completion in modal dialogs
advanced.setting.search.everywhere.wait.for.contributors=Wait for all contributors to finish before showing results
advanced.setting.search.everywhere.show.only.similar.indicator=Show message above search results when no exact matches are found
advanced.setting.search.everywhere.show.results.notification=Show notifications above search results
advanced.setting.search.everywhere.wait.for.contributors.description=Enabling this option will stop the search items to change their position in the displayed search results. This process might cause a delay.
advanced.setting.search.everywhere.contributors.wait.timeout=Contributors waiting timeout (ms)
advanced.setting.search.everywhere.recent.at.top=Show recent files at the top of results list

View File

@@ -14056,6 +14056,7 @@ f:com.intellij.ide.actions.searcheverywhere.PSIPresentationBgRendererWrapper
- com.intellij.ide.actions.searcheverywhere.EssentialContributor
- com.intellij.ide.actions.searcheverywhere.PossibleSlowContributor
- com.intellij.ide.actions.searcheverywhere.ScopeSupporting
- com.intellij.ide.actions.searcheverywhere.SearchEverywhereContributorWrapper
- com.intellij.ide.actions.searcheverywhere.SearchEverywhereExtendedInfoProvider
- com.intellij.ide.actions.searcheverywhere.SearchEverywherePreviewProvider
- com.intellij.ide.actions.searcheverywhere.WeightedSearchEverywhereContributor
@@ -14069,6 +14070,7 @@ f:com.intellij.ide.actions.searcheverywhere.PSIPresentationBgRendererWrapper
- getAutocompleteItems(java.lang.String,I):java.util.List
- getDataForItem(java.lang.Object,java.lang.String):java.lang.Object
- getDelegate():com.intellij.ide.actions.searcheverywhere.AbstractGotoSEContributor
- getEffectiveContributor():com.intellij.ide.actions.searcheverywhere.SearchEverywhereContributor
- getElementPriority(java.lang.Object,java.lang.String):I
- getElementsRenderer():javax.swing.ListCellRenderer
- getFullGroupName():java.lang.String
@@ -14259,6 +14261,8 @@ f:com.intellij.ide.actions.searcheverywhere.SearchEverywhereContributorValidatio
- com.intellij.internal.statistic.eventLog.validator.rules.impl.UtilValidationRule
- <init>():V
- getRuleId():java.lang.String
com.intellij.ide.actions.searcheverywhere.SearchEverywhereContributorWrapper
- a:getEffectiveContributor():com.intellij.ide.actions.searcheverywhere.SearchEverywhereContributor
com.intellij.ide.actions.searcheverywhere.SearchEverywhereEmptyTextProvider
- a:updateEmptyStatus(com.intellij.util.ui.StatusText,kotlin.jvm.functions.Function0):V
c:com.intellij.ide.actions.searcheverywhere.SearchEverywhereFiltersAction
@@ -14324,6 +14328,7 @@ f:com.intellij.ide.actions.searcheverywhere.SearchEverywhereHeader
- getTabs():java.util.List
- isEverywhere():Z
- resetScope():V
- setResultsNotifyCallback(java.util.function.Consumer):V
- switchToNextTab():V
- switchToPrevTab():V
- switchToTab(com.intellij.ide.actions.searcheverywhere.SearchEverywhereHeader$SETab):V
@@ -14337,6 +14342,7 @@ f:com.intellij.ide.actions.searcheverywhere.SearchEverywhereHeader$SETab
- getName():java.lang.String
- getReportableID():java.lang.String
- isSingleContributor():Z
- setResultsNotifyCallback(java.util.function.Consumer):V
- setSelected(Z):V
com.intellij.ide.actions.searcheverywhere.SearchEverywhereManager
- a:getCurrentlyShownUI():com.intellij.ide.actions.searcheverywhere.SearchEverywhereUI

View File

@@ -75,8 +75,8 @@ final class MixedListFactory extends SEResultsListFactory {
public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
if (value == SearchListModel.MORE_ELEMENT) {
return getMoreElementRenderer(list, index, isSelected, cellHasFocus);
} else if (value == SearchListModel.HAS_ONLY_SIMILAR_ELEMENT) {
return getOnlySimilarElementRenderer(list, index, isSelected, cellHasFocus);
} else if (value instanceof SearchListModel.ResultsNotificationElement) {
return resultsNotificationElementRenderer.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
}
return getNonMoreElementRenderer(list, value, index, isSelected, cellHasFocus, model, myRenderersCache);

View File

@@ -68,7 +68,7 @@ final class MixedResultsSearcher implements SESearcher {
Function<ProgressIndicator, ResultsAccumulator> accumulatorSupplier = indicator ->
new ResultsAccumulator(map, myEqualityProvider, myListener, myNotificationExecutor, indicator);
return performSearch(contributorsAndLimits.keySet(), pattern, accumulatorSupplier, myListener);
return performSearch(contributorsAndLimits.keySet(), pattern, accumulatorSupplier);
}
@Override
@@ -78,14 +78,13 @@ final class MixedResultsSearcher implements SESearcher {
Function<ProgressIndicator, ResultsAccumulator> accumulatorSupplier = indicator ->
new ResultsAccumulator(alreadyFound, contributorsAndLimits, myEqualityProvider, myListener, myNotificationExecutor, indicator);
return performSearch(contributorsAndLimits.keySet(), pattern, accumulatorSupplier, myListener);
return performSearch(contributorsAndLimits.keySet(), pattern, accumulatorSupplier);
}
@NotNull
private static ProgressIndicator performSearch(@NotNull Collection<? extends SearchEverywhereContributor<?>> contributors,
@NotNull String pattern,
@NotNull Function<? super ProgressIndicator, ? extends ResultsAccumulator> accumulatorSupplier,
@NotNull SearchListener searchListener) {
@NotNull Function<? super ProgressIndicator, ? extends ResultsAccumulator> accumulatorSupplier) {
ProgressIndicator indicator;
ResultsAccumulator accumulator;
if (!contributors.isEmpty()) {
@@ -96,7 +95,7 @@ final class MixedResultsSearcher implements SESearcher {
for (SearchEverywhereContributor<?> contributor : contributors) {
Runnable task = createSearchTask(pattern, accumulator,
indicatorWithCancelListener, contributor, () -> latch.countDown(), searchListener);
indicatorWithCancelListener, contributor, () -> latch.countDown());
ApplicationManager.getApplication().executeOnPooledThread(task);
}
@@ -127,12 +126,11 @@ final class MixedResultsSearcher implements SESearcher {
ResultsAccumulator accumulator,
ProgressIndicator indicator,
SearchEverywhereContributor<?> contributor,
Runnable finalCallback,
SearchListener searchListener) {
Runnable finalCallback) {
//noinspection unchecked
ContributorSearchTask<?> task = new ContributorSearchTask<>(
(SearchEverywhereContributor<Object>)contributor, pattern,
accumulator, indicator, finalCallback, searchListener);
accumulator, indicator, finalCallback);
return ConcurrencyUtil.underThreadNameRunnable("SE-SearchTask-" + contributor.getSearchProviderId(), task);
}
@@ -159,20 +157,16 @@ final class MixedResultsSearcher implements SESearcher {
private final SearchEverywhereContributor<Item> myContributor;
private final String myPattern;
private final ProgressIndicator myIndicator;
private final SearchListener mySearchListener;
private ContributorSearchTask(SearchEverywhereContributor<Item> contributor,
String pattern,
ResultsAccumulator accumulator,
ProgressIndicator indicator,
Runnable callback,
SearchListener searchListener
) {
Runnable callback) {
myContributor = contributor;
myPattern = pattern;
myAccumulator = accumulator;
myIndicator = indicator;
mySearchListener = searchListener;
finishCallback = callback;
}
@@ -186,13 +180,6 @@ final class MixedResultsSearcher implements SESearcher {
do {
ProgressIndicator wrapperIndicator = new SensitiveProgressWrapper(myIndicator);
try {
var effectiveContributor = (myContributor instanceof PSIPresentationBgRendererWrapper wrapper)
? wrapper.getDelegate() : myContributor;
if (effectiveContributor instanceof SearchEverywhereContributorWithListener myContributorWithListener) {
myContributorWithListener.setSearchListener(mySearchListener);
}
if (myContributor instanceof WeightedSearchEverywhereContributor) {
((WeightedSearchEverywhereContributor<Item>)myContributor).fetchWeightedElements(myPattern, wrapperIndicator,
descriptor -> processFoundItem(

View File

@@ -6,22 +6,20 @@ import com.intellij.openapi.options.advanced.AdvancedSettings;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Conditions;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
final class MixedSearchListModel extends SearchListModel {
private final Map<SearchEverywhereContributor<?>, Boolean> hasMoreContributors = new HashMap<>();
@ApiStatus.Experimental
private final Map<String, Boolean> noStandardResultsByContributor = new HashMap<>();
@ApiStatus.Experimental
private final Map<String, Boolean> hasOnlySimilarByContributor = new HashMap<>();
final private AtomicReference<SearchEverywhereFoundElementInfo> myNotificationElement = new AtomicReference<>();
private final SearchEverywhereReorderingService myReorderingService = SearchEverywhereReorderingService.getInstance();
@@ -65,14 +63,14 @@ final class MixedSearchListModel extends SearchListModel {
listElements.clear();
if (lastIndex >= 0) fireIntervalRemoved(this, 0, lastIndex);
addHasOnlySimilarIfApplicable(items);
addNotificationIfApplicable();
listElements.addAll(items);
if (!listElements.isEmpty()) fireIntervalAdded(this, 0, listElements.size() - 1);
resultsExpired = false;
}
else {
addHasOnlySimilarIfApplicable(items);
addNotificationIfApplicable();
int startIndex = listElements.size();
listElements.addAll(items);
int endIndex = listElements.size() - 1;
@@ -102,18 +100,22 @@ final class MixedSearchListModel extends SearchListModel {
}
}
private void addHasOnlySimilarIfApplicable(List<? extends SearchEverywhereFoundElementInfo> items) {
if (AdvancedSettings.getBoolean("search.everywhere.show.only.similar.indicator")) {
items.stream()
.map(item -> item.contributor)
.distinct()
.forEach(contributor -> {
var noStandardResults = Boolean.TRUE.equals(noStandardResultsByContributor.get(contributor.getSearchProviderId()));
var onlySimilarElements = hasOnlySimilarElements(contributor);
if (noStandardResults && !onlySimilarElements) {
setHasOnlySimilarElements(contributor);
}
});
private void addNotificationIfApplicable() {
var notificationElement = myNotificationElement.getAndSet(null);
if (notificationElement != null && AdvancedSettings.getBoolean("search.everywhere.show.results.notification")) {
var lastItemIndex = listElements.size() - 1;
listElements.removeIf(info -> info.getElement() instanceof ResultsNotificationElement);
var newLastItemIndex = listElements.size() - 1;
if (newLastItemIndex < lastItemIndex) {
fireIntervalRemoved(this, 0, lastItemIndex - newLastItemIndex - 1);
}
listElements.add(notificationElement);
newLastItemIndex++;
fireIntervalAdded(this, newLastItemIndex, newLastItemIndex);
if (myMaxFrozenIndex != -1) {
myMaxFrozenIndex++;
}
}
}
@@ -172,31 +174,10 @@ final class MixedSearchListModel extends SearchListModel {
}
}
@ApiStatus.Experimental
@Override
public void standardSearchFoundNoResults(SearchEverywhereContributor<?> contributor) {
noStandardResultsByContributor.put(contributor.getSearchProviderId(), true);
}
@ApiStatus.Experimental
@Override
public boolean hasOnlySimilarElements(SearchEverywhereContributor<?> contributor) {
return Boolean.TRUE.equals(hasOnlySimilarByContributor.get(contributor.getSearchProviderId()));
}
@ApiStatus.Experimental
@Override
public void setHasOnlySimilarElements(SearchEverywhereContributor<?> contributor) {
hasOnlySimilarByContributor.put(contributor.getSearchProviderId(), true);
var lastItemIndex = listElements.size() - 1;
listElements.add(new SearchEverywhereFoundElementInfo(HAS_ONLY_SIMILAR_ELEMENT, Integer.MAX_VALUE - 1, null));
lastItemIndex++;
fireIntervalAdded(this, lastItemIndex, lastItemIndex);
if (myMaxFrozenIndex != -1) {
myMaxFrozenIndex++;
}
SearchEverywhereUsageTriggerCollector.HAS_ONLY_SIMILAR_ITEM_SHOWN.log();
public void addNotificationElement(@NotNull String label) {
myNotificationElement.getAndSet(new SearchEverywhereFoundElementInfo(
new ResultsNotificationElement(label), Integer.MAX_VALUE - 1, null));
}
@Override
@@ -209,17 +190,15 @@ final class MixedSearchListModel extends SearchListModel {
@Override
public void clear() {
hasMoreContributors.clear();
hasOnlySimilarByContributor.clear();
noStandardResultsByContributor.clear();
myMaxFrozenIndex = -1;
myNotificationElement.set(null);
super.clear();
}
@Override
public void expireResults() {
super.expireResults();
hasOnlySimilarByContributor.clear();
noStandardResultsByContributor.clear();
myNotificationElement.set(null);
myMaxFrozenIndex = -1;
}
}

View File

@@ -35,7 +35,8 @@ import java.util.function.Function;
public final class PSIPresentationBgRendererWrapper implements WeightedSearchEverywhereContributor<Object>, ScopeSupporting,
AutoCompletionContributor, PossibleSlowContributor, EssentialContributor,
SearchEverywhereExtendedInfoProvider, SearchEverywherePreviewProvider {
SearchEverywhereExtendedInfoProvider, SearchEverywherePreviewProvider,
SearchEverywhereContributorWrapper {
private final AbstractGotoSEContributor myDelegate;
public PSIPresentationBgRendererWrapper(AbstractGotoSEContributor delegate) { myDelegate = delegate; }
@@ -57,6 +58,12 @@ public final class PSIPresentationBgRendererWrapper implements WeightedSearchEve
return EssentialContributor.checkEssential(myDelegate);
}
@NotNull
@Override
public SearchEverywhereContributor<?> getEffectiveContributor() {
return myDelegate;
}
public static WeightedSearchEverywhereContributor<Object> wrapIfNecessary(AbstractGotoSEContributor delegate) {
if (Registry.is("psi.element.list.cell.renderer.background")) {
return new PSIPresentationBgRendererWrapper(delegate);

View File

@@ -63,7 +63,7 @@ abstract class SEResultsListFactory {
};
@ApiStatus.Experimental
protected static final ListCellRenderer<Object> hasOnlySimilarElementRenderer = new ColoredListCellRenderer<>() {
protected static final ListCellRenderer<Object> resultsNotificationElementRenderer = new ColoredListCellRenderer<>() {
@Override
protected int getMinHeight() {
return 2 * super.getMinHeight();
@@ -73,12 +73,12 @@ abstract class SEResultsListFactory {
protected void customizeCellRenderer(@NotNull JList<?> list, Object value, int index, boolean selected, boolean hasFocus) {
clear();
mySelected = false;
if (value != SearchListModel.HAS_ONLY_SIMILAR_ELEMENT) {
if (!(value instanceof SearchListModel.ResultsNotificationElement)) {
throw new AssertionError(value);
}
setFont(StartupUiUtil.getLabelFont().deriveFont(UIUtil.getFontSize(UIUtil.FontSize.NORMAL)));
append(IdeBundle.message("search.everywhere.no.exact.matches"), new SimpleTextAttributes(
SimpleTextAttributes.STYLE_PLAIN, UIUtil.getLabelDisabledForeground()));
append(((SearchListModel.ResultsNotificationElement)value).label(), new SimpleTextAttributes(
SimpleTextAttributes.STYLE_PLAIN, UIUtil.getLabelInfoForeground()));
setIpad(JBInsets.create(4, 17));
setMyBorder(null);
}
@@ -99,11 +99,6 @@ abstract class SEResultsListFactory {
return selectablePanel;
}
@ApiStatus.Internal
Component getOnlySimilarElementRenderer(@NotNull JList<?> list, int index, boolean selected, boolean hasFocus) {
return hasOnlySimilarElementRenderer.getListCellRendererComponent(list, SearchListModel.HAS_ONLY_SIMILAR_ELEMENT, index, selected, hasFocus);
}
@ApiStatus.Internal
Component getNonMoreElementRenderer(@NotNull JList<?> list, Object value, int index, boolean selected, boolean hasFocus,
SearchListModel searchListModel, Map<String, ListCellRenderer<? super Object>> renderersCache) {

View File

@@ -0,0 +1,6 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.ide.actions.searcheverywhere
interface SearchEverywhereContributorWrapper {
fun getEffectiveContributor(): SearchEverywhereContributor<*>
}

View File

@@ -31,6 +31,7 @@ import java.awt.event.MouseEvent;
import java.util.List;
import java.util.*;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
@@ -333,6 +334,12 @@ public final class SearchEverywhereHeader {
contributors, actions, filter);
}
public void setResultsNotifyCallback(@NotNull Consumer<@NotNull @Nls String> notifyCallback) {
for (SETab tab: myTabs) {
tab.setResultsNotifyCallback(notifyCallback);
}
}
public static final class SETab {
private final @NotNull @NonNls String id;
private final @NlsContexts.Label @NotNull String name;
@@ -382,6 +389,17 @@ public final class SearchEverywhereHeader {
return getReportableContributorID(contributors.get(0));
}
public void setResultsNotifyCallback(@NotNull Consumer<@NotNull @Nls String> notifyCallback) {
for (SearchEverywhereContributor<?> contributor : contributors) {
var effectiveContributor = (contributor instanceof SearchEverywhereContributorWrapper wrapper)
? wrapper.getEffectiveContributor() : contributor;
if (effectiveContributor instanceof SearchEverywhereResultsNotifier notifierContributor) {
notifierContributor.setNotifyCallback(notifyCallback);
}
}
}
public boolean isSingleContributor() {
return contributors.size() == 1;
}

View File

@@ -2,9 +2,11 @@
package com.intellij.ide.actions.searcheverywhere
import org.jetbrains.annotations.ApiStatus
import org.jetbrains.annotations.Nls
import org.jetbrains.annotations.NotNull
import java.util.function.Consumer
@ApiStatus.Internal
@ApiStatus.Experimental
interface SearchEverywhereContributorWithListener {
var searchListener: SearchListener?
}
interface SearchEverywhereResultsNotifier {
var notifyCallback: Consumer<@NotNull @Nls String>?
}

View File

@@ -211,6 +211,8 @@ public final class SearchEverywhereUI extends BigPopupUI implements DataProvider
}
init();
myHeader.setResultsNotifyCallback(myListModel::addNotificationElement); // should be performed after myListModel creation in init()
myHintHelper = new HintHelper(mySearchField);
List<SEResultsEqualityProvider> equalityProviders = SEResultsEqualityProvider.getProviders();
@@ -540,7 +542,8 @@ public final class SearchEverywhereUI extends BigPopupUI implements DataProvider
private @NotNull List<SearchEverywhereFoundElementInfo> getSelectedInfos() {
return Arrays.stream(myResultsList.getSelectedIndices())
.mapToObj(myListModel::getRawFoundElementAt)
.filter(o -> o.getElement() != SearchListModel.MORE_ELEMENT && o.getElement() != SearchListModel.HAS_ONLY_SIMILAR_ELEMENT)
.filter(o -> o.getElement() != SearchListModel.MORE_ELEMENT
&& !(o.getElement() instanceof SearchListModel.ResultsNotificationElement))
.collect(Collectors.toList());
}
@@ -981,7 +984,7 @@ public final class SearchEverywhereUI extends BigPopupUI implements DataProvider
int selectedIndex = myResultsList.getSelectedIndex();
if (prevSelectedIndex != -1 &&
selectedIndex != -1 &&
myListModel.getElementAt(selectedIndex) == SearchListModel.HAS_ONLY_SIMILAR_ELEMENT) {
myListModel.getElementAt(selectedIndex) instanceof SearchListModel.ResultsNotificationElement) {
int newIndex;
int listSize = myListModel.getSize();
if (prevSelectedIndex == selectedIndex + 1) {
@@ -1141,7 +1144,7 @@ public final class SearchEverywhereUI extends BigPopupUI implements DataProvider
private void showDescriptionForIndex(int index) {
if (index < 0 || myListModel.isMoreElement(index)
|| myListModel.getElementAt(index) == SearchListModel.HAS_ONLY_SIMILAR_ELEMENT) {
|| myListModel.getElementAt(index) instanceof SearchListModel.ResultsNotificationElement) {
return;
}
@@ -1292,7 +1295,7 @@ public final class SearchEverywhereUI extends BigPopupUI implements DataProvider
boolean multiSelectMode = e.isShiftDown() || UIUtil.isControlKeyDown(e);
boolean isPreviewDoubleClick = !isPreviewActive() || !hasPreviewProvider(myHeader.getSelectedTab()) || e.getClickCount() == 2;
int selectedIndex = myResultsList.locationToIndex(e.getPoint());
if (myListModel.getElementAt(selectedIndex) == SearchListModel.HAS_ONLY_SIMILAR_ELEMENT) {
if (myListModel.getElementAt(selectedIndex) instanceof SearchListModel.ResultsNotificationElement) {
int listSize = myListModel.getSize();
if (prevSelectedIndex == (selectedIndex - 1 + listSize) % listSize) {
myResultsList.setSelectedIndex(selectedIndex + 1);
@@ -1338,15 +1341,16 @@ public final class SearchEverywhereUI extends BigPopupUI implements DataProvider
showMoreElements(contributor);
return;
}
if (indexes.length == 1 && myListModel.getElementAt(indexes[0]) == SearchListModel.HAS_ONLY_SIMILAR_ELEMENT) {
if (indexes.length == 1 && myListModel.getElementAt(indexes[0]) instanceof SearchListModel.ResultsNotificationElement) {
return;
}
boolean hasOnlySimilarElement = ContainerUtil.find(myListModel.getItems(), SearchListModel.HAS_ONLY_SIMILAR_ELEMENT) != null;
boolean hasNotificationElement = ContainerUtil.find(
myListModel.getItems(), element -> element instanceof SearchListModel.ResultsNotificationElement) != null;
indexes = Arrays.stream(indexes)
.filter(i -> !myListModel.isMoreElement(i))
.filter(i -> myListModel.getElementAt(i) != SearchListModel.HAS_ONLY_SIMILAR_ELEMENT)
.filter(i -> !(myListModel.getElementAt(i) instanceof SearchListModel.ResultsNotificationElement))
.toArray();
String searchText = getSearchPattern();
@@ -1369,8 +1373,8 @@ public final class SearchEverywhereUI extends BigPopupUI implements DataProvider
if (selectedTabContributorID != null) {
data.add(SearchEverywhereUsageTriggerCollector.CURRENT_TAB_FIELD.with(selectedTabContributorID));
}
data.add(SearchEverywhereUsageTriggerCollector.SELECTED_ITEM_NUMBER.with(hasOnlySimilarElement ? (i - 1) : i));
data.add(SearchEverywhereUsageTriggerCollector.HAS_ONLY_SIMILAR_ELEMENT.with(hasOnlySimilarElement));
data.add(SearchEverywhereUsageTriggerCollector.SELECTED_ITEM_NUMBER.with(hasNotificationElement ? (i - 1) : i));
data.add(SearchEverywhereUsageTriggerCollector.HAS_ONLY_SIMILAR_ELEMENT.with(hasNotificationElement));
PsiElement psi = toPsi(value);
if (psi != null) {
data.add(EventFields.Language.with(psi.getLanguage()));
@@ -1383,7 +1387,7 @@ public final class SearchEverywhereUI extends BigPopupUI implements DataProvider
if (myMlService != null) {
var tabId = myHeader.getSelectedTab().getID();
var correctIndexes = hasOnlySimilarElement ? Arrays.stream(indexes).map(i -> (i - 1)).toArray() : indexes;
var correctIndexes = hasNotificationElement ? Arrays.stream(indexes).map(i -> (i - 1)).toArray() : indexes;
myMlService.onItemSelected(
myProject, tabId, correctIndexes, selectedItems, () -> myListModel.getFoundElementsInfo(), closePopup, searchText);
}
@@ -1747,7 +1751,7 @@ public final class SearchEverywhereUI extends BigPopupUI implements DataProvider
if (selectedItem instanceof SearchEverywhereSpellCheckResult.Correction && myListModel.getSize() > 1) {
myResultsList.setSelectedIndex(1);
}
if (myResultsList.getSelectedValue() == SearchListModel.HAS_ONLY_SIMILAR_ELEMENT && myListModel.getSize() > 1) {
if (myResultsList.getSelectedValue() instanceof SearchListModel.ResultsNotificationElement && myListModel.getSize() > 1) {
myResultsList.setSelectedIndex(myResultsList.getSelectedIndex() + 1);
}
@@ -1805,12 +1809,6 @@ public final class SearchEverywhereUI extends BigPopupUI implements DataProvider
myExternalSearchListeners.forEach(listener -> listener.contributorFinished(contributor, hasMore));
}
@ApiStatus.Experimental
@Override
public void standardSearchFoundNoResults(@NotNull SearchEverywhereContributor<?> contributor) {
myListModel.standardSearchFoundNoResults(contributor);
}
private void updateEmptyText(String pattern) {
StatusText emptyStatus = myResultsList.getEmptyText();
emptyStatus.clear();

View File

@@ -4,6 +4,7 @@ package com.intellij.ide.actions.searcheverywhere;
import com.google.common.collect.Lists;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -18,8 +19,9 @@ import java.util.stream.Collectors;
public abstract class SearchListModel extends AbstractListModel<Object> {
static final Object MORE_ELEMENT = new Object();
@ApiStatus.Experimental
static final Object HAS_ONLY_SIMILAR_ELEMENT = new Object();
public record ResultsNotificationElement(@NotNull @Nls String label) {
}
protected final List<SearchEverywhereFoundElementInfo> listElements = new ArrayList<>();
protected boolean resultsExpired = false;
@@ -58,7 +60,7 @@ public abstract class SearchListModel extends AbstractListModel<Object> {
return listElements.stream()
.filter(info -> info.getContributor() == contributor &&
info.getElement() != MORE_ELEMENT &&
info.getElement() != HAS_ONLY_SIMILAR_ELEMENT)
!(info.getElement() instanceof ResultsNotificationElement))
.map(info -> info.getElement())
.collect(Collectors.toList());
}
@@ -77,14 +79,7 @@ public abstract class SearchListModel extends AbstractListModel<Object> {
public abstract void setHasMore(SearchEverywhereContributor<?> contributor, boolean contributorHasMore);
@ApiStatus.Experimental
public void standardSearchFoundNoResults(SearchEverywhereContributor<?> contributor) { }
@ApiStatus.Experimental
public boolean hasOnlySimilarElements(SearchEverywhereContributor<?> contributor) { return false; }
@ApiStatus.Experimental
public void setHasOnlySimilarElements(SearchEverywhereContributor<?> contributor) { }
public void addNotificationElement(@NotNull @Nls String label) { }
public abstract void addElements(List<? extends SearchEverywhereFoundElementInfo> items);
@@ -120,12 +115,13 @@ public abstract class SearchListModel extends AbstractListModel<Object> {
@NotNull
public List<SearchEverywhereFoundElementInfo> getFoundElementsInfo() {
return ContainerUtil.filter(listElements, info -> info.element != MORE_ELEMENT && info.element != HAS_ONLY_SIMILAR_ELEMENT);
return ContainerUtil.filter(listElements, info -> info.element != MORE_ELEMENT
&& !(info.element instanceof ResultsNotificationElement));
}
public Map<SearchEverywhereContributor<?>, Collection<SearchEverywhereFoundElementInfo>> getFoundElementsMap() {
return listElements.stream()
.filter(info -> info.element != MORE_ELEMENT && info.element != HAS_ONLY_SIMILAR_ELEMENT)
.filter(info -> info.element != MORE_ELEMENT && !(info.element instanceof ResultsNotificationElement))
.collect(Collectors.groupingBy(o -> o.getContributor(), Collectors.toCollection(ArrayList::new)));
}
}

View File

@@ -3,6 +3,7 @@ package com.intellij.ide.actions.searcheverywhere
import com.intellij.codeWithMe.ClientId.Companion.current
import com.intellij.ide.DataManager
import com.intellij.ide.actions.searcheverywhere.SearchListModel.ResultsNotificationElement
import com.intellij.openapi.actionSystem.ActionUpdateThread
import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
@@ -112,8 +113,8 @@ private class ElementInfoManager(private val seUI: SearchEverywhereUI) {
content.text = "'More...' element"
return
}
if (info.element == SearchListModel.HAS_ONLY_SIMILAR_ELEMENT) {
content.text = "'No exact matches...' element"
if (info.element is ResultsNotificationElement) {
content.text = "Results notification element"
return
}

View File

@@ -1696,7 +1696,6 @@ big.popup.filter.button.all=All
big.popup.filter.button.none=None
big.popup.filter.button.invert=Invert
search.everywhere.points.more=... more
search.everywhere.no.exact.matches=No exact matches found. Here are similar results:
search.everywhere.action.tooltip.text=Search Everywhere<br/>Press <b>{0}</b> to access <ul style=''list-style-type:none;margin:0;padding:0;''>\
<li>- {1}</li><li>- Files</li><li>- Tool Windows</li><li>- Actions</li><li>- Settings</li></ul>
search.everywhere.action.tooltip.description.text=Searches for: <ul style=''list-style-type:none;margin:0;padding:0;''>\

View File

@@ -1633,7 +1633,7 @@
<advancedSetting id="documentation.auto.show.in.modal.dialogs" default="false"
groupKey="group.advanced.settings.documentation.components"/>
<advancedSetting id="search.everywhere.wait.for.contributors" default="true" groupKey="group.advanced.settings.se"/>
<advancedSetting id="search.everywhere.show.only.similar.indicator" default="true" groupKey="group.advanced.settings.se"/>
<advancedSetting id="search.everywhere.show.results.notification" default="true" groupKey="group.advanced.settings.se"/>
<advancedSetting id="search.everywhere.contributors.wait.timeout" default="2000" groupKey="group.advanced.settings.se"/>
<advancedSetting id="search.everywhere.recent.at.top" default="true" groupKey="group.advanced.settings.se"/>
<advancedSetting id="search.everywhere.send.search.history.statistics" default="true" groupKey="group.advanced.settings.se"/>

View File

@@ -79,7 +79,7 @@ class SearchEverywhereMlRankingService : SearchEverywhereMlService {
val elementId = ReadAction.compute<Int?, Nothing> { session.itemIdProvider.getId(element) }
val mlElementInfo = state.getElementFeatures(elementId, element, contributor, priority, session.mixedListInfo, session.cachedContextInfo)
val effectiveContributor = if (contributor is PSIPresentationBgRendererWrapper) contributor.delegate else contributor
val effectiveContributor = if (contributor is SearchEverywhereContributorWrapper) contributor.getEffectiveContributor() else contributor
val mlWeight = if (shouldCalculateMlWeight(effectiveContributor, state, element)) state.getMLWeight(session.cachedContextInfo, mlElementInfo) else null
return if (isShowDiff()) SearchEverywhereFoundElementInfoBeforeDiff(element, priority, contributor, mlWeight, mlElementInfo.features)

View File

@@ -7,3 +7,4 @@ search.everywhere.ml.semantic.actions.api.authentication.failed=Remote semantic
search.everywhere.ml.semantic.open.registry=Open semantic search settings
advanced.setting.search.everywhere.ml.name=Search Everywhere
search.everywhere.no.exact.matches=No exact matches found. Here are similar results:

View File

@@ -2,10 +2,11 @@ package com.intellij.searchEverywhereMl.semantics.contributors
import com.intellij.ide.actions.searcheverywhere.FoundItemDescriptor
import com.intellij.ide.actions.searcheverywhere.MergeableElement
import com.intellij.ide.actions.searcheverywhere.SearchEverywhereContributorWithListener
import com.intellij.ide.actions.searcheverywhere.SearchEverywhereResultsNotifier
import com.intellij.openapi.application.readActionBlocking
import com.intellij.openapi.progress.*
import com.intellij.platform.ml.embeddings.search.utils.ScoredText
import com.intellij.searchEverywhereMl.semantics.SemanticSearchBundle
import com.intellij.searchEverywhereMl.semantics.providers.StreamSemanticItemsProvider
import com.intellij.util.Processor
import com.intellij.util.TimeoutUtil
@@ -14,9 +15,8 @@ import kotlinx.coroutines.ensureActive
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import org.jetbrains.annotations.ApiStatus
interface SearchEverywhereConcurrentElementsFetcher<I : MergeableElement, E : Any>: SearchEverywhereContributorWithListener {
interface SearchEverywhereConcurrentElementsFetcher<I : MergeableElement, E : Any>: SearchEverywhereResultsNotifier {
val itemsProvider: StreamSemanticItemsProvider<I>
val useReadAction: Boolean
@@ -31,9 +31,6 @@ interface SearchEverywhereConcurrentElementsFetcher<I : MergeableElement, E : An
fun prepareStandardDescriptor(descriptor: FoundItemDescriptor<E>,
knownItems: MutableList<FoundItemDescriptor<I>>): () -> FoundItemDescriptor<E>
@ApiStatus.Experimental
fun onStandardSearchFoundNoResults() {}
fun prepareSemanticDescriptor(descriptor: FoundItemDescriptor<I>,
knownItems: MutableList<FoundItemDescriptor<I>>,
durationMs: Long): () -> FoundItemDescriptor<E>?
@@ -69,9 +66,10 @@ interface SearchEverywhereConcurrentElementsFetcher<I : MergeableElement, E : An
val semanticMatches = itemsProvider.searchIfEnabled(pattern, priorityThresholds[DescriptorPriority.LOW])
if (semanticMatches.isEmpty()) return@launch
standardSearchJob.join()
val noStandardResults = knownItems.isEmpty()
// Allow the scope to change automatically:
if (knownItems.isEmpty() && checkScopeIsDefaultAndAutoSet()) return@launch
if (knownItems.isEmpty()) onStandardSearchFoundNoResults()
if (noStandardResults && checkScopeIsDefaultAndAutoSet()) return@launch
var sentNotification = false
suspend fun iterate() {
for (priority in ORDERED_PRIORITIES) {
@@ -90,6 +88,10 @@ interface SearchEverywhereConcurrentElementsFetcher<I : MergeableElement, E : An
ensureActive()
val prepareDescriptor = prepareSemanticDescriptor(descriptor, knownItems, TimeoutUtil.getDurationMillis(searchStart))
mutex.withLock { prepareDescriptor() }?.let {
if (!sentNotification) {
notifyCallback?.accept(SemanticSearchBundle.getMessage("search.everywhere.no.exact.matches"))
sentNotification = true
}
consumer.process(it)
foundItemsCount++
}

View File

@@ -4,7 +4,6 @@ import com.intellij.concurrency.SensitiveProgressWrapper
import com.intellij.ide.actions.searcheverywhere.ActionSearchEverywhereContributor
import com.intellij.ide.actions.searcheverywhere.FoundItemDescriptor
import com.intellij.ide.actions.searcheverywhere.PossibleSlowContributor
import com.intellij.ide.actions.searcheverywhere.SearchListener
import com.intellij.ide.util.gotoByName.GotoActionModel
import com.intellij.ide.util.gotoByName.GotoActionModel.ActionWrapper
import com.intellij.ide.util.gotoByName.GotoActionModel.MatchedValue
@@ -19,6 +18,7 @@ import com.intellij.openapi.progress.blockingContext
import com.intellij.openapi.util.registry.Registry
import com.intellij.platform.ml.embeddings.search.utils.ScoredText
import com.intellij.searchEverywhereMl.SemanticSearchEverywhereContributor
import com.intellij.searchEverywhereMl.semantics.SemanticSearchBundle
import com.intellij.searchEverywhereMl.semantics.contributors.SearchEverywhereConcurrentElementsFetcher.Companion.ORDERED_PRIORITIES
import com.intellij.searchEverywhereMl.semantics.contributors.SearchEverywhereConcurrentElementsFetcher.DescriptorPriority
import com.intellij.searchEverywhereMl.semantics.providers.LocalSemanticActionsProvider
@@ -32,6 +32,7 @@ import kotlinx.coroutines.*
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import org.jetbrains.annotations.ApiStatus
import java.util.function.Consumer
import javax.swing.ListCellRenderer
@@ -55,7 +56,7 @@ class SemanticActionSearchEverywhereContributor(defaultContributor: ActionSearch
override val priorityThresholds
get() = PRIORITY_THRESHOLDS
override var searchListener: SearchListener? = null
override var notifyCallback: Consumer<String>? = null
override fun isElementSemantic(element: Any): Boolean {
return (element is MatchedValue && element.type == GotoActionModel.MatchedValueType.SEMANTIC)
@@ -129,8 +130,9 @@ class SemanticActionSearchEverywhereContributor(defaultContributor: ActionSearch
val semanticMatches = itemsProvider.searchIfEnabled(pattern, priorityThresholds[DescriptorPriority.LOW])
if (semanticMatches.isEmpty()) return@launch
standardSearchJob.join()
if (knownItems.isEmpty() && checkScopeIsDefaultAndAutoSet()) return@launch // allow the scope to change automatically
if (knownItems.isEmpty()) onStandardSearchFoundNoResults()
val noStandardResults = knownItems.isEmpty()
if (noStandardResults && checkScopeIsDefaultAndAutoSet()) return@launch // allow the scope to change automatically
var sentNotification = false
for (priority in ORDERED_PRIORITIES) {
val iterator = if (priority == DescriptorPriority.HIGH) semanticMatches.iterator()
@@ -147,6 +149,10 @@ class SemanticActionSearchEverywhereContributor(defaultContributor: ActionSearch
for (descriptor in itemsProvider.createItemDescriptors(match.text, match.similarity, pattern)) {
val prepareDescriptor = prepareSemanticDescriptor(descriptor, knownItems, TimeoutUtil.getDurationMillis(searchStart))
mutex.withLock { prepareDescriptor() }?.let {
if (!sentNotification) {
notifyCallback?.accept(SemanticSearchBundle.getMessage("search.everywhere.no.exact.matches"))
sentNotification = true
}
blockingContext { consumer.process(it) }
val descriptorValue = it.item.value
// Only count available actions
@@ -195,10 +201,6 @@ class SemanticActionSearchEverywhereContributor(defaultContributor: ActionSearch
}
}
override fun onStandardSearchFoundNoResults() {
searchListener?.standardSearchFoundNoResults(this)
}
override fun defaultFetchElements(pattern: String,
progressIndicator: ProgressIndicator,
consumer: Processor<in FoundItemDescriptor<MatchedValue>>) {

View File

@@ -6,7 +6,6 @@ import com.intellij.ide.actions.searcheverywhere.ClassSearchEverywhereContributo
import com.intellij.ide.actions.searcheverywhere.FoundItemDescriptor
import com.intellij.ide.actions.searcheverywhere.PossibleSlowContributor
import com.intellij.ide.actions.searcheverywhere.PsiItemWithSimilarity
import com.intellij.ide.actions.searcheverywhere.SearchListener
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.progress.ProgressIndicator
import com.intellij.openapi.progress.ProgressManager
@@ -15,6 +14,7 @@ import com.intellij.searchEverywhereMl.SemanticSearchEverywhereContributor
import com.intellij.searchEverywhereMl.semantics.providers.SemanticClassesProvider
import com.intellij.util.Processor
import org.jetbrains.annotations.ApiStatus
import java.util.function.Consumer
@ApiStatus.Experimental
class SemanticClassSearchEverywhereContributor(initEvent: AnActionEvent)
@@ -24,9 +24,9 @@ class SemanticClassSearchEverywhereContributor(initEvent: AnActionEvent)
override val itemsProvider = SemanticClassesProvider(project, createModel(project))
override val psiElementsRenderer = elementsRenderer as SearchEverywherePsiRenderer
override var notifyCallback: Consumer<String>? = null
override var searchListener: SearchListener? = null
override val psiElementsRenderer = elementsRenderer as SearchEverywherePsiRenderer
override fun getSearchProviderId(): String = ClassSearchEverywhereContributor::class.java.simpleName
@@ -44,10 +44,6 @@ class SemanticClassSearchEverywhereContributor(initEvent: AnActionEvent)
super.fetchWeightedElements(pattern, progressIndicator, consumer)
}
override fun onStandardSearchFoundNoResults() {
searchListener?.standardSearchFoundNoResults(this)
}
override fun checkScopeIsDefaultAndAutoSet(): Boolean = isScopeDefaultAndAutoSet
override fun syncSearchSettings() {

View File

@@ -6,7 +6,6 @@ import com.intellij.ide.actions.searcheverywhere.FileSearchEverywhereContributor
import com.intellij.ide.actions.searcheverywhere.FoundItemDescriptor
import com.intellij.ide.actions.searcheverywhere.PossibleSlowContributor
import com.intellij.ide.actions.searcheverywhere.PsiItemWithSimilarity
import com.intellij.ide.actions.searcheverywhere.SearchListener
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.progress.ProgressIndicator
import com.intellij.openapi.progress.ProgressManager
@@ -15,6 +14,7 @@ import com.intellij.searchEverywhereMl.SemanticSearchEverywhereContributor
import com.intellij.searchEverywhereMl.semantics.providers.SemanticFilesProvider
import com.intellij.util.Processor
import org.jetbrains.annotations.ApiStatus
import java.util.function.Consumer
/**
* Contributor that adds semantic search functionality when searching for files in Search Everywhere.
@@ -30,9 +30,9 @@ open class SemanticFileSearchEverywhereContributor(initEvent: AnActionEvent)
override val itemsProvider = SemanticFilesProvider(project, createModel(project))
override val psiElementsRenderer = elementsRenderer as SearchEverywherePsiRenderer
override var notifyCallback: Consumer<String>? = null
override var searchListener: SearchListener? = null
override val psiElementsRenderer = elementsRenderer as SearchEverywherePsiRenderer
override fun getSearchProviderId(): String = FileSearchEverywhereContributor::class.java.simpleName
@@ -50,10 +50,6 @@ open class SemanticFileSearchEverywhereContributor(initEvent: AnActionEvent)
super.fetchWeightedElements(pattern, progressIndicator, consumer)
}
override fun onStandardSearchFoundNoResults() {
searchListener?.standardSearchFoundNoResults(this)
}
override fun checkScopeIsDefaultAndAutoSet(): Boolean = isScopeDefaultAndAutoSet
override fun syncSearchSettings() {

View File

@@ -6,7 +6,6 @@ import com.intellij.ide.actions.searcheverywhere.SymbolSearchEverywhereContribut
import com.intellij.ide.actions.searcheverywhere.FoundItemDescriptor
import com.intellij.ide.actions.searcheverywhere.PossibleSlowContributor
import com.intellij.ide.actions.searcheverywhere.PsiItemWithSimilarity
import com.intellij.ide.actions.searcheverywhere.SearchListener
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.progress.ProgressIndicator
import com.intellij.openapi.progress.ProgressManager
@@ -15,6 +14,7 @@ import com.intellij.searchEverywhereMl.SemanticSearchEverywhereContributor
import com.intellij.searchEverywhereMl.semantics.providers.SemanticSymbolsProvider
import com.intellij.util.Processor
import org.jetbrains.annotations.ApiStatus
import java.util.function.Consumer
/**
* Contributor that adds semantic search functionality when searching for symbols in Search Everywhere.
@@ -31,9 +31,9 @@ class SemanticSymbolSearchEverywhereContributor(initEvent: AnActionEvent)
override val itemsProvider = SemanticSymbolsProvider(project, createModel(project))
override val psiElementsRenderer = elementsRenderer as SearchEverywherePsiRenderer
override var notifyCallback: Consumer<String>? = null
override var searchListener: SearchListener? = null
override val psiElementsRenderer = elementsRenderer as SearchEverywherePsiRenderer
override fun getSearchProviderId(): String = SymbolSearchEverywhereContributor::class.java.simpleName
@@ -51,10 +51,6 @@ class SemanticSymbolSearchEverywhereContributor(initEvent: AnActionEvent)
super.fetchWeightedElements(pattern, progressIndicator, consumer)
}
override fun onStandardSearchFoundNoResults() {
searchListener?.standardSearchFoundNoResults(this)
}
override fun checkScopeIsDefaultAndAutoSet(): Boolean = isScopeDefaultAndAutoSet
override fun syncSearchSettings() {