@@ -7,17 +7,14 @@ import org.javacs.kt.util.filePath
77import org.javacs.kt.util.describeURI
88import org.javacs.kt.index.SymbolIndex
99import org.javacs.kt.progress.Progress
10- import org.javacs.kt.IndexingConfiguration
1110import com.intellij.lang.Language
12- import com.intellij.psi.PsiFile
13- import com.intellij.openapi.fileTypes.FileType
14- import com.intellij.openapi.fileTypes.LanguageFileType
1511import org.jetbrains.kotlin.container.ComponentProvider
1612import org.jetbrains.kotlin.container.getService
1713import org.jetbrains.kotlin.descriptors.ModuleDescriptor
1814import org.jetbrains.kotlin.psi.KtFile
1915import org.jetbrains.kotlin.resolve.BindingContext
2016import org.jetbrains.kotlin.resolve.CompositeBindingContext
17+ import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
2118import kotlin.concurrent.withLock
2219import java.nio.file.Path
2320import java.nio.file.Paths
@@ -33,7 +30,6 @@ class SourcePath(
3330 private val parseDataWriteLock = ReentrantLock ()
3431
3532 private val indexAsync = AsyncExecutor ()
36- private var indexInitialized: Boolean = false
3733 var indexEnabled: Boolean by indexingConfig::enabled
3834 val index = SymbolIndex ()
3935
@@ -99,14 +95,16 @@ class SourcePath(
9995 private fun doCompile () {
10096 LOG .debug(" Compiling {}" , path?.fileName)
10197
98+ val oldFile = clone()
99+
102100 val (context, container) = cp.compiler.compileKtFile(parsed!! , allIncludingThis(), kind)
103101 parseDataWriteLock.withLock {
104102 compiledContext = context
105103 compiledContainer = container
106104 compiledFile = parsed
107105 }
108106
109- initializeIndexAsyncIfNeeded(container )
107+ refreshWorkspaceIndexes(listOfNotNull(oldFile), listOfNotNull( this ) )
110108 }
111109
112110 private fun doCompileIfChanged () {
@@ -125,6 +123,9 @@ class SourcePath(
125123 if (isTemporary) (all().asSequence() + sequenceOf(parsed!! )).toList()
126124 else all()
127125 }
126+
127+ // Creates a shallow copy
128+ fun clone (): SourceFile = SourceFile (uri, content, path, parsed, compiledFile, compiledContext, compiledContainer, language, isTemporary)
128129 }
129130
130131 private fun sourceFile (uri : URI ): SourceFile {
@@ -161,6 +162,8 @@ class SourcePath(
161162 }
162163
163164 fun delete (uri : URI ) {
165+ files[uri]?.let { refreshWorkspaceIndexes(listOf (it), listOf ()) }
166+
164167 files.remove(uri)
165168 }
166169
@@ -195,7 +198,20 @@ class SourcePath(
195198 // Compile changed files
196199 fun compileAndUpdate (changed : List <SourceFile >, kind : CompilationKind ): BindingContext ? {
197200 if (changed.isEmpty()) return null
201+
202+ // Get clones of the old files, so we can remove the old declarations from the index
203+ val oldFiles = changed.mapNotNull {
204+ if (it.compiledFile?.text != it.content || it.parsed?.text != it.content) {
205+ it.clone()
206+ } else {
207+ null
208+ }
209+ }
210+
211+ // Parse the files that have changed
198212 val parse = changed.associateWith { it.apply { parseIfChanged() }.parsed!! }
213+
214+ // Get all the files. This will parse them if they changed
199215 val allFiles = all()
200216 beforeCompileCallback.invoke()
201217 val (context, container) = cp.compiler.compileKtFiles(parse.values, allFiles, kind)
@@ -214,7 +230,7 @@ class SourcePath(
214230
215231 // Only index normal files, not build files
216232 if (kind == CompilationKind .DEFAULT ) {
217- initializeIndexAsyncIfNeeded(container )
233+ refreshWorkspaceIndexes(oldFiles, parse.keys.toList() )
218234 }
219235
220236 return context
@@ -230,18 +246,62 @@ class SourcePath(
230246 return CompositeBindingContext .create(combined)
231247 }
232248
249+ fun compileAllFiles () {
250+ // TODO: Investigate the possibility of compiling all files at once, instead of iterating here
251+ // At the moment, compiling all files at once sometimes leads to an internal error from the TopDownAnalyzer
252+ files.keys.forEach {
253+ compileFiles(listOf (it))
254+ }
255+ }
256+
257+ fun refreshDependencyIndexes () {
258+ compileAllFiles()
259+
260+ val container = files.values.first { it.compiledContainer != null }.compiledContainer
261+ if (container != null ) {
262+ refreshDependencyIndexes(container)
263+ }
264+ }
265+
233266 /* *
234- * Initialized the symbol index asynchronously, if not
235- * already done.
267+ * Refreshes the indexes. If already done, refreshes only the declarations in the files that were changed.
236268 */
237- private fun initializeIndexAsyncIfNeeded (container : ComponentProvider ) = indexAsync.execute {
238- if (indexEnabled && ! indexInitialized) {
239- indexInitialized = true
269+ private fun refreshWorkspaceIndexes (oldFiles : List <SourceFile >, newFiles : List <SourceFile >) = indexAsync.execute {
270+ if (indexEnabled) {
271+ val oldDeclarations = getDeclarationDescriptors(oldFiles)
272+ val newDeclarations = getDeclarationDescriptors(newFiles)
273+
274+ // Index the new declarations in the Kotlin source files that were just compiled, removing the old ones
275+ index.updateIndexes(oldDeclarations, newDeclarations)
276+ }
277+ }
278+
279+ /* *
280+ * Refreshes the indexes. If already done, refreshes only the declarations in the files that were changed.
281+ */
282+ private fun refreshDependencyIndexes (container : ComponentProvider ) = indexAsync.execute {
283+ if (indexEnabled) {
240284 val module = container.getService(ModuleDescriptor ::class .java)
241- index.refresh(module)
285+ val declarations = getDeclarationDescriptors(files.values)
286+ index.refresh(module, declarations)
242287 }
243288 }
244289
290+ // Gets all the declaration descriptors for the collection of files
291+ private fun getDeclarationDescriptors (files : Collection <SourceFile >) =
292+ files.flatMap { file ->
293+ val compiledFile = file.compiledFile ? : file.parsed
294+ val compiledContainer = file.compiledContainer
295+ if (compiledFile != null && compiledContainer != null ) {
296+ val module = compiledContainer.getService(ModuleDescriptor ::class .java)
297+ module.getPackage(compiledFile.packageFqName).memberScope.getContributedDescriptors(
298+ DescriptorKindFilter .ALL
299+ ) { name -> compiledFile.declarations.map { it.name }.contains(name.toString()) }
300+ } else {
301+ listOf ()
302+ }
303+ }.asSequence()
304+
245305 /* *
246306 * Recompiles all source files that are initialized.
247307 */
0 commit comments