Skip to content

Commit a80fac9

Browse files
committed
Setup CachedClassPathResolver
1 parent de778f6 commit a80fac9

File tree

6 files changed

+100
-88
lines changed

6 files changed

+100
-88
lines changed
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package org.javacs.kt.classpath
2+
3+
import kotlinx.serialization.Serializable
4+
import org.javacs.kt.storage.SetOfPathsAsStringSerializer
5+
import org.javacs.kt.storage.Storage
6+
import java.nio.file.Path
7+
8+
/** A classpath resolver that caches another resolver */
9+
internal class CachedClassPathResolver(private val wrapped: ClassPathResolver, private val storage: Storage?) : ClassPathResolver {
10+
override val resolverType: String get() = "Cached + ${wrapped.resolverType}"
11+
12+
private var cachedClassPath: ClasspathCache? = storage?.getObject("cachedClasspath")
13+
private var cachedBuildScriptClassPath: Set<Path>? = storage?.getObject("cachedBuildScriptClassPath", SetOfPathsAsStringSerializer)
14+
15+
override val classpath: Set<ClassPathEntry> get() {
16+
cachedClassPath?.let { if (!dependenciesChanged()) return it.classpathEntries }
17+
18+
val newClasspath = wrapped.classpath
19+
updateClasspathCache(ClasspathCache(newClasspath, false))
20+
21+
return newClasspath
22+
}
23+
24+
override val buildScriptClasspath: Set<Path> get() {
25+
cachedBuildScriptClassPath?.let { if (!dependenciesChanged()) return it }
26+
27+
val newBuildScriptClasspath = wrapped.buildScriptClasspath
28+
29+
updateBuildScriptClasspathCache(newBuildScriptClasspath)
30+
return newBuildScriptClasspath
31+
}
32+
33+
override val classpathWithSources: Set<ClassPathEntry> get() {
34+
cachedClassPath?.let { if (!dependenciesChanged() && it.includesSources) return it.classpathEntries }
35+
36+
val newClasspath = wrapped.classpathWithSources
37+
updateClasspathCache(ClasspathCache(newClasspath, true))
38+
39+
return newClasspath
40+
}
41+
42+
override fun getCurrentBuildFileVersion(): Long = wrapped.getCurrentBuildFileVersion()
43+
44+
private fun updateClasspathCache(newClasspathCache: ClasspathCache) {
45+
storage?.setObject("cachedClasspath", newClasspathCache)
46+
storage?.setObject("cachedBuildFileVersion", getCurrentBuildFileVersion())
47+
cachedClassPath = newClasspathCache
48+
}
49+
50+
private fun updateBuildScriptClasspathCache(newClasspath: Set<Path>) {
51+
storage?.setObject("cachedBuildScriptClassPath", newClasspath, SetOfPathsAsStringSerializer)
52+
storage?.setObject("cachedBuildFileVersion", getCurrentBuildFileVersion())
53+
cachedBuildScriptClassPath = newClasspath
54+
}
55+
56+
private fun dependenciesChanged(): Boolean {
57+
return storage?.getObject<Long>("cachedBuildFileVersion") ?: 0 < wrapped.getCurrentBuildFileVersion()
58+
}
59+
}
60+
61+
@Serializable
62+
private data class ClasspathCache(
63+
val classpathEntries: Set<ClassPathEntry>,
64+
val includesSources: Boolean
65+
)

shared/src/main/kotlin/org/javacs/kt/classpath/ClassPathResolver.kt

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package org.javacs.kt.classpath
22

33
import org.javacs.kt.LOG
44
import java.nio.file.Path
5+
import kotlin.math.max
56

