Skip to content

KotlinCompletionSupport.kt

src/jvmMain/kotlin/afield/runtime/completion/KotlinCompletionSupport.kt · 2915 lines

package and imports — 66 lines, from line 1
package afield.runtime.completion

import afield.animation.interpolation.interpolant
import afield.app.server.app
import afield.app.server.documents
import afield.data.table.div
import afield.data.table.minus
import afield.data.table.plus
import afield.data.table.rem
import afield.data.table.times
import afield.document.graph.introImports
import afield.document.graph.preamble
import afield.runtime.execution.plus
import afield.runtime.kotlin.ImportResolution
import afield.runtime.kotlin.cache
import afield.runtime.kotlin.exported
import afield.runtime.lifecycle.plus
import afield.runtime.lifecycle.rem
import afield.runtime.lifecycle.then
import afield.ui.notation.double
import afield.ui.notation.long
import afield.ui.notation.string
import afield.ui.notation.with
import afield.ui.output.stats
import afield.ui.output.tree
import afield.ui.visualization.matrix
import afield.ui.visualization.table
import afield.ui.visualization.viz

import afield.app.protocol.Metadata
import afield.app.protocol.SourceCardResult
import afield.app.protocol.SourceFileResponse
import afield.audio.synth.*
import afield.data.table.get
import afield.document.graph.Box
import afield.document.graph.box
import afield.document.workspace.metadata
import afield.graphics.vulkan.*
import afield.media.stream.*
import afield.media.video.*
import afield.runtime.kotlin.KotlinInterface
import afield.runtime.kotlin.KotlinScriptEngine
import afield.runtime.kotlin.findClassesNamed
import afield.runtime.kotlin.getTokenBounds
import afield.runtime.kotlin.shortenType
import afield.runtime.lifecycle.end
import afield.runtime.lifecycle.map
import afield.ui.output.markdown
import afield.ui.output.templates.filter
import afield.ui.visualization.typed

import afield.runtime.completion.KotlinSourceIndex
import afield.app.diagnostics.logging.lprintln
import org.jetbrains.kotlin.com.intellij.lang.Language
import org.jetbrains.kotlin.com.intellij.psi.PsiElement
import org.jetbrains.kotlin.com.intellij.psi.PsiFileFactory
import org.jetbrains.kotlin.psi.KtCallExpression
import java.net.URI
import java.net.URLEncoder
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
import java.util.concurrent.ConcurrentHashMap
import java.util.zip.ZipFile
import kotlin.reflect.KParameter
import kotlin.reflect.full.primaryConstructor

internal object KotlinCompletionSupport

internal object KotlinCompletionSupport {

Prefer the API already visible to a box. Package splits must not leave this ranking tied to a retired namespace or a second hand-maintained copy of the preamble.

    private val defaultBoxImports by lazy { introImports.map { it.removePrefix("import ") }.toSet() }
    private fun isDefaultBoxSymbol(name: String): Boolean =
        name in defaultBoxImports || name.substringBeforeLast('.', "") + ".*" in defaultBoxImports

    private val constructorInfoCache = HashMap<String, ConstructorInfo?>()
    private val callableInfoCache = HashMap<String, CallableInfo?>()
    private val classpathSourceCache = HashMap<String, List<SourceCallableInfo>>()
    private val classpathClassDocCache = HashMap<String, ReceiverDocumentationInfo?>()
    private val receiverTypealiasDocCache = HashMap<String, ReceiverDocumentationInfo?>()

data class ReplacementBounds(val start: Int, val end: Int)

data class NamedArgumentCompletions

body — 221 lines, from line 83
    data class NamedArgumentCompletions(
        val bounds: ReplacementBounds,
        val metadata: List<Metadata>
    )

    private data class ConstructorParameterInfo(
        val name: String,
        val type: String = "",
        val defaultValue: String? = null,
        val documentation: String? = null,
        val optional: Boolean = false,
        val vararg: Boolean = false
    ) {
        fun signature(includeDefault: Boolean = true): String {
            val prefix = buildString {
                if (vararg) append("vararg ")
                append(name)
            }
            val typed = if (type.isBlank()) prefix else "$prefix: ${shortenType(type)}"
            return if (includeDefault && !defaultValue.isNullOrBlank()) "$typed = ${defaultValue.trim()}" else typed
        }

        fun namedArgumentPreview(): String =
            if (defaultValue.isNullOrBlank()) "$name = " else "$name = ${defaultValue.trim()}"

        fun parameterSummary(): String =
            buildString {
                append("- `").append(signature()).append('`')
                documentation?.trim()?.takeIf { it.isNotEmpty() }?.let {
                    append("  ")
                    append(it)
                }
                if (optional && defaultValue.isNullOrBlank()) {
                    append("  _(default available)_")
                }
            }
    }

    private data class ConstructorInfo(
        val simpleName: String,
        val fqName: String? = null,
        val parameters: List<ConstructorParameterInfo> = emptyList(),
        val classDocumentation: String? = null,
        val sourceFile: String? = null,
        val sourceLine: Int = -1,
        val isObject: Boolean = false
    ) {
        fun signature(): String =
            if (isObject) simpleName else "$simpleName(${parameters.joinToString(", ") { it.signature() }})"

        fun namedArgumentSignature(): String =
            if (isObject) simpleName else "$simpleName(${parameters.joinToString(", ") { it.namedArgumentPreview().trimEnd() }})"

        fun label(): String = namedArgumentSignature()

        fun namedArgumentBlock(): String = when {
            isObject -> simpleName
            parameters.isEmpty() -> "$simpleName()"
            parameters.size <= 2 -> "$simpleName(${parameters.joinToString(", ") { it.namedArgumentPreview().trimEnd() }})"
            else -> buildString {
                append(simpleName).append("(\n")
                parameters.forEachIndexed { index, parameter ->
                    append("    ").append(parameter.namedArgumentPreview().trimEnd())
                    if (index != parameters.lastIndex) append(',')
                    append('\n')
                }
                append(')')
            }
        }

        fun documentationMarkdown(existingDocumentation: String = ""): String {
            val sections = mutableListOf<String>()

            sections += buildString {
                append("```kotlin\n")
                append(namedArgumentBlock())
                append("\n```")
            }

            classDocumentation?.trim()?.takeIf { it.isNotEmpty() }?.let { sections += it }

            if (parameters.isNotEmpty()) {
                sections += buildString {
                    append("**Parameters**\n")
                    parameters.forEach {
                        append(it.parameterSummary()).append('\n')
                    }
                }.trimEnd()
            }

            buildList {
                fqName?.takeIf { it.isNotBlank() }?.let { add("**Type** `$it`") }
                sourceFile?.takeIf { it.isNotBlank() }?.let { add("**Source** `$it`") }
            }.takeIf { it.isNotEmpty() }?.let { sections += it.joinToString("  \n") }

            existingDocumentation.trim().takeIf { it.isNotEmpty() }?.let { sections += it }

            return sections.joinToString("\n\n")
        }
    }

    private data class CallableInfo(
        val name: String,
        val ownerName: String? = null,
        val parameters: List<ConstructorParameterInfo> = emptyList(),
        val returnType: String? = null,
        val documentation: String? = null,
        val sourceFile: String? = null,
        val sourceLine: Int = -1,
        val sourceName: String? = null
    ) {
        private fun typedSignature(includeDefaults: Boolean = true): String {
            val renderedParams = parameters.joinToString(", ") { it.signature(includeDefault = includeDefaults) }
            val suffix = returnType?.takeIf { it.isNotBlank() }?.let { ": ${shortenType(it)}" }.orEmpty()
            return "fun $name($renderedParams)$suffix"
        }

        private fun multilineSignature(): String {
            if (parameters.isEmpty()) return typedSignature()
            val singleLine = typedSignature()
            if (parameters.size <= 2 && singleLine.length <= 110) return singleLine
            return buildString {
                append("fun ").append(name).append("(\n")
                parameters.forEachIndexed { index, parameter ->
                    append("    ").append(parameter.signature())
                    if (index != parameters.lastIndex) append(',')
                    append('\n')
                }
                append(')')
                returnType?.takeIf { it.isNotBlank() }?.let {
                    append(": ").append(shortenType(it))
                }
            }
        }

        fun label(): String = typedSignature(includeDefaults = false)

        fun documentationMarkdown(existingDocumentation: String = ""): String {
            val sections = mutableListOf<String>()

            sections += buildString {
                append("```kotlin\n")
                append(multilineSignature())
                append("\n```")
            }

            documentation?.trim()?.takeIf { it.isNotEmpty() }?.let { sections += it }

            val documentedParameters = parameters.filter { !it.documentation.isNullOrBlank() }
            if (documentedParameters.isNotEmpty()) {
                sections += buildString {
                    append("**Parameters**\n")
                    documentedParameters.forEach { parameter ->
                        append(parameter.parameterSummary()).append('\n')
                    }
                }.trimEnd()
            }

            returnType?.takeIf { it.isNotBlank() }?.let {
                sections += "**Returns** `${shortenType(it)}`"
            }

            buildList {
                ownerName?.takeIf { it.isNotBlank() }?.let { add("**Defined In** `$it`") }
                sourceFile?.takeIf { it.isNotBlank() }?.let { add("**Source** `$it`") }
            }.takeIf { it.isNotEmpty() }?.let { sections += it.joinToString("  \n") }

            existingDocumentation.trim().takeIf { it.isNotEmpty() }?.let { sections += it }

            return sections.joinToString("\n\n")
        }
    }

    private data class ReflectionConstructorInfo(
        val fqName: String?,
        val parameters: List<ConstructorParameterInfo>
    )

    private data class SourceConstructorInfo(
        val sourceFile: String,
        val sourceLine: Int,
        val sourceName: String,
        val isObject: Boolean = false,
        val classDocumentation: String? = null,
        val parameters: List<ConstructorParameterInfo> = emptyList()
    )

    private data class SourceCallableInfo(
        val ownerName: String? = null,
        val sourceFile: String,
        val sourceLine: Int,
        val sourceName: String,
        val documentation: String? = null,
        val parameters: List<ConstructorParameterInfo> = emptyList(),
        val returnType: String? = null
    )

    private data class ReceiverDocumentationInfo(
        val displayName: String,
        val documentation: String,
        val sourceName: String = "",
        val sourcePath: String = "",
        val sourceLine: Int = -1
    )

    private data class IndexedDocumentationTarget(
        val sourceName: String,
        val sourceFile: String,
        val sourceLine: Int,
        val hasDocumentation: Boolean
    )

    private data class ParsedKDoc(
        val body: String? = null,
        val parameterDocs: Map<String, String> = emptyMap()
    )

    private data class CallContext(
        val calleeText: String,
        val namedArguments: Set<String>
    )

fun enrichMetadata

body — 89 lines, from line 305
    fun enrichMetadata(
        box: Box,
        allCode: String,
        metadata: Metadata,
        position: Int,
        receiverTypeHint: String? = null,
        requireReceiverOwnerMatch: Boolean = false,
        descriptorText: String? = null
    ): Metadata {
        // There was a `symbolName` parameter here, taken from the completion item's
        // `DeclarationDescriptor`. The K2 2.4 scripting jars expose no descriptor, so its only caller
        // could only ever pass null and this fallback is the sole path.
        val name = metadata.text.substringBefore('<').substringBefore('(').trim()

        if (name.isBlank()) return metadata

        val receiverOwnerHint = inferReceiverOwnerName(box, allCode, position, receiverTypeHint)
            ?: descriptorOwnerName(descriptorText)
        val receiverDocumentation = receiverDocumentationInfo(box, allCode, receiverOwnerHint)

        if (metadata.icon in setOf("property", "val", "var")) {
            val target = resolveIndexedDocumentationTarget(
                simpleName = name,
                receiverOwnerHint = receiverOwnerHint,
                kinds = setOf("val", "var"),
                requireReceiverOwnerMatch = requireReceiverOwnerMatch
            ) ?: return metadata.withReceiverDocumentation(receiverDocumentation)
            val baseDocumentation = if (target.hasDocumentation) {
                metadata.documentation
            } else {
                leadingFallbackSignature(metadata.documentation)
            }
            // A constant's value is what the panel was opened for; the engine only knows its type.
            val documentedValue = declarationInitializer(target.sourceFile, target.sourceLine)
                ?.let { withDeclarationInitializer(baseDocumentation, it) }
                ?: baseDocumentation
            return metadata.copy(
                documentation = linkifyKDocSymbols(documentedValue),
                generateDocumentation = !target.hasDocumentation,
                documentationSourceName = target.sourceName,
                documentationSourcePath = target.sourceFile,
                documentationSourceLine = target.sourceLine
            ).withReceiverDocumentation(receiverDocumentation)
        }

        if (name.firstOrNull()?.isUpperCase() == true) {
            if (requireReceiverOwnerMatch) return metadata
            val info = resolveConstructorInfo(box, allCode, name) ?: return metadata.withReceiverDocumentation(receiverDocumentation)
            val fallbackDocumentation = withoutLeadingFallbackSignature(metadata.documentation)
            val documentation = info.documentationMarkdown(
                if (info.sourceFile == null) fallbackDocumentation else ""
            )
            return metadata.copy(
                displayText = info.label(),
                documentation = linkifyKDocSymbols(documentation),
                generateDocumentation = info.classDocumentation.isNullOrBlank() && info.sourceFile != null,
                documentationSourceName = info.fqName.orEmpty(),
                documentationSourcePath = info.sourceFile.orEmpty(),
                documentationSourceLine = info.sourceLine
            ).withReceiverDocumentation(receiverDocumentation)
        }

        val callableInfo = resolveCallableInfo(
            box = box,
            allCode = allCode,
            simpleName = name,
            position = position,
            displayText = metadata.displayText,
            tail = metadata.tail,
            receiverOwnerHint = receiverOwnerHint,
            requireReceiverOwnerMatch = requireReceiverOwnerMatch,
            descriptorText = descriptorText
        )

        if (callableInfo == null) return metadata.withReceiverDocumentation(receiverDocumentation)

        val hasSourceDocumentation = !callableInfo.documentation.isNullOrBlank() ||
            callableInfo.parameters.any { !it.documentation.isNullOrBlank() }

        return metadata.copy(
            displayText = callableInfo.label(),
            tail = callableInfo.returnType?.takeIf { it.isNotBlank() }?.let { ": ${shortenType(it)}" } ?: metadata.tail,
            documentation = linkifyKDocSymbols(callableInfo.documentationMarkdown()),
            generateDocumentation = !hasSourceDocumentation && !callableInfo.sourceFile.isNullOrBlank(),
            documentationSourceName = callableInfo.sourceName.orEmpty(),
            documentationSourcePath = callableInfo.sourceFile.orEmpty(),
            documentationSourceLine = callableInfo.sourceLine
        ).withReceiverDocumentation(receiverDocumentation)
    }

fun enrichReceiverDocumentation

