Implement UsagesSearch

This commit is contained in:
Alexey Sedunov
2013-10-24 15:48:25 +04:00
parent 6502f21144
commit 790fb2acff
3 changed files with 243 additions and 0 deletions
@@ -0,0 +1,6 @@
<root>
<item
name='com.intellij.psi.PsiReferenceService java.util.List&lt;com.intellij.psi.PsiReference&gt; getReferences(com.intellij.psi.PsiElement, com.intellij.psi.PsiReferenceService.Hints)'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
</root>
@@ -0,0 +1,74 @@
/*
* Copyright 2010-2013 JetBrains s.r.o.
*
* 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 org.jetbrains.jet.plugin.search.usagesSearch
import com.intellij.psi.PsiReference
import com.intellij.psi.PsiNamedElement
import org.jetbrains.jet.plugin.search.usagesSearch.UsagesSearchFilter.*
class OrFilter(val filter1: UsagesSearchFilter, val filter2: UsagesSearchFilter): UsagesSearchFilter {
override fun accepts(ref: PsiReference, item: UsagesSearchRequestItem): Boolean =
filter1.accepts(ref, item) || filter2.accepts(ref, item)
}
class AndFilter(val filter1: UsagesSearchFilter, val filter2: UsagesSearchFilter): UsagesSearchFilter {
override fun accepts(ref: PsiReference, item: UsagesSearchRequestItem): Boolean =
filter1.accepts(ref, item) && filter2.accepts(ref, item)
}
class NotFilter(val filter: UsagesSearchFilter): UsagesSearchFilter {
override fun accepts(ref: PsiReference, item: UsagesSearchRequestItem): Boolean =
!filter.accepts(ref, item)
}
fun UsagesSearchFilter.or(that: UsagesSearchFilter): UsagesSearchFilter = when {
this == False -> that
that == False -> this
this == True, that == True -> True
else -> OrFilter(this, that)
}
fun UsagesSearchFilter.and(that: UsagesSearchFilter): UsagesSearchFilter = when {
this == True -> that
that == True -> this
this == False, that == False -> False
else -> AndFilter(this, that)
}
fun UsagesSearchFilter.not(): UsagesSearchFilter = when {
this == False -> True
this == True -> False
else -> NotFilter(this)
}
fun UsagesSearchFilter.ifElse(condition: Boolean, elseFilter: UsagesSearchFilter): UsagesSearchFilter =
if (condition) this else elseFilter
fun UsagesSearchFilter.ifOrFalse(condition: Boolean): UsagesSearchFilter = ifElse(condition, False)
fun UsagesSearchFilter.ifOrTrue(condition: Boolean): UsagesSearchFilter = ifElse(condition, True)
val (PsiReference.() -> Boolean).searchFilter: UsagesSearchFilter
get() = object: UsagesSearchFilter {
override fun accepts(ref: PsiReference, item: UsagesSearchRequestItem): Boolean = ref.this@searchFilter()
}
val <T: PsiNamedElement> (PsiReference.(T) -> Boolean).searchFilter: UsagesSearchFilter
get() = object: UsagesSearchFilter {
[suppress("UNCHECKED_CAST")]
override fun accepts(ref: PsiReference, item: UsagesSearchRequestItem): Boolean = ref.this@searchFilter(item.target.element as T)
}
@@ -0,0 +1,163 @@
/*
* Copyright 2010-2013 JetBrains s.r.o.
*
* 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 org.jetbrains.jet.plugin.search.usagesSearch
import com.intellij.psi.PsiReference
import com.intellij.util.QueryFactory
import com.intellij.psi.search.SearchScope
import com.intellij.psi.PsiElement
import com.intellij.psi.search.SearchRequestCollector
import com.intellij.psi.search.SearchSession
import com.intellij.psi.search.PsiSearchHelper
import com.intellij.util.Query
import com.intellij.util.MergeQuery
import com.intellij.psi.search.SearchRequestQuery
import com.intellij.util.UniqueResultsQuery
import com.intellij.psi.search.searches.ReferenceDescriptor
import com.intellij.util.containers.ContainerUtil
import com.intellij.openapi.application.ApplicationManager
import com.intellij.psi.search.UsageSearchContext
import com.intellij.psi.PsiElement
import com.intellij.psi.search.RequestResultProcessor
import com.intellij.util.Processor
import com.intellij.openapi.application.QueryExecutorBase
import com.intellij.psi.PsiReferenceService
import com.intellij.openapi.progress.ProgressManager
import com.intellij.psi.ReferenceRange
import java.util.Collections
import com.intellij.openapi.extensions.ExtensionPointName
import com.intellij.openapi.extensions.Extensions
import java.util.ArrayList
import com.intellij.util.EmptyQuery
import com.intellij.openapi.project.Project
import java.util.ArrayDeque
import org.jetbrains.jet.plugin.search.usagesSearch.UsagesSearchFilter.*
public data class UsagesSearchLocation(
val inCode: Boolean = true,
val inComments: Boolean = false,
val inStrings: Boolean = false,
val inPlainText: Boolean = false
) {
class object {
public val DEFAULT: UsagesSearchLocation = UsagesSearchLocation()
public val EVERYWHERE: UsagesSearchLocation = UsagesSearchLocation(true, true, true, true)
}
val searchContext: Short
get() {
var res = 0
if (inCode) res += UsageSearchContext.IN_CODE + UsageSearchContext.IN_FOREIGN_LANGUAGES
if (inComments) res += UsageSearchContext.IN_COMMENTS
if (inStrings) res += UsageSearchContext.IN_STRINGS
if (inPlainText) res += UsageSearchContext.IN_PLAIN_TEXT
return res.toShort()
}
}
public data class UsagesSearchTarget<out T : PsiElement>(
val element: T,
val scope: SearchScope,
val location: UsagesSearchLocation = UsagesSearchLocation.DEFAULT,
val restrictByTargetScope: Boolean = true
)
public trait UsagesSearchFilter {
class object {
object False: UsagesSearchFilter {
override fun accepts(ref: PsiReference, item: UsagesSearchRequestItem): Boolean = false
}
object True: UsagesSearchFilter {
override fun accepts(ref: PsiReference, item: UsagesSearchRequestItem): Boolean = true
}
}
fun accepts(ref: PsiReference, item: UsagesSearchRequestItem): Boolean
}
public data class UsagesSearchRequestItem(
val target: UsagesSearchTarget<PsiElement>,
val words: List<String>,
val filter: UsagesSearchFilter
)
public data class UsagesSearchRequest(val project: Project, val items: List<UsagesSearchRequestItem>) {
val collector: SearchRequestCollector = SearchRequestCollector(SearchSession())
}
public object UsagesSearch: QueryFactory<PsiReference, UsagesSearchRequest>() {
{
class ResultProcessorImpl(private val node: UsagesSearchRequestItem) : RequestResultProcessor() {
private val referenceService = PsiReferenceService.getService()!!
override fun processTextOccurrence(element: PsiElement, offsetInElement: Int, consumer: Processor<PsiReference>): Boolean {
referenceService.getReferences(element, PsiReferenceService.Hints.NO_HINTS).all { ref ->
ProgressManager.checkCanceled()
when {
!ReferenceRange.containsOffsetInElement(ref, offsetInElement) -> true
!node.filter.accepts(ref, node) -> true
else -> consumer.process(ref)
}
}
}
}
object ExecutorImpl: QueryExecutorBase<PsiReference, UsagesSearchRequest>() {
override fun processQuery(request: UsagesSearchRequest, consumer: Processor<PsiReference>) {
for (item in request.items) {
with (item) {
if (filter != False) {
ApplicationManager.getApplication()?.runReadAction {
for (word in words) {
request.collector.searchWord(
word, target.scope, target.location.searchContext, true, ResultProcessorImpl(item)
)
}
}
}
}
}
}
}
registerExecutor(ExecutorImpl)
}
fun search(request: UsagesSearchRequest): Query<PsiReference> = with(request) {
createQuery(this) merge SearchRequestQuery(project, collector).unique
}
}
fun UsagesSearchRequest.search(): Query<PsiReference> = UsagesSearch.search(this)
fun <A: PsiElement, B: PsiElement> UsagesSearchTarget<A>.retarget(element: B) =
UsagesSearchTarget<B>(element, scope, location, restrictByTargetScope)
val <T: PsiElement> UsagesSearchTarget<T>.effectiveScope: SearchScope
get() = if (restrictByTargetScope) scope.intersectWith(element.effectiveScope) else scope
val PsiElement.effectiveScope: SearchScope
get() = PsiSearchHelper.SERVICE.getInstance(getProject())!!.getUseScope(this)
fun <T> Query<T>.merge(that: Query<T>): Query<T> = MergeQuery(this, that)
val Query<PsiReference>.unique: Query<PsiReference>
get() = UniqueResultsQuery(this, ContainerUtil.canonicalStrategy(), ReferenceDescriptor.MAPPER)