IDEA-354377 Create workspace via Project Wizard

GitOrigin-RevId: dee62d7a48553003b9efd12ea24b315e85d25343
This commit is contained in:
Dmitry Avdeev
2024-06-03 14:30:34 +02:00
committed by intellij-monorepo-bot
parent 4b32abfd69
commit ebfbc30fc8
15 changed files with 238 additions and 244 deletions

View File

@@ -13,6 +13,7 @@ import com.intellij.ide.util.projectWizard.*;
import com.intellij.ide.wizard.*;
import com.intellij.ide.wizard.LanguageNewProjectWizard;
import com.intellij.ide.wizard.language.*;
import com.intellij.ide.workspace.configuration.NewWorkspaceWizard;
import com.intellij.internal.statistic.utils.PluginInfoDetectorKt;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.diagnostic.Logger;
@@ -377,6 +378,7 @@ public final class ProjectTypeStep extends ModuleWizardStep implements SettingsS
generators.sort(Comparator.comparing(it -> it.getOrdinal()));
if (context.isCreatingNewProject()) {
generators.add(new EmptyProjectGeneratorNewProjectWizard());
generators.add(new NewWorkspaceWizard());
}
var generatorItems = ContainerUtil.map(generators, it -> new LanguageGeneratorItem(it));
for (var generatorItem : generatorItems) {

View File

@@ -560,15 +560,9 @@ tooltip.bookmarked=Bookmarked
progress.title.searching.for.classes.for.test=Searching for classes for test\u2026
progress.title.searching.for.tests.for.class=Searching for tests for class\u2026
chooser.title.select.workspace.directory=Select Workspace Location
chooser.title.select.file.or.directory.to.import=Select Files or Directories to Import
new.workspace.dialog.title=New Workspace
manage.workspace.dialog.title=Manage Workspace
new.workspace.dialog.name.label=Name:
new.workspace.dialog.location.label=Location:
new.workspace.dialog.hint=Projects will be opened in the same window as a workspace. \
Projects remain in the same place on disk, \
the workspace only stores common project settings.
new.workspace.dialog.default.workspace.name=workspace
prompt.open.project.attach.to.workspace=<html><body>Projects can either be opened in a new window, or replace the project in the current window, \
or be attached to the current workspace.<br>How would you like to open the project?</body></html>
@@ -581,6 +575,8 @@ project.remove.confirmation.prompt=Remove {1, choice, 1#project|2#projects} {0}
border.title.linked.projects=Linked Projects
action.add.projects.text=Add Projects\u2026
dialog.message.project.can.t.be.added.to.workspace=Project ''{0}'' cannot be added to workspace
workspace.project.type.name=Workspace
workspace.project.type.comment=A special project type intended to work with multiple projects in the same window
target.usages.option=usages
target.text.occurrences.option=text occurrences

View File

@@ -19202,6 +19202,14 @@ c:com.intellij.ide.util.treeView.SmartElementDescriptor
- p:isMarkModified():Z
- p:isMarkReadOnly():Z
- update():Z
f:com.intellij.ide.workspace.configuration.NewWorkspaceWizard
- com.intellij.ide.wizard.GeneratorNewProjectWizard
- <init>():V
- createStep(com.intellij.ide.util.projectWizard.WizardContext):com.intellij.ide.wizard.NewProjectWizardStep
- getIcon():javax.swing.Icon
- getId():java.lang.String
- getName():java.lang.String
- isEnabled():Z
f:com.intellij.internal.AddInlayBlockInternalAction
- com.intellij.openapi.actionSystem.AnAction
- com.intellij.openapi.project.DumbAware

View File

@@ -1,68 +0,0 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.ide.workspace
import com.intellij.ide.impl.OpenProjectTask
import com.intellij.ide.impl.TrustedPaths
import com.intellij.openapi.actionSystem.ActionUpdateThread
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.project.DumbAwareAction
import com.intellij.openapi.project.Project
import com.intellij.openapi.project.ex.ProjectManagerEx
import com.intellij.openapi.startup.StartupManager
import com.intellij.util.concurrency.annotations.RequiresEdt
import java.nio.file.Files
import java.nio.file.Path
internal abstract class BaseWorkspaceAction(private val workspaceOnly: Boolean): DumbAwareAction() {
override fun getActionUpdateThread() = ActionUpdateThread.BGT
override fun update(e: AnActionEvent) {
val project = e.project
e.presentation.isEnabledAndVisible = isWorkspaceSupportEnabled &&
project != null &&
(workspaceOnly && project.isWorkspace
|| !workspaceOnly && getAllSubprojects(project).isNotEmpty())
}
}
internal open class CreateWorkspaceAction: BaseWorkspaceAction(false) {
override fun actionPerformed(e: AnActionEvent) {
val project = requireNotNull(e.project)
createWorkspace(project)
}
}
@RequiresEdt
internal fun createWorkspace(project: Project) {
val subprojects = getAllSubprojects(project).associateBy { it.projectPath }
val dialog = NewWorkspaceDialog(project, subprojects.values, true)
if (!dialog.showAndGet()) return
ApplicationManager.getApplication().executeOnPooledThread {
val workspace = createAndOpenWorkspaceProject(project, dialog.projectPath, dialog.projectName)
?: return@executeOnPooledThread
StartupManager.getInstance(workspace).runAfterOpened {
addToWorkspace(workspace, dialog.projectPaths)
}
}
}
private fun createAndOpenWorkspaceProject(project: Project,
workspacePath: Path,
projectName: String): Project? {
val options = OpenProjectTask {
projectToClose = project
this.projectName = projectName
forceReuseFrame = true
isNewProject = true
isProjectCreatedWithWizard = true
isRefreshVfsNeeded = true
beforeOpen = { workspace ->
setWorkspace(workspace)
true
}
}
Files.createDirectories(workspacePath)
TrustedPaths.getInstance().setProjectPathTrusted(workspacePath, true)
return ProjectManagerEx.getInstanceEx().openProject(workspacePath, options)
}

View File

@@ -1,157 +0,0 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.ide.workspace
import com.intellij.ide.IdeBundle
import com.intellij.ide.RecentProjectsManager
import com.intellij.ide.workspace.projectView.isWorkspaceNode
import com.intellij.lang.LangBundle
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.fileChooser.FileChooser
import com.intellij.openapi.fileChooser.FileChooserDescriptor
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory
import com.intellij.openapi.project.Project
import com.intellij.openapi.project.guessProjectDir
import com.intellij.openapi.ui.DialogWrapper
import com.intellij.openapi.ui.TextFieldWithBrowseButton
import com.intellij.openapi.util.NlsSafe
import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.ui.*
import com.intellij.ui.components.JBList
import com.intellij.ui.components.JBTextField
import com.intellij.ui.dsl.builder.*
import com.intellij.util.SystemProperties
import java.io.File
import java.nio.file.Path
import java.nio.file.Paths
import javax.swing.Action
import javax.swing.Icon
import javax.swing.JComponent
import javax.swing.JList
import kotlin.io.path.pathString
internal class NewWorkspaceDialog(
private val project: Project,
initialProjects: Collection<Subproject>,
private val isNewWorkspace: Boolean
) : DialogWrapper(project) {
private lateinit var nameField: JBTextField
private lateinit var locationField: TextFieldWithBrowseButton
private val listModel = CollectionListModel(initialProjects.map { Item(it.name, it.projectPath, it.handler.subprojectIcon) })
private val projectList = JBList(listModel).apply {
cellRenderer = Renderer().apply { iconTextGap = 3 }
}
val projectName: String get() = nameField.text
val location: Path get() = Paths.get(locationField.text)
val projectPath: Path get() = location.resolve(nameField.text)
init {
if (isNewWorkspace) {
title = LangBundle.message("new.workspace.dialog.title")
okAction.putValue(Action.NAME, IdeBundle.message("button.create"))
}
else {
title = LangBundle.message("manage.workspace.dialog.title")
}
init()
}
val projectPaths: List<String>
get() = listModel.items.map { it.path }
override fun createCenterPanel(): JComponent {
val suggestLocation = RecentProjectsManager.getInstance().suggestNewProjectLocation()
val suggestName = if (isNewWorkspace)
FileUtil.createSequentFileName(File(suggestLocation),
LangBundle.message("new.workspace.dialog.default.workspace.name"), "") { !it.exists() }
else
project.name
val toolbarDecorator = ToolbarDecorator.createDecorator(projectList)
.setPanelBorder(IdeBorderFactory.createTitledBorder(LangBundle.message("border.title.linked.projects")))
.disableUpDownActions()
.setAddActionName(LangBundle.message("action.add.projects.text"))
.setAddAction { addProjects() }
return panel {
row(LangBundle.message("new.workspace.dialog.name.label")) {
nameField = textField()
.text(suggestName)
.columns(COLUMNS_MEDIUM)
.align(Align.FILL)
.component
}
if (isNewWorkspace) {
row(LangBundle.message("new.workspace.dialog.location.label")) {
val descriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor()
descriptor.isHideIgnored = false
descriptor.title = LangBundle.message("chooser.title.select.workspace.directory")
locationField = textFieldWithBrowseButton(project = project, fileChooserDescriptor = descriptor)
.text(suggestLocation)
.columns(COLUMNS_MEDIUM)
.align(Align.FILL)
.component
}
}
row {
cell(toolbarDecorator.createPanel()).align(Align.FILL)
}
if (isNewWorkspace) {
row {
comment(LangBundle.message("new.workspace.dialog.hint"), maxLineLength = 60)
}
}
}
}
override fun doOKAction() {
if (isNewWorkspace) {
RecentProjectsManager.getInstance().setLastProjectCreationLocation(location)
}
super.doOKAction()
}
private fun addProjects() {
val files = browseForProjects(project)
val allItems = listModel.items
for (file in files) {
if (allItems.any { it.path == file.path }) continue
val handler = getHandlers(file).firstOrNull() ?: continue
listModel.add(Item(file.name, file.path, handler.subprojectIcon))
}
}
private data class Item(@NlsSafe val name: String, @NlsSafe val path: String, val icon: Icon?)
private class Renderer: ColoredListCellRenderer<Item>() {
override fun customizeCellRenderer(list: JList<out Item>, value: Item?, index: Int, selected: Boolean, hasFocus: Boolean) {
value ?: return
icon = value.icon
append(value.name + " ", SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES)
@Suppress("HardCodedStringLiteral") val userHome = SystemProperties.getUserHome()
append(Path.of(value.path).parent.pathString.replaceFirst(userHome, "~"), SimpleTextAttributes.GRAY_ATTRIBUTES)
}
}
}
private fun browseForProjects(project: Project): Array<out VirtualFile> {
val handlers = SubprojectHandler.EP_NAME.extensionList
val descriptor = FileChooserDescriptor(true, true, false, false, false, true)
descriptor.title = LangBundle.message("chooser.title.select.file.or.directory.to.import")
descriptor.withFileFilter { file -> handlers.any { it.canImportFromFile(file) } }
return FileChooser.chooseFiles(descriptor, project, project.guessProjectDir()?.parent)
}
internal class AddProjectsToWorkspaceAction: BaseWorkspaceAction(true) {
override fun actionPerformed(e: AnActionEvent) {
val project = requireNotNull(e.project)
val files = browseForProjects(project)
if (files.isEmpty()) return
addToWorkspace(project, files.map { it.path })
}
override fun update(e: AnActionEvent) {
e.presentation.isEnabledAndVisible = isWorkspaceNode(e)
}
}

View File

@@ -0,0 +1,20 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.ide.workspace.configuration
import com.intellij.ide.workspace.getAllSubprojects
import com.intellij.ide.workspace.isWorkspace
import com.intellij.ide.workspace.isWorkspaceSupportEnabled
import com.intellij.openapi.actionSystem.ActionUpdateThread
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.project.DumbAwareAction
internal abstract class BaseWorkspaceAction(private val workspaceOnly: Boolean): DumbAwareAction() {
override fun getActionUpdateThread() = ActionUpdateThread.BGT
override fun update(e: AnActionEvent) {
val project = e.project
e.presentation.isEnabledAndVisible = isWorkspaceSupportEnabled &&
project != null &&
(workspaceOnly && project.isWorkspace
|| !workspaceOnly && getAllSubprojects(project).isNotEmpty())
}
}

View File

@@ -1,8 +1,11 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.ide.workspace
package com.intellij.ide.workspace.configuration
import com.intellij.ide.projectView.ProjectView
import com.intellij.ide.workspace.addToWorkspace
import com.intellij.ide.workspace.getAllSubprojects
import com.intellij.ide.workspace.projectView.isWorkspaceNode
import com.intellij.ide.workspace.removeSubprojects
import com.intellij.idea.ActionsBundle
import com.intellij.openapi.actionSystem.ActionPlaces
import com.intellij.openapi.actionSystem.AnActionEvent
@@ -11,14 +14,14 @@ import com.intellij.openapi.project.ex.ProjectEx
internal open class ManageWorkspaceAction: BaseWorkspaceAction(true) {
override fun actionPerformed(e: AnActionEvent) {
val project = requireNotNull(e.project)
val subprojects = getAllSubprojects(project)
val dialog = NewWorkspaceDialog(project, subprojects, false)
val dialog = ManageWorkspaceDialog(project)
if (!dialog.showAndGet()) return
if (dialog.projectName != project.name) {
(project as ProjectEx).setProjectName(dialog.projectName)
ProjectView.getInstance(project).currentProjectViewPane?.updateFromRoot(true)
}
val subprojects = getAllSubprojects(project)
val set = dialog.projectPaths.toSet()
val removed = subprojects.filter { !set.contains(it.projectPath) }
removeSubprojects(project, removed)

View File

@@ -0,0 +1,75 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.ide.workspace.configuration
import com.intellij.ide.workspace.SubprojectHandler
import com.intellij.ide.workspace.addToWorkspace
import com.intellij.ide.workspace.projectView.isWorkspaceNode
import com.intellij.lang.LangBundle
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.fileChooser.FileChooser
import com.intellij.openapi.fileChooser.FileChooserDescriptor
import com.intellij.openapi.project.Project
import com.intellij.openapi.project.guessProjectDir
import com.intellij.openapi.ui.DialogWrapper
import com.intellij.openapi.ui.TextFieldWithBrowseButton
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.ui.components.JBTextField
import com.intellij.ui.dsl.builder.*
import java.nio.file.Path
import java.nio.file.Paths
import javax.swing.JComponent
internal class ManageWorkspaceDialog(private val project: Project) : DialogWrapper(project) {
private lateinit var nameField: JBTextField
private lateinit var locationField: TextFieldWithBrowseButton
private val subprojectList: SubprojectList = SubprojectList(project)
val projectName: String get() = nameField.text
val location: Path get() = Paths.get(locationField.text)
val projectPath: Path get() = location.resolve(nameField.text)
init {
title = LangBundle.message("manage.workspace.dialog.title")
init()
}
val projectPaths: List<String>
get() = subprojectList.projectPaths
override fun createCenterPanel(): JComponent {
return panel {
row(LangBundle.message("new.workspace.dialog.name.label")) {
nameField = textField()
.text(project.name)
.columns(COLUMNS_MEDIUM)
.align(Align.FILL)
.component
}
row {
cell(subprojectList.createDecorator().createPanel()).align(Align.FILL)
}
}
}
}
internal fun browseForProjects(project: Project?): Array<out VirtualFile> {
val handlers = SubprojectHandler.EP_NAME.extensionList
val descriptor = FileChooserDescriptor(true, true, false, false, false, true)
descriptor.title = LangBundle.message("chooser.title.select.file.or.directory.to.import")
descriptor.withFileFilter { file -> handlers.any { it.canImportFromFile(file) } }
return FileChooser.chooseFiles(descriptor, project, project?.guessProjectDir()?.parent)
}
internal class AddProjectsToWorkspaceAction: BaseWorkspaceAction(true) {
override fun actionPerformed(e: AnActionEvent) {
val project = requireNotNull(e.project)
val files = browseForProjects(project)
if (files.isEmpty()) return
addToWorkspace(project, files.map { it.path })
}
override fun update(e: AnActionEvent) {
e.presentation.isEnabledAndVisible = isWorkspaceNode(e)
}
}

View File

@@ -0,0 +1,56 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.ide.workspace.configuration
import com.intellij.icons.ExpUiIcons
import com.intellij.ide.impl.ProjectUtil
import com.intellij.ide.util.projectWizard.WizardContext
import com.intellij.ide.wizard.*
import com.intellij.ide.wizard.NewProjectWizardChainStep.Companion.nextStep
import com.intellij.ide.wizard.comment.CommentNewProjectWizardStep
import com.intellij.ide.workspace.addToWorkspace
import com.intellij.ide.workspace.isWorkspaceSupportEnabled
import com.intellij.ide.workspace.setWorkspace
import com.intellij.lang.LangBundle
import com.intellij.openapi.project.Project
import com.intellij.openapi.startup.StartupManager
import com.intellij.ui.dsl.builder.Align
import com.intellij.ui.dsl.builder.Panel
import org.jetbrains.annotations.Nls
import javax.swing.Icon
class NewWorkspaceWizard: GeneratorNewProjectWizard {
override val id: String = "jb.workspace"
override val name: @Nls(capitalization = Nls.Capitalization.Title) String = LangBundle.message("workspace.project.type.name")
override val icon: Icon = ExpUiIcons.Nodes.Workspace
override fun createStep(context: WizardContext): NewProjectWizardStep =
RootNewProjectWizardStep(context)
.nextStep(::CommentStep)
.nextStep { parent -> newProjectWizardBaseStepWithoutGap(parent).apply { defaultName = "workspace" } }
.nextStep(::GitNewProjectWizardStep)
.nextStep(::Step)
override fun isEnabled(): Boolean = isWorkspaceSupportEnabled
private class CommentStep(parent: NewProjectWizardStep) : CommentNewProjectWizardStep(parent) {
override val comment: @Nls(capitalization = Nls.Capitalization.Sentence) String =
LangBundle.message("workspace.project.type.comment")
}
private class Step(parent: NewProjectWizardStep) : AbstractNewProjectWizardStep(parent) {
private val subprojectList = SubprojectList(ProjectUtil.getActiveProject())
override fun setupUI(builder: Panel) {
builder.row {
cell(subprojectList.createDecorator().createPanel()).align(Align.FILL)
}
}
override fun setupProject(project: Project) {
setWorkspace(project)
StartupManager.getInstance(project).runAfterOpened {
addToWorkspace(project, subprojectList.projectPaths)
}
}
}
}

View File

@@ -0,0 +1,60 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.ide.workspace.configuration
import com.intellij.ide.workspace.getAllSubprojects
import com.intellij.ide.workspace.getHandlers
import com.intellij.lang.LangBundle
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.NlsSafe
import com.intellij.ui.*
import com.intellij.ui.components.JBList
import com.intellij.util.SystemProperties
import java.nio.file.Path
import javax.swing.Icon
import javax.swing.JList
import kotlin.io.path.pathString
internal class SubprojectList(private val currentProject: Project?) {
private val listModel: CollectionListModel<Item>
private val projectList: JBList<Item>
init {
val subprojects = currentProject?.let { getAllSubprojects(currentProject) } ?: emptyList()
listModel = CollectionListModel(subprojects.map { Item(it.name, it.projectPath, it.handler.subprojectIcon) })
projectList = JBList(listModel).apply {
cellRenderer = Renderer().apply { iconTextGap = 3 }
}
}
fun createDecorator(): ToolbarDecorator = ToolbarDecorator.createDecorator(projectList)
.setPanelBorder(IdeBorderFactory.createTitledBorder(LangBundle.message("border.title.linked.projects")))
.disableUpDownActions()
.setAddActionName(LangBundle.message("action.add.projects.text"))
.setAddAction { addProjects() }
val projectPaths: List<String>
get() = listModel.items.map { it.path }
private data class Item(@NlsSafe val name: String, @NlsSafe val path: String, val icon: Icon?)
private class Renderer: ColoredListCellRenderer<Item>() {
override fun customizeCellRenderer(list: JList<out Item>, value: Item?, index: Int, selected: Boolean, hasFocus: Boolean) {
value ?: return
icon = value.icon
append(value.name + " ", SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES)
@Suppress("HardCodedStringLiteral") val userHome = SystemProperties.getUserHome()
append(Path.of(value.path).parent.pathString.replaceFirst(userHome, "~"), SimpleTextAttributes.GRAY_ATTRIBUTES)
}
}
private fun addProjects() {
val files = browseForProjects(currentProject)
val allItems = listModel.items
for (file in files) {
if (allItems.any { it.path == file.path }) continue
val handler = getHandlers(file).firstOrNull() ?: continue
listModel.add(Item(file.name, file.path, handler.subprojectIcon))
}
}
}

View File

@@ -10754,10 +10754,12 @@ f:com.intellij.ide.wizard.NewProjectWizardBaseStep
- com.intellij.ide.wizard.NewProjectWizardBaseData
- sf:Companion:com.intellij.ide.wizard.NewProjectWizardBaseStep$Companion
- <init>(com.intellij.ide.wizard.NewProjectWizardStep):V
- f:getDefaultName():java.lang.String
- getName():java.lang.String
- getNameProperty():com.intellij.openapi.observable.properties.GraphProperty
- getPath():java.lang.String
- getPathProperty():com.intellij.openapi.observable.properties.GraphProperty
- f:setDefaultName(java.lang.String):V
- setName(java.lang.String):V
- setPath(java.lang.String):V
- setupProject(com.intellij.openapi.project.Project):V

View File

@@ -43,6 +43,8 @@ class NewProjectWizardBaseStep(parent: NewProjectWizardStep) : AbstractNewProjec
override var name: String by nameProperty
override var path: String by pathProperty
var defaultName: String = "untitled"
internal var bottomGap: Boolean = true
private fun suggestLocation(): Path {
@@ -70,8 +72,8 @@ class NewProjectWizardBaseStep(parent: NewProjectWizardStep) : AbstractNewProjec
private fun suggestUniqueName(): String {
val moduleNames = findAllModules().map { it.name }.toSet()
val path = path.toNioPathOrNull() ?: return "untitled"
return FileUtil.createSequentFileName(path.toFile(), "untitled", "") {
val path = path.toNioPathOrNull() ?: return defaultName
return FileUtil.createSequentFileName(path.toFile(), defaultName, "") {
!it.exists() && it.name !in moduleNames
}
}

View File

@@ -2800,7 +2800,6 @@ action.SwitchFileBasedIndexStorageAction.text=Switch File-Based Index Storage
action.AddToWorkspace.text=Add Projects to Workspace\u2026
action.ManageWorkspace.text=Manage Workspace\u2026
action.CreateWorkspace.text=Create Workspace\u2026
action.remove.workspace.subproject.x.text=Remove ''{0}'' from Workspace
action.remove.workspace.subprojects.text=Remove Projects from Workspace

View File

@@ -1656,15 +1656,14 @@
<group id="WorkspaceProjectGroup">
<separator/>
<action id="ManageWorkspace" class="com.intellij.ide.workspace.ManageWorkspaceAction"/>
<action id="CreateWorkspace" class="com.intellij.ide.workspace.CreateWorkspaceAction"/>
<action id="ManageWorkspace" class="com.intellij.ide.workspace.configuration.ManageWorkspaceAction"/>
<separator/>
<add-to-group group-id="FileMenu" relative-to-action="FileMainSettingsGroup" anchor="before"/>
</group>
<group id="WorkspaceContextMenuGroup">
<action id="AddToWorkspace" class="com.intellij.ide.workspace.AddProjectsToWorkspaceAction"/>
<action id="ManageWorkspacePopup" class="com.intellij.ide.workspace.ManageWorkspacePopupAction" use-shortcut-of="EditSource"/>
<action id="AddToWorkspace" class="com.intellij.ide.workspace.configuration.AddProjectsToWorkspaceAction"/>
<action id="ManageWorkspacePopup" class="com.intellij.ide.workspace.configuration.ManageWorkspacePopupAction" use-shortcut-of="EditSource"/>
<separator/>
<add-to-group group-id="ProjectViewPopupMenu" anchor="first"/>
</group>

View File

@@ -9,7 +9,6 @@ import com.intellij.testFramework.useProject
import kotlinx.coroutines.runBlocking
import org.jetbrains.plugins.gradle.testFramework.GradleTestCase
import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
@@ -29,8 +28,6 @@ class WorkspaceTest: GradleTestCase() {
val event = TestActionEvent.createTestEvent(SimpleDataContext.getProjectContext(it))
ActionManager.getInstance().getAction("ManageWorkspace").update(event)
assertFalse(event.presentation.isEnabledAndVisible)
ActionManager.getInstance().getAction("CreateWorkspace").update(event)
assertTrue(event.presentation.isEnabledAndVisible)
}
}
}