mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
BAZEL-2972: Fix project icon not loaded for projects with regular file identities
`RecentProjectIconHelper.getDotIdeaPath` assumes the `./.idea/.idea.<name>/.idea` layout for every non-directory project root. This layout seems to be special case for Rider. For other cases standard `ProjectStorePathManager.getInstance().getStoreDescriptor(file).dotIdea` should be used. This issue affected Bazel plugin projects - icon is not shown correctly GitOrigin-RevId: cea7b3f48555ea96325d4dbaceb2d30007c1b499
This commit is contained in:
committed by
intellij-monorepo-bot
parent
1297be84fa
commit
08fe83d7f5
@@ -72,7 +72,9 @@ class RecentProjectIconHelper {
|
||||
val fileName = file.fileName.toString()
|
||||
val dotIndex = fileName.lastIndexOf('.')
|
||||
val fileNameWithoutExt = if (dotIndex == -1) fileName else fileName.take(dotIndex)
|
||||
return file.parent.resolve("$DIRECTORY_STORE_FOLDER/$DIRECTORY_STORE_FOLDER.$fileNameWithoutExt/$DIRECTORY_STORE_FOLDER")
|
||||
val nestedDotIdea = file.parent.resolve("$DIRECTORY_STORE_FOLDER/$DIRECTORY_STORE_FOLDER.$fileNameWithoutExt/$DIRECTORY_STORE_FOLDER")
|
||||
if (Files.isDirectory(nestedDotIdea)) return nestedDotIdea
|
||||
return ProjectStorePathManager.getInstance().getStoreDescriptor(file).dotIdea
|
||||
}
|
||||
catch (_: InvalidPathException) {
|
||||
return null
|
||||
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
+98
-19
@@ -3,22 +3,36 @@
|
||||
|
||||
package com.intellij.openapi.project.impl
|
||||
|
||||
import com.intellij.configurationStore.FileStorageAnnotation
|
||||
import com.intellij.configurationStore.ProjectStoreDescriptor
|
||||
import com.intellij.configurationStore.ProjectStorePathCustomizer
|
||||
import com.intellij.configurationStore.StateStorageManager
|
||||
import com.intellij.ide.AppLifecycleListener
|
||||
import com.intellij.ide.ProjectGroup
|
||||
import com.intellij.ide.ProjectGroupActionGroup
|
||||
import com.intellij.ide.RecentProjectListActionProvider
|
||||
import com.intellij.ide.RecentProjectsManager
|
||||
import com.intellij.ide.RecentProjectsManagerBase
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.application.EDT
|
||||
import com.intellij.openapi.components.PersistentStateComponent
|
||||
import com.intellij.openapi.components.State
|
||||
import com.intellij.openapi.components.StateStorageOperation
|
||||
import com.intellij.openapi.components.Storage
|
||||
import com.intellij.openapi.extensions.ExtensionPointName
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.project.ProjectCloseListener
|
||||
import com.intellij.openapi.project.ex.ProjectManagerEx
|
||||
import com.intellij.project.stateStore
|
||||
import com.intellij.testFramework.ExtensionTestUtil
|
||||
import com.intellij.testFramework.PlatformTestUtil
|
||||
import com.intellij.testFramework.TemporaryDirectoryExtension
|
||||
import com.intellij.testFramework.assertions.Assertions.assertThat
|
||||
import com.intellij.testFramework.common.timeoutRunBlocking
|
||||
import com.intellij.testFramework.createTestOpenProjectOptions
|
||||
import com.intellij.testFramework.junit5.TestApplication
|
||||
import com.intellij.testFramework.junit5.TestDisposable
|
||||
import com.intellij.ui.DeferredIconImpl
|
||||
import com.intellij.ui.JBColor
|
||||
import com.intellij.util.IconUtil
|
||||
@@ -26,12 +40,16 @@ import com.intellij.util.PathUtil
|
||||
import com.intellij.util.messages.SimpleMessageBusConnection
|
||||
import com.intellij.util.ui.AvatarUtils
|
||||
import com.intellij.util.ui.EmptyIcon
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.extension.RegisterExtension
|
||||
import java.awt.Color
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import javax.swing.Icon
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
@TestApplication
|
||||
@@ -138,25 +156,24 @@ class RecentProjectsTest {
|
||||
val icon = (rpm.getProjectIcon(slnFile.toString(), isProjectValid = true) as DeferredIconImpl<*>).evaluateAsync()
|
||||
assertThat(icon).isNotInstanceOf(EmptyIcon::class.java)
|
||||
|
||||
// For custom icons we add a 2px empty border
|
||||
val emptyBorderWidth = 2
|
||||
val iconSize = 20
|
||||
assertSingleColorIcon(icon, if (JBColor.isBright()) Color.BLUE else Color.RED)
|
||||
}
|
||||
|
||||
// Check that image is loaded from file, and not generated by IDE
|
||||
val iconImage = IconUtil.toBufferedImage(icon)
|
||||
for (x in 0 until iconImage.width) {
|
||||
for (y in 0 until iconImage.height) {
|
||||
val color = iconImage.getRGB(x, y)
|
||||
|
||||
if (x >= emptyBorderWidth && x < (iconSize - emptyBorderWidth) &&
|
||||
y >= emptyBorderWidth && y < (iconSize - emptyBorderWidth)) {
|
||||
assertThat(color).isEqualTo(if (JBColor.isBright()) Color.BLUE.rgb else Color.RED.rgb)
|
||||
}
|
||||
else {
|
||||
assertThat(color).isEqualTo(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
@Test
|
||||
fun projectIconForRegularFileProjectIdentity(@TestDisposable disposable: Disposable) = timeoutRunBlocking {
|
||||
JBColor.setDark(false)
|
||||
ExtensionTestUtil.maskExtensions(
|
||||
pointName = PROJECT_STORE_PATH_CUSTOMIZER_EP,
|
||||
newExtensions = listOf(CustomDotIdeaProjectStorePathCustomizer()),
|
||||
parentDisposable = disposable,
|
||||
fireEvents = false,
|
||||
)
|
||||
val rpm = (RecentProjectsManager.getInstance() as RecentProjectsManagerBase)
|
||||
val projectDir = Path.of("${PlatformTestUtil.getPlatformTestDataPath()}/recentProjects/regularFileIdentityProject")
|
||||
val identityFile = projectDir.resolve(".projectidentity")
|
||||
val icon = (rpm.getProjectIcon(identityFile.toString(), isProjectValid = true) as DeferredIconImpl<*>).evaluateAsync()
|
||||
assertThat(icon).isNotInstanceOf(EmptyIcon::class.java)
|
||||
assertSingleColorIcon(icon, Color.BLUE)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -183,6 +200,27 @@ class RecentProjectsTest {
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun assertSingleColorIcon(icon: Icon, expectedColor: Color) = withContext(Dispatchers.EDT) {
|
||||
// For custom icons we add a 2px empty border
|
||||
val emptyBorderWidth = 2
|
||||
val iconSize = 20
|
||||
// Check that image is loaded from file, and not generated by IDE
|
||||
val iconImage = IconUtil.toBufferedImage(icon)
|
||||
for (x in 0 until iconImage.width) {
|
||||
for (y in 0 until iconImage.height) {
|
||||
val color = iconImage.getRGB(x, y)
|
||||
|
||||
if (x >= emptyBorderWidth && x < (iconSize - emptyBorderWidth) &&
|
||||
y >= emptyBorderWidth && y < (iconSize - emptyBorderWidth)) {
|
||||
assertThat(color).isEqualTo(expectedColor.rgb)
|
||||
}
|
||||
else {
|
||||
assertThat(color).isEqualTo(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getProjectOpenTimestamp(@Suppress("SameParameterValue") projectName: String): Long {
|
||||
val additionalInfo = RecentProjectsManagerBase.getInstanceEx().state.additionalInfo
|
||||
for (s in additionalInfo.keys) {
|
||||
@@ -249,4 +287,45 @@ class RecentProjectsTest {
|
||||
projectManager.forceCloseProjectAsync(project)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val PROJECT_STORE_PATH_CUSTOMIZER_EP = ExtensionPointName.create<ProjectStorePathCustomizer>("com.intellij.projectStorePathCustomizer")
|
||||
|
||||
private class CustomDotIdeaProjectStorePathCustomizer : ProjectStorePathCustomizer {
|
||||
override fun getStoreDirectoryPath(projectRoot: Path): ProjectStoreDescriptor? {
|
||||
if (projectRoot.fileName?.toString() != ".projectidentity") return null
|
||||
val projectDir = projectRoot.parent
|
||||
return CustomDotIdeaProjectStoreDescriptor(
|
||||
projectIdentityFile = projectRoot,
|
||||
dotIdea = projectDir.resolve(".idea.custom"),
|
||||
historicalProjectBasePath = projectDir,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class CustomDotIdeaProjectStoreDescriptor(
|
||||
override val projectIdentityFile: Path,
|
||||
override val dotIdea: Path,
|
||||
override val historicalProjectBasePath: Path,
|
||||
) : ProjectStoreDescriptor {
|
||||
|
||||
override fun testStoreDirectoryExistsForProjectRoot(): Boolean = Files.isRegularFile(projectIdentityFile)
|
||||
|
||||
override fun getJpsBridgeAwareStorageSpec(filePath: String, project: Project): Storage =
|
||||
FileStorageAnnotation.PROJECT_FILE_STORAGE_ANNOTATION
|
||||
|
||||
override fun getModuleStorageSpecs(
|
||||
component: PersistentStateComponent<*>,
|
||||
stateSpec: State,
|
||||
operation: StateStorageOperation,
|
||||
storageManager: StateStorageManager,
|
||||
project: Project,
|
||||
): List<Storage> = listOf(FileStorageAnnotation.MODULE_FILE_STORAGE_ANNOTATION)
|
||||
|
||||
override fun <T : Any> getStorageSpecs(
|
||||
component: PersistentStateComponent<T>,
|
||||
stateSpec: State,
|
||||
operation: StateStorageOperation,
|
||||
storageManager: StateStorageManager,
|
||||
): List<Storage> = listOf(FileStorageAnnotation.PROJECT_FILE_STORAGE_ANNOTATION)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user