    fun enrichReceiverDocumentation(
        box: Box,
        allCode: String,
        metadata: Metadata,
        position: Int,
        receiverTypeHint: String? = null,
        descriptorText: String? = null
    ): Metadata {
        val receiverOwnerHint = inferReceiverOwnerName(box, allCode, position, receiverTypeHint)
            ?: descriptorOwnerName(descriptorText)
        return metadata.withReceiverDocumentation(receiverDocumentationInfo(box, allCode, receiverOwnerHint))
    }

    private fun Metadata.withReceiverDocumentation(info: ReceiverDocumentationInfo?): Metadata {
        if (info == null || info.documentation.isBlank()) return this
        return copy(
            receiverDisplayName = info.displayName,
            receiverDocumentation = info.documentation,
            receiverDocumentationSourceName = info.sourceName,
            receiverDocumentationSourcePath = info.sourcePath,
            receiverDocumentationSourceLine = info.sourceLine
        )
    }

fun classifyMetadata

    fun classifyMetadata(
        metadata: Metadata,
        symbolName: String?,
        extraInfo: Map<String, Any?> = emptyMap()
    ): Metadata {
        val name = symbolName
            ?.substringBefore('<')
            ?.substringBefore('(')
            ?.substringAfterLast('.')
            ?.trim()
            ?.takeIf { it.isNotBlank() }
            ?: metadata.text.substringBefore('<').substringBefore('(').substringAfterLast('.').trim()

        // The docs index is keyed by symbol NAME, so with no source file to disambiguate it answers for
        // whichever documented `set`/`pos`/`get` it happens to know — it filed a graphics preamble's
        // `Mesh.pos` under Data and `Mesh.set` under Imaging. Where the candidate is actually declared is
        // specific to THIS row, so it wins whenever the docs lookup would be a name-only guess; a lookup
        // backed by a real source file is curated knowledge and still comes first.
        val docsAreSourceBacked =
            metadata.documentationSourceName.isNotBlank() || metadata.documentationSourcePath.isNotBlank()
        val category = normalizedCompletionCategory(metadata.category)
            ?: normalizedCompletionCategory(extraInfo["completionCategory"]?.toString())
            ?: normalizedCompletionCategory(extraInfo["category"]?.toString())
            ?: declarationCategory(extraInfo).takeIf { !docsAreSourceBacked }
            ?: KotlinSourceIndex.getCompletionCategoryForSymbol(
                symbolName = name,
                sourceName = metadata.documentationSourceName.takeIf { it.isNotBlank() },
                sourcePath = metadata.documentationSourcePath.takeIf { it.isNotBlank() },
                // The declaring container turns "what category is the name `scale`?" — a question with 42
                // answers in this tree — into "what category is `FLine.scale`?", which has one.
                declaredIn = extraInfo[AaCompletionClient.DECLARED_IN]?.toString()?.takeIf { it.isNotBlank() }
            )?.let(::normalizedCompletionCategory)
            ?: declarationCategory(extraInfo)
            ?: inferCompletionCategory(metadata, name, extraInfo)
            ?: return metadata

        return if (metadata.category == category) metadata else metadata.copy(category = category)
    }

fun isHiddenCompletionCategory(category: String): Boolean

    fun isHiddenCompletionCategory(category: String): Boolean =
        normalizedCompletionCategory(category) == "Internal"

fun withCategoryDocumentationHeader(metadata: Metadata): Metadata

body — 50 lines, from line 461
    fun withCategoryDocumentationHeader(metadata: Metadata): Metadata {
        val category = normalizedCompletionCategory(metadata.category) ?: return metadata
        val existing = metadata.documentation.trim()
        val header = "**Category** `$category`"
        if (existing.startsWith(header)) return metadata
        return metadata.copy(
            documentation = if (existing.isBlank()) header else "$header\n\n$existing"
        )
    }

    private fun inferCompletionCategory(
        metadata: Metadata,
        simpleName: String,
        extraInfo: Map<String, Any?>
    ): String? {
        val declaredIn = extraInfo[AaCompletionClient.DECLARED_IN]?.toString().orEmpty()
        val sourcePath = metadata.documentationSourcePath.lowercase()
        val sourceName = metadata.documentationSourceName.lowercase()
        val tail = metadata.tail.lowercase()
        val text = listOf(
            simpleName,
            metadata.text,
            metadata.displayText,
            metadata.tail,
            metadata.documentationSourceName,
            // The receiver is deliberately absent when the declaration is known — see above.
            if (declaredIn.isBlank()) extraInfo["receiverQualifiedName"]?.toString().orEmpty() else "",
            if (declaredIn.isBlank()) extraInfo["receiverType"]?.toString().orEmpty() else "",
            extraInfo["original"]?.toString().orEmpty()
        ).joinToString(" ").lowercase()

        categoryFromPath(sourcePath)?.let { return it }
        categoryFromQualifiedName(sourceName)?.let { return it }
        // Where the candidate is DECLARED, before anything inferred from its type or its spelling. It was
        // read above only to decide whether the receiver joins `text`, never mapped — so a member of
        // `afield.geometry.path.FLine` whose own file carries no `@file:CompletionCategory` fell through to
        // guessing from its return type. `categoryFromQualifiedName` already knew the answer.
        categoryFromQualifiedName(declaredIn.lowercase())?.let { return it }
        categoryFromQualifiedName(tail)?.let { return it }
        categoryFromQualifiedName(text)?.let { return it }

        if (metadata.icon == "keyword") return "Kotlin"
        if (sourcePath.startsWith("jar:")) return "External"
        if (tail.startsWith("kotlin.") || text.contains(" defined in kotlin")) return "Kotlin"
        if (tail.startsWith("java.") || tail.startsWith("javax.") || tail.startsWith("org.") || tail.startsWith("com.")) {
            return "External"
        }

        return null
    }

The section a candidate belongs in judged by WHERE IT IS DECLARED, or null when the AA path told us nothing (a K1 result, a keyword, a snippet).

This is the fix for section headers that described the receiver instead of the row: the fallback inference blends receiverQualifiedName into the text it matches on, so on a ComputeMesh. panel the receiver's own package (afield.graphics.render) matched first and let, also, apply and equals were all announced as GRAPHICS. Every row claiming to be graphics is no grouping at all.

source — 234 lines, from line 521
    private fun declarationCategory(extraInfo: Map<String, Any?>): String? {
        val declaredIn = extraInfo[AaCompletionClient.DECLARED_IN]?.toString().orEmpty()
        val proximity = extraInfo[AaCompletionClient.DECLARATION_PROXIMITY]?.toString().orEmpty()
        if (declaredIn.isNotBlank()) {
            categoryFromQualifiedName(declaredIn)?.let { return it }
            // A universal declaration we can't place by package is still worth separating: it applies to
            // every receiver, so it belongs with the other generic helpers rather than among this type's.
            if (proximity == "universal") return "Generic"
            return null
        }
        // No declaring package at all, but it came back for a receiver: it was declared in the box's own
        // preamble or body (a `prop<>` binding, a supermap's `Mesh.pos`, an `@exported` value). Those are
        // the most specific names in the panel, and "Local" is both true and the client's top-weighted
        // category.
        return if (proximity.isNotBlank()) "Local" else null
    }

    private fun normalizedCompletionCategory(raw: String?): String? {
        val value = raw?.trim()?.removeSurrounding("\"")?.takeIf { it.isNotEmpty() } ?: return null
        return when (value.lowercase().replace("_", " ").replace("-", " ")) {
            "math", "maths", "numeric", "numbers", "linear algebra" -> "Math"
            "visualization", "visualisation", "viz", "plot", "plotting" -> "Visualization"
            "graphics", "drawing", "rendering", "shader", "shaders", "vulkan", "geometry" -> "Graphics"
            "internal", "implementation", "system" -> "Internal"
            "audio", "synth", "sound", "music" -> "Audio"
            "data", "io", "files", "file", "json", "csv", "tables" -> "Data"
            "workspace", "document", "documents", "persistence" -> "Workspace"
            "kotlin", "language" -> "Kotlin"
            "generic", "universal" -> "Generic"
            "local", "box" -> "Local"
            "external", "library", "classpath" -> "External"
            "ai", "assistant" -> "AI"
            else -> value.replaceFirstChar { if (it.isLowerCase()) it.titlecase() else it.toString() }
        }
    }

    private fun categoryFromPath(path: String): String? = when {
        path.isBlank() -> null
        path.contains("/afield/math/") -> "Math"
        path.contains("/afield/visualization/") || path.contains("plotly") || path.contains("visualizer") -> "Visualization"
        path.contains("/afield/graphics/") || path.contains("/afield/four/") || path.contains("/afield/stacksl/") -> "Graphics"
        path.contains("/afield/internal/") || path.contains("/afield/server/") || path.contains("/afield/ai/") -> "Internal"
        path.contains("/afield/workspace/") || path.contains("/afield/docs/") -> "Workspace"
        path.contains("/afield/stdlib/") && path.containsAny("math", "linalg", "stats", "random", "correlation", "interpolant") -> "Math"
        path.contains("/afield/stdlib/") && path.containsAny("color", "shader", "drawing", "fline", "stacksl", "voronoi") -> "Graphics"
        path.contains("/afield/stdlib/") && path.containsAny("json", "file", "saved", "graph", "cluster", "wfc", "table") -> "Data"
        path.contains("/afield/stdlib/") && path.containsAny("osc", "audio", "synth") -> "Audio"
        path.startsWith("jar:") && path.contains("kotlin-stdlib") -> "Kotlin"
        path.startsWith("jar:") -> "External"
        else -> null
    }

    private fun categoryFromQualifiedName(value: String): String? = when {
        value.isBlank() -> null
        // `also`/`let`/`apply` are declared in the ROOT `kotlin` package, whose fq name has no trailing
        // dot — matching only "kotlin." filed every one of them as Generic instead.
        value == "kotlin" || value.startsWith("kotlin.") || value.contains(" defined in kotlin") -> "Kotlin"
        value.startsWith("java.") || value.startsWith("javax.") || value.startsWith("org.") || value.startsWith("com.") -> {
            when {
                value.contains("afield.math.linalg") || value.contains("matrix") || value.contains("vector") || value.contains("quaternion") -> "Math"
                else -> "External"
            }
        }
        value.contains("synth") || value.contains("afield.audio") -> "Audio"
        value.contains("afield.math") -> "Math"
        value.contains("afield.ui.visualization") || value.contains("plotly") || value.contains("visualizer") -> "Visualization"
        value.contains("afield.graphics") || value.contains("fline") ||
            value.contains("shader") || value.contains("stacksl") || value.contains("color") -> "Graphics"
        value.contains("afield.app.") || value.contains("afield.tools.") || value.contains("afield.ai") -> "Internal"
        value.contains("afield.document.") || value.contains("document") -> "Workspace"
        value.containsAny(" sin ", " cos ", " tan ", " sqrt ", " pow ", " abs ", " floor ", " ceil ", " lerp ", " clamp ") -> "Math"
        else -> null
    }

    private fun String.containsAny(vararg needles: String): Boolean =
        needles.any { contains(it) }

    private fun receiverDocumentationInfo(
        box: Box,
        allCode: String,
        receiverOwnerHint: String?
    ): ReceiverDocumentationInfo? {
        val raw = receiverOwnerHint
            ?.trim()
            ?.substringBefore('<')
            ?.substringBefore('?')
            ?.takeIf { it.isNotBlank() }
            ?: return null

        sourceTypealiasReceiverDocumentation(raw)?.let { return it }

        val classNames = LinkedHashSet<String>()
        resolveReceiverClassNames(allCode, raw).forEach { classNames.add(it) }
        if (classNames.isEmpty()) return null

        val classLoader = box.ki.classLoader ?: Thread.currentThread().contextClassLoader
        return classNames.asSequence()
            .mapNotNull { className ->
                runCatching { Class.forName(className, false, classLoader) }
                    .recoverCatching { classLoader.loadClass(className) }
                    .getOrNull()
            }
            .mapNotNull { classpathSourceClassDoc(it) }
            .firstOrNull()
    }

    private fun sourceTypealiasReceiverDocumentation(typeName: String): ReceiverDocumentationInfo? {
        val raw = typeName.trim().substringBefore('<').substringBefore('?')
        if (receiverTypealiasDocCache.containsKey(raw)) return receiverTypealiasDocCache[raw]
        val resolved = indexedTypealiasReceiverDocumentation(raw) ?: scanSourceTypealiasReceiverDocumentation(raw)
        receiverTypealiasDocCache[raw] = resolved
        return resolved
    }

    private fun scanSourceTypealiasReceiverDocumentation(raw: String): ReceiverDocumentationInfo? {
        val root = Paths.get(System.getProperty("user.dir")).normalize()
        val rawSimple = raw.substringAfterLast('.')
        val candidates = mutableListOf<ReceiverDocumentationInfo>()

        defaultSourceRoots(root).forEach { sourceRoot ->
            if (!Files.exists(sourceRoot)) return@forEach
            Files.walk(sourceRoot).use { paths ->
                paths
                    .filter { Files.isRegularFile(it) && it.fileName.toString().endsWith(".kt") }
                    .forEach pathLoop@{ path ->
                        val text = runCatching { Files.readString(path) }.getOrNull() ?: return@pathLoop
                        val packageName = packageName(text)
                        typealiasRegex.findAll(text).forEach aliasLoop@{ match ->
                            val alias = match.groupValues[1]
                            val target = match.groupValues[2].trim()
                            val targetSimple = target.substringAfterLast('.')
                            if (alias != rawSimple && target != raw && targetSimple != rawSimple) return@aliasLoop
                            val doc = leadingKDoc(text, match.range.first)?.takeIf { it.isNotBlank() } ?: return@aliasLoop
                            val relativePath = runCatching { root.relativize(path).toString().replace("\\", "/") }
                                .getOrDefault(path.toString())
                            candidates += ReceiverDocumentationInfo(
                                displayName = alias,
                                documentation = doc,
                                sourceName = if (packageName.isBlank()) alias else "$packageName.$alias",
                                sourcePath = relativePath,
                                sourceLine = text.substring(0, match.range.first).count { it == '\n' } + 1
                            )
                        }
                    }
            }
        }

        return candidates.sortedWith(
            compareByDescending<ReceiverDocumentationInfo> { isDefaultBoxSymbol(it.sourceName) }
                .thenBy { it.sourcePath }
                .thenBy { it.sourceLine }
        ).firstOrNull()
    }

