@@ -22,6 +22,7 @@ import com.intellij.openapi.progress.ProgressIndicator
22
22
import com.intellij.openapi.progress.ProgressManager
23
23
import com.intellij.openapi.progress.Task
24
24
import com.intellij.openapi.project.Project
25
+ import com.intellij.openapi.util.Key
25
26
import com.intellij.psi.PsiElement
26
27
import com.intellij.psi.search.GlobalSearchScope
27
28
import com.intellij.psi.search.searches.ReferencesSearch
@@ -39,6 +40,7 @@ import org.jetbrains.kotlin.idea.stubindex.KotlinSourceFilterScope
39
40
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
40
41
import org.jetbrains.kotlin.idea.util.application.runReadAction
41
42
import org.jetbrains.kotlin.psi.*
43
+ import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType
42
44
import org.jetbrains.kotlin.psi.psiUtil.forEachDescendantOfType
43
45
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
44
46
import org.jetbrains.kotlin.resolve.BindingContext
@@ -116,84 +118,104 @@ fun UsageReplacementStrategy.replaceUsages(
116
118
) {
117
119
GuiUtils .invokeLaterIfNeeded({
118
120
project.executeWriteCommand(commandName) {
119
- // we should delete imports later to not affect other usages
120
- val importsToDelete = arrayListOf<KtImportDirective >()
121
- val replacements = mutableListOf<KtElement >()
122
-
123
- var invalidUsagesFound = false
124
-
125
121
val targetDeclaration = targetPsiElement as ? KtNamedDeclaration
126
122
127
- val usagesChildrenFirst = usages.sortChildrenFirst()
128
- usages@ for (usage in usagesChildrenFirst) {
129
- try {
130
- if (! usage.isValid) {
131
- invalidUsagesFound = true
132
- continue
133
- }
123
+ val usagesByFile = usages.groupBy { it.containingFile }
134
124
135
- val usageParent = usage.parent
136
- when (usageParent) {
137
- is KtCallableReferenceExpression -> {
138
- val grandParent = usageParent.parent
139
- ConvertReferenceToLambdaIntention ().applyTo(usageParent, null )
140
- (grandParent as ? KtElement )?.let {
141
- doRefactoringInside(it, targetDeclaration?.name, targetDeclaration?.descriptor)
142
- }
143
- continue @usages
144
- }
145
- is KtCallElement -> {
146
- val lambdaArguments = usageParent.lambdaArguments
147
- if (lambdaArguments.isNotEmpty()) {
148
- val grandParent = usageParent.parent
149
- val specifySignature = SpecifyExplicitLambdaSignatureIntention ()
150
- for (lambdaArgument in lambdaArguments) {
151
- val lambdaExpression = lambdaArgument.getLambdaExpression()
152
- val functionDescriptor =
153
- lambdaExpression.functionLiteral.resolveToDescriptorIfAny() as ? FunctionDescriptor ? : continue
154
- if (functionDescriptor.valueParameters.isNotEmpty()) {
155
- specifySignature.applyTo(lambdaExpression, null )
156
- }
157
- }
158
- (grandParent as ? KtElement )?.let {
159
- doRefactoringInside(it, targetDeclaration?.name, targetDeclaration?.descriptor)
160
- }
161
- continue @usages
162
- }
163
- }
125
+ val KEY = Key <Unit >(" UsageReplacementStrategy.replaceUsages" )
164
126
165
- }
127
+ for ((file, usagesInFile) in usagesByFile) {
128
+ usagesInFile.forEach { it.putCopyableUserData(KEY , Unit ) }
166
129
167
- // TODO: keep the import if we don't know how to replace some of the usages
168
- val importDirective = usage.getStrictParentOfType<KtImportDirective >()
169
- if (importDirective != null ) {
170
- if (! importDirective.isAllUnder && importDirective.targetDescriptors().size == 1 ) {
171
- importsToDelete.add(importDirective)
172
- }
173
- continue
174
- }
130
+ // we should delete imports later to not affect other usages
131
+ val importsToDelete = mutableListOf<KtImportDirective >()
175
132
176
- val replacement = createReplacer(usage)?.invoke()
177
- if (replacement != null ) {
178
- replacements + = replacement
179
- }
180
- }
181
- catch (e: Throwable ) {
182
- LOG .error(e)
133
+ var usagesToProcess = usagesInFile
134
+ while (usagesToProcess.isNotEmpty()) {
135
+ if (processUsages(usagesToProcess, targetDeclaration, importsToDelete)) break
136
+
137
+ // some usages may get invalidated we need to find them in the tree
138
+ usagesToProcess = file.collectDescendantsOfType<KtSimpleNameExpression > { it.getCopyableUserData(KEY ) != null }
183
139
}
140
+
141
+ file.forEachDescendantOfType<KtSimpleNameExpression > { it.putCopyableUserData(KEY , null ) }
142
+
143
+ importsToDelete.forEach { it.delete() }
144
+ }
145
+
146
+ postAction()
147
+ }
148
+ }, ModalityState .NON_MODAL )
149
+ }
150
+
151
+ /* *
152
+ * @return false if some usages were invalidated
153
+ */
154
+ private fun UsageReplacementStrategy.processUsages (
155
+ usages : List <KtSimpleNameExpression >,
156
+ targetDeclaration : KtNamedDeclaration ? ,
157
+ importsToDelete : MutableList <KtImportDirective >
158
+ ): Boolean {
159
+ var invalidUsagesFound = false
160
+ for (usage in usages.sortChildrenFirst()) {
161
+ try {
162
+ if (! usage.isValid) {
163
+ invalidUsagesFound = true
164
+ continue
184
165
}
185
166
186
- if (invalidUsagesFound && targetDeclaration != null ) {
187
- val name = targetDeclaration.name
188
- val descriptor = targetDeclaration.descriptor
189
- for (replacement in replacements) {
190
- doRefactoringInside(replacement, name, descriptor)
167
+ if (specialUsageProcessing(usage, targetDeclaration)) continue
168
+
169
+ // TODO: keep the import if we don't know how to replace some of the usages
170
+ val importDirective = usage.getStrictParentOfType<KtImportDirective >()
171
+ if (importDirective != null ) {
172
+ if (! importDirective.isAllUnder && importDirective.targetDescriptors().size == 1 ) {
173
+ importsToDelete.add(importDirective)
191
174
}
175
+ continue
192
176
}
193
177
194
- importsToDelete.forEach { it.delete() }
178
+ createReplacer(usage)?.invoke()
179
+ }
180
+ catch (e: Throwable ) {
181
+ LOG .error(e)
182
+ }
183
+ }
184
+ return ! invalidUsagesFound
185
+ }
195
186
196
- postAction()
187
+ private fun UsageReplacementStrategy.specialUsageProcessing (usage : KtSimpleNameExpression , targetDeclaration : KtNamedDeclaration ? ): Boolean {
188
+ val usageParent = usage.parent
189
+ when (usageParent) {
190
+ is KtCallableReferenceExpression -> {
191
+ val grandParent = usageParent.parent
192
+ ConvertReferenceToLambdaIntention ().applyTo(usageParent, null )
193
+ (grandParent as ? KtElement )?.let {
194
+ doRefactoringInside(it, targetDeclaration?.name, targetDeclaration?.descriptor)
195
+ }
196
+ return true
197
197
}
198
- }, ModalityState .NON_MODAL )
198
+
199
+ is KtCallElement -> {
200
+ val lambdaArguments = usageParent.lambdaArguments
201
+ if (lambdaArguments.isNotEmpty()) {
202
+ val grandParent = usageParent.parent
203
+ val specifySignature = SpecifyExplicitLambdaSignatureIntention ()
204
+ for (lambdaArgument in lambdaArguments) {
205
+ val lambdaExpression = lambdaArgument.getLambdaExpression()
206
+ val functionDescriptor =
207
+ lambdaExpression.functionLiteral.resolveToDescriptorIfAny() as ? FunctionDescriptor ? : continue
208
+ if (functionDescriptor.valueParameters.isNotEmpty()) {
209
+ specifySignature.applyTo(lambdaExpression, null )
210
+ }
211
+ }
212
+ (grandParent as ? KtElement )?.let {
213
+ doRefactoringInside(it, targetDeclaration?.name, targetDeclaration?.descriptor)
214
+ }
215
+ return true
216
+ }
217
+ }
218
+
219
+ }
220
+ return false
199
221
}
0 commit comments