Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class KtFileMetadataIndex private constructor(
SQLiteIndex(
descriptor = KtFileMetadataDescriptor,
context = context,
dbName = null,
dbName = dbName,
name = "kt-file-metadata",
)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,14 @@ package com.itsaky.androidide.lsp.kotlin

import com.itsaky.androidide.app.BaseApplication
import com.itsaky.androidide.app.configuration.IJdkDistributionProvider
import com.itsaky.androidide.eventbus.events.BuildCompletedEvent
import com.itsaky.androidide.eventbus.events.editor.DocumentChangeEvent
import com.itsaky.androidide.eventbus.events.editor.DocumentCloseEvent
import com.itsaky.androidide.eventbus.events.editor.DocumentOpenEvent
import com.itsaky.androidide.eventbus.events.editor.DocumentSaveEvent
import com.itsaky.androidide.eventbus.events.file.FileCreationEvent
import com.itsaky.androidide.eventbus.events.file.FileDeletionEvent
import com.itsaky.androidide.eventbus.events.file.FileRenameEvent
import com.itsaky.androidide.lsp.api.ILanguageClient
import com.itsaky.androidide.lsp.api.ILanguageServer
import com.itsaky.androidide.lsp.api.IServerSettings
Expand Down Expand Up @@ -55,6 +59,7 @@ import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch
import org.appdevforall.codeonthego.indexing.jvm.JvmLibraryIndexingService
import org.appdevforall.codeonthego.indexing.jvm.JvmSymbolIndex
import org.appdevforall.codeonthego.indexing.jvm.KtFileMetadataIndex
Expand Down Expand Up @@ -312,4 +317,87 @@ class KotlinLanguageServer : ILanguageServer {
compiler?.compilationEnvironmentFor(event.savedFile)
?.onFileSaved(event.savedFile)
}

@Subscribe
@Suppress("unused")
fun onBuildCompleted(event: BuildCompletedEvent) {
compiler?.refreshSources()
}

@Subscribe
@Suppress("unused")
fun onFileCreated(event: FileCreationEvent) {
val path = event.file.toPath()
if (!DocumentUtils.isKotlinFile(path)) {
return
}

scope.launch {
compiler?.compilationEnvironmentFor(path)
?.ktSymbolIndex
?.submitForIndexing(path)
}
}

@Subscribe
@Suppress("unused")
fun onFileDeleted(event: FileDeletionEvent) {
val path = event.file.toPath()
if (!DocumentUtils.isKotlinFile(path)) {
return
}

scope.launch {
compiler?.compilationEnvironmentFor(path)
?.ktSymbolIndex
?.removeFromIndex(path)
}
}

@Subscribe
@Suppress("unused")
fun onFileRenamed(event: FileRenameEvent) {
val fromPath = event.file.toPath()
val toPath = event.newFile.toPath()

scope.launch {
val oldIsKotlinFile = DocumentUtils.isKotlinFile(fromPath)
val newIsKotlinFile = DocumentUtils.isKotlinFile(toPath)

if (!oldIsKotlinFile && newIsKotlinFile) {
// only the new file is a Kotlin file
// so just submit it for indexing
compiler?.compilationEnvironmentFor(toPath)
?.ktSymbolIndex
?.submitForIndexing(toPath)
return@launch
}

if (oldIsKotlinFile && !newIsKotlinFile) {
// only the old file was a Kotlin file
// so just remove it from the index
compiler?.compilationEnvironmentFor(fromPath)
?.ktSymbolIndex
?.removeFromIndex(fromPath)
return@launch
}

val fromKind = runCatching { compiler?.compilationKindFor(fromPath) }.getOrNull()
val toKind = runCatching { compiler?.compilationKindFor(toPath) }.getOrNull()
val fromEnv = fromKind?.let { compiler?.compilationEnvironmentFor(it) }
val toEnv = toKind?.let { compiler?.compilationEnvironmentFor(it) }

if (fromKind != null && fromEnv == toEnv && toEnv != null) {
// file was renamed within the same compilation environment
toEnv.ktSymbolIndex.onFileMoved(fromPath, toPath)
return@launch
}

// file may have been moved from one compilation environment to another
// remove from old env's index
// and submit to the new env for indexing
fromEnv?.ktSymbolIndex?.removeFromIndex(fromPath)
toEnv?.ktSymbolIndex?.submitForIndexing(toPath)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ import com.itsaky.androidide.lsp.kotlin.compiler.services.KtLspService
import com.itsaky.androidide.lsp.kotlin.compiler.services.ProjectStructureProvider
import com.itsaky.androidide.lsp.kotlin.compiler.services.WriteAccessGuard
import com.itsaky.androidide.lsp.kotlin.compiler.services.latestLanguageVersionSettings
import com.itsaky.androidide.utils.KeyedDebouncingAction
import com.itsaky.androidide.lsp.kotlin.diagnostic.collectDiagnosticsFor
import com.itsaky.androidide.lsp.kotlin.utils.SymbolVisibilityChecker
import com.itsaky.androidide.projects.FileManager
import com.itsaky.androidide.projects.api.Workspace
import com.itsaky.androidide.utils.KeyedDebouncingAction
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
Expand Down Expand Up @@ -110,6 +110,7 @@ import kotlin.time.Duration.Companion.milliseconds
@OptIn(K1Deprecation::class)
internal class CompilationEnvironment(
name: String,
val kind: CompilationKind,
workspace: Workspace,
val ktProject: KotlinProjectModel,
val intellijPluginRoot: Path,
Expand Down Expand Up @@ -179,6 +180,7 @@ internal class CompilationEnvironment(

val ktSymbolIndex by lazy {
KtSymbolIndex(
kind = kind,
project = project,
modules = modules,
fileIndex = requireFileIndex,
Expand Down Expand Up @@ -397,6 +399,12 @@ internal class CompilationEnvironment(
}
}

fun refreshSources() {
ktSymbolIndex.refreshSources()
// TODO: Should also update/notify Java file services about possibly changed Java files
// But that's a bit problematic right now, scheduled for later
}

fun openFileIfNeeded(path: Path) {
ktSymbolIndex.getOpenedKtFile(path)
?: onFileOpen(path)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,37 @@
package com.itsaky.androidide.lsp.kotlin.compiler

import java.nio.file.Path
import kotlin.io.path.extension

/**
* The kind of compilation being performed in a [Compiler].
*/
enum class CompilationKind {
sealed interface CompilationKind {

/**
* The types of files this compilation kind accepts.
*/
val fileExtensions: List<String>

/**
* Whether this compilation kind accepts the given file path. The default
* implementation simply checks the accepted [fileExtensions].
*/
fun acceptsFile(path: Path): Boolean {
return path.extension in fileExtensions
}

/**
* The default compilation kind. Mostly used for normal Kotlin source files.
*/
Default,
data object Default : CompilationKind {
override val fileExtensions = listOf("kt")
}

/**
* Compilation kind for compiling Kotlin scripts.
*/
Script,
data object Script : CompilationKind {
override val fileExtensions = listOf("kts")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ internal class Compiler(
init {
defaultCompilationEnv = CompilationEnvironment(
name = "default",
kind = CompilationKind.Default,
workspace = workspace,
ktProject = projectModel,
intellijPluginRoot = intellijPluginRoot,
Expand All @@ -53,14 +54,19 @@ internal class Compiler(
VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.FILE_PROTOCOL)
}

fun refreshSources() {
defaultCompilationEnv.refreshSources()
}

fun updateLanguageClient(client: ILanguageClient?) {
defaultCompilationEnv.languageClient = client
// TODO: update client for script env once we have one
}

fun compilationKindFor(file: Path): CompilationKind {
// TODO: This should return a different environment for Kotlin script files
return CompilationKind.Default
if (CompilationKind.Default.acceptsFile(file)) return CompilationKind.Default
if (CompilationKind.Script.acceptsFile(file)) return CompilationKind.Script
throw IllegalStateException("Cannot get compilation kind for file: ${file.pathString}. It may not be supported.")
}

fun compilationEnvironmentFor(file: Path): CompilationEnvironment? {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ package com.itsaky.androidide.lsp.kotlin.compiler.index

import org.jetbrains.kotlin.com.intellij.openapi.vfs.VirtualFile
import org.jetbrains.kotlin.psi.KtFile
import java.nio.file.Path

internal sealed interface IndexCommand {
data object Stop : IndexCommand
data object SourceScanningComplete: IndexCommand
data object IndexingComplete: IndexCommand
data class ScanSourceFile(val vf: VirtualFile): IndexCommand
data class IndexModifiedFile(val ktFile: KtFile): IndexCommand {

}
data class IndexModifiedFile(val ktFile: KtFile): IndexCommand
data class IndexSourceFile(val vf: VirtualFile): IndexCommand
data class RemoveFromIndex(val path: Path): IndexCommand
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@ package com.itsaky.androidide.lsp.kotlin.compiler.index
import com.itsaky.androidide.lsp.kotlin.compiler.CompilationEnvironment
import com.itsaky.androidide.lsp.kotlin.compiler.modules.backingFilePath
import com.itsaky.androidide.lsp.kotlin.compiler.read
import com.itsaky.androidide.lsp.kotlin.utils.toNioPathOrNull
import com.itsaky.androidide.progress.ICancelChecker
import com.itsaky.androidide.utils.KeyedDebouncingAction
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.isActive
import org.appdevforall.codeonthego.indexing.jvm.JvmSymbolIndex
import org.appdevforall.codeonthego.indexing.jvm.KtFileMetadata
import org.appdevforall.codeonthego.indexing.jvm.KtFileMetadataIndex
Expand All @@ -14,6 +17,7 @@ import org.jetbrains.kotlin.com.intellij.psi.PsiManager
import org.jetbrains.kotlin.psi.KtFile
import org.slf4j.LoggerFactory
import java.nio.file.Path
import kotlin.io.path.pathString

internal class IndexWorker(
private val project: Project,
Expand Down Expand Up @@ -42,7 +46,7 @@ internal class IndexWorker(
operator fun component2() = ktFile
}

suspend fun start() {
suspend fun start() = coroutineScope {
var scanCount = 0
var sourceIndexCount = 0

Expand All @@ -55,17 +59,23 @@ internal class IndexWorker(
sourceIndexCount++
}

while (true) {
when (val command = queue.take()) {
while (isActive) {
when (val cmd = queue.take()) {
is IndexCommand.RemoveFromIndex -> {
val filePath = cmd.path.pathString
fileIndex.remove(filePath)
sourceIndex.removeBySource(filePath)
}
Comment thread
dara-abijo-adfa marked this conversation as resolved.

is IndexCommand.IndexSourceFile -> {
if (command.vf.fileSystem.protocol != "file") {
logger.warn("Unknown source file protocol: {}", command.vf.path)
if (cmd.vf.fileSystem.protocol != "file") {
logger.warn("Unknown source file protocol: {}", cmd.vf.path)
continue
}

val ktFile = project.read {
PsiManager.getInstance(project)
.findFile(command.vf) as? KtFile
.findFile(cmd.vf) as? KtFile
}

if (ktFile == null) {
Expand All @@ -87,8 +97,8 @@ internal class IndexWorker(
is IndexCommand.IndexModifiedFile -> {
modifiedFileIndexer.schedule(
ModFileIndexKey(
command.ktFile.backingFilePath!!,
command.ktFile
cmd.ktFile.backingFilePath!!,
cmd.ktFile
)
)
}
Expand All @@ -103,7 +113,7 @@ internal class IndexWorker(

is IndexCommand.ScanSourceFile -> {
val ktFile = project.read {
PsiManager.getInstance(project).findFile(command.vf) as? KtFile
PsiManager.getInstance(project).findFile(cmd.vf) as? KtFile
}
?: continue

Expand Down
Loading
Loading