    private fun indexedTypealiasReceiverDocumentation(typeName: String): ReceiverDocumentationInfo? {
        val index = KotlinSourceIndex.latestIndex ?: KotlinSourceIndex.loadIndex() ?: return null
        val raw = typeName.trim().substringBefore('<').substringBefore('?')
        val rawSimple = raw.substringAfterLast('.')
        val definition = index.definitions
            .asSequence()
            .filter { it.kind == "typealias" && !it.kdoc.isNullOrBlank() }
            .firstOrNull {
                val alias = it.name.substringAfterLast('.')
                alias == rawSimple || indexedTypealiasTargetMatches(it, raw, rawSimple)
            } ?: return null
        return ReceiverDocumentationInfo(
            displayName = definition.name.substringAfterLast('.'),
            documentation = definition.kdoc.orEmpty(),
            sourceName = definition.name,
            sourcePath = definition.filePath,
            sourceLine = definition.line
        )
    }

    private fun indexedTypealiasTargetMatches(
        definition: afield.runtime.completion.KotlinDefinitionRecord,
        raw: String,
        rawSimple: String
    ): Boolean {
        val root = Paths.get(System.getProperty("user.dir")).normalize()
        val path = root.resolve(definition.filePath).normalize()
        val text = runCatching { Files.readString(path) }.getOrNull() ?: return false
        val lineStart = lineStartOffsets(text).getOrNull((definition.line - 1).coerceAtLeast(0)) ?: return false
        val lineEnd = text.indexOf('\n', lineStart).takeIf { it >= 0 } ?: text.length
        val line = text.substring(lineStart, lineEnd)
        val match = typealiasRegex.find(line) ?: return false
        val target = match.groupValues[2].trim()
        return target == raw || target.substringAfterLast('.') == rawSimple
    }

    private val typealiasRegex =
        Regex("""\btypealias\s+([A-Za-z_][A-Za-z0-9_]*)\s*=\s*([A-Za-z_][A-Za-z0-9_.]*)""")

    private fun resolveIndexedDocumentationTarget(
        simpleName: String,
        receiverOwnerHint: String?,
        kinds: Set<String>,
        requireReceiverOwnerMatch: Boolean
    ): IndexedDocumentationTarget? {
        val index = KotlinSourceIndex.latestIndex ?: KotlinSourceIndex.loadIndex() ?: return null
        val candidates = index.definitions.filter {
            it.kind in kinds && it.name.substringAfterLast('.') == simpleName
        }
        val definition = (if (!receiverOwnerHint.isNullOrBlank()) {
            candidates.firstOrNull { ownerMatches(indexedOwnerName(it.name), receiverOwnerHint) }
        } else if (requireReceiverOwnerMatch) {
            null
        } else {
            candidates.singleOrNull() ?: candidates.firstOrNull { it.name == simpleName }
        }) ?: return null

        return IndexedDocumentationTarget(
            sourceName = definition.name,
            sourceFile = definition.filePath,
            sourceLine = definition.line,
            hasDocumentation = !definition.kdoc.isNullOrBlank()
        )
    }

    private fun withoutLeadingFallbackSignature(documentation: String): String {
        val trimmed = documentation.trim()
        if (!trimmed.startsWith("```kotlin")) return trimmed
        val closingFence = trimmed.indexOf("```", startIndex = "```kotlin".length)
        if (closingFence == -1) return trimmed
        return trimmed.substring(closingFence + 3).trim()
    }

    private fun leadingFallbackSignature(documentation: String): String {
        val trimmed = documentation.trim()
        if (!trimmed.startsWith("```kotlin")) return trimmed
        val closingFence = trimmed.indexOf("```", startIndex = "```kotlin".length)
        if (closingFence == -1) return trimmed
        return trimmed.substring(0, closingFence + 3).trim()
    }

Longest initializer spliced into a signature fence; past this it is a body, not a signature.

    private const val MAX_INLINE_INITIALIZER = 160

A val/var declaration that carries an initializer, capturing the right-hand side.

    private val declarationInitializerRegex = Regex(
        """^(?:@\w+\s+)*""" +
            """(?:(?:private|internal|public|protected|const|lateinit|open|override|final|inline|external|actual|expect)\s+)*""" +
            """(?:val|var)\s+[\w`]+\s*(?::\s*[^=]+?)?\s*=\s*(.+)$"""
    )

Declaration source keyed by path:lastModified. Cached by file rather than by declaration: one popup can offer a dozen constants out of the same file, and a per-declaration cache would re-read the whole file for each of them while someone waits for the list.

    private val declarationSourceCache = ConcurrentHashMap<String, List<String>>()

internal fun declarationInitializer(sourceFile: String, line: Int): String?

The initializer a val/var was declared with, read from its own source line: "input x =e <e <e * <e * 160 *" for val ROPE = "input x =e <e <e * <e * 160 *".

The completion engine knows a constant's type and nothing else, so the doc panel could only say ROPE: String — true, and never the thing the panel was opened to find out. For a constant the value usually is the documentation: a program string, a magic number, a default.

Single-line initializers only, and only short ones. A val whose right-hand side is a builder block or a forty-line table is a body rather than a signature, and the "source" footer under the panel already exists for reading bodies.

    internal fun declarationInitializer(sourceFile: String, line: Int): String? {
        if (sourceFile.isBlank() || line < 1) return null
        val root = Paths.get(System.getProperty("user.dir")).normalize()
        val path = runCatching { root.resolve(sourceFile).normalize() }.getOrNull() ?: return null
        if (!path.startsWith(root) || !Files.isRegularFile(path)) return null
        val stamp = runCatching { Files.getLastModifiedTime(path).toMillis() }.getOrNull() ?: return null
        if (declarationSourceCache.size > 512) declarationSourceCache.clear()
        val lines = declarationSourceCache.getOrPut("$path:$stamp") {
            runCatching { Files.readAllLines(path) }.getOrNull() ?: emptyList()
        }
        return initializerAt(lines, line)
    }

internal fun readDeclarationInitializer(path: Path, line: Int): String?

Uncached read of one declaration, for callers holding a path rather than a repo-relative name.

    internal fun readDeclarationInitializer(path: Path, line: Int): String? {
        val lines = runCatching { Files.readAllLines(path) }.getOrNull() ?: return null
        return initializerAt(lines, line)
    }

    private fun initializerAt(lines: List<String>, line: Int): String? {
        if (line < 1 || line > lines.size) return null
        val declaration = lines[line - 1].trim()
        val initializer = declarationInitializerRegex.find(declaration)?.groupValues?.get(1)?.trim()
            ?: return null
        if (initializer.length > MAX_INLINE_INITIALIZER) return null
        // A trailing brace or an unclosed bracket means the value carries on below this line.
        if (initializer.endsWith("{")) return null
        if (initializer.count { it == '(' } != initializer.count { it == ')' }) return null
        return initializer
    }

internal fun withDeclarationInitializer(documentation: String, initializer: String): String

Splice = <initializer> into the leading kotlin signature fence, so ROPE: String becomes ROPE: String = "input x =e <e <e * <e * 160 *". Left alone when there is no leading fence, when the signature spans lines, or when it already carries a value of its own.

    internal fun withDeclarationInitializer(documentation: String, initializer: String): String {
        val trimmed = documentation.trim()
        val opening = "```kotlin\n"
        if (!trimmed.startsWith(opening)) return documentation
        val closingFence = trimmed.indexOf("\n```", startIndex = opening.length)
        if (closingFence == -1) return documentation
        val signature = trimmed.substring(opening.length, closingFence)
        if (signature.isBlank() || '=' in signature || '\n' in signature) return documentation
        return opening + signature.trimEnd() + " = " + initializer + trimmed.substring(closingFence)
    }

fun fieldSnippetCompletions(activeToken: String, allCode: String, tokenStart: Int): List<Metadata>

Field's own snippets, offered alongside whatever the engine found.

These are top-level constructs: go is Box.go, a call you write bare inside a box. They only make sense where a new expression begins, so they are suppressed at a member selection — offering go { } on "hello world". was nonsense.

tokenStart is the start of the identifier being typed (see getTokenBounds), not the engine's replacement bounds, so the gate still holds when the engine returned nothing.

    fun fieldSnippetCompletions(activeToken: String, allCode: String, tokenStart: Int): List<Metadata> {
        if (isMemberSelection(allCode, tokenStart)) return emptyList()
        val token = activeToken.trim()
        if (token.isNotEmpty() && !"go".startsWith(token)) return emptyList()
        return listOf(
            Metadata(
                icon = "snippet",
                tail = "Field coroutine",
                text = "go {\n    \n}",
                displayText = "go { }",
                documentation = "```kotlin\n" +
                    "go {\n" +
                    "    \n" +
                    "}\n" +
                    "```\n\n" +
                    "Launch a suspendable block on this box with Field error reporting.",
                category = "snippets",
                documentationSourceName = "afield.runtime.execution.go",
                documentationSourcePath = "src/jvmMain/kotlin/afield/runtime/execution/actors.kt",
                documentationSourceLine = 166
            )
        )
    }

fun isDotSelection(allCode: String, offset: Int): Boolean

The caret is selecting a member of the expression to its left: it sits after a . or a ?. (endsWith(".") covers both).

Only spaces and tabs are skipped, never a newline. x. at the end of a line is one expression to Kotlin, but a comment ending in a full stop is far more common than that style, and treating // done. + newline as a member selection would suppress everything on the next line.

    fun isDotSelection(allCode: String, offset: Int): Boolean =
        codeBefore(allCode, offset).endsWith(".")

fun isMemberSelection(allCode: String, offset: Int): Boolean

isDotSelection, plus ::. Anything that binds the caret to the expression on its left, and so rules out a fresh top-level construct.

Kept separate from isDotSelection because the two answer different questions: a callable reference has no members to document or to look a receiver's KDoc up against, but it is still no place for a snippet.

    fun isMemberSelection(allCode: String, offset: Int): Boolean =
        codeBefore(allCode, offset).let { it.endsWith(".") || it.endsWith("::") }

    private fun codeBefore(allCode: String, offset: Int): String =
        allCode.substring(0, offset.coerceIn(0, allCode.length)).trimEnd(' ', '\t')

fun namedArgumentCompletions

body — 857 lines, from line 892
    fun namedArgumentCompletions(
        box: Box,
        allCode: String,
        position: Int
    ): NamedArgumentCompletions? {
        val callContext = findCallContext(box.ki, allCode, position) ?: return null
        val calleeName = callContext.calleeText.substringAfterLast('.').trim()
        if (calleeName.isBlank() || calleeName.firstOrNull()?.isUpperCase() != true) return null

        val info = resolveConstructorInfo(box, allCode, calleeName) ?: return null
        if (info.parameters.isEmpty()) return null

        val bounds = getTokenBounds(allCode, position)
        val metadata = info.parameters
            .filterNot { callContext.namedArguments.contains(it.name) }
            .map { parameter ->
                Metadata(
                    icon = "property",
                    tail = shortenType(parameter.type),
                    text = "${parameter.name} = ",
                    displayText = parameter.namedArgumentPreview(),
                    documentation = buildString {
                        append("```kotlin\n")
                        append(parameter.namedArgumentPreview().trimEnd())
                        append("\n```")
                        append("\n\n")
                        append("`").append(parameter.signature()).append("`")
                        parameter.documentation?.trim()?.takeIf { it.isNotEmpty() }?.let {
                            append("\n\n").append(it)
                        }
                        append("\n\n**In** `").append(info.simpleName).append("`")
                    }.trim()
                )
            }

        if (metadata.isEmpty()) return null
        return NamedArgumentCompletions(
            bounds = ReplacementBounds(bounds.start, bounds.end),
            metadata = metadata
        )
    }

    @Synchronized
    private fun resolveConstructorInfo(box: Box, allCode: String, simpleName: String): ConstructorInfo? {
        val cacheKey = buildCacheKey(allCode, simpleName)
        if (constructorInfoCache.containsKey(cacheKey)) return constructorInfoCache[cacheKey]

        val resolved = run {
            val candidates = resolveClassCandidates(allCode, simpleName)
            val sourceInfo = candidates.asSequence()
                .mapNotNull { resolveSourceInfo(simpleName, it) }
                .firstOrNull()
                ?: resolveSourceInfo(simpleName, null)

            val reflectionInfo = candidates.asSequence()
                .mapNotNull { resolveReflectionInfo(box, it) }
                .firstOrNull()
                ?.takeUnless { reflected ->
                    sourceInfo != null &&
                        reflected.fqName != null &&
                        reflected.fqName != sourceInfo.sourceName
                }

            if (reflectionInfo == null && sourceInfo == null) return@run null

            val mergedNames = LinkedHashSet<String>()
            reflectionInfo?.parameters?.forEach { mergedNames.add(it.name) }
            sourceInfo?.parameters?.forEach { mergedNames.add(it.name) }

            val reflectedByName = reflectionInfo?.parameters?.associateBy { it.name }.orEmpty()
            val sourceByName = sourceInfo?.parameters?.associateBy { it.name }.orEmpty()

            val parameters = mergedNames.mapNotNull { name ->
                val reflected = reflectedByName[name]
                val source = sourceByName[name]
                val merged = ConstructorParameterInfo(
                    name = name,
                    type = reflected?.type ?: source?.type.orEmpty(),
                    defaultValue = source?.defaultValue ?: reflected?.defaultValue,
                    documentation = source?.documentation ?: reflected?.documentation,
                    optional = reflected?.optional ?: source?.optional ?: false,
                    vararg = reflected?.vararg ?: source?.vararg ?: false
                )
                merged.takeIf { it.name.isNotBlank() }
            }

            val fqName = sourceInfo?.sourceName ?: reflectionInfo?.fqName ?: candidates.firstOrNull()
            ConstructorInfo(
                simpleName = simpleName,
                fqName = fqName,
                parameters = parameters,
                classDocumentation = sourceInfo?.classDocumentation,
                sourceFile = sourceInfo?.sourceFile,
                sourceLine = sourceInfo?.sourceLine ?: -1,
                isObject = sourceInfo?.isObject ?: false
            )
        }
        constructorInfoCache[cacheKey] = resolved
        return resolved
    }

