mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-03-22 15:19:59 +07:00
IDEA-354377 Create workspace via Project Wizard
GitOrigin-RevId: dee62d7a48553003b9efd12ea24b315e85d25343
This commit is contained in:
committed by
intellij-monorepo-bot
parent
4b32abfd69
commit
ebfbc30fc8
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user