[Workspace Model] [IDEA-288277] Do not try to save artifacts with conflicting names

At the moment, when the user renames artifact, IJ saves the new name on every keystroke. That means, that during typing, one artifact may have the same name as some other artifact even if the target name is not the same.
Workspace model doesn't allow us to store artifacts with the same names, so we should not update the artifact name in case it already exists.

GitOrigin-RevId: a0565388e618dbbcbd6524cbf22e54c4748983f0
This commit is contained in:
Alex Plate
2022-02-11 16:13:03 +03:00
committed by intellij-monorepo-bot
parent b7018ba3b7
commit cdbd61282d
4 changed files with 99 additions and 3 deletions

View File

@@ -11,7 +11,6 @@ import com.intellij.openapi.application.invokeAndWaitIfNeeded
import com.intellij.openapi.application.runReadAction
import com.intellij.openapi.application.runWriteAction
import com.intellij.openapi.extensions.ExtensionPointName
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Disposer
import com.intellij.packaging.artifacts.ArtifactManager
import com.intellij.packaging.artifacts.ArtifactPropertiesProvider
@@ -60,6 +59,16 @@ class ArtifactTest : ArtifactsTestCase() {
TestCase.assertEquals(anotherName, artifactObject.name)
}
fun `test add artifact via model 2`() = runWriteAction {
assumeTrue(WorkspaceModel.enabledForArtifacts)
addArtifact("A")
addArtifact("A2")
val artifactsCount = artifactManager.artifacts.size
TestCase.assertEquals(2, artifactsCount)
}
fun `test add artifact mix bridge and model`() = runWriteAction {
assumeTrue(WorkspaceModel.enabledForArtifacts)

View File

@@ -14,5 +14,6 @@
<orderEntry type="library" scope="TEST" name="assertJ" level="project" />
<orderEntry type="module" module-name="intellij.platform.tests" scope="TEST" />
<orderEntry type="module" module-name="intellij.platform.core.ui" />
<orderEntry type="module" module-name="intellij.java.compiler" scope="TEST" />
</component>
</module>

View File

@@ -21,6 +21,7 @@ import com.intellij.openapi.ui.ComboBox;
import com.intellij.openapi.util.Comparing;
import com.intellij.packaging.artifacts.Artifact;
import com.intellij.packaging.artifacts.ArtifactType;
import com.intellij.packaging.artifacts.ModifiableArtifactModel;
import com.intellij.ui.SimpleListCellRenderer;
import javax.swing.*;
@@ -37,8 +38,12 @@ public class ArtifactConfigurable extends ArtifactConfigurableBase {
public void setDisplayName(String name) {
final String oldName = getArtifact().getName();
if (name != null && !name.equals(oldName) && !isUpdatingNameFieldFromDisplayName()) {
myArtifactsStructureContext.getOrCreateModifiableArtifactModel().getOrCreateModifiableArtifact(myOriginalArtifact).setName(name);
getEditor().updateOutputPath(oldName, name);
ModifiableArtifactModel modifiableArtifactModel = myArtifactsStructureContext.getOrCreateModifiableArtifactModel();
// Modify artifact name only in case no other artifacts have the same name because names should be unique between the artifacts
if (modifiableArtifactModel.findArtifact(name) == null) {
modifiableArtifactModel.getOrCreateModifiableArtifact(myOriginalArtifact).setName(name);
getEditor().updateOutputPath(oldName, name);
}
}
}

View File

@@ -0,0 +1,81 @@
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.openapi.roots.ui.configuration.artifacts
import com.intellij.openapi.Disposable
import com.intellij.openapi.application.runWriteAction
import com.intellij.openapi.extensions.ExtensionPointName
import com.intellij.openapi.roots.ui.configuration.ModulesConfigurator
import com.intellij.openapi.roots.ui.configuration.ProjectStructureConfigurable
import com.intellij.openapi.roots.ui.configuration.projectRoot.StructureConfigurableContext
import com.intellij.openapi.util.Disposer
import com.intellij.packaging.artifacts.ArtifactListener
import com.intellij.packaging.artifacts.ArtifactManager
import com.intellij.packaging.artifacts.ArtifactType
import com.intellij.packaging.elements.CompositePackagingElement
import com.intellij.packaging.elements.PackagingElementFactory
import com.intellij.packaging.elements.PackagingElementOutputKind
import com.intellij.testFramework.HeavyPlatformTestCase
import com.intellij.util.ui.EmptyIcon
import java.util.function.Supplier
import javax.swing.Icon
class ArtifactTest : HeavyPlatformTestCase() {
fun `test rename`() {
runWithRegisteredExtension(MockArtifactTypeForRename(), ArtifactType.EP_NAME) {
ArtifactManager.getInstance(project).addArtifact("X", MockArtifactTypeForRename(), null)
val artifact = ArtifactManager.getInstance(project).addArtifact("unnamed", MockArtifactTypeForRename(), null)
val context = ArtifactsStructureConfigurableContextImpl(StructureConfigurableContext(project, ModulesConfigurator(project,
ProjectStructureConfigurable.getInstance(
project))),
project,
ArtifactEditorSettings(),
object : ArtifactListener {})
val configurable = ArtifactConfigurable(artifact,
context, Runnable { })
configurable.displayName = "X"
var artifacts = context.actualModifiableModel!!.artifacts.map { it.name }
assertContainsElements(artifacts, "unnamed")
configurable.displayName = "X2"
artifacts = context.actualModifiableModel!!.artifacts.map { it.name }
assertContainsElements(artifacts, "X")
assertContainsElements(artifacts, "X2")
}
}
}
private class MockArtifactTypeForRename : ArtifactType("mock", Supplier { "Mock" }) {
companion object {
fun getInstance() = EP_NAME.findExtension(MockArtifactTypeForRename::class.java)!!
}
override fun getIcon(): Icon = EmptyIcon.ICON_16
override fun getDefaultPathFor(kind: PackagingElementOutputKind): String? = ""
override fun createRootElement(artifactName: String): CompositePackagingElement<*> {
return PackagingElementFactory.getInstance().createArtifactRootElement()
}
}
private inline fun <T> runWithRegisteredExtension(extension: T, extensionPoint: ExtensionPointName<T>, action: () -> Unit) {
val disposable = Disposer.newDisposable()
registerExtension(extension, extensionPoint, disposable)
try {
action()
}
finally {
Disposer.dispose(disposable)
}
}
private fun <T> registerExtension(type: T, extensionPointName: ExtensionPointName<T>, disposable: Disposable) {
val artifactTypeDisposable = Disposer.newDisposable()
Disposer.register(disposable, Disposable {
runWriteAction {
Disposer.dispose(artifactTypeDisposable)
}
})
extensionPointName.point.registerExtension(type, artifactTypeDisposable)
}