    @Synchronized
    private fun resolveCallableInfo(
        box: Box,
        allCode: String,
        simpleName: String,
        position: Int,
        displayText: String,
        tail: String,
        receiverOwnerHint: String?,
        requireReceiverOwnerMatch: Boolean,
        descriptorText: String?
    ): CallableInfo? {
        if (requireReceiverOwnerMatch && receiverOwnerHint.isNullOrBlank()) return null
        val cacheKey = buildCallableCacheKey(
            allCode,
            simpleName,
            receiverOwnerHint,
            displayText,
            descriptorText,
            tail,
            requireReceiverOwnerMatch
        )
        if (callableInfoCache.containsKey(cacheKey)) return callableInfoCache[cacheKey]

        val resolved = resolveSourceCallableInfo(
            allCode = allCode,
            simpleName = simpleName,
            receiverOwnerHint = receiverOwnerHint,
            requireReceiverOwnerMatch = requireReceiverOwnerMatch,
            displayText = displayText,
            descriptorText = descriptorText,
            tail = tail
        ) ?: resolveClasspathCallableInfo(
            box = box,
            allCode = allCode,
            simpleName = simpleName,
            receiverOwnerHint = receiverOwnerHint,
            requireReceiverOwnerMatch = requireReceiverOwnerMatch,
            displayText = displayText,
            descriptorText = descriptorText,
            tail = tail
        )

        val callable = resolved?.let {
            CallableInfo(
                name = simpleName,
                ownerName = it.ownerName,
                parameters = it.parameters,
                returnType = it.returnType,
                documentation = it.documentation,
                sourceFile = it.sourceFile,
                sourceLine = it.sourceLine,
                sourceName = it.sourceName
            )
        }

        callableInfoCache[cacheKey] = callable
        return callable
    }

    private fun buildCacheKey(allCode: String, simpleName: String): String {
        val imports = importCandidates(allCode, simpleName).sorted().joinToString("|")
        return "$simpleName::$imports"
    }

    private fun buildCallableCacheKey(
        allCode: String,
        simpleName: String,
        receiverOwnerHint: String?,
        displayText: String,
        descriptorText: String?,
        tail: String,
        requireReceiverOwnerMatch: Boolean
    ): String {
        val imports = importCandidates(allCode, simpleName).sorted().joinToString("|")
        return listOf(
            simpleName,
            receiverOwnerHint.orEmpty(),
            displayText,
            descriptorText.orEmpty(),
            tail.trim(),
            requireReceiverOwnerMatch.toString(),
            imports
        ).joinToString("::")
    }

    private fun resolveClassCandidates(allCode: String, simpleName: String): List<String> {
        val ordered = LinkedHashSet<String>()

        importCandidates(allCode, simpleName).forEach { ordered.add(it) }
        findClassesNamed(simpleName)
            ?.sortedWith(
                compareBy<String>(
                    { !it.startsWith("afield.") },
                    { !it.startsWith("kotlin.") },
                    { !it.startsWith("java.") },
                    { it }
                )
            )
            ?.forEach { ordered.add(it) }

        return ordered.toList()
    }

    private fun importCandidates(allCode: String, simpleName: String): List<String> {
        val direct = Regex(
            pattern = "^\\s*import\\s+([A-Za-z0-9_.]+\\.${Regex.escape(simpleName)})\\s*$",
            options = setOf(RegexOption.MULTILINE)
        )
        return direct.findAll(allCode).map { it.groupValues[1] }.distinct().toList()
    }

    private fun resolveReflectionInfo(box: Box, fqName: String): ReflectionConstructorInfo? {
        val classLoader = box.ki.classLoader ?: Thread.currentThread().contextClassLoader
        val clazz = runCatching { Class.forName(fqName, false, classLoader) }.getOrNull() ?: return null
        val kClass = runCatching { clazz.kotlin }.getOrNull() ?: return null
        val constructor = kClass.primaryConstructor ?: kClass.constructors.minByOrNull { it.parameters.size } ?: return null
        val parameters = constructor.parameters
            .filter { it.kind == KParameter.Kind.VALUE && !it.name.isNullOrBlank() }
            .map {
                ConstructorParameterInfo(
                    name = it.name!!,
                    type = it.type.toString(),
                    defaultValue = null,
                    documentation = null,
                    optional = it.isOptional,
                    vararg = it.isVararg
                )
            }

        return ReflectionConstructorInfo(
            fqName = kClass.qualifiedName ?: fqName,
            parameters = parameters
        )
    }

    private fun resolveSourceInfo(simpleName: String, fqName: String?): SourceConstructorInfo? {
        val root = Paths.get(System.getProperty("user.dir")).normalize()
        val candidateFiles = sourceFileCandidates(root, simpleName, fqName)
        candidateFiles.forEach { path ->
            val raw = runCatching { Files.readString(path) }.getOrNull() ?: return@forEach
            val parsed = parseSourceInfo(raw, path, root, simpleName, fqName)
            if (parsed != null) return parsed
        }
        return null
    }

    private fun resolveSourceCallableInfo(
        allCode: String,
        simpleName: String,
        receiverOwnerHint: String?,
        requireReceiverOwnerMatch: Boolean,
        displayText: String,
        descriptorText: String?,
        tail: String
    ): SourceCallableInfo? {
        val index = KotlinSourceIndex.latestIndex ?: KotlinSourceIndex.loadIndex() ?: return null
        val root = Paths.get(System.getProperty("user.dir")).normalize()
        val signatureText = descriptorText?.takeIf { it.isNotBlank() } ?: displayText
        val expectedParamCount = parameterCountHint(signatureText)
        val expectedParamTypes = parameterTypeHints(signatureText)
        val expectedReturnType = tail.removePrefix(":").trim().ifBlank { null }
        val hasSignatureHints = expectedParamCount != null || !expectedParamTypes.isNullOrEmpty()

        val candidates = index.definitions
            .asSequence()
            .filter { it.kind == "fun" && it.name.substringAfterLast('.') == simpleName }
            .mapNotNull { definition ->
                parseSourceCallableInfo(definition, root, simpleName)?.let { definition to it }
            }
            .filter { (_, info) ->
                !requireReceiverOwnerMatch || ownerMatches(info.ownerName, receiverOwnerHint) || hasSignatureHints
            }
            .toList()

        if (candidates.isEmpty()) return null

        val scored = candidates.map { (definition, info) ->
            scoreCallableCandidate(
                definitionName = definition.name,
                info = info,
                receiverOwnerHint = receiverOwnerHint,
                expectedParamCount = expectedParamCount,
                expectedParamTypes = expectedParamTypes,
                expectedReturnType = expectedReturnType,
                displayText = displayText,
                allCode = allCode
            ) to info
        }.sortedByDescending { it.first }

        val best = scored.firstOrNull() ?: return null
        if (best.first <= 0) return null
        return mergeCallableInfoFromCompatibleBase(best.second, candidates.map { it.second })
    }

    private fun mergeCallableInfoFromCompatibleBase(
        selected: SourceCallableInfo,
        candidates: List<SourceCallableInfo>
    ): SourceCallableInfo {
        val needsDefaults = selected.parameters.any { it.defaultValue.isNullOrBlank() }
        val needsDocumentation = selected.documentation.isNullOrBlank() ||
            selected.parameters.any { it.documentation.isNullOrBlank() }
        if (!needsDefaults && !needsDocumentation) return selected

        val base = candidates.asSequence()
            .filter { it !== selected }
            .filter { callableShapeMatches(selected, it) }
            .filter { ownerIsAssignableFrom(it.ownerName.orEmpty(), selected.ownerName.orEmpty()) }
            .sortedByDescending { inherited ->
                var score = 0
                if (!inherited.documentation.isNullOrBlank()) score += 20
                score += inherited.parameters.count { !it.defaultValue.isNullOrBlank() } * 3
                score += inherited.parameters.count { !it.documentation.isNullOrBlank() }
                score
            }
            .firstOrNull()
            ?: return selected

        return selected.copy(
            documentation = selected.documentation?.takeIf { it.isNotBlank() } ?: base.documentation,
            parameters = selected.parameters.mapIndexed { index, parameter ->
                val inherited = base.parameters.getOrNull(index) ?: return@mapIndexed parameter
                parameter.copy(
                    defaultValue = parameter.defaultValue?.takeIf { it.isNotBlank() } ?: inherited.defaultValue,
                    documentation = parameter.documentation?.takeIf { it.isNotBlank() } ?: inherited.documentation,
                    optional = parameter.optional || inherited.optional || inherited.defaultValue != null
                )
            }
        )
    }

    private fun callableShapeMatches(
        selected: SourceCallableInfo,
        candidate: SourceCallableInfo
    ): Boolean {
        if (selected.parameters.size != candidate.parameters.size) return false
        selected.returnType?.let { selectedReturn ->
            candidate.returnType?.let { candidateReturn ->
                if (!parameterTypesMatch(candidateReturn, selectedReturn)) return false
            }
        }
        return selected.parameters.indices.all { index ->
            val left = selected.parameters[index].type
            val right = candidate.parameters[index].type
            left.isBlank() || right.isBlank() || parameterTypesMatch(left, right)
        }
    }

    private fun resolveClasspathCallableInfo(
        box: Box,
        allCode: String,
        simpleName: String,
        receiverOwnerHint: String?,
        requireReceiverOwnerMatch: Boolean,
        displayText: String,
        descriptorText: String?,
        tail: String
    ): SourceCallableInfo? {
        if (receiverOwnerHint.isNullOrBlank()) return null
        val signatureText = descriptorText?.takeIf { it.isNotBlank() } ?: displayText
        val expectedParamCount = parameterCountHint(signatureText)
        val expectedParamTypes = parameterTypeHints(signatureText)
        val expectedReturnType = tail.removePrefix(":").trim().ifBlank { null }
        val owners = resolveReceiverClassNames(allCode, receiverOwnerHint)
        if (owners.isEmpty()) {
            lprintln("info,completion", "completion classpath-source miss: no class candidates for receiver=$receiverOwnerHint symbol=$simpleName")
            return null
        }

        val classLoader = box.ki.classLoader ?: Thread.currentThread().contextClassLoader
        val candidates = owners.asSequence()
            .mapNotNull { ownerName ->
                val clazz = runCatching { Class.forName(ownerName, false, classLoader) }
                    .recoverCatching { classLoader.loadClass(ownerName) }
                    .getOrNull()
                if (clazz == null) {
                    lprintln("info,completion", "completion classpath-source miss: could not load receiver=$ownerName symbol=$simpleName")
                    null
                } else {
                    classpathSourceMethods(clazz, simpleName)
                }
            }
            .flatten()
            .filter { !requireReceiverOwnerMatch || ownerMatches(it.ownerName, receiverOwnerHint) }
            .toList()

        if (candidates.isEmpty()) {
            lprintln(
                "info,completion",
                "completion classpath-source miss: no source methods receiver=$receiverOwnerHint owners=${owners.take(4)} symbol=$simpleName display='$displayText' tail='$tail'"
            )
            return null
        }

        val scored = candidates.map { info ->
            scoreCallableCandidate(
                definitionName = info.sourceName,
                info = info,
                receiverOwnerHint = receiverOwnerHint,
                expectedParamCount = expectedParamCount,
                expectedParamTypes = expectedParamTypes,
                expectedReturnType = expectedReturnType,
                displayText = displayText,
                allCode = allCode
            ) to info
        }.sortedByDescending { it.first }

        val best = scored.firstOrNull() ?: return null
        if (best.first <= 0) {
            lprintln(
                "info,completion",
                "completion classpath-source miss: candidates scored <=0 receiver=$receiverOwnerHint symbol=$simpleName best=${best.first} display='$displayText'"
            )
            return null
        }
        return best.second
    }

    private fun resolveReceiverClassNames(allCode: String, receiverOwnerHint: String): List<String> {
        val raw = receiverOwnerHint.trim().substringBefore('<').substringBefore('?')
        if (raw.isBlank()) return emptyList()
        if ('.' in raw) return listOf(raw)

        val imported = Regex(
            pattern = "^\\s*import\\s+([A-Za-z0-9_.]+\\.${Regex.escape(raw)})\\s*$",
            options = setOf(RegexOption.MULTILINE)
        ).findAll(allCode).map { it.groupValues[1] }.toList()
        if (imported.isNotEmpty()) return imported

        return findClassesNamed(raw)
            ?.filter { '$' !in it }
            ?.sortedWith(
                compareBy<String>(
                    { !it.startsWith("afield.") },
                    { it }
                )
            )
            .orEmpty()
    }

    private fun classpathSourceMethods(clazz: Class<*>, simpleName: String): List<SourceCallableInfo> {
        val key = "${clazz.name}#$simpleName"
        classpathSourceCache[key]?.let { return it }
        val loaded = loadClasspathSourceMethods(clazz, simpleName)
        classpathSourceCache[key] = loaded
        return loaded
    }

    private fun classpathSourceClassDoc(clazz: Class<*>): ReceiverDocumentationInfo? {
        val key = clazz.name
        if (classpathClassDocCache.containsKey(key)) return classpathClassDocCache[key]
        val loaded = loadClasspathSourceClassDoc(clazz)
        classpathClassDocCache[key] = loaded
        return loaded
    }

    private fun loadClasspathSourceMethods(clazz: Class<*>, simpleName: String): List<SourceCallableInfo> {
        val source = classpathSourceFor(clazz) ?: return emptyList()
        return source.use { (zip, entryName, sourcePath) ->
            val entry = zip.getEntry(entryName) ?: return@use emptyList()
            val text = zip.getInputStream(entry).bufferedReader().use { it.readText() }
            parseJavaSourceMethods(
                text = text,
                ownerName = clazz.name,
                simpleName = simpleName,
                sourcePath = sourcePath
            )
        }
    }

    private fun loadClasspathSourceClassDoc(clazz: Class<*>): ReceiverDocumentationInfo? {
        val source = classpathSourceFor(clazz) ?: return null
        return source.use { (zip, entryName, sourcePath) ->
            val entry = zip.getEntry(entryName) ?: return@use null
            val text = zip.getInputStream(entry).bufferedReader().use { it.readText() }
            parseJavaSourceClassDoc(
                text = text,
                ownerName = clazz.name,
                sourcePath = sourcePath
            )
        }
    }

    private data class OpenClasspathSource(
        val zip: ZipFile,
        val entryName: String,
        val sourcePath: String
    ) : AutoCloseable {
        override fun close() {
            zip.close()
        }
    }