67
/** A source for creating class paths */
78
interface ClassPathResolver {
@@ -28,6 +29,8 @@ interface ClassPathResolver {
2829

2930
val classpathWithSources: Set<ClassPathEntry> get() = classpath
3031

32+
fun getCurrentBuildFileVersion(): Long = 1L
33+
3134
companion object {
3235
/** A default empty classpath implementation */
3336
val empty = object : ClassPathResolver {
@@ -54,7 +57,8 @@ internal class UnionClassPathResolver(val lhs: ClassPathResolver, val rhs: Class
5457
override val classpathOrEmpty get() = lhs.classpathOrEmpty + rhs.classpathOrEmpty
5558
override val buildScriptClasspath get() = lhs.buildScriptClasspath + rhs.buildScriptClasspath
5659
override val buildScriptClasspathOrEmpty get() = lhs.buildScriptClasspathOrEmpty + rhs.buildScriptClasspathOrEmpty
57-
override val classpathWithSources = lhs.classpathWithSources + rhs.classpathWithSources
60+
override val classpathWithSources get() = lhs.classpathWithSources + rhs.classpathWithSources
61+
override fun getCurrentBuildFileVersion(): Long = max(lhs.getCurrentBuildFileVersion(), rhs.getCurrentBuildFileVersion())
5862
}
5963

6064
internal class FirstNonEmptyClassPathResolver(val lhs: ClassPathResolver, val rhs: ClassPathResolver) : ClassPathResolver {
@@ -63,5 +67,6 @@ internal class FirstNonEmptyClassPathResolver(val lhs: ClassPathResolver, val rh
6367
override val classpathOrEmpty get() = lhs.classpathOrEmpty.takeIf { it.isNotEmpty() } ?: rhs.classpathOrEmpty
6468
override val buildScriptClasspath get() = lhs.buildScriptClasspath.takeIf { it.isNotEmpty() } ?: rhs.buildScriptClasspath
6569
override val buildScriptClasspathOrEmpty get() = lhs.buildScriptClasspathOrEmpty.takeIf { it.isNotEmpty() } ?: rhs.buildScriptClasspathOrEmpty
66-
override val classpathWithSources = lhs.classpathWithSources.takeIf { it.isNotEmpty() } ?: rhs.classpathWithSources
70+
override val classpathWithSources get() = lhs.classpathWithSources.takeIf { it.isNotEmpty() } ?: rhs.classpathWithSources
71+
override fun getCurrentBuildFileVersion(): Long = max(lhs.getCurrentBuildFileVersion(), rhs.getCurrentBuildFileVersion())
6772
}

shared/src/main/kotlin/org/javacs/kt/classpath/DefaultClassPathResolver.kt

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,26 @@ import java.nio.file.PathMatcher
77
import java.nio.file.FileSystems
88

99
fun defaultClassPathResolver(workspaceRoots: Collection<Path>, storage: Storage?): ClassPathResolver =
10-
WithStdlibResolver(
11-
ShellClassPathResolver.global(workspaceRoots.firstOrNull())
12-
.or(workspaceRoots.asSequence().flatMap { workspaceResolvers(it, storage) }.joined)
13-
).or(BackupClassPathResolver)
10+
CachedClassPathResolver(
11+
WithStdlibResolver(
12+
ShellClassPathResolver.global(workspaceRoots.firstOrNull())
13+
.or(workspaceRoots.asSequence().flatMap { workspaceResolvers(it) }.joined)
14+
).or(BackupClassPathResolver),
15+
storage
16+
)
1417

1518
/** Searches the workspace for all files that could provide classpath info. */
16-
private fun workspaceResolvers(workspaceRoot: Path, storage: Storage?): Sequence<ClassPathResolver> {
19+
private fun workspaceResolvers(workspaceRoot: Path): Sequence<ClassPathResolver> {
1720
val ignored: List<PathMatcher> = ignoredPathPatterns(workspaceRoot, workspaceRoot.resolve(".gitignore"))
18-
return folderResolvers(workspaceRoot, ignored, storage).asSequence()
21+
return folderResolvers(workspaceRoot, ignored).asSequence()
1922
}
2023

2124
/** Searches the folder for all build-files. */
22-
private fun folderResolvers(root: Path, ignored: List<PathMatcher>, storage: Storage?): Collection<ClassPathResolver> =
25+
private fun folderResolvers(root: Path, ignored: List<PathMatcher>): Collection<ClassPathResolver> =
2326
root.toFile()
2427
.walk()
2528
.onEnter { file -> ignored.none { it.matches(file.toPath()) } }
26-
.mapNotNull { asClassPathProvider(it.toPath(), storage) }
29+
.mapNotNull { asClassPathProvider(it.toPath()) }
2730
.toList()
2831

2932
/** Tries to read glob patterns from a gitignore. */
@@ -48,7 +51,7 @@ private fun ignoredPathPatterns(root: Path, gitignore: Path): List<PathMatcher>
4851
?: emptyList()
4952

5053
/** Tries to create a classpath resolver from a file using as many sources as possible */
51-
private fun asClassPathProvider(path: Path, storage: Storage?): ClassPathResolver? =
52-
MavenClassPathResolver.maybeCreate(path, storage)
53-
?: GradleClassPathResolver.maybeCreate(path, storage)
54+
private fun asClassPathProvider(path: Path): ClassPathResolver? =
55+
MavenClassPathResolver.maybeCreate(path)
56+
?: GradleClassPathResolver.maybeCreate(path)
5457
?: ShellClassPathResolver.maybeCreate(path)

shared/src/main/kotlin/org/javacs/kt/classpath/GradleClassPathResolver.kt

Lines changed: 6 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
package org.javacs.kt.classpath
22

33
import org.javacs.kt.LOG
4-
import org.javacs.kt.storage.SetOfPathsAsStringSerializer
5-
import org.javacs.kt.storage.Storage
64
import org.javacs.kt.util.execAndReadStdoutAndStderr
75
import org.javacs.kt.util.KotlinLSException
86
import org.javacs.kt.util.isOSWindows
@@ -12,65 +10,37 @@ import java.nio.file.Files
1210
import java.nio.file.Path
1311
import java.nio.file.Paths
1412

15-
internal class GradleClassPathResolver(private val path: Path, private val includeKotlinDSL: Boolean, private val storage: Storage?): ClassPathResolver {
13+
internal class GradleClassPathResolver(private val path: Path, private val includeKotlinDSL: Boolean): ClassPathResolver {
1614
override val resolverType: String = "Gradle"
1715
private val projectDirectory: Path get() = path.parent
1816

19-
private var cachedClassPath: Set<ClassPathEntry>? = storage?.getObject("gradleClassPath")
20-
private var cachedBuildScriptClassPath: Set<Path>? = storage?.getObject("gradleBuildScriptClassPath", SetOfPathsAsStringSerializer)
21-
2217
override val classpath: Set<ClassPathEntry> get() {
23-
cachedClassPath?.let { if (!dependenciesChanged()) return it }
24-
2518
val scripts = listOf("projectClassPathFinder.gradle")
2619
val tasks = listOf("kotlinLSPProjectDeps")
2720

28-
val newClasspath = readDependenciesViaGradleCLI(projectDirectory, scripts, tasks)
21+
return readDependenciesViaGradleCLI(projectDirectory, scripts, tasks)
2922
.apply { if (isNotEmpty()) LOG.info("Successfully resolved dependencies for '${projectDirectory.fileName}' using Gradle") }
3023
.map { ClassPathEntry(it, null) }.toSet()
31-
32-
updateClasspathCache(newClasspath)
33-
return newClasspath
3424
}
3525
override val buildScriptClasspath: Set<Path> get() {
3626
return if (includeKotlinDSL) {
37-
cachedBuildScriptClassPath?.let { if (!dependenciesChanged()) return it }
38-
3927
val scripts = listOf("kotlinDSLClassPathFinder.gradle")
4028
val tasks = listOf("kotlinLSPKotlinDSLDeps")
4129

42-
val newBuildScriptClasspath = readDependenciesViaGradleCLI(projectDirectory, scripts, tasks)
30+
return readDependenciesViaGradleCLI(projectDirectory, scripts, tasks)
4331
.apply { if (isNotEmpty()) LOG.info("Successfully resolved build script dependencies for '${projectDirectory.fileName}' using Gradle") }
44-
45-
updateBuildScriptClasspathCache(newBuildScriptClasspath)
46-
return newBuildScriptClasspath
4732
} else {
4833
emptySet()
4934
}
5035
}
5136

52-
private fun updateClasspathCache(newClasspath: Set<ClassPathEntry>) {
53-
storage?.setObject("gradleClasspath", newClasspath)
54-
storage?.setObject("buildScriptFileVersion", getCurrentBuildScriptFileVersion())
55-
cachedClassPath = newClasspath
56-
}
57-
58-
private fun updateBuildScriptClasspathCache(newClasspath: Set<Path>) {
59-
storage?.setObject("gradleBuildScriptClasspath", newClasspath, SetOfPathsAsStringSerializer)
60-
storage?.setObject("buildScriptFileVersion", getCurrentBuildScriptFileVersion())
61-
cachedBuildScriptClassPath = newClasspath
62-
}
63-
64-
private fun dependenciesChanged(): Boolean =
65-
storage?.getObject<Long>("buildScriptFileVersion") ?: 0 < getCurrentBuildScriptFileVersion()
66-
67-
private fun getCurrentBuildScriptFileVersion(): Long = path.toFile().lastModified()
37+
override fun getCurrentBuildFileVersion(): Long = path.toFile().lastModified()
6838

6939
companion object {
7040
/** Create a Gradle resolver if a file is a pom. */
71-
fun maybeCreate(file: Path, storage: Storage?): GradleClassPathResolver? =
41+
fun maybeCreate(file: Path): GradleClassPathResolver? =
7242
file.takeIf { file.endsWith("build.gradle") || file.endsWith("build.gradle.kts") }
73-
?.let { GradleClassPathResolver(it, includeKotlinDSL = file.toString().endsWith(".kts"), storage) }
43+
?.let { GradleClassPathResolver(it, includeKotlinDSL = file.toString().endsWith(".kts")) }
7444
}
7545
}
7646

shared/src/main/kotlin/org/javacs/kt/classpath/MavenClassPathResolver.kt

Lines changed: 6 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,19 @@
11
package org.javacs.kt.classpath
22

3-
import kotlinx.serialization.Serializable
43
import org.javacs.kt.LOG
5-
import org.javacs.kt.storage.Storage
64
import org.javacs.kt.util.findCommandOnPath
75
import org.javacs.kt.util.execAndReadStdoutAndStderr
86
import java.nio.file.Path
97
import java.nio.file.Files
108
import java.io.File
119

1210
/** Resolver for reading maven dependencies */
13-
internal class MavenClassPathResolver private constructor(private val pom: Path, private val storage: Storage?) : ClassPathResolver {
11+
internal class MavenClassPathResolver private constructor(private val pom: Path) : ClassPathResolver {
1412
private var artifacts: Set<Artifact>? = null
1513

1614
override val resolverType: String = "Maven"
1715

18-
private var cachedClassPath: MavenClasspathCache? = storage?.getObject("mavenClasspath")
19-
2016
override val classpath: Set<ClassPathEntry> get() {
21-
cachedClassPath?.let { if (!dependenciesChanged()) return it.classpathEntries }
22-
2317
val dependenciesOutput = generateMavenDependencyList(pom)
2418
val artifacts = readMavenDependencyList(dependenciesOutput)
2519

@@ -33,16 +27,10 @@ internal class MavenClassPathResolver private constructor(private val pom: Path,
3327

3428
this.artifacts = artifacts
3529

36-
val newClasspath = artifacts.mapNotNull { findMavenArtifact(it, false)?.let { it1 -> ClassPathEntry(it1, null) } }.toSet()
37-
38-
updateClasspathCache(MavenClasspathCache(newClasspath, false))
39-
40-
return newClasspath
30+
return artifacts.mapNotNull { findMavenArtifact(it, false)?.let { it1 -> ClassPathEntry(it1, null) } }.toSet()
4131
}
4232

4333
override val classpathWithSources: Set<ClassPathEntry> get() {
44-
cachedClassPath?.let { if (!dependenciesChanged() && it.includesSources) return it.classpathEntries }
45-
4634
// Fetch artifacts if not yet present.
4735
var artifacts: Set<Artifact>
4836
if (this.artifacts != null) {
@@ -59,33 +47,19 @@ internal class MavenClassPathResolver private constructor(private val pom: Path,
5947
artifacts = readMavenDependencyListWithSources(artifacts, sourcesOutput)
6048

6149
Files.deleteIfExists(sourcesOutput)
62-
val newClasspath = artifacts.mapNotNull {
50+
return artifacts.mapNotNull {
6351
findMavenArtifact(it, false)?.let {
6452
it1 -> ClassPathEntry(it1, if (it.source) findMavenArtifact(it, it.source) else null)
6553
}
6654
}.toSet()
67-
68-
updateClasspathCache(MavenClasspathCache(newClasspath, true))
69-
70-
return newClasspath
71-
}
72-
73-
private fun updateClasspathCache(newClasspathCache: MavenClasspathCache) {
74-
storage?.setObject("mavenClasspath", newClasspathCache)
75-
storage?.setObject("mavenPomFileVersion", getCurrentPomFileVersion())
76-
cachedClassPath = newClasspathCache
7755
}
7856

79-
private fun dependenciesChanged(): Boolean {
80-
return storage?.getObject<Long>("mavenPomFileVersion") ?: 0 < getCurrentPomFileVersion()
81-
}
82-
83-
private fun getCurrentPomFileVersion(): Long = pom.toFile().lastModified()
57+
override fun getCurrentBuildFileVersion(): Long = pom.toFile().lastModified()
8458

8559
companion object {
8660
/** Create a maven resolver if a file is a pom. */
87-
fun maybeCreate(file: Path, storage: Storage?): MavenClassPathResolver? =
88-
file.takeIf { it.endsWith("pom.xml") }?.let { MavenClassPathResolver(it, storage) }
61+
fun maybeCreate(file: Path): MavenClassPathResolver? =
62+
file.takeIf { it.endsWith("pom.xml") }?.let { MavenClassPathResolver(it) }
8963
}
9064
}
9165

@@ -231,9 +205,3 @@ data class Artifact(
231205
) {
232206
override fun toString() = "$group:$artifact:$version"
233207
}
234-
235-
@Serializable
236-
private data class MavenClasspathCache(
237-
val classpathEntries: Set<ClassPathEntry>,
238-
val includesSources: Boolean
239-
)

shared/src/main/kotlin/org/javacs/kt/classpath/WithStdlibResolver.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ internal class WithStdlibResolver(private val wrapped: ClassPathResolver) : Clas
99
override val classpathOrEmpty: Set<ClassPathEntry> get() = wrapWithStdlibEntries(wrapped.classpathOrEmpty)
1010
override val buildScriptClasspath: Set<Path> get() = wrapWithStdlib(wrapped.buildScriptClasspath)
1111
override val buildScriptClasspathOrEmpty: Set<Path> get() = wrapWithStdlib(wrapped.buildScriptClasspathOrEmpty)
12-
override val classpathWithSources: Set<ClassPathEntry> = wrapWithStdlibEntries(wrapped.classpathWithSources)
12+
override val classpathWithSources: Set<ClassPathEntry> get() = wrapWithStdlibEntries(wrapped.classpathWithSources)
13+
override fun getCurrentBuildFileVersion(): Long = wrapped.getCurrentBuildFileVersion()
1314
}
1415

1516
private fun wrapWithStdlibEntries(paths: Set<ClassPathEntry>): Set<ClassPathEntry> {

0 commit comments

Comments
 (0)