Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(intellij): add atomizer label to toolwindow #2203

Merged
merged 1 commit into from
Jul 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
feat(intellij): add atomizer label to toolwindow
  • Loading branch information
MaxKless committed Jul 9, 2024
commit eb91eba6ffc78b4d66656c1a35732d306a26dd88
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import dev.nx.console.nxls.NxlsService
import dev.nx.console.settings.NxConsoleSettingsProvider
import dev.nx.console.telemetry.TelemetryService
import dev.nx.console.utils.Notifier
import dev.nx.console.utils.NxProjectJsonToProjectMap
import dev.nx.console.utils.nxBasePath
import dev.nx.console.utils.sync_services.NxProjectJsonToProjectMap
import java.io.File

private val logger = logger<ProjectPostStartup>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ data class NxWorkspace(
val isEncapsulatedNx: Boolean,
val isPartial: Boolean?,
val workspaceLayout: WorkspaceLayout?,
val cloudStatus: NxCloudStatus?
) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ import kotlinx.coroutines.withContext

class NxToolWindowPanel(private val project: Project) : SimpleToolWindowPanel(true, true) {

private val projectTree = NxProjectsTree()
private val projectTree = NxProjectsTree(project)
private val projectStructure = NxTreeStructure(projectTree, project)

private val projectTreeComponent = ScrollPaneFactory.createScrollPane(projectTree, 0)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ sealed class NxSimpleNode(parent: NxSimpleNode?) : CachingSimpleNode(parent) {
private val displayName: String,
val nxTargetName: String,
val nxProjectName: String,
val nonAtomizedTarget: String? = null,
parent: NxSimpleNode
) : NxSimpleNode(parent) {
override val id: String = "target_${nxProjectName}_$nxTargetName"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,31 @@ package dev.nx.console.nx_toolwindow.tree

import com.intellij.openapi.actionSystem.DataKey
import com.intellij.openapi.actionSystem.DataProvider
import com.intellij.openapi.project.Project
import com.intellij.ui.ColoredTreeCellRenderer
import com.intellij.ui.SimpleTextAttributes
import com.intellij.ui.SimpleTextAttributes.STYLE_SMALLER
import com.intellij.ui.treeStructure.SimpleTree
import com.intellij.util.ui.NamedColorUtil
import com.intellij.util.ui.tree.TreeUtil
import dev.nx.console.models.NxProject
import dev.nx.console.utils.sync_services.NxCloudStatusSyncAccessService
import javax.swing.JTree
import javax.swing.UIManager
import javax.swing.tree.TreeSelectionModel

val NxTreeNodeKey = DataKey.create<NxSimpleNode?>("NX_TREE_NODE")

val NxTreeNodeProjectKey = DataKey.create<NxProject?>("NX_TREE_NODE_PROJECT")

class NxProjectsTree : SimpleTree(), DataProvider {
class NxProjectsTree(project: Project) : SimpleTree(), DataProvider {
init {
isRootVisible = true
isRootVisible = false
showsRootHandles = true
emptyText.text = "There are no nx projects to display."
selectionModel.selectionMode = TreeSelectionModel.SINGLE_TREE_SELECTION

setCellRenderer(AtomizerTreeCellRenderer(project))
}

override fun getData(dataId: String): Any? {
Expand All @@ -28,4 +39,40 @@ class NxProjectsTree : SimpleTree(), DataProvider {
}
return null
}

class AtomizerTreeCellRenderer(private val project: Project) : ColoredTreeCellRenderer() {
private val nxCloudSyncService = NxCloudStatusSyncAccessService.getInstance(project)

override fun customizeCellRenderer(
tree: JTree,
value: Any?,
selected: Boolean,
expanded: Boolean,
leaf: Boolean,
row: Int,
hasFocus: Boolean
) {
val node = TreeUtil.getUserObject(value) as NxSimpleNode

icon = node.icon
append(node.name, SimpleTextAttributes.REGULAR_ATTRIBUTES, true)

if (node is NxSimpleNode.Target && node.nonAtomizedTarget != null) {
val isConnected = nxCloudSyncService.cloudStatus?.isConnected ?: true
append(
" Atomizer",
SimpleTextAttributes(
STYLE_SMALLER,
if (isConnected) NamedColorUtil.getInactiveTextColor()
else UIManager.getColor("Tree.errorForeground")
),
false
)
toolTipText =
"Nx automatically split the potentially slow ${node.nonAtomizedTarget} task into separate tasks for each file. Enable ${if(isConnected) "Nx Agents" else "Nx Cloud"} to benefit from task distribution and flaky task re-runs."
} else {
toolTipText = null
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import com.intellij.execution.executors.DefaultDebugExecutor
import com.intellij.execution.executors.DefaultRunExecutor
import com.intellij.execution.impl.RunDialog
import com.intellij.icons.AllIcons
import com.intellij.icons.ExpUiIcons
import com.intellij.lang.javascript.JavaScriptBundle
import com.intellij.openapi.Disposable
import com.intellij.openapi.actionSystem.*
Expand Down Expand Up @@ -35,7 +36,9 @@ import dev.nx.console.run.*
import dev.nx.console.settings.NxConsoleProjectSettingsProvider
import dev.nx.console.settings.options.ToolWindowStyles
import dev.nx.console.utils.ProjectLevelCoroutineHolderService
import java.awt.Desktop
import java.awt.event.MouseEvent
import java.net.URI
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
Expand Down Expand Up @@ -94,11 +97,15 @@ class NxTreeStructure(

// Tree Node Actions
private fun installPopupActions() {
val nxConnectAction =
ActionManager.getInstance().getAction("dev.nx.console.run.actions.NxConnectAction")
val actionList: MutableList<AnAction> =
mutableListOf(
RunAction(),
RunWithDebugAction(),
EditRunSettingsAction(),
LearnMoreAboutAtomizerAction(),
nxConnectAction,
Separator(),
ShowNxProjectConfigurationAction(),
NxGraphFocusProjectAction(),
Expand Down Expand Up @@ -152,6 +159,8 @@ class NxTreeStructure(
}

override fun update(e: AnActionEvent) {
super.update(e)

val taskSet: NxTaskSet? = createTaskSetFromSelectedNode()
e.presentation.isEnabledAndVisible = taskSet != null
if (taskSet != null) {
Expand All @@ -161,6 +170,11 @@ class NxTreeStructure(
*arrayOf<Any>(taskSet.suggestedName)
)
}

val targetNode = tree.selectedNode as? NxSimpleNode.Target ?: return
if (targetNode.nonAtomizedTarget != null) {
e.presentation.isVisible = false
}
}

override fun actionPerformed(e: AnActionEvent) {
Expand Down Expand Up @@ -209,11 +223,17 @@ class NxTreeStructure(
}

override fun update(e: AnActionEvent) {
super.update(e)
val taskSet: NxTaskSet? = createTaskSetFromSelectedNode()
e.presentation.isEnabledAndVisible = taskSet != null
if (taskSet != null) {
e.presentation.text = executor.getStartActionText(taskSet.suggestedName)
}

val targetNode = tree.selectedNode as? NxSimpleNode.Target
if (targetNode?.nonAtomizedTarget != null) {
e.presentation.isEnabledAndVisible = false
}
}

override fun actionPerformed(e: AnActionEvent) {
Expand All @@ -230,6 +250,19 @@ class NxTreeStructure(
}
}

private inner class LearnMoreAboutAtomizerAction : AnAction(ExpUiIcons.Toolwindow.Web) {
override fun actionPerformed(e: AnActionEvent) {
val url = "https://nx.dev/ci/features/split-e2e-tasks?utm_source=nx-console"
Desktop.getDesktop().browse(URI.create(url))
}

override fun update(e: AnActionEvent) {
val targetNode = tree.selectedNode as? NxSimpleNode.Target
e.presentation.text = "Learn More About Atomizer"
e.presentation.isEnabledAndVisible = targetNode?.nonAtomizedTarget != null
}
}

private fun createTaskSetFromSelectedNode(): NxTaskSet? {
val targetNode = tree.selectedNode as? NxSimpleNode.Target

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ abstract class NxTreeBuilderBase(private val nxWorkspace: NxWorkspace?) {
displayName = it.key,
nxTargetName = it.key,
nxProjectName = projectNode.nxProjectName,
nonAtomizedTarget = it.value.metadata?.nonAtomizedTarget,
parent = projectNode
)
}
Expand Down Expand Up @@ -116,6 +117,7 @@ abstract class NxTreeBuilderBase(private val nxWorkspace: NxWorkspace?) {
displayName = it.key,
nxTargetName = it.key,
nxProjectName = targetGroupNode.nxProjectName,
nonAtomizedTarget = it.value.metadata?.nonAtomizedTarget,
parent = targetGroupNode
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFile
import dev.nx.console.models.NxVersion
import dev.nx.console.nxls.NxRefreshWorkspaceAction
import dev.nx.console.utils.NxVersionUtil
import dev.nx.console.utils.sync_services.NxVersionUtil
import java.util.function.Supplier

class ProjectDetailsEditorWithPreview(project: Project, file: VirtualFile) :
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,33 @@ import com.intellij.openapi.application.EDT
import com.intellij.openapi.components.Service
import com.intellij.openapi.project.Project
import dev.nx.console.NxIcons
import dev.nx.console.nx_toolwindow.tree.NxSimpleNode
import dev.nx.console.nx_toolwindow.tree.NxTreeNodeKey
import dev.nx.console.nxls.NxlsService
import dev.nx.console.telemetry.TelemetryService
import dev.nx.console.utils.Notifier
import dev.nx.console.utils.NxGeneralCommandLine
import dev.nx.console.utils.sync_services.NxCloudStatusSyncAccessService
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

class NxConnectAction : AnAction() {

override fun update(e: AnActionEvent) {
val project = e.project ?: return
val nxCloudStatusSyncAccessService = NxCloudStatusSyncAccessService.getInstance(project)
if (nxCloudStatusSyncAccessService.cloudStatus?.isConnected == true) {
e.presentation.isEnabledAndVisible = false
return
}
val nxTreeNode = e.getData(NxTreeNodeKey) ?: return
if (nxTreeNode !is NxSimpleNode.Target || nxTreeNode.nonAtomizedTarget == null) {
e.presentation.isEnabledAndVisible = false
}
}

override fun actionPerformed(e: AnActionEvent) {
val project = e.project ?: return
NxConnectService.getInstance(project).connectToCloud()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import com.intellij.psi.PsiRecursiveElementWalkingVisitor
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.psi.util.elementType
import com.intellij.psi.util.parentOfType
import dev.nx.console.utils.sync_services.NxProjectJsonToProjectMap
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.contract

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package dev.nx.console.utils.sync_services

import com.intellij.openapi.components.Service
import com.intellij.openapi.project.Project
import dev.nx.console.models.NxCloudStatus
import dev.nx.console.nxls.NxWorkspaceRefreshListener
import dev.nx.console.nxls.NxlsService
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch

@Service(Service.Level.PROJECT)
class NxCloudStatusSyncAccessService(private val project: Project, private val cs: CoroutineScope) {
var cloudStatus: NxCloudStatus? = null
private set

private var nxlsService = NxlsService.getInstance(project)

init {
cs.launch {
nxlsService.awaitStarted()
cloudStatus = nxlsService.cloudStatus()
}

project.messageBus
.connect()
.subscribe(
NxlsService.NX_WORKSPACE_REFRESH_TOPIC,
NxWorkspaceRefreshListener {
if (project.isDisposed) {
return@NxWorkspaceRefreshListener
}
cs.launch { cloudStatus = nxlsService.cloudStatus() }
}
)
}

companion object {
fun getInstance(project: Project): NxCloudStatusSyncAccessService {
return project.getService(NxCloudStatusSyncAccessService::class.java)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package dev.nx.console.utils
package dev.nx.console.utils.sync_services

import com.intellij.json.psi.JsonFile
import com.intellij.openapi.components.Service
Expand All @@ -7,6 +7,7 @@ import com.intellij.psi.PsiFile
import dev.nx.console.models.NxProject
import dev.nx.console.nxls.NxWorkspaceRefreshListener
import dev.nx.console.nxls.NxlsService
import dev.nx.console.utils.findNxConfigurationFiles
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package dev.nx.console.utils
package dev.nx.console.utils.sync_services

import com.intellij.openapi.components.Service
import com.intellij.openapi.project.Project
Expand Down
2 changes: 2 additions & 0 deletions apps/intellij/src/main/resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@
text="Nx: Analyze Nx Configuration Files"
description="Makes sure project.json and nx.json files conform to the schema"/>

<action id="dev.nx.console.run.actions.NxConnectAction" class="dev.nx.console.run.actions.NxConnectAction"
text="Nx: Connect to Nx Cloud" icon="dev.nx.console.NxIcons.Action"/>
<!-- Generate -->
<action id="dev.nx.console.generate.actions.NxGenerateUiAction"
class="dev.nx.console.generate.actions.NxGenerateUiAction"
Expand Down
Loading