    private fun classpathSourceFor(clazz: Class<*>): OpenClasspathSource? {
        val codeSource = clazz.protectionDomain?.codeSource?.location ?: return null
        val binaryPath = runCatching { Paths.get(codeSource.toURI()) }.getOrNull() ?: return null
        if (!Files.isRegularFile(binaryPath) || binaryPath.fileName.toString().endsWith("-sources.jar")) return null

        val sourceJarName = binaryPath.fileName.toString().removeSuffix(".jar") + "-sources.jar"
        val searchRoot = binaryPath.parent?.parent ?: binaryPath.parent ?: return null
        val sourceJar = runCatching {
            Files.walk(searchRoot, 3).use { stream ->
                stream
                    .filter { Files.isRegularFile(it) && it.fileName.toString() == sourceJarName }
                    .findFirst()
                    .orElse(null)
            }
        }.getOrNull() ?: return null

        val entryBase = clazz.name.substringBefore('$').replace('.', '/')
        val zip = ZipFile(sourceJar.toFile())
        val entryName = listOf("$entryBase.java", "$entryBase.kt")
            .firstOrNull { zip.getEntry(it) != null }
        if (entryName == null) {
            zip.close()
            return null
        }
        return OpenClasspathSource(
            zip = zip,
            entryName = entryName,
            sourcePath = "jar:${sourceJar.toUri()}!/$entryName"
        )
    }

    private fun parseJavaSourceMethods(
        text: String,
        ownerName: String,
        simpleName: String,
        sourcePath: String
    ): List<SourceCallableInfo> {
        val out = mutableListOf<SourceCallableInfo>()
        val lines = text.split('\n')
        var pendingDoc: ParsedKDoc? = null
        var i = 0
        while (i < lines.size) {
            val trimmed = lines[i].trim()
            if (trimmed.startsWith("/**")) {
                val docLines = mutableListOf<String>()
                var done = false
                while (i < lines.size) {
                    val line = lines[i].trim()
                    docLines += line
                    if (line.contains("*/")) {
                        done = true
                        break
                    }
                    i++
                }
                pendingDoc = if (done) parseJavaDoc(docLines.joinToString("\n")) else null
                i++
                continue
            }

            if (trimmed.isBlank() || trimmed.startsWith("@")) {
                i++
                continue
            }

            parseJavaMethodLine(trimmed, simpleName)?.let { method ->
                val doc = pendingDoc ?: ParsedKDoc()
                out += SourceCallableInfo(
                    ownerName = ownerName.substringAfterLast('.'),
                    sourceFile = sourcePath,
                    sourceLine = i + 1,
                    sourceName = "$ownerName.$simpleName",
                    documentation = doc.body,
                    parameters = method.parameters.map { parameter ->
                        parameter.copy(documentation = doc.parameterDocs[parameter.name] ?: parameter.documentation)
                    },
                    returnType = method.returnType
                )
            }
            pendingDoc = null
            i++
        }
        return out
    }

    private fun parseJavaSourceClassDoc(
        text: String,
        ownerName: String,
        sourcePath: String
    ): ReceiverDocumentationInfo? {
        val simpleName = ownerName.substringAfterLast('.').substringBefore('$')
        val lines = text.split('\n')
        var pendingDoc: ParsedKDoc? = null
        var i = 0
        while (i < lines.size) {
            val trimmed = lines[i].trim()
            if (trimmed.startsWith("/**")) {
                val docLines = mutableListOf<String>()
                var done = false
                while (i < lines.size) {
                    val line = lines[i].trim()
                    docLines += line
                    if (line.contains("*/")) {
                        done = true
                        break
                    }
                    i++
                }
                pendingDoc = if (done) parseJavaDoc(docLines.joinToString("\n")) else null
                i++
                continue
            }

            if (trimmed.isBlank() || trimmed.startsWith("@")) {
                i++
                continue
            }

            val isClassDeclaration = Regex("""\b(class|interface|enum)\s+${Regex.escape(simpleName)}\b""")
                .containsMatchIn(trimmed)
            if (isClassDeclaration) {
                val doc = pendingDoc?.body?.trim().orEmpty()
                if (doc.isBlank()) return null
                return ReceiverDocumentationInfo(
                    displayName = simpleName,
                    documentation = doc,
                    sourceName = ownerName,
                    sourcePath = sourcePath,
                    sourceLine = i + 1
                )
            }

            pendingDoc = null
            i++
        }
        return null
    }

    private data class ParsedJavaMethod(
        val returnType: String,
        val parameters: List<ConstructorParameterInfo>
    )

    private fun parseJavaMethodLine(line: String, simpleName: String): ParsedJavaMethod? {
        if (!line.contains("$simpleName(")) return null
        val beforeParen = line.substringBefore('(').trim()
        val afterParen = line.substringAfter('(', "")
        if (afterParen.isBlank()) return null
        val rawName = beforeParen.substringAfterLast(' ')
        if (rawName != simpleName) return null
        if (beforeParen.contains("=")) return null

        val returnType = beforeParen
            .split(Regex("\\s+"))
            .asReversed()
            .drop(1)
            .firstOrNull { it !in javaMethodModifiers }
            ?: return null
        val closeIndex = line.indexOf(')', line.indexOf('('))
        if (closeIndex == -1) return null
        val parameterText = line.substring(line.indexOf('(') + 1, closeIndex)
        val parameters = splitTopLevelConstructorParameters(parameterText)
            .mapNotNull { parseJavaParameter(it) }
        return ParsedJavaMethod(returnType = javaTypeToKotlinish(returnType), parameters = parameters)
    }

    private val javaMethodModifiers = setOf(
        "public", "protected", "private", "static", "final", "abstract", "synchronized", "native",
        "strictfp", "default", "transient", "volatile"
    )

    private fun parseJavaParameter(raw: String): ConstructorParameterInfo? {
        var text = raw.trim()
        if (text.isBlank()) return null
        text = text.replace(Regex("@[A-Za-z0-9_.]+(\\([^)]*\\))?\\s*"), "")
        text = text.replace(Regex("\\bfinal\\s+"), "").trim()
        val name = text.substringAfterLast(' ').trim().removePrefix("...")
        val type = text.substringBeforeLast(' ', "").trim().replace("...", "...")
        if (name.isBlank() || type.isBlank()) return null
        return ConstructorParameterInfo(
            name = name,
            type = javaTypeToKotlinish(type),
            vararg = "..." in type
        )
    }

    private fun javaTypeToKotlinish(raw: String): String {
        val arrays = raw.count { it == '[' }
        val base = raw.replace("[]", "").replace("...", "").trim()
        val mapped = when (base) {
            "boolean" -> "Boolean"
            "byte" -> "Byte"
            "char" -> "Char"
            "double" -> "Double"
            "float" -> "Float"
            "int" -> "Int"
            "long" -> "Long"
            "short" -> "Short"
            "void" -> "Unit"
            else -> base.substringAfterLast('.')
        }
        return if (arrays > 0) "Array<$mapped>" else mapped
    }

    private fun parseJavaDoc(raw: String): ParsedKDoc {
        val body = mutableListOf<String>()
        val parameterDocs = linkedMapOf<String, StringBuilder>()
        var activeParameter: String? = null
        raw.lineSequence()
            .map { line ->
                line.trim()
                    .removePrefix("/**")
                    .removeSuffix("*/")
                    .trim()
                    .removePrefix("*")
                    .trim()
            }
            .forEach { line ->
                val parameterMatch = Regex("""^@param\s+([A-Za-z_][A-Za-z0-9_]*)\s*(.*)$""").matchEntire(line)
                when {
                    line.isBlank() -> {
                        activeParameter = null
                        if (body.lastOrNull()?.isNotBlank() == true) body += ""
                    }
                    parameterMatch != null -> {
                        val name = parameterMatch.groupValues[1]
                        parameterDocs.getOrPut(name) { StringBuilder() }
                            .append(cleanJavaDocMarkdown(parameterMatch.groupValues[2].trim()))
                        activeParameter = name
                    }
                    line.startsWith("@return") -> {
                        activeParameter = null
                    }
                    line.startsWith("@") -> {
                        activeParameter = null
                    }
                    activeParameter != null -> {
                        parameterDocs.getValue(activeParameter).append(' ').append(cleanJavaDocMarkdown(line))
                    }
                    else -> {
                        activeParameter = null
                        body += cleanJavaDocMarkdown(line)
                    }
                }
            }
        return ParsedKDoc(
            body = body.joinToString("\n").trim().ifBlank { null },
            parameterDocs = parameterDocs.mapValues { it.value.toString().trim() }
        )
    }

    private fun cleanJavaDocMarkdown(raw: String): String =
        raw.replace(Regex("<code>(.*?)</code>"), "`$1`")
            .replace("<p>", "\n\n")
            .replace("</p>", "")
            .replace(Regex("<[^>]+>"), "")
            .replace("&lt;", "<")
            .replace("&gt;", ">")
            .replace("&amp;", "&")

    private fun parseSourceCallableInfo(
        definition: afield.runtime.completion.KotlinDefinitionRecord,
        root: Path,
        simpleName: String
    ): SourceCallableInfo? {
        val path = root.resolve(definition.filePath).normalize()
        if (!Files.exists(path)) return null
        val rawText = runCatching { Files.readString(path) }.getOrNull() ?: return null
        val lineOffsets = lineStartOffsets(rawText)
        val lineStart = lineOffsets.getOrNull((definition.line - 1).coerceAtLeast(0)) ?: return null
        val funIndex = rawText.indexOf("fun", lineStart).takeIf { it >= 0 } ?: return null
        val parenOpen = rawText.indexOf('(', funIndex).takeIf { it > funIndex } ?: return null
        val close = matchingDelimiter(rawText, parenOpen, '(', ')')
        if (close <= parenOpen) return null

        val header = rawText.substring(funIndex, close)
        val headerName = header.substringAfter("fun").trim()
        val rawCallableName = headerName.substringBefore('(').substringAfterLast(' ').trim()
        val parsedName = rawCallableName.substringAfterLast('.').trim()
        if (parsedName != simpleName) return null

        val parsedKDoc = parseKDoc(definition.kdoc)
        val parameters = splitTopLevelConstructorParameters(rawText.substring(parenOpen + 1, close))
            .mapNotNull { parseParameter(it) }
            .map { parameter ->
                parameter.copy(
                    documentation = parsedKDoc.parameterDocs[parameter.name] ?: parameter.documentation
                )
            }

        var cursor = skipWhitespace(rawText, close + 1)
        var returnType: String? = null
        if (cursor < rawText.length && rawText[cursor] == ':') {
            cursor++
            val end = findFunctionHeaderEnd(rawText, cursor)
            returnType = rawText.substring(cursor, end).trim().ifBlank { null }
        } else if (cursor < rawText.length && rawText[cursor] == '=') {
            val expressionStart = skipWhitespace(rawText, cursor + 1)
            returnType = inferExpressionBodyReturnType(rawText, expressionStart)
        }

        val relativePath = runCatching { root.relativize(path).toString().replace("\\", "/") }
            .getOrDefault(path.toString())
        val ownerName = rawCallableName.substringBeforeLast('.', missingDelimiterValue = "")
            .takeIf { it.isNotBlank() }
            ?.substringAfterLast('.')
            ?: definition.name.substringBeforeLast('.', "").ifBlank { null }

        return SourceCallableInfo(
            ownerName = ownerName,
            sourceFile = relativePath,
            sourceLine = definition.line,
            sourceName = definition.name,
            documentation = parsedKDoc.body,
            parameters = parameters,
            returnType = returnType
        )
    }

    private fun inferExpressionBodyReturnType(rawText: String, expressionStart: Int): String? {
        if (expressionStart !in rawText.indices) return null
        val snippet = rawText.substring(expressionStart, rawText.length.coerceAtMost(expressionStart + 240))
        val constructorLike = Regex("""^([A-Za-z_][A-Za-z0-9_.]*)\s*(?:<[^(){}=\n]*>)?\s*\(""")
            .find(snippet)
            ?.groupValues
            ?.getOrNull(1)
            ?.takeIf { it.substringAfterLast('.').firstOrNull()?.isUpperCase() == true }
        return constructorLike
    }

    private fun parseKDoc(kdoc: String?): ParsedKDoc {
        if (kdoc.isNullOrBlank()) return ParsedKDoc()

        val body = mutableListOf<String>()
        val parameterDocs = linkedMapOf<String, StringBuilder>()
        var activeParameter: String? = null

        kdoc.lineSequence().forEach { rawLine ->
            val line = rawLine.trimEnd()
            val parameterMatch = Regex("""^\s*@param\s+([A-Za-z_][A-Za-z0-9_]*)\s*(.*)$""").matchEntire(line)
            when {
                parameterMatch != null -> {
                    val name = parameterMatch.groupValues[1]
                    parameterDocs.getOrPut(name) { StringBuilder() }
                        .append(parameterMatch.groupValues[2].trim())
                    activeParameter = name
                }
                line.trimStart().startsWith("@") -> activeParameter = null
                activeParameter != null && line.isNotBlank() -> {
                    parameterDocs.getValue(activeParameter).append(' ').append(line.trim())
                }
                else -> {
                    activeParameter = null
                    body += line
                }
            }
        }

        return ParsedKDoc(
            body = body.joinToString("\n").trim().ifBlank { null },
            parameterDocs = parameterDocs.mapValues { it.value.toString().trim() }
        )
    }

Source-card navigation (documentation-panel code links)

    @Volatile
    private var cardIndexRef: afield.runtime.completion.KotlinSourceIndexDb? = null

    @Volatile
    private var cardSimpleNameLookup: Map<String, List<afield.runtime.completion.KotlinDefinitionRecord>> = emptyMap()

    private fun simpleNameLookup(): Map<String, List<afield.runtime.completion.KotlinDefinitionRecord>> {
        val index = KotlinSourceIndex.latestIndex ?: KotlinSourceIndex.loadIndex() ?: return emptyMap()
        if (cardIndexRef !== index) {
            cardSimpleNameLookup = index.definitions.groupBy { it.name.substringAfterLast('.') }
            cardIndexRef = index
        }
        return cardSimpleNameLookup
    }

    private fun cardKindRank(kind: String): Int = when (kind) {
        "typealias" -> 5
        "class", "object", "interface" -> 4
        "fun" -> 3
        "val", "var" -> 2
        else -> 1
    }

    private val cardCandidateComparator =
        compareByDescending<afield.runtime.completion.KotlinDefinitionRecord> { cardKindRank(it.kind) }
            .thenByDescending { isDefaultBoxSymbol(it.name) }
            .thenBy { it.filePath }
            .thenBy { it.line }

