[platform UI][se] IJPL-223925 Find and apply scope to FilesTabSEContributor

(cherry picked from commit 76af54e96c2370df727bd458c601e3a56d325b38)

IJ-CR-193777

GitOrigin-RevId: 7429a00fd3c7b591b9a6aa992dfe65c11e04dbb3
This commit is contained in:
Aydar Mukhametzyanov
2026-02-19 14:15:06 +01:00
committed by intellij-monorepo-bot
parent 8d2a8bcf74
commit a8c8727a82
4 changed files with 94 additions and 25 deletions

View File

@@ -170,6 +170,15 @@ abstract class AbstractGotoSEContributor @ApiStatus.Internal protected construct
}
return current
}
@ApiStatus.Internal
fun createScopes(project: Project, psiContext: SmartPsiElementPointer<PsiElement?>?): List<ScopeDescriptor> {
@Suppress("DEPRECATION")
return project.getService(ScopeService::class.java)
.createModel(EnumSet.of(ScopeOption.LIBRARIES, ScopeOption.EMPTY_SCOPES))
.getScopesImmediately(createContext(project, psiContext))
.scopeDescriptors
}
}
@ApiStatus.Internal
@@ -190,10 +199,7 @@ abstract class AbstractGotoSEContributor @ApiStatus.Internal protected construct
protected open fun createScopes(): List<ScopeDescriptor> {
@Suppress("DEPRECATION")
return myProject.getService(ScopeService::class.java)
.createModel(EnumSet.of(ScopeOption.LIBRARIES, ScopeOption.EMPTY_SCOPES))
.getScopesImmediately(createContext(myProject, myPsiContext))
.scopeDescriptors
return createScopes(myProject, myPsiContext)
}
override fun getSearchProviderId(): String = javaClass.simpleName

View File

