fun getUsagesPreprocessor()

in src/main/kotlin/org/arend/refactoring/changeSignature/ArendChangeSignatureProcessor.kt [94:262]


        fun getUsagesPreprocessor(usages: List<ArendUsageInfo>,
                                  project: Project,
                                  fileChangeMap: MutableMap<PsiFile, SortedList<Pair<TextRange, String>>>,
                                  rootPsiWithArendErrors: MutableSet<PsiElement>,
                                  rootPsiWithErrors: MutableSet<PsiElement>,
                                  changeInfoNsCmds: List<NsCmdRefactoringAction>,
                                  deferredNsCmds: MutableList<NsCmdRefactoringAction>): Runnable =
            Runnable {
            val concreteSet = LinkedHashSet<ConcreteDataItem>()
            val textReplacements =
                LinkedHashMap<PsiElement, String>()
            val rangeData = java.util.HashMap<Concrete.SourceNode, TextRange>()
            val rootPsiEntries = usages.filter { it.psiParentRoot != null }.map { it.psiParentRoot }
                .toCollection(LinkedHashSet())
            val referableData = usages.map { it.task }.toCollection(HashSet()).toList()

            for (action in changeInfoNsCmds)
                if (!deferredNsCmds.contains(action))
                    deferredNsCmds.add(action)

            runReadAction {
                val progressIndicator = ProgressManager.getInstance().progressIndicator

                for (usage in usages) {
                    val errors = (usage.psi.containingFile as? ArendFile)?.let {
                        project.service<ErrorService>().getErrors(it)
                    }
                    if (errors?.any {
                            val textRange = BasePass.getImprovedTextRange(it.error)
                            textRange?.contains(usage.psi.textRange) == true
                        } == true) usage.psiParentRoot?.let {
                        rootPsiWithArendErrors.add(it)
                    }
                }

                for ((index, rootPsi) in rootPsiEntries.withIndex())
                    if (rootPsi != null && !rootPsiWithArendErrors.contains(rootPsi)) {
                        progressIndicator.fraction = index.toDouble() / rootPsiEntries.size
                        progressIndicator.checkCanceled()
                        val errorReporter = CountingErrorReporter(GeneralError.Level.ERROR, DummyErrorReporter.INSTANCE)
                        when (rootPsi) {
                            is ArendArgumentAppExpr ->
                                appExprToConcrete(rootPsi, false, errorReporter)?.let {
                                if (errorReporter.errorsNumber == 0) concreteSet.add(ConcreteDataItem(rootPsi, it))
                            }

                            is ArendAtomFieldsAcc -> appExprToConcrete(rootPsi, false, errorReporter)?.let {
                                if (errorReporter.errorsNumber == 0) concreteSet.add(ConcreteDataItem(rootPsi, it))
                            }

                            is ArendTypeTele -> rootPsi.type?.let { appExprToConcrete(it) }?.let { concreteSet.add(ConcreteDataItem(rootPsi, it)) }

                            is ArendPattern -> patternToConcrete(rootPsi, errorReporter)?.let {
                                if (errorReporter.errorsNumber == 0) concreteSet.add(ConcreteDataItem(rootPsi, it))
                            }

                            is CoClauseBase -> concreteSet.add(ConcreteDataItem(rootPsi, null))
                        }
                        if (errorReporter.errorsNumber > 0)
                            rootPsiWithErrors.add(rootPsi)
                    }

                for ((index, callEntry) in concreteSet.withIndex()) {
                    progressIndicator.fraction = index.toDouble() / concreteSet.size
                    progressIndicator.checkCanceled()
                    val refactoringContext = ChangeSignatureRefactoringContext(referableData, textReplacements, rangeData, deferredNsCmds)
                    rangeData.clear()

                    try {
                        when (callEntry.psi) {
                            is ArendArgumentAppExpr -> {
                                val expr = callEntry.concreteExpr as Concrete.Expression
                                getBounds(expr, callEntry.psi.node.getChildren(null).toList(), rangeData)
                                val printResult = printAppExpr(expr, callEntry.psi, refactoringContext)
                                printResult.text
                            }

                            is ArendTypeTele -> {
                                val expr = callEntry.concreteExpr as Concrete.Expression
                                if (expr is Concrete.ReferenceExpression) {
                                    val d = refactoringContext.identifyDescriptor(expr.referent)
                                    if (d != null) {
                                        val printResult = NoArgumentsEntry(expr, refactoringContext, d).printUsageEntry(expr.referent as? GlobalReferable)
                                        if (!callEntry.psi.isExplicit) "{${printResult.text}}" else if (printResult.isAtomic) printResult.text else "(${printResult.text})"
                                    } else null
                                } else null
                            }

                            is ArendAtomFieldsAcc -> {
                                val expr = callEntry.concreteExpr
                                val argumentAppExpr = callEntry.psi.ancestor<ArendArgumentAppExpr>()
                                if (expr is Concrete.ProjExpression) {
                                    val atom = expr.expression
                                    if (argumentAppExpr != null) {
                                        getBounds(atom, argumentAppExpr.node.getChildren(null).toList(), rangeData)
                                        val printResult = printAppExpr(atom, argumentAppExpr, refactoringContext)
                                        (if (printResult.isAtomic) printResult.text else "(${printResult.text})") + ".${expr.field + 1}"
                                    } else null
                                } else null
                            }

                            is ArendPattern -> {
                                val concretePattern = callEntry.concreteExpr as Concrete.ConstructorPattern
                                getBounds(concretePattern, callEntry.psi.node.getChildren(null).toList(), rangeData)
                                val printResult = printPattern(concretePattern, callEntry.psi, refactoringContext)
                                printResult.text
                            }

                            is CoClauseBase -> {
                                val referable = callEntry.psi.longName?.resolve as? Referable
                                if (referable != null) {
                                    val descriptor = refactoringContext.identifyDescriptor(referable)
                                    CoClauseEntry(callEntry.psi, refactoringContext, descriptor!!).printUsageEntry().text //TODO: Null safety
                                } else throw IllegalStateException()
                            }

                            else -> null
                        }?.let { result ->
                            textReplacements[callEntry.psi] = result
                        }
                    } catch (e: IllegalArgumentException) {
                        rootPsiWithErrors.add(callEntry.psi)
                    }

                    for (action in refactoringContext.deferredNsCmds) if (!deferredNsCmds.contains(action)) deferredNsCmds.add(action)
                }

                for ((psi, text) in textReplacements) {
                    val file = psi.containingFile
                    val comparator = Comparator<Pair<TextRange, String>> { o1, o2 ->
                        val i = o1.first.startOffset - o2.first.startOffset
                        if (i > 0) 1 else if (i < 0) -1 else 0
                    }
                    val changesList = fileChangeMap.computeIfAbsent(file) {
                        SortedList<Pair<TextRange, String>>(comparator)
                    }

                    val changeText = if (psi is ArendPattern && !psi.isExplicit) "{$text}" else text
                    changesList.add(Pair(psi.textRange, changeText))
                }

                for (changeEntry in fileChangeMap) { // Leave only non-overlapping top-level text changes
                    var sampleEntry: Pair<TextRange, String>? = null
                    var index = 0
                    val list = changeEntry.value
                    while (index < list.size) {
                        val nextEntry = list[index]
                        if (sampleEntry == null || sampleEntry.first.endOffset <= nextEntry.first.startOffset) {
                            sampleEntry = nextEntry
                        } else if (sampleEntry.first.contains(nextEntry.first)) {
                            list.remove(nextEntry)
                            continue
                        } else if (nextEntry.first.contains(sampleEntry.first)) {
                            list.remove(sampleEntry)
                            sampleEntry = nextEntry
                            continue
                        } else throw IllegalStateException() // Changes should not overlap!
                        index++
                    }
                }

                // Assert text changes do not overlap
                for (entry in fileChangeMap) for (i in 1 until entry.value.size) {
                    val precedingRange = entry.value[i - 1].first
                    val currentRange = entry.value[i].first
                    assert(precedingRange.endOffset <= currentRange.startOffset)
                }
            }
        }