shared-indexes tests: speedup "compareIndices" command: resolve files only once.
[idea/community.git] / platform / lang-impl / src / com / intellij / util / indexing / diagnostic / dump / IndexContentDiagnosticDumper.kt
1 // Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
2 package com.intellij.util.indexing.diagnostic.dump
3
4 import com.intellij.openapi.application.runReadAction
5 import com.intellij.openapi.progress.ProgressIndicator
6 import com.intellij.openapi.project.Project
7 import com.intellij.openapi.roots.impl.FilePropertyPusher
8 import com.intellij.openapi.vfs.VirtualFile
9 import com.intellij.util.containers.ConcurrentBitSet
10 import com.intellij.util.indexing.*
11 import com.intellij.util.indexing.diagnostic.dump.paths.IndexedFilePath
12 import com.intellij.util.indexing.diagnostic.dump.paths.PortableFilePaths
13 import kotlin.streams.asSequence
14
15 object IndexContentDiagnosticDumper {
16
17   fun getIndexContentDiagnostic(project: Project, indicator: ProgressIndicator): IndexContentDiagnostic {
18     val providers = (FileBasedIndex.getInstance() as FileBasedIndexImpl).getOrderedIndexableFilesProviders(project)
19     val visitedFiles = ConcurrentBitSet()
20
21     indicator.text = IndexingBundle.message("index.content.diagnostic.dumping")
22     indicator.isIndeterminate = false
23     indicator.fraction = 0.0
24
25     val indexedFilePaths = arrayListOf<IndexedFilePath>()
26     val providerNameToOriginalFileIds = hashMapOf<String, MutableSet<Int>>()
27     val filesFromUnsupportedFileSystem = arrayListOf<IndexedFilePath>()
28
29     for ((index, provider) in providers.withIndex()) {
30       indicator.text2 = provider.debugName
31       val providerFileIds = hashSetOf<Int>()
32       providerNameToOriginalFileIds[provider.debugName] = providerFileIds
33       provider.iterateFiles(project, { fileOrDir ->
34         if (!fileOrDir.isDirectory) {
35           val indexedFilePath = createIndexedFilePath(fileOrDir, project)
36           if (PortableFilePaths.isSupportedFileSystem(fileOrDir)) {
37             indexedFilePaths += indexedFilePath
38             providerFileIds += indexedFilePath.originalFileSystemId
39           }
40           else {
41             // TODO: consider not excluding any file systems.
42             filesFromUnsupportedFileSystem += indexedFilePath
43             return@iterateFiles true
44           }
45         }
46         true
47       }, visitedFiles)
48       indicator.fraction = (index + 1).toDouble() / providers.size
49     }
50     return IndexContentDiagnostic(
51       indexedFilePaths,
52       filesFromUnsupportedFileSystem,
53       providerNameToOriginalFileIds
54     )
55   }
56
57   fun doesFileHaveProvidedIndex(file: VirtualFile, extension: FileBasedIndexExtension<*, *>, project: Project): Boolean {
58     val fileId = FileBasedIndex.getFileId(file)
59     return FileBasedIndexInfrastructureExtension.EP_NAME.extensions().asSequence()
60       .mapNotNull { it.createFileIndexingStatusProcessor(project) }
61       .any { it.hasIndexForFile(file, fileId, extension) }
62   }
63
64   fun createIndexedFilePath(fileOrDir: VirtualFile, project: Project): IndexedFilePath {
65     val fileId = FileBasedIndex.getFileId(fileOrDir)
66     val fileUrl = fileOrDir.url
67     val fileType = if (fileOrDir.isDirectory) null else fileOrDir.fileType.name
68     val substitutedFileType = if (fileOrDir.isDirectory) {
69       null
70     }
71     else {
72       runReadAction {
73         SubstitutedFileType.substituteFileType(fileOrDir, fileOrDir.fileType, project).name.takeIf { it != fileType }
74       }
75     }
76     val fileSize = if (fileOrDir.isDirectory) null else fileOrDir.length
77     val portableFilePath = PortableFilePaths.getPortableFilePath(fileOrDir, project)
78     val resolvedFile = PortableFilePaths.findFileByPath(portableFilePath, project)
79     val allPusherValues = dumpFilePropertyPusherValues(fileOrDir, project).mapValues { it.value?.toString() ?: "<null-value>" }
80     val indexedFilePath = IndexedFilePath(
81       fileId,
82       fileType,
83       substitutedFileType,
84       fileSize,
85       fileUrl,
86       portableFilePath,
87       allPusherValues
88     )
89     check(fileUrl == resolvedFile?.url) {
90       buildString {
91         appendln("File cannot be resolved")
92         appendln("Original URL: $fileUrl")
93         appendln("Resolved URL: ${resolvedFile?.url}")
94         appendln(indexedFilePath.toString())
95       }
96     }
97     return indexedFilePath
98   }
99
100   fun dumpFilePropertyPusherValues(file: VirtualFile, project: Project): Map<String, Any?> {
101     val map = hashMapOf<String, Any?>()
102     FilePropertyPusher.EP_NAME.forEachExtensionSafe { pusher ->
103       if (file.isDirectory && pusher.acceptsDirectory(file, project)
104           || !file.isDirectory && pusher.acceptsFile(file, project)
105       ) {
106         map[pusher.pusherName] = pusher.getImmediateValue(project, file)
107       }
108     }
109     return map
110   }
111
112   private val FilePropertyPusher<*>.pusherName: String
113     get() = javaClass.name
114       .removePrefix("com.")
115       .removePrefix("intellij.")
116       .removePrefix("jetbrains.")
117       .replace("util.", "")
118       .replace("impl.", "")
119
120
121 }