diff --git a/server/build.gradle.kts b/server/build.gradle.kts index 915dcc767..972fb1508 100644 --- a/server/build.gradle.kts +++ b/server/build.gradle.kts @@ -18,7 +18,13 @@ val applicationName = "kotlin-language-server" application { mainClass.set(serverMainClassName) description = "Code completions, diagnostics and more for Kotlin" - applicationDefaultJvmArgs = listOf("-DkotlinLanguageServer.version=$version") + applicationDefaultJvmArgs = listOf( + "-DkotlinLanguageServer.version=$version", + "-Xms256m", + "-Xmx1024m", + "-XX:+UseG1GC", + "-XX:+UseStringDeduplication" + ) applicationDistribution.into("bin") { filePermissions { unix("755".toInt(radix = 8)) } } } diff --git a/server/src/main/kotlin/org/javacs/kt/definition/GoToDefinition.kt b/server/src/main/kotlin/org/javacs/kt/definition/GoToDefinition.kt index 77c8a05de..909a2b973 100644 --- a/server/src/main/kotlin/org/javacs/kt/definition/GoToDefinition.kt +++ b/server/src/main/kotlin/org/javacs/kt/definition/GoToDefinition.kt @@ -23,7 +23,7 @@ import java.io.File import java.nio.file.Paths private val cachedTempFiles = mutableMapOf() -private val definitionPattern = Regex("(?:class|interface|object|fun)\\s+(\\w+)") +private val definitionPattern = Regex("(?:class|interface|object|fun|val|var)\\s+(\\w+)") fun goToDefinition( file: CompiledFile, diff --git a/server/src/main/kotlin/org/javacs/kt/externalsources/ClassContentProvider.kt b/server/src/main/kotlin/org/javacs/kt/externalsources/ClassContentProvider.kt index 9b3d6618b..ba814cb1a 100644 --- a/server/src/main/kotlin/org/javacs/kt/externalsources/ClassContentProvider.kt +++ b/server/src/main/kotlin/org/javacs/kt/externalsources/ClassContentProvider.kt @@ -25,7 +25,8 @@ class ClassContentProvider( ) { /** Maps recently used (source-)KLS-URIs to their source contents (e.g. decompiled code) and the file extension. */ private val cachedContents = object : LinkedHashMap>() { - override fun removeEldestEntry(eldest: MutableMap.MutableEntry>) = size > 5 + // Decompilation is expensive; larger cache reduces repeated decompilation overhead + override fun removeEldestEntry(eldest: MutableMap.MutableEntry>) = size > 50 } /** diff --git a/shared/src/main/kotlin/org/javacs/kt/SourceExclusions.kt b/shared/src/main/kotlin/org/javacs/kt/SourceExclusions.kt index de4a512c2..db738f2fd 100644 --- a/shared/src/main/kotlin/org/javacs/kt/SourceExclusions.kt +++ b/shared/src/main/kotlin/org/javacs/kt/SourceExclusions.kt @@ -16,7 +16,8 @@ class SourceExclusions( val excludedPatterns = (listOf( ".git", ".hg", ".svn", // Version control systems ".idea", ".idea_modules", ".vs", ".vscode", ".code-workspace", ".settings", // IDEs - "bazel-*", "bin", "build", "node_modules", "target", // Build systems + "bazel-*", "bin", "node_modules", "target", // Build systems + "build/classes", "build/libs", "build/tmp", "build/reports", "build/kotlin", "build/resources", // Gradle build outputs (but allow build/generated-src) ) + when { !scriptsConfig.enabled -> listOf("*.kts") !scriptsConfig.buildScriptsEnabled -> listOf("*.gradle.kts") diff --git a/shared/src/main/kotlin/org/javacs/kt/classpath/CachedClassPathResolver.kt b/shared/src/main/kotlin/org/javacs/kt/classpath/CachedClassPathResolver.kt index d81667deb..868987be6 100644 --- a/shared/src/main/kotlin/org/javacs/kt/classpath/CachedClassPathResolver.kt +++ b/shared/src/main/kotlin/org/javacs/kt/classpath/CachedClassPathResolver.kt @@ -55,47 +55,63 @@ internal class CachedClassPathResolver( ) : ClassPathResolver { override val resolverType: String get() = "Cached + ${wrapped.resolverType}" + // In-memory cache layer to avoid repeated DB transactions + private var inMemoryClassPathEntries: Set? = null + private var inMemoryBuildScriptEntries: Set? = null + private var inMemoryMetadata: ClasspathMetadata? = null + private var cachedClassPathEntries: Set - get() = transaction(db) { + get() = inMemoryClassPathEntries ?: transaction(db) { ClassPathCacheEntryEntity.all().map { ClassPathEntry( compiledJar = Paths.get(it.compiledJar), sourceJar = it.sourceJar?.let(Paths::get) ) }.toSet() - } - set(newEntries) = transaction(db) { - ClassPathCacheEntry.deleteAll() - newEntries.map { - ClassPathCacheEntryEntity.new { - compiledJar = it.compiledJar.toString() - sourceJar = it.sourceJar?.toString() + }.also { inMemoryClassPathEntries = it } + set(newEntries) { + inMemoryClassPathEntries = newEntries + transaction(db) { + ClassPathCacheEntry.deleteAll() + newEntries.map { + ClassPathCacheEntryEntity.new { + compiledJar = it.compiledJar.toString() + sourceJar = it.sourceJar?.toString() + } } } } private var cachedBuildScriptClassPathEntries: Set - get() = transaction(db) { BuildScriptClassPathCacheEntryEntity.all().map { Paths.get(it.jar) }.toSet() } - set(newEntries) = transaction(db) { - BuildScriptClassPathCacheEntry.deleteAll() - newEntries.map { BuildScriptClassPathCacheEntryEntity.new { jar = it.toString() } } + get() = inMemoryBuildScriptEntries ?: transaction(db) { + BuildScriptClassPathCacheEntryEntity.all().map { Paths.get(it.jar) }.toSet() + }.also { inMemoryBuildScriptEntries = it } + set(newEntries) { + inMemoryBuildScriptEntries = newEntries + transaction(db) { + BuildScriptClassPathCacheEntry.deleteAll() + newEntries.map { BuildScriptClassPathCacheEntryEntity.new { jar = it.toString() } } + } } - private var cachedClassPathMetadata - get() = transaction(db) { + private var cachedClassPathMetadata: ClasspathMetadata? + get() = inMemoryMetadata ?: transaction(db) { ClassPathMetadataCacheEntity.all().map { ClasspathMetadata( includesSources = it.includesSources, buildFileVersion = it.buildFileVersion ) }.firstOrNull() - } - set(newClassPathMetadata) = transaction(db) { - ClassPathMetadataCache.deleteAll() - val newClassPathMetadataRow = newClassPathMetadata ?: ClasspathMetadata() - ClassPathMetadataCacheEntity.new { - includesSources = newClassPathMetadataRow.includesSources - buildFileVersion = newClassPathMetadataRow.buildFileVersion + }.also { inMemoryMetadata = it } + set(newClassPathMetadata) { + inMemoryMetadata = newClassPathMetadata + transaction(db) { + ClassPathMetadataCache.deleteAll() + val newClassPathMetadataRow = newClassPathMetadata ?: ClasspathMetadata() + ClassPathMetadataCacheEntity.new { + includesSources = newClassPathMetadataRow.includesSources + buildFileVersion = newClassPathMetadataRow.buildFileVersion + } } } diff --git a/shared/src/main/kotlin/org/javacs/kt/database/DatabaseService.kt b/shared/src/main/kotlin/org/javacs/kt/database/DatabaseService.kt index 0f80f73b4..620fd17c8 100644 --- a/shared/src/main/kotlin/org/javacs/kt/database/DatabaseService.kt +++ b/shared/src/main/kotlin/org/javacs/kt/database/DatabaseService.kt @@ -62,7 +62,14 @@ class DatabaseService { private fun getDbFromFile(storagePath: Path?): Database? { return storagePath?.let { if (Files.isDirectory(it)) { - Database.connect("jdbc:sqlite:${getDbFilePath(it)}") + Database.connect("jdbc:sqlite:${getDbFilePath(it)}").also { db -> + transaction(db) { + // WAL mode: better concurrent read performance + exec("PRAGMA journal_mode = WAL") + // Reduce fsync calls (safe with WAL mode) + exec("PRAGMA synchronous = NORMAL") + } + } } else { null }