optimize validateRecentProjects

GitOrigin-RevId: 65f9120b40f14761a6a15a18f980fb5e77f40cf2
This commit is contained in:
Vladimir Krivosheev
2023-01-23 18:43:46 +01:00
committed by intellij-monorepo-bot
parent bcd8532304
commit 0aa8fb366c
4 changed files with 96 additions and 32 deletions

View File

@@ -1,16 +1,14 @@
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
@file:Suppress("ReplaceGetOrSet")
package com.intellij.ide
import com.intellij.openapi.components.BaseState
import com.intellij.openapi.options.advanced.AdvancedSettings
import com.intellij.openapi.wm.impl.FrameInfo
import com.intellij.util.xmlb.annotations.Attribute
import com.intellij.util.xmlb.annotations.MapAnnotation
import com.intellij.util.xmlb.annotations.OptionTag
import com.intellij.util.xmlb.annotations.Property
import java.util.concurrent.atomic.LongAdder
class RecentProjectMetaInfo : BaseState() {
@get:Attribute
@@ -55,24 +53,4 @@ class RecentProjectManagerState : BaseState() {
var lastProjectLocation by string()
var lastOpenedProject by string()
fun validateRecentProjects(modCounter: LongAdder) {
val limit = AdvancedSettings.getInt("ide.max.recent.projects")
if (additionalInfo.size <= limit || limit < 1) {
return
}
// might be freezing for many projects that were stored as "opened"
while (additionalInfo.size > limit) {
val iterator = additionalInfo.keys.iterator()
while (iterator.hasNext()) {
val path = iterator.next()
if (!additionalInfo.get(path)!!.opened) {
iterator.remove()
break
}
}
}
modCounter.increment()
}
}

View File

@@ -19,6 +19,7 @@ import com.intellij.openapi.application.ex.ApplicationManagerEx
import com.intellij.openapi.components.*
import com.intellij.openapi.diagnostic.getOrLogException
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.options.advanced.AdvancedSettings
import com.intellij.openapi.project.Project
import com.intellij.openapi.project.ProjectCloseListener
import com.intellij.openapi.project.ProjectManager
@@ -339,7 +340,7 @@ open class RecentProjectsManagerBase : RecentProjectsManager, PersistentStateCom
info.projectOpenTimestamp = openTimestamp
state.lastOpenedProject = projectPath
state.validateRecentProjects(modCounter)
validateRecentProjects(modCounter, state.additionalInfo)
}
withContext(Dispatchers.EDT + ModalityState.any().asContextElement()) {
@@ -384,7 +385,7 @@ open class RecentProjectsManagerBase : RecentProjectsManager, PersistentStateCom
fun getRecentPaths(): List<String> {
synchronized(stateLock) {
state.validateRecentProjects(modCounter)
validateRecentProjects(modCounter, state.additionalInfo)
return state.additionalInfo.keys.reversed()
}
}
@@ -818,3 +819,15 @@ private fun updateSystemDockMenu() {
}
}
}
private fun validateRecentProjects(modCounter: LongAdder, map: MutableMap<String, RecentProjectMetaInfo>) {
val limit = AdvancedSettings.getInt("ide.max.recent.projects")
if (limit < 1 || map.size <= limit) {
return
}
var toRemove = map.size - limit
if (map.values.removeIf { !it.opened && toRemove++ >= 0 }) {
modCounter.increment()
}
}

View File

@@ -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.
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.ide
import com.intellij.configurationStore.deserializeInto
@@ -560,4 +560,78 @@ class RecentProjectManagerTest {
"/home/boo/Documents/BrightFuturesTestProj",
"/home/boo/Documents/GoogleTestTests")
}
@Test
fun `validate a lot of recent opened projects`() {
val manager = RecentProjectsManagerBase()
val entries = StringBuilder()
val openedProjectCount = 2000
for (i in 0 until openedProjectCount) {
//language=XML
entries.append("""
<entry key="/home/boo/project-$i">
<value>
<RecentProjectMetaInfo opened="true">
</RecentProjectMetaInfo>
</value>
</entry>
""".trimIndent())
}
@Language("XML")
val element = JDOMUtil.load("""
<application>
<component name="RecentDirectoryProjectsManager">
<option name="additionalInfo">
<map>
$entries
</map>
</option>
</component>
</application>
""".trimIndent())
val state = RecentProjectManagerState()
element.getChild("component")!!.deserializeInto(state)
manager.loadState(state)
assertThat(manager.getRecentPaths().first()).isEqualTo("/home/boo/project-1999")
assertThat(manager.getRecentPaths()).hasSize(openedProjectCount)
}
@Test
fun `validate a lot of recent projects`() {
val manager = RecentProjectsManagerBase()
val entries = StringBuilder()
val openedProjectCount = 2000
for (i in 0 until openedProjectCount) {
//language=XML
entries.append("""
<entry key="/home/boo/project-$i">
<value>
<RecentProjectMetaInfo opened="${i % 2 == 0}">
</RecentProjectMetaInfo>
</value>
</entry>
""".trimIndent())
}
@Language("XML")
val element = JDOMUtil.load("""
<application>
<component name="RecentDirectoryProjectsManager">
<option name="additionalInfo">
<map>
$entries
</map>
</option>
</component>
</application>
""".trimIndent())
val state = RecentProjectManagerState()
element.getChild("component")!!.deserializeInto(state)
manager.loadState(state)
assertThat(manager.getRecentPaths().first()).isEqualTo("/home/boo/project-1998")
assertThat(manager.getRecentPaths()).hasSize(openedProjectCount / 2)
}
}

View File

@@ -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.
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
@file:Suppress("ReplaceGetOrSet")
package com.intellij.openapi.project.impl
@@ -22,7 +22,6 @@ import org.junit.Test
import org.junit.rules.ExternalResource
import java.awt.Color
import java.nio.file.Path
import java.util.*
class RecentProjectsTest {
companion object {
@@ -48,7 +47,7 @@ class RecentProjectsTest {
val tempDir = TemporaryDirectory()
@Test
fun testMostRecentOnTop() = runBlocking {
fun mostRecentOnTop() = runBlocking {
val p1 = createAndOpenProject("p1")
val p2 = createAndOpenProject("p2")
val p3 = createAndOpenProject("p3")
@@ -61,7 +60,7 @@ class RecentProjectsTest {
}
@Test
fun testGroupsOrder() = runBlocking {
fun groupOrder() = runBlocking {
val p1 = createAndOpenProject("p1")
val p2 = createAndOpenProject("p2")
val p3 = createAndOpenProject("p3")
@@ -98,7 +97,7 @@ class RecentProjectsTest {
project = projectManager.openProjectAsync(z1, createTestOpenProjectOptions(runPostStartUpActivities = false))!!
recentProjectManager.projectOpened(project)
recentProjectManager.updateLastProjectPath()
// "Timestamp for opened project has not been updated"
// "Timestamp for an opened project has not been updated"
assertThat(getProjectOpenTimestamp("z1")).isGreaterThan(timestamp)
}
finally {
@@ -107,7 +106,7 @@ class RecentProjectsTest {
}
@Test
fun testSlnLikeProjectIcon() {
fun solutionLikeProjectIcon() {
// For Rider
val rpm = (RecentProjectsManager.getInstance() as RecentProjectsManagerBase)