    private fun bestDefinitionForSimpleName(simpleName: String): afield.runtime.completion.KotlinDefinitionRecord? {
        if (simpleName.isBlank()) return null
        return simpleNameLookup()[simpleName]?.minWithOrNull(cardCandidateComparator)
    }

Resolve a KDoc [Symbol](../../../../../../reference/source/jvmMain/afield/document/persistence/edn.md#L45) reference to a project source location, or null if it isn't a known project declaration. Increment 1 covers the project index + typealiases only, so classpath-only types (e.g. kotlin.Float) stay plain text rather than becoming dead links.

reference may be qualified (Foo.bar): resolution keys on the last segment, but a qualifier is verified against each candidate's owner so a bare bar declared under an unrelated type never becomes a wrong link for [Foo.bar](../../../../../../reference/source/jvmMain/afield/ui/visualization/tables.md#L219). An unverifiable qualifier resolves to null (left literal), never a wrong link.

    private fun resolveSymbolLocation(reference: String): IndexedDocumentationTarget? {
        if (reference.isBlank()) return null
        val simpleName = reference.substringAfterLast('.')
        val d = if (reference.contains('.')) {
            val owner = reference.substringBeforeLast('.').substringAfterLast('.')
            simpleNameLookup()[simpleName]
                ?.filter { it.name.substringBeforeLast('.', "").substringAfterLast('.') == owner }
                ?.minWithOrNull(cardCandidateComparator)
        } else {
            bestDefinitionForSimpleName(simpleName)
        } ?: return null
        return IndexedDocumentationTarget(
            sourceName = d.name,
            sourceFile = d.filePath,
            sourceLine = d.line,
            hasDocumentation = !d.kdoc.isNullOrBlank()
        )
    }

Matches either a KDoc [display](../../../../../../reference/source/jvmMain/afield/graphics/vulkan/Renderer.md#L66) alternate-link (group 1 = display text, group 2 = target symbol) or a bare [Ident](../../../../../../reference/source/jvmMain/afield/runtime/macros/RewriteDsl.md#L40) reference (group 3).

    private val kdocLinkRegex =
        Regex("""\[([^\[\]]+)]\[([A-Za-z_][A-Za-z0-9_.]*)]|\[([A-Za-z_][A-Za-z0-9_.]*)]""")

    private fun sourceCardHref(name: String, path: String, line: Int): String {
        fun enc(s: String) = URLEncoder.encode(s, "UTF-8")
        return "field-source:?path=${enc(path)}&line=$line&name=${enc(name)}"
    }

internal fun linkifyKDocSymbols(markdown: String): String

Rewrite bare KDoc [Symbol](../../../../../../reference/source/jvmMain/afield/document/persistence/edn.md#L45) cross-references into markdown links with a field-source: href, but only when the symbol resolves to a known source location. Existing [label](../../../../../../reference/url) links and anything inside code — both fenced ` blocks and inline code `` spans — are left untouched.

    internal fun linkifyKDocSymbols(markdown: String): String {
        if (markdown.isEmpty() || !markdown.contains('[')) return markdown
        val parts = markdown.split("```")
        val sb = StringBuilder(markdown.length + 64)
        parts.forEachIndexed { index, part ->
            if (index > 0) sb.append("```")
            // Even segments are outside code fences; odd segments are code.
            if (index % 2 == 0) sb.append(linkifyOutsideFence(part)) else sb.append(part)
        }
        return sb.toString()
    }

Linkify a non-fenced region, still skipping inline `code` spans: split on single backticks and only rewrite the even (non-code) segments, mirroring the fence handling one level up.

    private fun linkifyOutsideFence(text: String): String {
        if (!text.contains('[')) return text
        if (!text.contains('`')) return linkifyKDocSegment(text)
        val spans = text.split('`')
        val sb = StringBuilder(text.length + 32)
        spans.forEachIndexed { index, span ->
            if (index > 0) sb.append('`')
            if (index % 2 == 0) sb.append(linkifyKDocSegment(span)) else sb.append(span)
        }
        return sb.toString()
    }

    private fun linkifyKDocSegment(text: String): String {
        if (!text.contains('[')) return text
        return kdocLinkRegex.replace(text) { match ->
            val target = match.groupValues[2]
            if (target.isNotEmpty()) {
                // KDoc `[display][Target]`: link the display text to the target
                // symbol only, never linkifying the display text on its own.
                val display = match.groupValues[1]
                val loc = resolveSymbolLocation(target)
                if (loc == null) match.value
                else "[$display](${sourceCardHref(loc.sourceName, loc.sourceFile, loc.sourceLine)})"
            } else {
                // Bare `[Ident]`. Skip the label of an existing [label](url) link.
                if (text.getOrNull(match.range.last + 1) == '(') return@replace match.value
                val label = match.groupValues[3]
                val loc = resolveSymbolLocation(label)
                if (loc == null) match.value
                else "[$label](${sourceCardHref(loc.sourceName, loc.sourceFile, loc.sourceLine)})"
            }
        }
    }

internal fun resolveSourceCard(symbol: String?, path: String?, line: Int): SourceCardResult

Build a documentation card for the declaration a reader clicked in the panel. Reuses the same markdown shape as completion documentation so the card renders identically, and linkifies its own [Symbol](../../../../../../reference/source/jvmMain/afield/document/persistence/edn.md#L45) references so navigation recurses.

body — 57 lines, from line 1888
    internal fun resolveSourceCard(symbol: String?, path: String?, line: Int): SourceCardResult {
        val index = KotlinSourceIndex.latestIndex ?: KotlinSourceIndex.loadIndex() ?: return SourceCardResult()
        val root = Paths.get(System.getProperty("user.dir")).normalize()

        val record = when {
            !path.isNullOrBlank() && line > 0 ->
                index.definitions.firstOrNull { it.filePath == path && it.line == line }
                    ?: index.definitions.firstOrNull { it.filePath.endsWith(path) && it.line == line }
                    ?: symbol?.let { bestDefinitionForSimpleName(it.substringAfterLast('.')) }
            !symbol.isNullOrBlank() -> bestDefinitionForSimpleName(symbol.substringAfterLast('.'))
            else -> null
        } ?: return SourceCardResult()

        val simpleName = record.name.substringAfterLast('.')
        val bodyMarkdown = when (record.kind) {
            "fun" -> parseSourceCallableInfo(record, root, simpleName)?.let { sci ->
                CallableInfo(
                    name = simpleName,
                    ownerName = sci.ownerName,
                    parameters = sci.parameters,
                    returnType = sci.returnType,
                    documentation = sci.documentation,
                    sourceFile = sci.sourceFile,
                    sourceLine = sci.sourceLine,
                    sourceName = sci.sourceName
                ).documentationMarkdown()
            } ?: genericCardMarkdown(record, root)
            else -> genericCardMarkdown(record, root)
        }
        if (bodyMarkdown.isBlank()) return SourceCardResult()

        val category = record.completionCategory.orEmpty()
        val documentation = buildString {
            if (category.isNotBlank()) append("**Category** `").append(category).append("`\n\n")
            append(bodyMarkdown)
        }

        return SourceCardResult(
            found = true,
            title = simpleName,
            documentation = linkifyKDocSymbols(documentation),
            category = category,
            sourceName = record.name,
            sourcePath = record.filePath,
            sourceLine = record.line
        )
    }

    private fun genericCardMarkdown(record: afield.runtime.completion.KotlinDefinitionRecord, root: Path): String {
        val text = runCatching { Files.readString(root.resolve(record.filePath).normalize()) }.getOrNull()
        val signature = text?.let { declarationSignature(it, record.line) }
            ?: "${record.kind} ${record.name.substringAfterLast('.')}"
        val sections = mutableListOf("```kotlin\n$signature\n```")
        parseKDoc(record.kdoc).body?.takeIf { it.isNotBlank() }?.let { sections += it }
        sections += "**Source** `${record.filePath}`"
        return sections.joinToString("\n\n")
    }

Extract a readable declaration header starting at line (1-based): accumulate source lines until the signature is balanced or a body { opens, then trim the trailing brace. Functions use the richer callable parser; this serves types, typealiases, objects and properties.

    private fun declarationSignature(text: String, line: Int): String? {
        val lines = text.split("\n")
        if (line < 1 || line > lines.size) return null
        val sb = StringBuilder()
        var i = line - 1
        val limit = (line - 1 + 8).coerceAtMost(lines.size)
        while (i < limit) {
            val raw = lines[i]
            val braceAt = raw.indexOf('{')
            if (braceAt >= 0) {
                sb.append(raw.substring(0, braceAt))
                break
            }
            sb.append(raw)
            val open = sb.count { it == '(' }
            val balanced = open > 0 && open == sb.count { it == ')' }
            if (open == 0 || balanced || sb.trim().endsWith("=")) break
            sb.append('\n')
            i++
        }
        return sb.toString().trim().trimEnd('{').trim().ifBlank { null }
    }

Raw source view (GET /api/source, Stage B)

    private fun sourceLanguageFor(name: String): String = when {
        name.endsWith(".kt", true) || name.endsWith(".kts", true) -> "kotlin"
        name.endsWith(".java", true) -> "java"
        name.endsWith(".glsl", true) || name.endsWith(".frag", true) || name.endsWith(".vert", true) -> "glsl"
        name.endsWith(".metal", true) || name.endsWith(".msl", true) -> "msl"
        name.endsWith(".js", true) || name.endsWith(".mjs", true) -> "javascript"
        else -> ""
    }

    private fun sourceWindow(lines: List<String>, line: Int, span: Int): Triple<Int, Int, String> {
        if (lines.isEmpty()) return Triple(0, 0, "")
        val start = if (line > 0) (line - 3).coerceIn(1, lines.size) else 1
        val end = if (line > 0) (start + span - 1).coerceAtMost(lines.size) else span.coerceAtMost(lines.size)
        return Triple(start, end, lines.subList(start - 1, end).joinToString("\n"))
    }

internal fun readSourceExcerpt(rawPath: String, line: Int, context: Int): SourceFileResponse

Read a window of source text for the "view source" affordance. Guarded: project files must resolve under ./src; jar entries must live in a -sources.jar. Returns SourceFileResponse.found = false for anything else (no dead views).

body — 146 lines, from line 1998
    internal fun readSourceExcerpt(rawPath: String, line: Int, context: Int): SourceFileResponse {
        val span = context.coerceIn(1, 400)
        return if (rawPath.startsWith("jar:")) readJarExcerpt(rawPath, line, span)
        else readProjectExcerpt(rawPath, line, span)
    }

    private fun readProjectExcerpt(rawPath: String, line: Int, span: Int): SourceFileResponse {
        val root = Paths.get(System.getProperty("user.dir")).toAbsolutePath().normalize()
        val srcRoot = root.resolve("src").normalize()
        val rel = rawPath.removePrefix("./").replace('\\', '/')
        val path = root.resolve(rel).normalize()
        if (!path.startsWith(srcRoot) || !Files.isRegularFile(path)) return SourceFileResponse()
        val lines = runCatching { Files.readAllLines(path) }.getOrNull() ?: return SourceFileResponse()
        val (start, end, code) = sourceWindow(lines, line, span)
        val relDisplay = runCatching { root.relativize(path).toString().replace('\\', '/') }.getOrDefault(rel)
        return SourceFileResponse(true, relDisplay, sourceLanguageFor(relDisplay), start, end, code)
    }

    private fun readJarExcerpt(rawPath: String, line: Int, span: Int): SourceFileResponse {
        // rawPath = jar:file:///abs/foo-sources.jar!/pkg/Foo.kt
        val body = rawPath.removePrefix("jar:")
        val sep = body.lastIndexOf("!/")
        if (sep < 0) return SourceFileResponse()
        val jarFile = runCatching { Paths.get(URI(body.substring(0, sep))).toFile() }.getOrNull()
            ?: return SourceFileResponse()
        val entryName = body.substring(sep + 2)
        if (!jarFile.name.endsWith("-sources.jar") || !jarFile.isFile) return SourceFileResponse()
        val text = runCatching {
            ZipFile(jarFile).use { zip ->
                val entry = zip.getEntry(entryName) ?: return SourceFileResponse()
                zip.getInputStream(entry).bufferedReader().use { it.readText() }
            }
        }.getOrNull() ?: return SourceFileResponse()
        val (start, end, code) = sourceWindow(text.split("\n"), line, span)
        return SourceFileResponse(true, entryName, sourceLanguageFor(entryName), start, end, code)
    }

    private fun scoreCallableCandidate(
        definitionName: String,
        info: SourceCallableInfo,
        receiverOwnerHint: String?,
        expectedParamCount: Int?,
        expectedParamTypes: List<String>?,
        expectedReturnType: String?,
        displayText: String,
        allCode: String
    ): Int {
        var score = 20

        if (!receiverOwnerHint.isNullOrBlank()) {
            score += ownerCompatibilityScore(info.ownerName, receiverOwnerHint)
        }

        expectedParamCount?.let {
            score += if (info.parameters.size == it) 40 else -20 * kotlin.math.abs(info.parameters.size - it)
        }

        expectedParamTypes?.takeIf { it.isNotEmpty() }?.let { expectedTypes ->
            expectedTypes.forEachIndexed { index, expected ->
                val actual = info.parameters.getOrNull(index)?.type?.let(::shortenType).orEmpty()
                score += when {
                    actual.isBlank() -> 0
                    parameterTypesMatch(actual, expected) -> 35
                    else -> -25
                }
            }
        }

        expectedReturnType?.let {
            val actualReturn = info.returnType?.let(::shortenType)
            score += if (actualReturn == shortenType(it)) 30 else -10
        }

        if (displayText.contains("fun ")) score += 5
        if (definitionName.substringAfterLast('.') in allCode) score += 10
        return score
    }

    private fun ownerCompatibilityScore(candidateOwner: String?, receiverOwner: String?): Int {
        val candidate = candidateOwner?.takeIf { it.isNotBlank() } ?: return 0
        val receiver = receiverOwner?.takeIf { it.isNotBlank() } ?: return 0
        if (candidate == receiver) return 120
        if (candidate.substringAfterLast('.') == receiver.substringAfterLast('.')) return 90
        if (ownerIsAssignableFrom(candidate, receiver)) return 70
        return -40
    }

    private fun ownerIsAssignableFrom(candidateOwner: String, receiverOwner: String): Boolean {
        val candidateNames = resolveOwnerClassNames(candidateOwner)
        val receiverNames = resolveOwnerClassNames(receiverOwner)
        if (candidateNames.isEmpty() || receiverNames.isEmpty()) return false
        val classLoader = Thread.currentThread().contextClassLoader
        val candidateClasses = candidateNames.mapNotNull { name ->
            runCatching { Class.forName(name, false, classLoader) }.getOrNull()
        }
        val receiverClasses = receiverNames.mapNotNull { name ->
            runCatching { Class.forName(name, false, classLoader) }.getOrNull()
        }
        return candidateClasses.any { candidate ->
            receiverClasses.any { receiver -> candidate.isAssignableFrom(receiver) }
        }
    }

    private fun resolveOwnerClassNames(owner: String): List<String> {
        val raw = owner.substringBefore('<').substringBefore('?').trim()
        if (raw.isBlank()) return emptyList()
        if ('.' in raw) return listOf(raw)
        return findClassesNamed(raw).orEmpty()
    }

    private fun descriptorOwnerName(descriptorText: String?): String? {
        val text = descriptorText?.takeIf { it.isNotBlank() } ?: return null
        return Regex("""\bdefined\s+in\s+([A-Za-z_][A-Za-z0-9_.$]*(?:\.[A-Za-z_][A-Za-z0-9_$]*)*)""")
            .find(text)
            ?.groupValues
            ?.getOrNull(1)
            ?.substringBefore('[')
            ?.trim()
            ?.takeIf { it.isNotBlank() }
    }

    private fun inferReceiverOwnerName(
        box: Box,
        allCode: String,
        position: Int,
        receiverTypeHint: String?
    ): String? {
        receiverTypeHint?.takeIf { it.isNotBlank() }?.let { return it.substringAfterLast('.') }
        inferEnclosingLambdaReceiverOwner(allCode, position)?.let { return it }

        val receiverExpression = extractReceiverExpressionAtCompletion(allCode, position) ?: return null
        val identifier = receiverExpression.trim()
        if (!identifier.matches(Regex("[A-Za-z_][A-Za-z0-9_]*"))) return null

        box.ki.context.vars[identifier]?.value?.let { value ->
            value::class.simpleName?.takeIf { it.isNotBlank() }?.let { return it }
        }

        val explicitType = explicitTypeForLocal(allCode, identifier)
        if (!explicitType.isNullOrBlank()) return explicitType.substringAfterLast('.')

        val constructedType = constructedTypeForLocal(allCode, identifier)
        if (!constructedType.isNullOrBlank()) return constructedType.substringAfterLast('.')

        return null
    }

fun receiverQualifiedNameAtCompletion

The receiver's qualified name at a dot-completion caret, or null when it cannot be pinned down.

inferReceiverOwnerName answers a similar question for documentation, but deliberately in simple names — a KDoc lookup matches on FLine.moveTo. CommonMemberPrior cannot use a simple name: it resolves the annotation with Class.forName. The AA backend forwards a qualified name on every row (receiverQualifiedName), so the prior works there. The K1 delegate forwards no receiver at all, and K1 serves every completion until the AA session has warmed — including the first ctrl-space after launch. Without this the prior is absent exactly when a person first meets the popup.

Sources, most certain first: the live value's own class, then the declared type, then the constructor called to make it. A simple name read out of the source text is accepted only when exactly one class on the classpath answers to it — guessing between homonyms would attach one type's prior to another type's members.

box may be null when there is no live REPL to ask; the source text then decides alone. resolveSimpleName is that classpath lookup, injectable because it is a whole-classpath scan whose answer depends on the personal layer and the import blacklist — see ImportResolution.

body — 751 lines, from line 2164
    fun receiverQualifiedNameAtCompletion(
        box: Box?,
        allCode: String,
        position: Int,
        resolveSimpleName: (String) -> List<String>? = ::findClassesNamed,
    ): String? {
        val identifier = extractReceiverExpressionAtCompletion(allCode, position)?.trim() ?: return null
        if (!identifier.matches(Regex("[A-Za-z_][A-Za-z0-9_]*"))) return null

        // A box that has already run knows the answer exactly: ask the live value for its own class.
        box?.ki?.context?.vars?.get(identifier)?.value?.let { value ->
            value::class.qualifiedName?.takeIf { it.isNotBlank() }?.let { return it }
        }

        val fromSource = explicitTypeForLocal(allCode, identifier)?.takeIf { it.isNotBlank() }
            ?: constructedTypeForLocal(allCode, identifier)?.takeIf { it.isNotBlank() }
            ?: return null
        if ('.' in fromSource) return fromSource
        return resolveSimpleName(fromSource)?.singleOrNull()
    }

    private fun inferEnclosingLambdaReceiverOwner(allCode: String, position: Int): String? {
        val callName = enclosingLambdaCallName(allCode, position) ?: return null
        val index = KotlinSourceIndex.latestIndex ?: KotlinSourceIndex.loadIndex() ?: return null
        val root = Paths.get(System.getProperty("user.dir")).normalize()

        return index.definitions.asSequence()
            .filter { it.kind == "fun" && it.name.substringAfterLast('.') == callName }
            .mapNotNull { parseSourceCallableInfo(it, root, callName) }
            .flatMap { it.parameters.asSequence() }
            .mapNotNull { parameter ->
                Regex("""([A-Za-z_][A-Za-z0-9_.]*)\s*\.\s*\(\s*\)\s*->""")
                    .find(parameter.type)
                    ?.groupValues
                    ?.get(1)
                    ?.substringAfterLast('.')
            }
            .firstOrNull()
    }

    private fun enclosingLambdaCallName(allCode: String, position: Int): String? {
        var cursor = position.coerceIn(0, allCode.length) - 1
        var braceDepth = 0

        while (cursor >= 0) {
            when (allCode[cursor]) {
                '}' -> braceDepth++
                '{' -> {
                    if (braceDepth > 0) {
                        braceDepth--
                    } else {
                        return callNameBeforeLambdaBrace(allCode, cursor)
                    }
                }
            }
            cursor--
        }
        return null
    }

    private fun callNameBeforeLambdaBrace(code: String, bracePosition: Int): String? {
        var cursor = bracePosition
        while (cursor > 0 && code[cursor - 1].isWhitespace()) cursor--

        if (cursor > 0 && code[cursor - 1] == ')') {
            var parenDepth = 1
            cursor--
            while (cursor > 0 && parenDepth > 0) {
                cursor--
                when (code[cursor]) {
                    ')' -> parenDepth++
                    '(' -> parenDepth--
                }
            }
            if (parenDepth != 0) return null
        }

        while (cursor > 0 && code[cursor - 1].isWhitespace()) cursor--
        val end = cursor
        while (cursor > 0 && (code[cursor - 1].isLetterOrDigit() || code[cursor - 1] == '_')) cursor--
        if (cursor == end) return null
        return code.substring(cursor, end)
    }

    private fun extractReceiverExpressionAtCompletion(allCode: String, position: Int): String? {
        if (position <= 0 || position > allCode.length) return null
        val bounds = getTokenBounds(allCode, position)
        val beforePrefix = bounds.start
        if (beforePrefix <= 0) return null

        val isSafeDot = beforePrefix >= 2 &&
            allCode[beforePrefix - 1] == '.' &&
            allCode[beforePrefix - 2] == '?'
        val isDot = !isSafeDot && allCode[beforePrefix - 1] == '.'
        if (!isDot && !isSafeDot) return null

        val dotPos = if (isSafeDot) beforePrefix - 2 else beforePrefix - 1
        return extractReceiverExpression(allCode, dotPos)
    }

    private fun extractReceiverExpression(text: String, endPos: Int): String {
        var pos = endPos - 1
        var parenDepth = 0
        var bracketDepth = 0

        while (pos >= 0) {
            val ch = text[pos]
            when {
                ch == ')' -> {
                    parenDepth++
                    pos--
                }
                ch == '(' && parenDepth > 0 -> {
                    parenDepth--
                    pos--
                }
                ch == ']' -> {
                    bracketDepth++
                    pos--
                }
                ch == '[' && bracketDepth > 0 -> {
                    bracketDepth--
                    pos--
                }
                parenDepth > 0 || bracketDepth > 0 -> pos--
                ch.isLetterOrDigit() || ch == '_' || ch == '.' || ch == '?' -> pos--
                else -> break
            }
        }

        return text.substring(pos + 1, endPos).trim()
    }

    private fun explicitTypeForLocal(allCode: String, identifier: String): String? {
        val regex = Regex("\\b(?:val|var)\\s+${Regex.escape(identifier)}\\s*:\\s*([A-Za-z0-9_.<>?]+)")
        val match = regex.find(allCode) ?: return null
        return match.groupValues[1]
            .substringBefore('<')
            .substringBefore('?')
            .trim()
            .takeIf { it.isNotBlank() }
    }

    private fun constructedTypeForLocal(allCode: String, identifier: String): String? {
        val regex = Regex("\\b(?:val|var)\\s+${Regex.escape(identifier)}\\s*=\\s*([A-Za-z_][A-Za-z0-9_.]*)\\s*(?:<[^\\n=(){}]*>)?\\s*\\(")
        val match = regex.find(allCode) ?: return null
        val callee = match.groupValues[1]
            .substringBefore('<')
            .trim()
            .takeIf { it.isNotBlank() }
            ?: return null

        if (callee.substringAfterLast('.').firstOrNull()?.isUpperCase() == true) return callee

        val openParen = allCode.indexOf('(', match.range.first).takeIf { it >= 0 } ?: return callee
        val closeParen = matchingDelimiter(allCode, openParen, '(', ')')
        val argumentCount = if (closeParen > openParen) {
            splitTopLevelConstructorParameters(allCode.substring(openParen + 1, closeParen)).size
        } else {
            null
        }

        return sourceFunctionReturnTypeForCall(allCode, callee, argumentCount) ?: callee
    }

    private fun sourceFunctionReturnTypeForCall(
        allCode: String,
        calleeName: String,
        argumentCount: Int?
    ): String? {
        val simpleName = calleeName.substringAfterLast('.')
        val index = KotlinSourceIndex.latestIndex ?: KotlinSourceIndex.loadIndex() ?: return null
        val root = Paths.get(System.getProperty("user.dir")).normalize()
        return index.definitions.asSequence()
            .filter { it.kind == "fun" && it.name.substringAfterLast('.') == simpleName }
            .mapNotNull { parseSourceCallableInfo(it, root, simpleName) }
            .filter { info -> argumentCount == null || info.parameters.size == argumentCount }
            .sortedWith(
                compareByDescending<SourceCallableInfo> { isDefaultBoxSymbol(it.sourceName) }
                    .thenByDescending { it.sourceName in allCode }
                    .thenBy { it.sourceFile }
                    .thenBy { it.sourceLine }
            )
            .mapNotNull { it.returnType?.takeIf { returnType -> returnType.isNotBlank() } }
            .firstOrNull()
    }

    private fun indexedOwnerName(definitionName: String): String? =
        definitionName.substringBeforeLast('.', "")
            .takeIf { it.isNotBlank() }

    private fun ownerMatches(candidateOwner: String?, receiverOwner: String?): Boolean {
        val candidate = candidateOwner?.takeIf { it.isNotBlank() } ?: return false
        val receiver = receiverOwner?.takeIf { it.isNotBlank() } ?: return false
        return candidate == receiver ||
            candidate.substringAfterLast('.') == receiver.substringAfterLast('.')
    }

    private fun parameterCountHint(displayText: String): Int? {
        val open = displayText.indexOf('(')
        val close = displayText.lastIndexOf(')')
        if (open == -1 || close <= open) return null
        val inside = displayText.substring(open + 1, close).trim()
        if (inside.isEmpty()) return 0
        return splitTopLevelConstructorParameters(inside).size
    }

    private fun parameterTypeHints(displayText: String): List<String>? {
        val open = displayText.indexOf('(')
        val close = displayText.lastIndexOf(')')
        if (open == -1 || close <= open) return null
        val inside = displayText.substring(open + 1, close).trim()
        if (inside.isEmpty()) return emptyList()
        return splitTopLevelConstructorParameters(inside)
            .mapNotNull { raw ->
                val withoutDefault = raw.substringBefore('=').trim()
                val typeText = if (':' in withoutDefault) {
                    withoutDefault.substringAfter(':').trim()
                } else {
                    withoutDefault
                }
                typeText
                    .removePrefix("vararg ")
                    .substringBefore('=')
                    .trim()
                    .takeIf { it.isNotBlank() }
                    ?.let(::shortenType)
            }
    }

    private fun parameterTypesMatch(actual: String, expected: String): Boolean {
        fun normalize(type: String): String =
            shortenType(type)
                .removeSuffix("?")
                .replace(" ", "")
        return normalize(actual) == normalize(expected)
    }

    private fun findFunctionHeaderEnd(text: String, start: Int): Int {
        var parenDepth = 0
        var angleDepth = 0
        var braceDepth = 0
        var bracketDepth = 0
        var inString = false
        var stringChar = '"'
        var i = start
        while (i < text.length) {
            val ch = text[i]
            if (inString) {
                if (ch == '\\' && i + 1 < text.length) {
                    i += 2
                    continue
                }
                if (ch == stringChar) inString = false
                i++
                continue
            }
            if (ch == '"' || ch == '\'') {
                inString = true
                stringChar = ch
                i++
                continue
            }
            when (ch) {
                '(' -> parenDepth++
                ')' -> if (parenDepth > 0) parenDepth--
                '<' -> angleDepth++
                '>' -> if (angleDepth > 0) angleDepth--
                '{' -> if (braceDepth == 0 && parenDepth == 0 && angleDepth == 0 && bracketDepth == 0) return i
                '=' -> if (braceDepth == 0 && parenDepth == 0 && angleDepth == 0 && bracketDepth == 0) return i
                '[' -> bracketDepth++
                ']' -> if (bracketDepth > 0) bracketDepth--
                '\n' -> if (parenDepth == 0 && angleDepth == 0 && bracketDepth == 0) {
                    val next = text.indexOfFirstNonWhitespace(i + 1)
                    if (next == -1 || text.getOrNull(next) in listOf('{', '=')) return i
                }
            }
            i++
        }
        return text.length
    }

    private fun lineStartOffsets(text: String): List<Int> {
        val offsets = mutableListOf(0)
        text.forEachIndexed { index, ch ->
            if (ch == '\n' && index + 1 < text.length) offsets += index + 1
        }
        return offsets
    }

    private fun String.indexOfFirstNonWhitespace(start: Int): Int {
        var cursor = start
        while (cursor < length && this[cursor].isWhitespace()) cursor++
        return if (cursor < length) cursor else -1
    }

    private fun sourceFileCandidates(root: Path, simpleName: String, fqName: String?): List<Path> {
        val ordered = LinkedHashSet<Path>()
        val index = KotlinSourceIndex.latestIndex ?: KotlinSourceIndex.loadIndex()
        index?.definitions
            ?.asSequence()
            ?.filter { it.kind in setOf("class", "object") && it.name.substringAfterLast('.') == simpleName }
            ?.map { root.resolve(it.filePath).normalize() }
            ?.filter { Files.exists(it) }
            ?.forEach { ordered.add(it) }

        if (ordered.isEmpty()) {
            defaultSourceRoots(root).forEach { sourceRoot ->
                if (!Files.exists(sourceRoot)) return@forEach
                Files.walk(sourceRoot).use { paths ->
                    paths.filter { Files.isRegularFile(it) && it.fileName.toString().endsWith(".kt") }
                        .forEach { path ->
                            val raw = runCatching { Files.readString(path) }.getOrNull() ?: return@forEach
                            if (Regex("\\b(?:class|object)\\s+${Regex.escape(simpleName)}\\b").containsMatchIn(raw)) {
                                ordered.add(path)
                            }
                        }
                }
            }
        }

        if (fqName == null || ordered.size <= 1) return ordered.toList()

        val targetPackage = fqName.substringBeforeLast('.', "")
        return ordered.sortedByDescending { path ->
            val filePackage = packageName(runCatching { Files.readString(path) }.getOrDefault(""))
            if (filePackage == targetPackage) 1 else 0
        }
    }

    private fun defaultSourceRoots(root: Path): List<Path> = listOf(
        root.resolve("src/jvmMain/kotlin"),
        root.resolve("src/commonMain/kotlin"),
        root.resolve("src/jsMain/kotlin"),
        root.resolve("src/personal/kotlin")
    )

    private fun parseSourceInfo(
        rawText: String,
        path: Path,
        root: Path,
        simpleName: String,
        fqName: String?
    ): SourceConstructorInfo? {
        val packageName = packageName(rawText)
        if (fqName != null) {
            val expectedPackage = fqName.substringBeforeLast('.', "")
            if (expectedPackage.isNotBlank() && packageName != expectedPackage) return null
        }

        val declarationMatch = Regex("\\b(class|object)\\s+${Regex.escape(simpleName)}\\b").find(rawText) ?: return null
        val isObject = declarationMatch.groupValues[1] == "object"
        val classStart = declarationMatch.range.first
        val classDocumentation = leadingKDoc(rawText, classStart)
        var cursor = declarationMatch.range.last + 1
        cursor = skipWhitespace(rawText, cursor)
        if (cursor < rawText.length && rawText[cursor] == '<') {
            val genericEnd = matchingDelimiter(rawText, cursor, '<', '>')
            if (genericEnd <= cursor) return null
            cursor = genericEnd + 1
            cursor = skipWhitespace(rawText, cursor)
        }
        val parameters = if (isObject) {
            emptyList()
        } else {
            if (cursor >= rawText.length || rawText[cursor] != '(') return null

            val close = matchingDelimiter(rawText, cursor, '(', ')')
            if (close <= cursor) return null

            val constructorText = rawText.substring(cursor + 1, close)
            splitTopLevelConstructorParameters(constructorText)
                .mapNotNull { parseParameter(it) }
        }

        val relativePath = runCatching { root.relativize(path).toString().replace("\\", "/") }
            .getOrDefault(path.toString())

        return SourceConstructorInfo(
            sourceFile = relativePath,
            sourceLine = rawText.substring(0, classStart).count { it == '\n' } + 1,
            sourceName = if (packageName.isBlank()) simpleName else "$packageName.$simpleName",
            isObject = isObject,
            classDocumentation = classDocumentation,
            parameters = parameters
        )
    }

    private fun packageName(rawText: String): String {
        val match = Regex("^\\s*package\\s+([A-Za-z0-9_.]+)", RegexOption.MULTILINE).find(rawText)
        return match?.groupValues?.get(1).orEmpty()
    }

    private fun leadingKDoc(rawText: String, declarationStart: Int): String? {
        val prefix = rawText.substring(0, declarationStart)
        val end = prefix.lastIndexOf("*/")
        if (end == -1) return null
        val start = prefix.lastIndexOf("/**", end)
        if (start == -1) return null
        val between = prefix.substring(end + 2)
        if (between.any { !it.isWhitespace() }) return null
        return cleanComment(prefix.substring(start, end + 2))
    }

    private fun splitTopLevelConstructorParameters(raw: String): List<String> {
        if (raw.isBlank()) return emptyList()
        val out = mutableListOf<String>()
        val current = StringBuilder()
        var parenDepth = 0
        var angleDepth = 0
        var braceDepth = 0
        var bracketDepth = 0
        var inString = false
        var stringChar = '"'
        var inLineComment = false
        var inBlockComment = false
        var i = 0
        while (i < raw.length) {
            val ch = raw[i]
            if (inLineComment) {
                current.append(ch)
                if (ch == '\n') inLineComment = false
                i++
                continue
            }
            if (inBlockComment) {
                current.append(ch)
                if (ch == '*' && i + 1 < raw.length && raw[i + 1] == '/') {
                    current.append('/')
                    inBlockComment = false
                    i += 2
                } else {
                    i++
                }
                continue
            }
            if (inString) {
                current.append(ch)
                if (ch == '\\' && i + 1 < raw.length) {
                    current.append(raw[i + 1])
                    i += 2
                    continue
                }
                if (ch == stringChar) inString = false
                i++
                continue
            }

            if (ch == '/' && i + 1 < raw.length && raw[i + 1] == '/') {
                current.append("//")
                inLineComment = true
                i += 2
                continue
            }
            if (ch == '/' && i + 1 < raw.length && raw[i + 1] == '*') {
                current.append("/*")
                inBlockComment = true
                i += 2
                continue
            }
            if (ch == '"' || ch == '\'') {
                inString = true
                stringChar = ch
                current.append(ch)
                i++
                continue
            }

            when (ch) {
                '(' -> parenDepth++
                ')' -> if (parenDepth > 0) parenDepth--
                '<' -> angleDepth++
                '>' -> if (angleDepth > 0) angleDepth--
                '{' -> braceDepth++
                '}' -> if (braceDepth > 0) braceDepth--
                '[' -> bracketDepth++
                ']' -> if (bracketDepth > 0) bracketDepth--
            }

            if (ch == ',' && parenDepth == 0 && angleDepth == 0 && braceDepth == 0 && bracketDepth == 0) {
                out += current.toString()
                current.setLength(0)
            } else {
                current.append(ch)
            }
            i++
        }

        current.toString().takeIf { it.isNotBlank() }?.let { out += it }
        return out
    }

    private fun parseParameter(rawSegment: String): ConstructorParameterInfo? {
        var segment = rawSegment.trim()
        if (segment.isBlank()) return null

        val documentation = leadingParameterComment(segment)
        segment = removeLeadingComment(segment).trimStart()
        segment = removeLeadingAnnotations(segment).trimStart()

        val words = segment.split(Regex("\\s+"), limit = 6)
        val modifiers = mutableSetOf<String>()
        var consumed = 0
        while (consumed < words.size) {
            val word = words[consumed]
            if (word in setOf("public", "private", "protected", "internal", "val", "var", "vararg", "crossinline", "noinline")) {
                modifiers.add(word)
                consumed++
            } else {
                break
            }
        }

        val remainder = segment.split(Regex("\\s+"), limit = consumed + 1).lastOrNull()?.takeIf { consumed > 0 } ?: segment
        val colon = topLevelIndexOf(remainder, ':')
        if (colon == -1) return null
        val equals = topLevelIndexOf(remainder, '=')
        val name = remainder.substring(0, colon).trim().substringAfterLast(' ').trim()
        if (name.isBlank()) return null
        val typeEnd = if (equals == -1) remainder.length else equals
        val type = remainder.substring(colon + 1, typeEnd).trim()
        val defaultValue = if (equals == -1) null else remainder.substring(equals + 1).trim()

        return ConstructorParameterInfo(
            name = name,
            type = type,
            defaultValue = defaultValue,
            documentation = documentation,
            optional = defaultValue != null,
            vararg = modifiers.contains("vararg")
        )
    }

    private fun leadingParameterComment(segment: String): String? {
        val trimmed = segment.trimStart()
        return when {
            trimmed.startsWith("/**") -> {
                val end = trimmed.indexOf("*/")
                if (end == -1) null else cleanComment(trimmed.substring(0, end + 2))
            }
            trimmed.startsWith("/*") -> {
                val end = trimmed.indexOf("*/")
                if (end == -1) null else cleanComment(trimmed.substring(0, end + 2))
            }
            trimmed.startsWith("//") -> trimmed.lineSequence().firstOrNull()?.removePrefix("//")?.trim()
            else -> null
        }
    }

    private fun removeLeadingComment(segment: String): String {
        val trimmed = segment.trimStart()
        return when {
            trimmed.startsWith("/**") || trimmed.startsWith("/*") -> {
                val end = trimmed.indexOf("*/")
                if (end == -1) trimmed else trimmed.substring(end + 2)
            }
            trimmed.startsWith("//") -> trimmed.substringAfter('\n', "")
            else -> trimmed
        }
    }

    private fun removeLeadingAnnotations(segment: String): String {
        var remaining = segment
        while (remaining.startsWith("@")) {
            var i = 1
            while (i < remaining.length && (remaining[i].isLetterOrDigit() || remaining[i] == '_' || remaining[i] == '.')) i++
            if (i < remaining.length && remaining[i] == '(') {
                val end = matchingDelimiter(remaining, i, '(', ')')
                if (end <= i) break
                i = end + 1
            }
            remaining = remaining.substring(i).trimStart()
        }
        return remaining
    }

    private fun cleanComment(rawComment: String): String {
        return rawComment
            .removePrefix("/**")
            .removePrefix("/*")
            .removeSuffix("*/")
            .lineSequence()
            .map { it.trim().removePrefix("*").trim() }
            .joinToString(" ")
            .replace(Regex("\\s+"), " ")
            .trim()
    }

    private fun findCallContext(ki: KotlinScriptEngine, code: String, position: Int): CallContext? {
        if (code.isBlank()) return null
        // PSI-based call context analysis requires the K1 compiler state
        val k1 = ki as? KotlinInterface ?: return null
        val project = k1.compiler.state.getCompilationState(k1.compilerConfig).environment.project
        val language = Language.findLanguageByID("kotlin") ?: return null
        val file = PsiFileFactory.getInstance(project).createFileFromText(language, code)
        val offsets = buildList {
            val bounded = position.coerceIn(0, code.length)
            add((bounded - 1).coerceAtLeast(0))
            if (bounded < code.length) add(bounded)
        }.distinct()

        offsets.forEach { offset ->
            val element = file.findElementAt(offset) ?: return@forEach
            val call = enclosingCallExpression(element, position) ?: return@forEach
            val calleeText = call.calleeExpression?.text?.trim().orEmpty()
            if (calleeText.isBlank()) return@forEach
            val namedArguments = call.valueArguments
                .mapNotNull { it.getArgumentName()?.asName?.asString() }
                .toSet()
            return CallContext(calleeText, namedArguments)
        }
        return null
    }

    private fun enclosingCallExpression(element: PsiElement, position: Int): KtCallExpression? {
        var current: PsiElement? = element
        while (current != null) {
            if (current is KtCallExpression) {
                val args = current.valueArgumentList
                if (args == null) {
                    current = current.parent
                    continue
                }
                val start = args.textRange.startOffset
                val end = args.textRange.endOffset
                if (position in start..end) return current
            }
            current = current.parent
        }
        return null
    }

    private fun skipWhitespace(text: String, start: Int): Int {
        var cursor = start
        while (cursor < text.length && text[cursor].isWhitespace()) cursor++
        return cursor
    }

    private fun matchingDelimiter(text: String, start: Int, open: Char, close: Char): Int {
        if (start !in text.indices || text[start] != open) return -1
        var depth = 0
        var inString = false
        var stringChar = '"'
        var inLineComment = false
        var inBlockComment = false
        var i = start
        while (i < text.length) {
            val ch = text[i]
            if (inLineComment) {
                if (ch == '\n') inLineComment = false
                i++
                continue
            }
            if (inBlockComment) {
                if (ch == '*' && i + 1 < text.length && text[i + 1] == '/') {
                    inBlockComment = false
                    i += 2
                } else {
                    i++
                }
                continue
            }
            if (inString) {
                if (ch == '\\' && i + 1 < text.length) {
                    i += 2
                    continue
                }
                if (ch == stringChar) inString = false
                i++
                continue
            }

            if (ch == '/' && i + 1 < text.length && text[i + 1] == '/') {
                inLineComment = true
                i += 2
                continue
            }
            if (ch == '/' && i + 1 < text.length && text[i + 1] == '*') {
                inBlockComment = true
                i += 2
                continue
            }
            if (ch == '"' || ch == '\'') {
                inString = true
                stringChar = ch
                i++
                continue
            }

            if (ch == open) depth++
            if (ch == close) {
                depth--
                if (depth == 0) return i
            }
            i++
        }
        return -1
    }

    private fun topLevelIndexOf(text: String, target: Char): Int {
        var parenDepth = 0
        var angleDepth = 0
        var braceDepth = 0
        var bracketDepth = 0
        var inString = false
        var stringChar = '"'
        var i = 0
        while (i < text.length) {
            val ch = text[i]
            if (inString) {
                if (ch == '\\' && i + 1 < text.length) {
                    i += 2
                    continue
                }
                if (ch == stringChar) inString = false
                i++
                continue
            }
            if (ch == '"' || ch == '\'') {
                inString = true
                stringChar = ch
                i++
                continue
            }
            when (ch) {
                '(' -> parenDepth++
                ')' -> if (parenDepth > 0) parenDepth--
                '<' -> angleDepth++
                '>' -> if (angleDepth > 0) angleDepth--
                '{' -> braceDepth++
                '}' -> if (braceDepth > 0) braceDepth--
                '[' -> bracketDepth++
                ']' -> if (bracketDepth > 0) bracketDepth--
            }
            if (ch == target && parenDepth == 0 && angleDepth == 0 && braceDepth == 0 && bracketDepth == 0) return i
            i++
        }
        return -1
    }

    private fun shortenType(type: String): String {
        val receiverFunctionMarker = "__afield_receiver_function_dot__"
        val receiverFunctionTypes = Regex("""((?:\b[_a-zA-Z$][_a-zA-Z0-9$]*\.)*)([_a-zA-Z$][_a-zA-Z0-9$]*)\.\(\)""")
        val protectedReceiverFunctions = type.replace(receiverFunctionTypes) {
            "${it.groupValues[2]}$receiverFunctionMarker()"
        }
        return protectedReceiverFunctions
            .replace("(\\b[_a-zA-Z$][_a-zA-Z0-9$]*\\b\\.)+".toRegex(), "")
            .replace(receiverFunctionMarker, ".")
    }
}