summaryrefslogtreecommitdiff
path: root/platform/lang-impl/src/com/intellij/util/indexing/RescanIndexesAction.kt
blob: 1f659a703777f83bb6c2a6dce7823428317c1d60 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.util.indexing

import com.intellij.ide.actions.cache.*
import com.intellij.lang.LangBundle
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.progress.ProgressIndicator
import com.intellij.openapi.progress.util.ProgressIndicatorUtils
import com.intellij.openapi.project.DumbModeTask
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.vfs.VirtualFileWithId
import com.intellij.psi.stubs.StubTreeBuilder
import com.intellij.psi.stubs.StubUpdatingIndex
import com.intellij.util.BooleanFunction
import com.intellij.util.indexing.diagnostic.ProjectIndexingHistoryImpl
import com.intellij.util.indexing.diagnostic.ScanningType
import com.intellij.util.indexing.roots.IndexableFilesIterator
import com.intellij.util.indexing.roots.ProjectIndexableFilesIteratorImpl
import org.jetbrains.annotations.ApiStatus
import org.jetbrains.annotations.Nls
import java.util.*
import java.util.concurrent.CompletableFuture

@ApiStatus.Internal
class RescanIndexesAction : RecoveryAction {
  override val performanceRate: Int
    get() = 9990
  override val presentableName: @Nls(capitalization = Nls.Capitalization.Title) String
    get() = LangBundle.message("rescan.indexes.recovery.action.name")
  override val actionKey: String
    get() = "rescan"

  override fun performSync(recoveryScope: RecoveryScope): List<CacheInconsistencyProblem> {
    val project = recoveryScope.project
    val historyFuture = CompletableFuture<ProjectIndexingHistoryImpl>()
    val stubAndIndexingStampInconsistencies = Collections.synchronizedList(arrayListOf<CacheInconsistencyProblem>())

    var predefinedIndexableFilesIterators: List<IndexableFilesIterator>? = null
    if (recoveryScope is FilesRecoveryScope) {
      predefinedIndexableFilesIterators = recoveryScope.files.map { ProjectIndexableFilesIteratorImpl(it) }
    }
    object : UnindexedFilesUpdater(project, false, false,
                                   predefinedIndexableFilesIterators, "Rescanning indexes recovery action",
                                   if(predefinedIndexableFilesIterators == null) ScanningType.FULL_FORCED else ScanningType.PARTIAL_FORCED) {
      private val stubIndex =
        runCatching { (FileBasedIndex.getInstance() as FileBasedIndexImpl).getIndex(StubUpdatingIndex.INDEX_ID) }
        .onFailure { logger<RescanIndexesAction>().error(it) }.getOrNull()

      private inner class StubAndIndexStampInconsistency(private val path: String): CacheInconsistencyProblem {
        override val message: String
          get() = "`$path` should have already indexed stub but it's not present"
      }

      override fun getForceReindexingTrigger(): BooleanFunction<IndexedFile>? {
        if (stubIndex != null) {
          return BooleanFunction<IndexedFile> {
            val fileId = (it.file as VirtualFileWithId).id
            if (stubIndex.getIndexingStateForFile(fileId, it) == FileIndexingState.UP_TO_DATE &&
                stubIndex.getIndexedFileData(fileId).isEmpty() &&
                isAbleToBuildStub(it.file)) {
              stubAndIndexingStampInconsistencies.add(StubAndIndexStampInconsistency(it.file.path))
              return@BooleanFunction true
            }
            false
          }
        }
        return null
      }

      private fun isAbleToBuildStub(file: VirtualFile): Boolean = runCatching {
        StubTreeBuilder.buildStubTree(FileContentImpl.createByFile(file))
      }.getOrNull() != null

      override fun performScanningAndIndexing(indicator: ProgressIndicator): ProjectIndexingHistoryImpl {
        try {
          IndexingFlag.cleanupProcessedFlag()
          val history = super.performScanningAndIndexing(indicator)
          historyFuture.complete(history)
          return history
        }
        catch (e: Exception) {
          historyFuture.completeExceptionally(e)
          throw e
        }
      }

      override fun tryMergeWith(taskFromQueue: DumbModeTask): DumbModeTask? =
        if (taskFromQueue is UnindexedFilesUpdater && project == taskFromQueue.myProject && taskFromQueue.javaClass == javaClass) this else null
    }.queue(project)
    try {
      return ProgressIndicatorUtils.awaitWithCheckCanceled(historyFuture).extractConsistencyProblems() +
             stubAndIndexingStampInconsistencies
    }
    catch (e: Exception) {
      return listOf(ExceptionalCompletionProblem(e))
    }
  }

  private fun ProjectIndexingHistoryImpl.extractConsistencyProblems(): List<CacheInconsistencyProblem> =
    scanningStatistics.filter { it.numberOfFilesForIndexing != 0 }.map {
      UnindexedFilesInconsistencyProblem(it.numberOfFilesForIndexing, it.providerName)
    }

  private class UnindexedFilesInconsistencyProblem(private val numberOfFilesForIndexing: Int, private val providerName: String) : CacheInconsistencyProblem {
    override val message: String
      get() = "Provider `$providerName` had $numberOfFilesForIndexing unindexed files"
  }
}