From 0aa8fb366ce4ac71f1c90cee91c9999b16915b87 Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Mon, 23 Jan 2023 18:43:46 +0100 Subject: [PATCH] optimize validateRecentProjects GitOrigin-RevId: 65f9120b40f14761a6a15a18f980fb5e77f40cf2 --- .../com/intellij/ide/RecentProjectMetaInfo.kt | 24 +----- .../intellij/ide/RecentProjectsManagerBase.kt | 17 ++++- .../intellij/ide/RecentProjectManagerTest.kt | 76 ++++++++++++++++++- .../project/impl/RecentProjectsTest.kt | 11 ++- 4 files changed, 96 insertions(+), 32 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/ide/RecentProjectMetaInfo.kt b/platform/platform-impl/src/com/intellij/ide/RecentProjectMetaInfo.kt index bddecbea7ae4..8d8de142be5a 100644 --- a/platform/platform-impl/src/com/intellij/ide/RecentProjectMetaInfo.kt +++ b/platform/platform-impl/src/com/intellij/ide/RecentProjectMetaInfo.kt @@ -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() - } } \ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/ide/RecentProjectsManagerBase.kt b/platform/platform-impl/src/com/intellij/ide/RecentProjectsManagerBase.kt index 3098cf52e267..e45fda214962 100644 --- a/platform/platform-impl/src/com/intellij/ide/RecentProjectsManagerBase.kt +++ b/platform/platform-impl/src/com/intellij/ide/RecentProjectsManagerBase.kt @@ -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 { 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) { + 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() + } +} \ No newline at end of file diff --git a/platform/platform-tests/testSrc/com/intellij/ide/RecentProjectManagerTest.kt b/platform/platform-tests/testSrc/com/intellij/ide/RecentProjectManagerTest.kt index 699ade2ae3ab..162eb2cd873b 100644 --- a/platform/platform-tests/testSrc/com/intellij/ide/RecentProjectManagerTest.kt +++ b/platform/platform-tests/testSrc/com/intellij/ide/RecentProjectManagerTest.kt @@ -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(""" + + + + + + + """.trimIndent()) + } + + @Language("XML") + val element = JDOMUtil.load(""" + + + + + + """.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(""" + + + + + + + """.trimIndent()) + } + + @Language("XML") + val element = JDOMUtil.load(""" + + + + + + """.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) + } } \ No newline at end of file diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/project/impl/RecentProjectsTest.kt b/platform/platform-tests/testSrc/com/intellij/openapi/project/impl/RecentProjectsTest.kt index 3fcd3704aa2a..3b09f10e7c62 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/project/impl/RecentProjectsTest.kt +++ b/platform/platform-tests/testSrc/com/intellij/openapi/project/impl/RecentProjectsTest.kt @@ -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)