fun correspondedSubExpr()

in src/main/kotlin/org/arend/refactoring/ArendSubExprUtils.kt [115:169]


fun correspondedSubExpr(range: TextRange, file: PsiFile, project: Project): SubExprResult {
    val exprAncestor = selectedExpr(file, range) { throw SubExprException(it) }

    val (head, tail) = collectArendExprs(exprAncestor.parent, range)
        ?: throw SubExprException("cannot find a suitable concrete expression")
    val subExpr =
        if (tail.isNotEmpty()) {
            val data = if (exprAncestor.textRange == range) exprAncestor else null
            parseBinOp(data, head, tail)
        }
        else SyntacticDesugarVisitor.desugar(ConcreteBuilder.convertExpression(head), DummyErrorReporter.INSTANCE)
    val resolver = subExpr.underlyingReferenceExpression?.let { refExpr -> refExpr.data?.let { MyResolverListener(it) } }

    // if (possibleParent is PsiWhiteSpace) return "selected text are whitespaces"
    val psiDef = exprAncestor.ancestor<TCDefinition>()
        ?: throw SubExprException("selected text is not in a definition")
    val concreteDef = PsiConcreteProvider(project, DummyErrorReporter.INSTANCE, null, true, resolver).getConcrete(psiDef) as? Concrete.Definition
    val body = concreteDef?.let { it to psiDef }

    val injectionContext = (file as? ArendFile)?.injectionContext
    val injectionHost = injectionContext?.containingFile as? PsiInjectionTextFile
    val errors: List<SubExprError>
    val result = (if (injectionHost != null) {
        val index = when (injectionHost.injectedExpressions.size) {
            0 -> null
            1 -> 0
            else -> InjectedLanguageManager.getInstance(project).getInjectedPsiFiles(injectionContext)?.indexOfFirst { it.first == file }
        }
        val injectedExpr = if (index != null) injectionHost.injectedExpressions[index] else null
        val cExpr = (psiDef as? ArendDefFunction)?.body?.expr?.let { ConcreteBuilder.convertExpression(it) }
        if (injectedExpr != null && cExpr != null && index != null && index < injectionHost.injectedExpressions.size) {
            val scope = CachingScope.make(injectionHost.scope)
            val subExprVisitor = CorrespondedSubExprVisitor(resolver?.result ?: subExpr)
            errors = subExprVisitor.errors
            SyntacticDesugarVisitor.desugar(cExpr.accept(ExpressionResolveNameVisitor(ArendReferableConverter, scope, null, DummyErrorReporter.INSTANCE, null), null), DummyErrorReporter.INSTANCE).accept(subExprVisitor, injectedExpr)
        } else {
            errors = emptyList()
            null
        }
    } else {
        concreteDef ?: throw SubExprException("selected text is not in a definition")
        val def = psiDef.tcReferable?.typechecked
            ?: throw SubExprException("underlying definition is not type checked")
        val subDefVisitor = CorrespondedSubDefVisitor(resolver?.result ?: subExpr)
        errors = subDefVisitor.exprError
        concreteDef.accept(subDefVisitor, def)
    }) ?: throw SubExprException(buildString {
        append("cannot find a suitable subexpression")

        if (errors.any { it.kind == SubExprError.Kind.MetaRef })
            append(" (maybe because you're using meta defs)")
    }, body)

    return SubExprResult(result.proj1, resolver?.originalExpr ?: result.proj2, exprAncestor)
}