diff --git a/annotations/com/intellij/psi/annotations.xml b/annotations/com/intellij/psi/annotations.xml new file mode 100644 index 00000000000..d99fc377d4e --- /dev/null +++ b/annotations/com/intellij/psi/annotations.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/idea/src/org/jetbrains/jet/plugin/search/usagesSearch/commonFilters.kt b/idea/src/org/jetbrains/jet/plugin/search/usagesSearch/commonFilters.kt new file mode 100644 index 00000000000..a830a893a8c --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/search/usagesSearch/commonFilters.kt @@ -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 (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) + } \ No newline at end of file diff --git a/idea/src/org/jetbrains/jet/plugin/search/usagesSearch/usagesSearch.kt b/idea/src/org/jetbrains/jet/plugin/search/usagesSearch/usagesSearch.kt new file mode 100644 index 00000000000..09857344e09 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/search/usagesSearch/usagesSearch.kt @@ -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( + 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, + val words: List, + val filter: UsagesSearchFilter +) + +public data class UsagesSearchRequest(val project: Project, val items: List) { + val collector: SearchRequestCollector = SearchRequestCollector(SearchSession()) +} + +public object UsagesSearch: QueryFactory() { + { + class ResultProcessorImpl(private val node: UsagesSearchRequestItem) : RequestResultProcessor() { + private val referenceService = PsiReferenceService.getService()!! + + override fun processTextOccurrence(element: PsiElement, offsetInElement: Int, consumer: Processor): 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() { + override fun processQuery(request: UsagesSearchRequest, consumer: Processor) { + 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 = with(request) { + createQuery(this) merge SearchRequestQuery(project, collector).unique + } +} + +fun UsagesSearchRequest.search(): Query = UsagesSearch.search(this) + +fun UsagesSearchTarget.retarget(element: B) = + UsagesSearchTarget(element, scope, location, restrictByTargetScope) + +val UsagesSearchTarget.effectiveScope: SearchScope + get() = if (restrictByTargetScope) scope.intersectWith(element.effectiveScope) else scope + +val PsiElement.effectiveScope: SearchScope + get() = PsiSearchHelper.SERVICE.getInstance(getProject())!!.getUseScope(this) + +fun Query.merge(that: Query): Query = MergeQuery(this, that) + +val Query.unique: Query + get() = UniqueResultsQuery(this, ContainerUtil.canonicalStrategy(), ReferenceDescriptor.MAPPER) \ No newline at end of file