Skip to content

Commit 1f089cc

Browse files
committed
Manage Java source path in CompilerClassPath
1 parent a4fad97 commit 1f089cc

File tree

7 files changed

+132
-88
lines changed

7 files changed

+132
-88
lines changed

server/src/main/kotlin/org/javacs/kt/Compiler.kt

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,8 @@ import org.jetbrains.kotlin.config.ApiVersion
2929
import org.jetbrains.kotlin.config.LanguageVersion
3030
import org.jetbrains.kotlin.container.ComponentProvider
3131
import org.jetbrains.kotlin.container.get
32-
import org.jetbrains.kotlin.context.ModuleContext
3332
import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
34-
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
3533
import org.jetbrains.kotlin.idea.KotlinLanguage
36-
import org.jetbrains.kotlin.load.java.JavaClassesTracker
3734
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil
3835
import org.jetbrains.kotlin.psi.*
3936
import org.jetbrains.kotlin.resolve.BindingContext
@@ -43,7 +40,6 @@ import org.jetbrains.kotlin.resolve.TopDownAnalysisMode
4340
import org.jetbrains.kotlin.resolve.calls.components.InferenceSession
4441
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
4542
import org.jetbrains.kotlin.resolve.extensions.ExtraImportsProviderExtension
46-
import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisHandlerExtension
4743
import org.jetbrains.kotlin.resolve.lazy.declarations.FileBasedDeclarationProviderFactory
4844
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
4945
import org.jetbrains.kotlin.scripting.compiler.plugin.ScriptingCompilerConfigurationComponentRegistrar
@@ -91,7 +87,7 @@ private val GRADLE_DSL_DEPENDENCY_PATTERN = Regex("^gradle-(?:kotlin-dsl|core).*
9187
* files and expressions.
9288
*/
9389
private class CompilationEnvironment(
94-
workspaceRoots: Set<Path>,
90+
javaSourcePath: Set<Path>,
9591
classPath: Set<Path>
9692
) : Closeable {
9793
private val disposable = Disposer.newDisposable()
@@ -118,10 +114,9 @@ private class CompilationEnvironment(
118114
put(CommonConfigurationKeys.LANGUAGE_VERSION_SETTINGS, languageVersionSettings)
119115
put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, LoggingMessageCollector)
120116
add(ComponentRegistrar.PLUGIN_COMPONENT_REGISTRARS, ScriptingCompilerConfigurationComponentRegistrar())
121-
addJvmClasspathRoots(classPath.map { it.toFile() })
122117

123-
// HACK: Use the SourcePath instead
124-
addJavaSourceRoots(workspaceRoots.flatMap { it.toFile().walk().filter { it.name.endsWith(".java") }.toList() })
118+
addJvmClasspathRoots(classPath.map { it.toFile() })
119+
addJavaSourceRoots(javaSourcePath.map { it.toFile() })
125120

126121
// Setup script templates (e.g. used by Gradle's Kotlin DSL)
127122
val scriptDefinitions: MutableList<ScriptDefinition> = mutableListOf(ScriptDefinition.getDefault(defaultJvmScriptingHostConfiguration))
@@ -172,7 +167,7 @@ private class CompilationEnvironment(
172167
// TODO: Update VFS source files dynamically inside the environment using updateClasspath
173168
// and use VirtualFileManager.getInstance instead of using a custom SourcePath/SourceFile mechanism.
174169
// Documentation on the VFS is available here: https://www.jetbrains.org/intellij/sdk/docs/basics/architectural_overview/virtual_file.html
175-
// Virtual files can be manipulated using input-/output-stream-readers. Note that each virtual file has an associated charset.
170+
// Virtual files can be manipulated using input-/output-stream-writers. Note that each virtual file has an associated charset.
176171

177172
// environment.addKotlinSourceRoots(workspaceRoots.map { it.toFile() })
178173

@@ -318,18 +313,7 @@ class Compiler(workspaceRoots: Set<Path>, classPath: Set<Path>, buildScriptClass
318313
compileLock.withLock {
319314
val compileEnv = compileEnvironmentFor(kind)
320315
val (container, trace) = compileEnv.createContainer(sourcePath)
321-
val module = container.get<ModuleDescriptor>()
322-
val moduleContext = container.get<ModuleContext>()
323-
val project = compileEnv.environment.project
324-
val analysisHandlerExtensions = AnalysisHandlerExtension.getInstances(project)
325-
326-
analysisHandlerExtensions.forEach { it.doAnalysis(project, module, moduleContext, files, trace, container) }
327-
328316
container.get<LazyTopDownAnalyzer>().analyzeDeclarations(TopDownAnalysisMode.TopLevelDeclarations, files)
329-
container.get<JavaClassesTracker>().onCompletedAnalysis(module)
330-
331-
analysisHandlerExtensions.forEach { it.analysisCompleted(project, module, trace, files) }
332-
333317
return Pair(trace.bindingContext, container)
334318
}
335319
}

server/src/main/kotlin/org/javacs/kt/CompilerClassPath.kt

Lines changed: 70 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,51 +2,64 @@ package org.javacs.kt
22

33
import org.javacs.kt.classpath.defaultClassPathResolver
44
import java.io.Closeable
5+
import java.nio.file.Files
6+
import java.nio.file.FileSystems
57
import java.nio.file.Path
8+
import java.util.stream.Collectors
69

710
class CompilerClassPath(private val config: CompilerConfiguration) : Closeable {
811
private val workspaceRoots = mutableSetOf<Path>()
12+
private val javaSourcePath = mutableSetOf<Path>()
913
private val classPath = mutableSetOf<Path>()
1014
private val buildScriptClassPath = mutableSetOf<Path>()
11-
var compiler = Compiler(workspaceRoots, classPath, buildScriptClassPath)
15+
var compiler = Compiler(javaSourcePath, classPath, buildScriptClassPath)
1216
private set
1317

1418
init {
1519
compiler.updateConfiguration(config)
1620
}
1721

18-
private fun refresh(updateBuildScriptClassPath: Boolean = true) {
22+
private fun refresh(
23+
updateClassPath: Boolean = true,
24+
updateBuildScriptClassPath: Boolean = true,
25+
updateJavaSourcePath: Boolean = false
26+
): Boolean {
1927
// TODO: Fetch class path and build script class path concurrently (and asynchronously)
2028
val resolver = defaultClassPathResolver(workspaceRoots)
21-
var refreshCompiler = false
29+
var refreshCompiler = updateJavaSourcePath
2230

23-
val newClassPath = resolver.classpathOrEmpty
24-
if (newClassPath != classPath) {
25-
syncClassPath(classPath, newClassPath)
26-
refreshCompiler = true
31+
if (updateClassPath) {
32+
val newClassPath = resolver.classpathOrEmpty
33+
if (newClassPath != classPath) {
34+
syncPath(classPath, newClassPath, "class path")
35+
refreshCompiler = true
36+
}
2737
}
2838

2939
if (updateBuildScriptClassPath) {
40+
LOG.info("Update build script path")
3041
val newBuildScriptClassPath = resolver.buildScriptClasspathOrEmpty
3142
if (newBuildScriptClassPath != buildScriptClassPath) {
32-
syncClassPath(buildScriptClassPath, newBuildScriptClassPath)
43+
syncPath(buildScriptClassPath, newBuildScriptClassPath, "class path")
3344
refreshCompiler = true
3445
}
3546
}
3647

3748
if (refreshCompiler) {
3849
compiler.close()
39-
compiler = Compiler(workspaceRoots, classPath, buildScriptClassPath)
50+
compiler = Compiler(javaSourcePath, classPath, buildScriptClassPath)
4051
updateCompilerConfiguration()
4152
}
53+
54+
return refreshCompiler
4255
}
4356

44-
private fun syncClassPath(dest: MutableSet<Path>, new: Set<Path>) {
57+
private fun syncPath(dest: MutableSet<Path>, new: Set<Path>, name: String) {
4558
val added = new - dest
4659
val removed = dest - new
4760

48-
logAdded(added)
49-
logRemoved(removed)
61+
logAdded(added, name)
62+
logRemoved(removed, name)
5063

5164
dest.removeAll(removed)
5265
dest.addAll(added)
@@ -56,53 +69,78 @@ class CompilerClassPath(private val config: CompilerConfiguration) : Closeable {
5669
compiler.updateConfiguration(config)
5770
}
5871

59-
fun addWorkspaceRoot(root: Path) {
60-
LOG.info("Searching for dependencies in workspace root {}", root)
72+
fun addWorkspaceRoot(root: Path): Boolean {
73+
LOG.info("Searching for dependencies and Java sources in workspace root {}", root)
6174

6275
workspaceRoots.add(root)
76+
javaSourcePath.addAll(findJavaSourceFiles(root))
6377

64-
refresh()
78+
return refresh(updateJavaSourcePath = true)
6579
}
6680

67-
fun removeWorkspaceRoot(root: Path) {
68-
LOG.info("Remove dependencies from workspace root {}", root)
81+
fun removeWorkspaceRoot(root: Path): Boolean {
82+
LOG.info("Removing dependencies and Java source path from workspace root {}", root)
6983

7084
workspaceRoots.remove(root)
85+
javaSourcePath.removeAll(findJavaSourceFiles(root))
7186

72-
refresh()
87+
return refresh(updateJavaSourcePath = true)
7388
}
7489

75-
fun createdOnDisk(file: Path) {
76-
changedOnDisk(file)
90+
fun createdOnDisk(file: Path): Boolean {
91+
if (isJavaSource(file)) {
92+
javaSourcePath.add(file)
93+
}
94+
return changedOnDisk(file)
7795
}
7896

79-
fun deletedOnDisk(file: Path) {
80-
changedOnDisk(file)
97+
fun deletedOnDisk(file: Path): Boolean {
98+
if (isJavaSource(file)) {
99+
javaSourcePath.remove(file)
100+
}
101+
return changedOnDisk(file)
81102
}
82103

83-
fun changedOnDisk(file: Path) {
84-
val name = file.fileName.toString()
85-
if (name == "pom.xml" || name == "build.gradle" || name == "build.gradle.kts")
86-
refresh(updateBuildScriptClassPath = false)
104+
fun changedOnDisk(file: Path): Boolean {
105+
val buildScript = isBuildScript(file)
106+
val javaSource = isJavaSource(file)
107+
if (buildScript || javaSource) {
108+
return refresh(updateClassPath = buildScript, updateBuildScriptClassPath = false, updateJavaSourcePath = javaSource)
109+
} else {
110+
return false
111+
}
87112
}
88113

114+
private fun isJavaSource(file: Path): Boolean = file.fileName.toString().endsWith(".java")
115+
116+
private fun isBuildScript(file: Path): Boolean = file.fileName.toString().let { it == "pom.xml" || it == "build.gradle" || it == "build.gradle.kts" }
117+
89118
override fun close() {
90119
compiler.close()
91120
}
92121
}
93122

94-
private fun logAdded(sources: Collection<Path>) {
123+
// TODO: Cut off branches that are excluded in the walker directly
124+
private fun findJavaSourceFiles(root: Path): Set<Path> {
125+
val sourceMatcher = FileSystems.getDefault().getPathMatcher("glob:*.java")
126+
val exclusions = SourceExclusions(root)
127+
return Files.walk(root)
128+
.filter { exclusions.isPathIncluded(it) && sourceMatcher.matches(it.fileName) }
129+
.collect(Collectors.toSet())
130+
}
131+
132+
private fun logAdded(sources: Collection<Path>, name: String) {
95133
when {
96134
sources.isEmpty() -> return
97-
sources.size > 5 -> LOG.info("Adding {} files to class path", sources.size)
98-
else -> LOG.info("Adding {} to class path", sources)
135+
sources.size > 5 -> LOG.info("Adding {} files to {}", sources.size, name)
136+
else -> LOG.info("Adding {} to {}", sources, name)
99137
}
100138
}
101139

102-
private fun logRemoved(sources: Collection<Path>) {
140+
private fun logRemoved(sources: Collection<Path>, name: String) {
103141
when {
104142
sources.isEmpty() -> return
105-
sources.size > 5 -> LOG.info("Removing {} files from class path", sources.size)
106-
else -> LOG.info("Removing {} from class path", sources)
143+
sources.size > 5 -> LOG.info("Removing {} files from {}", sources.size, name)
144+
else -> LOG.info("Removing {} from {}", sources, name)
107145
}
108146
}

server/src/main/kotlin/org/javacs/kt/KotlinLanguageServer.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,10 @@ class KotlinLanguageServer : LanguageServer, LanguageClientAware, Closeable {
7373
val root = Paths.get(parseURI(params.rootUri))
7474

7575
sourceFiles.addWorkspaceRoot(root)
76-
classPath.addWorkspaceRoot(root)
76+
val refreshed = classPath.addWorkspaceRoot(root)
77+
if (refreshed) {
78+
sourcePath.refresh()
79+
}
7780
}
7881

7982
return completedFuture(InitializeResult(serverCapabilities))

server/src/main/kotlin/org/javacs/kt/KotlinWorkspaceService.kt

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,15 +66,15 @@ class KotlinWorkspaceService(
6666
when (change.type) {
6767
FileChangeType.Created -> {
6868
sf.createdOnDisk(uri)
69-
path?.let(cp::createdOnDisk)
69+
path?.let(cp::createdOnDisk)?.let { if (it) sp.refresh() }
7070
}
7171
FileChangeType.Deleted -> {
7272
sf.deletedOnDisk(uri)
73-
path?.let(cp::deletedOnDisk)
73+
path?.let(cp::deletedOnDisk)?.let { if (it) sp.refresh() }
7474
}
7575
FileChangeType.Changed -> {
7676
sf.changedOnDisk(uri)
77-
path?.let(cp::changedOnDisk)
77+
path?.let(cp::changedOnDisk)?.let { if (it) sp.refresh() }
7878
}
7979
null -> {
8080
// Nothing to do
@@ -147,15 +147,21 @@ class KotlinWorkspaceService(
147147
val root = Paths.get(parseURI(change.uri))
148148

149149
sf.addWorkspaceRoot(root)
150-
cp.addWorkspaceRoot(root)
150+
val refreshed = cp.addWorkspaceRoot(root)
151+
if (refreshed) {
152+
sp.refresh()
153+
}
151154
}
152155
for (change in params.event.removed) {
153156
LOG.info("Dropping workspace {} from source path", change.uri)
154157

155158
val root = Paths.get(parseURI(change.uri))
156159

157160
sf.removeWorkspaceRoot(root)
158-
cp.removeWorkspaceRoot(root)
161+
val refreshed = cp.removeWorkspaceRoot(root)
162+
if (refreshed) {
163+
sp.refresh()
164+
}
159165
}
160166
}
161167
}

server/src/main/kotlin/org/javacs/kt/SourceFiles.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,8 +216,9 @@ private fun patch(sourceText: String, change: TextDocumentContentChangeEvent): S
216216
}
217217
}
218218

219+
// TODO: Cut off branches that are excluded in the walker directly
219220
private fun findSourceFiles(root: Path): Set<URI> {
220-
val sourceMatcher = FileSystems.getDefault().getPathMatcher("glob:*.{kt,kts,java}")
221+
val sourceMatcher = FileSystems.getDefault().getPathMatcher("glob:*.{kt,kts}")
221222
val exclusions = SourceExclusions(root)
222223
return Files.walk(root)
223224
.filter { exclusions.isPathIncluded(it) && sourceMatcher.matches(it.fileName) }

0 commit comments

Comments
 (0)