mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
KTIJ-29329 Failed to save settings: "Cannot get KotlinCompilationErrorFileTimeStamps component state"
GitOrigin-RevId: af1eafe4088ff6b033c00b55999b5e940b85de15
This commit is contained in:
committed by
intellij-monorepo-bot
parent
812fe3f28a
commit
a710fe7b9d
@@ -1,5 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="FacetManager">
|
||||
<facet type="kotlin-language" name="Kotlin">
|
||||
<configuration version="5" platform="JVM 17" allPlatforms="JVM [17]" useProjectSettings="false">
|
||||
<compilerSettings>
|
||||
<option name="additionalArguments" value="-Xjvm-default=all" />
|
||||
</compilerSettings>
|
||||
<compilerArguments>
|
||||
<stringArguments>
|
||||
<stringArg name="jvmTarget" arg="17" />
|
||||
<stringArg name="apiVersion" arg="1.9" />
|
||||
<stringArg name="languageVersion" arg="1.9" />
|
||||
</stringArguments>
|
||||
<arrayArguments>
|
||||
<arrayArg name="pluginClasspaths">
|
||||
<args>$KOTLIN_BUNDLED$/lib/kotlinx-serialization-compiler-plugin.jar</args>
|
||||
</arrayArg>
|
||||
<arrayArg name="pluginOptions" />
|
||||
</arrayArguments>
|
||||
</compilerArguments>
|
||||
</configuration>
|
||||
</facet>
|
||||
</component>
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
@@ -19,5 +41,6 @@
|
||||
<orderEntry type="module" module-name="intellij.java.impl" />
|
||||
<orderEntry type="module" module-name="intellij.gradle.common" />
|
||||
<orderEntry type="module" module-name="kotlin.base.compiler-configuration" />
|
||||
<orderEntry type="library" name="kotlinx-serialization-core" level="project" />
|
||||
</component>
|
||||
</module>
|
||||
+3
-1
@@ -1,4 +1,4 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.kotlin.idea.statistics
|
||||
|
||||
import com.intellij.internal.statistic.eventLog.EventLogGroup
|
||||
@@ -29,6 +29,7 @@ import org.jetbrains.kotlin.idea.compiler.configuration.KotlinIdePlugin
|
||||
import org.jetbrains.kotlin.idea.configuration.BuildSystemType
|
||||
import org.jetbrains.kotlin.idea.configuration.buildSystemType
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.UnsafeCastFunction
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
import kotlin.math.absoluteValue
|
||||
import kotlin.random.Random
|
||||
@@ -299,6 +300,7 @@ class KotlinOnboardingJ2KSessionService(private val project: Project, private va
|
||||
/**
|
||||
* Caches if the project contains a Kotlin file.
|
||||
*/
|
||||
@OptIn(UnsafeCastFunction::class)
|
||||
internal suspend fun hasKotlinFiles(): Boolean {
|
||||
hasKotlinFile?.let { return it }
|
||||
return readAction {
|
||||
|
||||
+3
-1
@@ -1,4 +1,4 @@
|
||||
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.kotlin.idea.statistics
|
||||
|
||||
import com.intellij.internal.statistic.libraryUsage.LibraryUsageImportProcessor
|
||||
@@ -8,9 +8,11 @@ import org.jetbrains.kotlin.idea.references.mainReference
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi.KtImportDirective
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedElementSelector
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.UnsafeCastFunction
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
|
||||
class KotlinLibraryUsageImportProcessor : LibraryUsageImportProcessor<KtImportDirective> {
|
||||
@OptIn(UnsafeCastFunction::class)
|
||||
override fun imports(file: PsiFile): List<KtImportDirective> = file.safeAs<KtFile>()?.importDirectives.orEmpty()
|
||||
|
||||
override fun isSingleElementImport(import: KtImportDirective): Boolean = !import.isAllUnder
|
||||
|
||||
+46
-57
@@ -1,19 +1,13 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.kotlin.idea.statistics.compilationError
|
||||
|
||||
import com.intellij.openapi.components.*
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.SimpleModificationTracker
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.util.io.DigestUtil
|
||||
import com.intellij.util.io.hashToHexString
|
||||
import com.intellij.util.xmlb.annotations.Tag
|
||||
import com.intellij.util.xmlb.annotations.XMap
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import kotlinx.serialization.Serializable
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||
import kotlin.concurrent.read
|
||||
import kotlin.concurrent.write
|
||||
|
||||
@Service(Service.Level.PROJECT)
|
||||
@State(
|
||||
@@ -22,75 +16,70 @@ import kotlin.concurrent.write
|
||||
reportStatistic = false,
|
||||
reloadable = false,
|
||||
)
|
||||
@ApiStatus.Internal
|
||||
class KotlinCompilationErrorProcessedFilesTimeStampRecorder :
|
||||
PersistentStateComponentWithModificationTracker<KotlinCompilationErrorProcessedFilesTimeStampRecorder.MyState> {
|
||||
internal class KotlinCompilationErrorProcessedFilesTimeStampRecorder :
|
||||
SerializablePersistentStateComponent<KotlinCompilationErrorProcessedFilesTimeStampRecorder.MyState>(EMPTY_STATE) {
|
||||
|
||||
override fun initializeComponent() = lock.write {
|
||||
if (dropOutdatedTimestamps()) {
|
||||
tracker.incModificationCount()
|
||||
override fun initializeComponent() {
|
||||
val currentTime = System.currentTimeMillis()
|
||||
if (state.timestamps.values.any { currentTime.isHourPassedSince(it) }) {
|
||||
updateState { state ->
|
||||
val timestamps = HashMap(state.timestamps)
|
||||
dropOutdatedTimestamps(currentTime = currentTime, timestamps = timestamps)
|
||||
if (timestamps.isEmpty()) EMPTY_STATE else MyState(timestamps = timestamps)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val lock = ReentrantReadWriteLock()
|
||||
private var state = MyState()
|
||||
private val tracker = SimpleModificationTracker()
|
||||
override fun getState(): MyState = lock.read { MyState(HashMap(state.timestamps)) }
|
||||
override fun getStateModificationCount(): Long = tracker.modificationCount
|
||||
|
||||
override fun loadState(state: MyState) = lock.write {
|
||||
this.state = state
|
||||
}
|
||||
|
||||
@Serializable
|
||||
class MyState(
|
||||
@XMap
|
||||
@JvmField
|
||||
val timestamps: MutableMap<Key, Long> = HashMap()
|
||||
) : BaseState() {
|
||||
data class Key(
|
||||
// Use md5 instead of the actual file path to make local storage smaller. Users' paths can be huge
|
||||
@Tag("filePathMd5")
|
||||
val filePathMd5: String,
|
||||
@Tag("compilationErrorId")
|
||||
val compilationErrorId: String
|
||||
)
|
||||
}
|
||||
@JvmField val timestamps: Map<String, Long> = emptyMap()
|
||||
)
|
||||
|
||||
fun keepOnlyIfHourPassedAndRecordTimestamps(vFile: VirtualFile, compilationErrorIds: List<String>): List<String> {
|
||||
if (compilationErrorIds.isEmpty()) return emptyList()
|
||||
val hash = pathMd5Hash(vFile)
|
||||
lock.write {
|
||||
val result = mutableListOf<String>()
|
||||
updateState { state ->
|
||||
val timestamps = HashMap(state.timestamps)
|
||||
val currentTime = System.currentTimeMillis()
|
||||
val result = compilationErrorIds.asSequence()
|
||||
result.clear()
|
||||
compilationErrorIds.asSequence()
|
||||
.distinct() /* `isHourPassed` + `recordTimestamp` don't allow the compilation error to happen for the
|
||||
second time in this pipeline, so `distinct` is just a small optimization */
|
||||
.filter { isHourPassed(hash, currentTime, it) }
|
||||
.onEach { recordTimestamp(hash, currentTime, it) }
|
||||
.toList()
|
||||
if (dropOutdatedTimestamps(currentTime) || result.isNotEmpty()) {
|
||||
tracker.incModificationCount()
|
||||
}
|
||||
return result
|
||||
.filter { isHourPassed(md5Hash = hash, currentTime = currentTime, compilationErrorId = it, timestamps = timestamps) }
|
||||
.onEach { recordTimestamp(md5Hash = hash, currentTime = currentTime, compilationErrorId = it, timestamps = timestamps) }
|
||||
.toCollection(result)
|
||||
dropOutdatedTimestamps(currentTime, timestamps)
|
||||
if (timestamps.isEmpty()) EMPTY_STATE else MyState(timestamps = timestamps)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun isHourPassed(md5Hash: String, currentTime: Long, compilationErrorId: String): Boolean {
|
||||
val fileTime = state.timestamps[MyState.Key(md5Hash, compilationErrorId)] ?: return true
|
||||
return currentTime.isHourPassedSince(fileTime)
|
||||
}
|
||||
|
||||
private fun recordTimestamp(md5Hash: String, currentTime: Long, compilationErrorId: String) {
|
||||
state.timestamps[MyState.Key(md5Hash, compilationErrorId)] = currentTime
|
||||
}
|
||||
|
||||
private fun dropOutdatedTimestamps(currentTime: Long = System.currentTimeMillis()): Boolean =
|
||||
state.timestamps.values.removeIf { currentTime.isHourPassedSince(it) }
|
||||
|
||||
companion object {
|
||||
fun getInstance(project: Project): KotlinCompilationErrorProcessedFilesTimeStampRecorder = project.service()
|
||||
}
|
||||
}
|
||||
|
||||
private val EMPTY_STATE = KotlinCompilationErrorProcessedFilesTimeStampRecorder.MyState()
|
||||
|
||||
private fun isHourPassed(md5Hash: String, currentTime: Long, compilationErrorId: String, timestamps: Map<String, Long>): Boolean {
|
||||
val fileTime = timestamps[createCompositeKey(md5Hash, compilationErrorId)] ?: return true
|
||||
return currentTime.isHourPassedSince(fileTime)
|
||||
}
|
||||
|
||||
// Use md5 instead of the actual file path to make local storage smaller.
|
||||
// Users' paths can be huge.
|
||||
// Key - combination of filePathMd5 + compilationErrorId, to avoid non-primitive key.
|
||||
private fun createCompositeKey(md5Hash: String, compilationErrorId: String) = "$md5Hash-$compilationErrorId"
|
||||
|
||||
private fun recordTimestamp(md5Hash: String, currentTime: Long, compilationErrorId: String, timestamps: MutableMap<String, Long>) {
|
||||
timestamps[createCompositeKey(md5Hash, compilationErrorId)] = currentTime
|
||||
}
|
||||
|
||||
private fun dropOutdatedTimestamps(currentTime: Long, timestamps: MutableMap<String, Long>): Boolean {
|
||||
return timestamps.values.removeIf { currentTime.isHourPassedSince(it) }
|
||||
}
|
||||
|
||||
private fun pathMd5Hash(virtualFile: VirtualFile): String = hashToHexString(virtualFile.path, DigestUtil.md5())
|
||||
|
||||
private fun Long.isHourPassedSince(lastTime: Long): Boolean = TimeUnit.MILLISECONDS.toHours(this - lastTime) >= 1
|
||||
Reference in New Issue
Block a user