@@ -2,6 +2,7 @@
package com.intellij.ide.actions.searcheverywhere
import com.intellij.ide.IdeBundle
import com.intellij.ide.actions.GotoActionBase
import com.intellij.ide.actions.GotoFileItemProvider
import com.intellij.ide.actions.SearchEverywherePsiRenderer
import com.intellij.ide.actions.searcheverywhere.footer.createPsiExtendedInfo
@@ -10,6 +11,7 @@ import com.intellij.ide.util.scopeChooser.ScopeDescriptor
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.application.ReadAction
import com.intellij.openapi.application.runReadAction
import com.intellij.openapi.application.runReadActionBlocking
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.progress.ProgressIndicator
import com.intellij.openapi.project.DumbAware
@@ -22,6 +24,8 @@ import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiFileSystemItem
import com.intellij.psi.PsiManager
import com.intellij.psi.SmartPointerManager
import com.intellij.psi.SmartPsiElementPointer
import com.intellij.psi.codeStyle.NameUtil
import com.intellij.util.Processor
import com.intellij.util.indexing.FilesDeque
@@ -39,6 +43,9 @@ import kotlin.concurrent.atomics.ExperimentalAtomicApi
*/
@ApiStatus.Internal
interface FilesTabSEContributor {
val psiContext: SmartPsiElementPointer<PsiElement?>?
val project: Project
fun setScope(scope: ScopeDescriptor)
fun setHiddenTypes(hiddenTypes: List<FileTypeRef>)
@@ -73,11 +80,14 @@ class NonIndexableFilesSEContributor(event: AnActionEvent) : WeightedSearchEvery
DumbAware,
FilesTabSEContributor,
SearchEverywhereExtendedInfoProvider {
private val project: Project = event.project!!
override val project: Project = event.project!!
private val navigationHandler: SearchEverywhereNavigationHandler = FileSearchEverywhereNavigationContributionHandler(project)
private val scope: AtomicReference<ScopeDescriptor?> = AtomicReference(null)
private val hiddenTypes: AtomicReference<Set<FileTypeRef>> = AtomicReference(emptySet())
override val psiContext: SmartPsiElementPointer<PsiElement?>? = GotoActionBase.getPsiContext(event)?.let { context ->
SmartPointerManager.getInstance(project).createSmartPsiElementPointer(context)
}
override fun getSearchProviderId(): String = ID
@@ -137,7 +147,7 @@ class NonIndexableFilesSEContributor(event: AnActionEvent) : WeightedSearchEvery
*/
val pathMatcher = GotoFileItemProvider.getQualifiedNameMatcher(pathPattern)
val nameMatcher = NameUtil.buildMatcher("*" + namePattern)
val nameMatcher = NameUtil.buildMatcher("*$namePattern")
.withMatchingMode(MatchingMode.IGNORE_CASE)
.preferringStartMatches()
.build()
@@ -163,7 +173,7 @@ class NonIndexableFilesSEContributor(event: AnActionEvent) : WeightedSearchEvery
if (file == null) break
val workspaceFileIndex = WorkspaceFileIndexEx.getInstance(project)
val nonIndexableRoot = runReadAction { workspaceFileIndex.findNonIndexableFileSet(file) }?.root
val nonIndexableRoot = runReadActionBlocking { workspaceFileIndex.findNonIndexableFileSet(file) }?.root
// path includes root
val pathFromNonIndexableRoot = file.path.removePrefix(nonIndexableRoot?.parent?.path ?: "").removePrefix("/")

View File

@@ -74,11 +74,7 @@ class SeTargetsProviderDelegate(private val contributorWrapper: SeAsyncContribut
scopeProviderDelegate?.let { scopeProviderDelegate ->
SeEverywhereFilter.isEverywhere(params.filter)?.let { isEverywhere ->
val selectedScopeId = scopeProviderDelegate.searchScopesInfo.getValue()?.let { searchScopesInfo ->
if (isEverywhere) searchScopesInfo.everywhereScopeId else searchScopesInfo.projectScopeId
} ?: return@let
scopeProviderDelegate.applyScope(selectedScopeId, false)
scopeProviderDelegate.applyScope(isEverywhere, false)
} ?: run {
val targetsFilter = SeTargetsFilter.from(params.filter)
SeTypeVisibilityStateProviderDelegate.applyTypeVisibilityStates<T>(contributor, targetsFilter.hiddenTypes)

View File

@@ -1,13 +1,19 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.platform.searchEverywhere.providers
import com.intellij.ide.actions.searcheverywhere.AbstractGotoSEContributor
import com.intellij.ide.actions.searcheverywhere.FilesTabSEContributor
import com.intellij.ide.actions.searcheverywhere.FilesTabSEContributor.Companion.unwrapFilesTabContributorIfPossible
import com.intellij.ide.actions.searcheverywhere.ScopeChooserAction
import com.intellij.ide.util.scopeChooser.ScopeDescriptor
import com.intellij.ide.util.scopeChooser.ScopeIdMapper
import com.intellij.openapi.application.readAction
import com.intellij.openapi.application.runReadActionBlocking
import com.intellij.platform.scopes.SearchScopeData
import com.intellij.platform.scopes.SearchScopesInfo
import com.intellij.platform.searchEverywhere.utils.SuspendLazyProperty
import com.intellij.platform.searchEverywhere.utils.suspendLazy
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.annotations.ApiStatus
import java.util.UUID
import kotlin.concurrent.atomics.AtomicReference
@@ -19,11 +25,13 @@ class ScopeChooserActionProviderDelegate private constructor(private val contrib
val searchScopesInfo: SuspendLazyProperty<SearchScopesInfo?> = suspendLazy { getSearchScopesInfo() }
@Volatile
private var scopeIdToScope: AtomicReference<Map<String, ScopeDescriptor>> = AtomicReference(emptyMap())
private val availableScopes: AtomicReference<SeScopeById> =
contributorWrapper.contributor.unwrapFilesTabContributorIfPossible()?.let { filesTabContributor ->
AtomicReference(SeScopeByIdFiles(filesTabContributor))
} ?: AtomicReference(SeScopeByIdAtomicMap(emptyMap(), null, null))
suspend fun getSearchScopesInfo(): SearchScopesInfo? {
val scopeChooserAction: ScopeChooserAction = contributorWrapper.contributor.getActions({ }).filterIsInstance<ScopeChooserAction>().firstOrNull()
private suspend fun getSearchScopesInfo(): SearchScopesInfo? {
val scopeChooserAction: ScopeChooserAction = contributorWrapper.contributor.getActions { }.filterIsInstance<ScopeChooserAction>().firstOrNull()
?: return null
@@ -33,54 +41,103 @@ class ScopeChooserActionProviderDelegate private constructor(private val contrib
val scopeDataList = readAction {
scopeChooserAction.scopesWithSeparators
}.mapNotNull { scope ->
val key = UUID.randomUUID().toString()
val data = SearchScopeData.Companion.from(scope, key)
val key = scope.displayName?.let {
"${UUID.randomUUID()}$SCOPE_ID_SEPARATOR${ScopeIdMapper.instance.getScopeSerializationId(it)}"
} ?: return@mapNotNull null
val data = SearchScopeData.from(scope, key)
if (data != null) all[key] = scope
data
}
scopeIdToScope.store(all)
val selectedScopeId = selectedScope.scope?.displayName.let { name ->
scopeDataList.firstOrNull {
@Suppress("HardCodedStringLiteral")
it.name == name
}?.scopeId
}
val everywhereScopeId = scopeChooserAction.everywhereScopeName?.let { name ->
scopeDataList.firstOrNull {
@Suppress("HardCodedStringLiteral")
it.name == name
}?.scopeId
}
val projectScopeId = scopeChooserAction.projectScopeName?.let { name ->
scopeDataList.firstOrNull {
@Suppress("HardCodedStringLiteral")
it.name == name
}?.scopeId
}
availableScopes.store(SeScopeByIdAtomicMap(all, everywhereScopeId = everywhereScopeId, projectScopeId = projectScopeId))
return SearchScopesInfo(scopeDataList,
selectedScopeId,
projectScopeId,
everywhereScopeId)
}
fun applyScope(isEverywhere: Boolean, isAutoTogglePossible: Boolean) {
val scope = availableScopes.load()[isEverywhere] ?: return
applyScope(scope, isAutoTogglePossible)
}
fun applyScope(scopeId: String?, isAutoTogglePossible: Boolean) {
if (scopeId == null) return
val scope = scopeIdToScope.load()[scopeId] ?: return
val scope = availableScopes.load()[scopeId] ?: return
applyScope(scope, isAutoTogglePossible)
}
fun applyScope(scope: ScopeDescriptor, isAutoTogglePossible: Boolean) {
contributorWrapper.contributor.getActions { }.filterIsInstance<ScopeChooserAction>().firstOrNull()?.let {
it.setScopeIsDefaultAndAutoSet(isAutoTogglePossible)
it.onScopeSelected(scope)
}
?: contributorWrapper.contributor.unwrapFilesTabContributorIfPossible()?.setScope(scope)
}
companion object {
fun createOrNull(contributorWrapper: SeAsyncContributorWrapper<Any>): ScopeChooserActionProviderDelegate? =
if (contributorWrapper.contributor.getActions { }.any { it is ScopeChooserAction })
if (contributorWrapper.contributor.unwrapFilesTabContributorIfPossible() != null ||
contributorWrapper.contributor.getActions { }.any { it is ScopeChooserAction })
ScopeChooserActionProviderDelegate(contributorWrapper)
else null
}
}
}
private const val SCOPE_ID_SEPARATOR = '_'
private interface SeScopeById {
operator fun get(isEverywhere: Boolean): ScopeDescriptor?
operator fun get(scopeId: String): ScopeDescriptor?
}
private class SeScopeByIdAtomicMap(
private val scopeIdToScope: Map<String, ScopeDescriptor>,
private val everywhereScopeId: String?,
private val projectScopeId: String?,
) : SeScopeById {
override fun get(isEverywhere: Boolean): ScopeDescriptor? =
(if (isEverywhere) everywhereScopeId else projectScopeId)?.let { scopeIdToScope[it] }
override fun get(scopeId: String): ScopeDescriptor? = scopeIdToScope[scopeId]
}
private class SeScopeByIdFiles(filesContributor: FilesTabSEContributor): SeScopeById {
private val scopes = runReadActionBlocking {
AbstractGotoSEContributor.createScopes(filesContributor.project, filesContributor.psiContext).mapNotNull {
val name = it.displayName ?: return@mapNotNull null
ScopeIdMapper.instance.getScopeSerializationId(name) to it
}.toMap()
}
private val projectScopeId: String = GlobalSearchScope.projectScope(filesContributor.project).displayName.let {
ScopeIdMapper.instance.getScopeSerializationId(it)
}
private val everywhereScopeId: String = GlobalSearchScope.everythingScope(filesContributor.project).displayName.let {
ScopeIdMapper.instance.getScopeSerializationId(it)
}
override fun get(isEverywhere: Boolean): ScopeDescriptor? = (if (isEverywhere) everywhereScopeId else projectScopeId).let { scopes[it] }
override fun get(scopeId: String): ScopeDescriptor? = scopes[scopeId.substringAfter(SCOPE_ID_SEPARATOR)]
}