diff --git a/build.gradle.kts b/build.gradle.kts index 387ce14..cb42c4f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -5,7 +5,7 @@ plugins { } group = "com.dashwave" -version = "2.0.1" +version = "3.1.0" repositories { mavenCentral() @@ -18,7 +18,7 @@ dependencies { // Configure Gradle IntelliJ Plugin // Read more: https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html intellij { - version.set("2022.2.5") + version.set("2024.1.1") type.set("IC") // Target IDE Platform plugins.set(listOf(/* Plugin Dependencies */)) @@ -35,8 +35,8 @@ tasks { } patchPluginXml { - sinceBuild.set("222") - untilBuild.set("232.*") + sinceBuild.set("222.*") + untilBuild.set("241.*") } signPlugin { diff --git a/src/main/kotlin/com/dashwave/plugin/PluginConfiguration.kt b/src/main/kotlin/com/dashwave/plugin/PluginConfiguration.kt new file mode 100644 index 0000000..1df9f86 --- /dev/null +++ b/src/main/kotlin/com/dashwave/plugin/PluginConfiguration.kt @@ -0,0 +1,35 @@ +package com.dashwave.plugin + +import com.intellij.openapi.components.PersistentStateComponent +import com.intellij.openapi.components.Service +import com.intellij.openapi.components.ServiceManager +import com.intellij.openapi.components.State +import com.intellij.openapi.components.Storage + +@Service +@State( + name = "PluginSettings", + storages = [Storage("PluginSettings.xml")] +) +class PluginConfiguration: PersistentStateComponent { + data class State( + var pluginMode:String = "local", + var pluginEnv:String = "" + ) + + private var myState = State() + + override fun getState(): State { + return myState + } + + override fun loadState(state: State) { + myState = state + } + + companion object { + fun getInstance(): PluginConfiguration { + return ServiceManager.getService(PluginConfiguration::class.java) + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/dashwave/plugin/PluginStartup.kt b/src/main/kotlin/com/dashwave/plugin/PluginStartup.kt index 8a82c6a..91a92b0 100644 --- a/src/main/kotlin/com/dashwave/plugin/PluginStartup.kt +++ b/src/main/kotlin/com/dashwave/plugin/PluginStartup.kt @@ -1,44 +1,132 @@ package com.dashwave.plugin +import com.dashwave.plugin.actions.BuildAction import com.dashwave.plugin.dialogbox.CreateProjectDialog +import com.dashwave.plugin.dialogbox.GitNotConfiguredDialog import com.dashwave.plugin.dialogbox.ReadyForBuildDialog import com.dashwave.plugin.messages.Messages +import com.dashwave.plugin.notif.BalloonNotif import com.dashwave.plugin.utils.DwCmds import com.dashwave.plugin.utils.Process import com.dashwave.plugin.windows.DashwaveWindow import com.intellij.execution.filters.HyperlinkInfo import com.intellij.execution.ui.ConsoleViewContentType import com.intellij.notification.* +import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.editor.markup.TextAttributes import com.intellij.openapi.project.Project import com.intellij.openapi.startup.StartupActivity +import com.intellij.openapi.ui.DialogWrapper +import kotlinx.serialization.json.* +import com.intellij.openapi.wm.ToolWindowManager import java.awt.Color import java.io.File import java.io.IOException import okhttp3.OkHttpClient import okhttp3.Request +import com.intellij.openapi.actionSystem.ActionManager +import com.intellij.openapi.actionSystem.Anchor +import com.intellij.openapi.actionSystem.Constraints +import com.intellij.openapi.actionSystem.DefaultActionGroup +import com.intellij.openapi.application.PathManager +import com.intellij.openapi.components.ServiceManager +import io.ktor.util.* import java.awt.Font +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import javax.swing.SwingUtilities +var PluginMode:String = "" +var PluginEnv:String = "" class PluginStartup: StartupActivity { + companion object { + var dwWindows:HashMap = HashMap() + var pluginMode:String = "" + var pluginEnv:String = "" + } + + init { + val pluginConfig = PluginConfiguration.getInstance() + pluginMode = pluginConfig.state.pluginMode + pluginEnv = pluginConfig.state.pluginEnv + } override fun runActivity(project: Project) { - DashwaveWindow.p = project - DashwaveWindow.show() - checkDW(project) + val dwWindow = DashwaveWindow(project) + dwWindow.show() + dwWindows[project.name] = dwWindow + checkDW(project, dwWindow) + +// start terminate gradle sync worker if pluginMode is 'workspace' + if(pluginMode == "workspace"){ + terminateGradleSync(project.basePath, dwWindow) + } + PluginMode = pluginMode + PluginEnv = pluginEnv } } -fun checkDW(project: Project) { - val dwCmd = DwCmds("check-update", "", true) +fun terminateGradleSync(pwd: String?, dwWindow: DashwaveWindow) { + println("Attempting to terminate Gradle sync...") + + GlobalScope.launch { + var attempt = 0 + val maxAttempts = 5 + val delayTime = 500L // 1 seconds delay + + while (attempt < maxAttempts) { + try { + val processBuilder = ProcessBuilder("./gradlew", "--stop") + if (pwd != null) { + processBuilder.directory(File(pwd)) + } + processBuilder.redirectErrorStream(true) + val process = processBuilder.start() + + val exitCode = process.waitFor() + println("Process exit code: $exitCode") + + SwingUtilities.invokeLater { + if (exitCode == 0) { + println("Rendering Dashwave window to hide sync") + dwWindow.show() +// break + } else { + dwWindow.displayInfo(Messages.GRADLE_SYNC_CANCELLATION_FAILED) + } + } + } catch (e: Exception) { + println("Exception occurred: ${e.message}") + dwWindow.displayInfo("Failed to start the process: ${e.message}") + } + + attempt++ + if (attempt < maxAttempts) { + println("Retrying in $delayTime milliseconds... (Attempt $attempt of $maxAttempts)") + delay(delayTime) + } else { + println("Max retry attempts reached.") + } + } + } + + SwingUtilities.invokeLater { + dwWindow.show() + } +} + +fun checkDW(project: Project, dwWindow: DashwaveWindow) { + val dwCmd = DwCmds("check-update", project.basePath, true, dwWindow) val exitCode = dwCmd.executeWithExitCode() if (exitCode == 0) { - DashwaveWindow.displayInfo(Messages.DW_INSTALLED_ALREADY) - verifyLogin(project?.basePath) + dwWindow.displayInfo(Messages.DW_INSTALLED_ALREADY) + verifyLogin(project?.basePath, dwWindow) }else if(exitCode == 11){ }else{ - DashwaveWindow.displayInfo(Messages.DW_NOT_INSTALLED) + dwWindow.displayInfo(Messages.DW_NOT_INSTALLED) showInstallDW(project) - installDW(project?.basePath) + installDW(project?.basePath, dwWindow) } } @@ -53,64 +141,133 @@ fun showInstallDW(project: Project){ notification.notify(project) } -fun installDW(pwd: String?){ - DashwaveWindow.displayOutput("🔨 Setting up plugin...\n\n", ConsoleViewContentType.NORMAL_OUTPUT) +fun installDW(pwd: String?, dwWindow: DashwaveWindow){ + dwWindow.displayOutput("🔨 Setting up plugin...\n\n", ConsoleViewContentType.NORMAL_OUTPUT) // Execute the script - val process = Process("curl -sSL https://cli.dashwave.io | bash", pwd, true) - process.start() + val process = Process("curl -sSL https://cli.dashwave.io | bash", pwd, true, dwWindow) + process.start(false) Thread{ val exitCode = process.wait() if (exitCode == 0) { - DashwaveWindow.displayInfo(Messages.DW_DEPS_INSTALL_SUCCESS) - DashwaveWindow.displayInfo(Messages.DW_DEPS_CONFIGURING) - val configCmd = DwCmds("config", pwd, true) + dwWindow.displayInfo(Messages.DW_DEPS_INSTALL_SUCCESS) + dwWindow.displayInfo(Messages.DW_DEPS_CONFIGURING) + + val configCmd = DwCmds("config", pwd, true, dwWindow) val exitcode = configCmd.executeWithExitCode() if(exitcode == 0){ - DashwaveWindow.displayInfo(Messages.DW_DEPS_CONFIGURE_SUCCESS) - verifyLogin(pwd) + if(PluginMode == "workspace"){ + dwWindow.displayInfo("🔨 Setting up workspace plugin...\n") + var workspaceCmd = "setup-workspace" + if(PluginEnv != ""){ + workspaceCmd += " -e ${PluginEnv}" + } + val setupWorkspaceCmd = DwCmds(workspaceCmd, pwd, true, dwWindow) + val exitCode = setupWorkspaceCmd.executeWithExitCode() + if(exitCode == 0){ + verifyLogin(pwd, dwWindow) + }else{ + dwWindow.displayError("❌ Could not setup plugin. Please contact us at hello@dashwave.io") + } + return@Thread + } + + dwWindow.displayInfo(Messages.DW_DEPS_CONFIGURE_SUCCESS) + verifyLogin(pwd, dwWindow) }else{ - DashwaveWindow.displayError(Messages.DW_DEPS_CONFIGURE_FAILED) + dwWindow.displayError(Messages.DW_DEPS_CONFIGURE_FAILED) } + } else { - DashwaveWindow.displayError(Messages.DW_DEPS_INSTALL_FAILED) + dwWindow.displayError(Messages.DW_DEPS_INSTALL_FAILED) } }.start() } -fun verifyLogin(pwd:String?){ - val currentUserLoginCmd = DwCmds("user", "", true) +fun verifyLogin(pwd:String?, dwWindow: DashwaveWindow){ + listUsers(pwd, dwWindow) + dwWindow.addModulesAndVariants(HashMap>(), "", "") + val currentUserLoginCmd = DwCmds("user", pwd, true, dwWindow) val exitCode = currentUserLoginCmd.executeWithExitCode() if (exitCode == 0){ - DashwaveWindow.enableRunButton() - checkProjectConnected(pwd) + dwWindow.enableRunButton() + checkProjectConnected(pwd, dwWindow) }else{ - loginUser(pwd) + if(PluginMode == "workspace") { + dwWindow.displayError("❌ User is not setup correctly. Please contact us at hello@dashwave.io") + return + } + loginUser(pwd, dwWindow) } } -fun checkProjectConnected(pwd:String?){ +fun checkProjectConnected(pwd:String?, dwWindow: DashwaveWindow){ + listUsers(pwd, dwWindow) + // check if .git folder exists + val gitConfigFilepath = "$pwd/.git" + if (!doesFileExist(gitConfigFilepath)){ + if(PluginMode == "workspace"){ + listModulesAndVariants(pwd, dwWindow); + // workspaces from template don't need .git folder +// dwWindow.displayError("❌ There is some issue in setting up your project (.git doesn't exist), please contact us at hello@dashwave.io") + return + } + dwWindow.displayOutput("❌ ${Messages.GIT_NOT_CONFIGURED}", ConsoleViewContentType.ERROR_OUTPUT) + val notif = BalloonNotif( + "Could not find .git folder", + "", + "This is not a git repository, initialise git and push codebase to proceed ", + NotificationType.ERROR, + ){} + notif.show(dwWindow.p) + + val dialog = GitNotConfiguredDialog() + dialog.show() + return + } + if (doesFileExist("$pwd/dashwave.yml")){ - DashwaveWindow.enableRunButton() - DashwaveWindow.displayOutput("✅ Project is successfully connected to dashwave. Run a cloud build using dashwave icon on toolbar\n\n", ConsoleViewContentType.NORMAL_OUTPUT) + dwWindow.enableRunButton() + listModulesAndVariants(pwd, dwWindow) + dwWindow.displayOutput("✅ Project is successfully connected to dashwave. Run a cloud build using dashwave icon on toolbar\n\n", ConsoleViewContentType.NORMAL_OUTPUT) val dd = ReadyForBuildDialog() dd.show() }else { - DashwaveWindow.displayOutput("⚠️ This project is not connected to dashwave, create a new project on dashwave\n\n", ConsoleViewContentType.NORMAL_OUTPUT) - openCreateProjectDialog(pwd, true) + if(PluginMode == "workspace"){ + dwWindow.displayError("❌ There is some issue in setting up your project (dashwave.yml doesn't exist), please contact us at hello@dashwave.io") + return + } + dwWindow.displayOutput("⚠️ This project is not connected to dashwave, create a new project on dashwave\n\n", ConsoleViewContentType.NORMAL_OUTPUT) + openCreateProjectDialog(pwd, true, dwWindow){} } } -fun loginUser(pwd:String?) { - val loginUserCmd = "login" - val exitCode = DwCmds(loginUserCmd, "", true).executeWithExitCode() +fun loginUser(pwd:String?, dwWindow: DashwaveWindow) { + val loginDialog = LoginDialog() + var accessCode: String = "" + + loginDialog.show() + + if (loginDialog.exitCode == DialogWrapper.OK_EXIT_CODE) { + accessCode = loginDialog.getAccessCode() + }else if (loginDialog.exitCode == DialogWrapper.CANCEL_EXIT_CODE){ + // handle cancellation logic if any + return + } + + var loginUserCmd = "login $accessCode" + if(PluginEnv != ""){ + loginUserCmd += " -e $PluginEnv" + } + val exitCode = DwCmds(loginUserCmd, pwd, true, dwWindow).executeWithExitCode() if (exitCode == 0) { - checkProjectConnected(pwd) + dwWindow.enableRunButton() + checkProjectConnected(pwd, dwWindow) }else{ var hyperlink = HyperlinkInfo { p: Project -> - loginUser(pwd) + loginUser(pwd, dwWindow) } - DashwaveWindow.console.printHyperlink(Messages.DW_LOGIN_FAILED, hyperlink) + dwWindow.console.printHyperlink(Messages.DW_LOGIN_FAILED, hyperlink) } } @@ -119,31 +276,92 @@ fun doesFileExist(path: String): Boolean { return file.exists() } -fun openCreateProjectDialog(pwd:String?, openTip:Boolean):Boolean{ - val createProjectDialog = CreateProjectDialog() - if (createProjectDialog.showAndGet()){ - val projectName = createProjectDialog.getProjectName() - val rootDir = createProjectDialog.getRootDir() - val techStack = createProjectDialog.getSelectedTechStack() - val success = createProject(projectName, techStack, rootDir,pwd, openTip) - return success - } - DashwaveWindow.show() - val yellowOutput = ConsoleViewContentType("YellowOutput", TextAttributes(Color.YELLOW, null,null, null, Font.PLAIN)) - DashwaveWindow.displayOutput("⚠️ You must create a new project to be able to run builds on dashwave\n\n", yellowOutput) - var hyperlink = HyperlinkInfo { p: Project -> - openCreateProjectDialog(pwd, openTip) +fun listUsers(pwd: String?, dwWindow: DashwaveWindow){ + val usersCmd = DwCmds("user ls", pwd, false, dwWindow) + val cmdOutput = usersCmd.executeWithOutput() + if(cmdOutput.first != 0){ + dwWindow.displayError("❌ Could not find logged in users\n"+cmdOutput.second) + return } - DashwaveWindow.console.printHyperlink("Click here", hyperlink) - DashwaveWindow.displayOutput(" to create a new dashwave project\n\n", ConsoleViewContentType.NORMAL_OUTPUT) - return false + val jsonText = cmdOutput.second.trim() + val cleanedJsonString = jsonText.dropWhile { it.code <= 32 } + println(cleanedJsonString) + val jsonObject = Json.parseToJsonElement(cleanedJsonString).jsonObject + + val users = jsonObject["users"]?.jsonArray?.mapNotNull { it.jsonPrimitive.contentOrNull } + val activeUser = jsonObject["active_user"]?.toString() + + dwWindow.addUsers(users, activeUser?:"",pwd) } +fun listModulesAndVariants(pwd:String?, dwWindow: DashwaveWindow) { + val configsCmd = DwCmds("build configs", pwd, false, dwWindow) + var cmdOutput = configsCmd.executeWithOutput() + if(cmdOutput.first != 0){ +// dwWindow.displayError("❌ Could not find modules and in variants\n"+cmdOutput.second) + return + } + val jsonText = cmdOutput.second.trim() + val cleanedJsonString = jsonText.dropWhile { it.code <= 32 } + val jsonObject = Json.parseToJsonElement(cleanedJsonString).jsonObject + val map = mutableMapOf>() + var defaultModule: String = "" + var defaultVariant: String = "" + var foundDefault:Boolean = false + jsonObject.forEach { (key, value) -> + val list = value.jsonArray.mapNotNull { it.jsonPrimitive.contentOrNull } + if (key == "default"){ + if (list.size >= 2){ + defaultModule = list[0] + defaultVariant = list[1] + foundDefault = true + } else { + println("Warning: 'default' project does not contain enough build types.") + } + } + map[key] = list + } -fun createProject(projectName: String, devStack:String, rootDir:String,pwd:String?, openTip: Boolean) :Boolean{ + if (!foundDefault && map.isNotEmpty()) { + map.entries.first().let { firstEntry -> + defaultModule = firstEntry.key + defaultVariant = firstEntry.value.firstOrNull() ?: "" + } + } + + dwWindow.addModulesAndVariants(map, defaultModule, defaultVariant) +} + +fun openCreateProjectDialog(pwd:String?, openTip:Boolean, dwWindow: DashwaveWindow,buildAction:()->Unit){ + ApplicationManager.getApplication().invokeLater{ + val createProjectDialog = CreateProjectDialog(dwWindow.p) + if (createProjectDialog.showAndGet()){ + val projectName = createProjectDialog.getProjectName() + val rootDir = createProjectDialog.getRootDir() + val techStack = createProjectDialog.getSelectedTechStack() + val success = createProject(projectName, techStack, rootDir,pwd, openTip, dwWindow) + if(success){ + buildAction() + } + }else{ +// dwWindow.show() + dwWindow.enableRunButton() + dwWindow.disableCancelButton() + val yellowOutput = ConsoleViewContentType("YellowOutput", TextAttributes(Color.YELLOW, null,null, null, Font.PLAIN)) + dwWindow.displayOutput("⚠️ You must create a new project to be able to run builds on dashwave\n\n", yellowOutput) + var hyperlink = HyperlinkInfo { p: Project -> + openCreateProjectDialog(pwd, openTip, dwWindow,buildAction) + } + dwWindow.console.printHyperlink("Click here", hyperlink) + dwWindow.displayOutput(" to create a new dashwave project\n\n", ConsoleViewContentType.NORMAL_OUTPUT) + } + } +} + +fun createProject(projectName: String, devStack:String, rootDir:String,pwd:String?, openTip: Boolean, dwWindow: DashwaveWindow) :Boolean{ val createProjectCmd = "create-project --no-prompt --name=$projectName --dev-stack=$devStack --root-dir=$rootDir" - val exitCode = DwCmds(createProjectCmd, pwd, true).executeWithExitCode() + val exitCode = DwCmds(createProjectCmd, pwd, true, dwWindow).executeWithExitCode() if(exitCode == 0){ - DashwaveWindow.enableRunButton() + dwWindow.enableRunButton() Notifications.Bus.notify( Notification( "YourPluginNotificationGroup", @@ -152,8 +370,8 @@ fun createProject(projectName: String, devStack:String, rootDir:String,pwd:Strin NotificationType.INFORMATION ) ) - DashwaveWindow.show() - DashwaveWindow.displayInfo(Messages.PROJECT_CONNECTION_SUCCESS) +// dwWindow.show() + dwWindow.displayInfo(Messages.PROJECT_CONNECTION_SUCCESS) if(openTip) { val dd = ReadyForBuildDialog() @@ -161,10 +379,17 @@ fun createProject(projectName: String, devStack:String, rootDir:String,pwd:Strin } return true } - DashwaveWindow.displayError(Messages.PROJECT_CONNECTION_FAILED) + dwWindow.displayError(Messages.PROJECT_CONNECTION_FAILED) + if(exitCode == 13){ + var hyperlink = HyperlinkInfo { p: Project -> + loginUser(pwd, dwWindow) + } + dwWindow.console.printHyperlink("Login here\n\n", hyperlink) + return false + } var hyperlink = HyperlinkInfo { p: Project -> - openCreateProjectDialog(pwd, openTip) + openCreateProjectDialog(pwd, openTip, dwWindow){} } - DashwaveWindow.console.printHyperlink("Please try again\n\n", hyperlink) + dwWindow.console.printHyperlink("Please try again\n\n", hyperlink) return false } \ No newline at end of file diff --git a/src/main/kotlin/com/dashwave/plugin/actions/BuildAction.kt b/src/main/kotlin/com/dashwave/plugin/actions/BuildAction.kt index 0321e95..1a80ccb 100644 --- a/src/main/kotlin/com/dashwave/plugin/actions/BuildAction.kt +++ b/src/main/kotlin/com/dashwave/plugin/actions/BuildAction.kt @@ -1,29 +1,28 @@ package com.dashwave.plugin.actions +import com.dashwave.plugin.PluginStartup import com.dashwave.plugin.windows.DashwaveWindow import com.dashwave.plugin.utils.DwBuild import com.intellij.openapi.actionSystem.AnAction import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.project.Project +import com.intellij.openapi.util.IconLoader +import javax.swing.Icon class BuildAction: AnAction() { - override fun update(e: AnActionEvent) { super.update(e) val presentation = e.presentation // Enable or disable the action based on your condition - presentation.isEnabled = DashwaveWindow.runEnabled + + presentation.isEnabled = PluginStartup.dwWindows?.get(e.project?.name)?.runEnabled?:false presentation.text = "Run build on dashwave" presentation.description = "Run build on dashwave" } override fun actionPerformed(e: AnActionEvent) { - val project = e.project - if(project != null){ - val buildConfigs = DashwaveWindow.getBuildConfigs(project) - val build = DwBuild(buildConfigs) - build.run(project) - } + PluginStartup.dwWindows?.get(e.project?.name)?.runButton?.doClick() } } \ No newline at end of file diff --git a/src/main/kotlin/com/dashwave/plugin/actions/RunDebugAction.kt b/src/main/kotlin/com/dashwave/plugin/actions/RunDebugAction.kt new file mode 100644 index 0000000..c92fa14 --- /dev/null +++ b/src/main/kotlin/com/dashwave/plugin/actions/RunDebugAction.kt @@ -0,0 +1,22 @@ +package com.dashwave.plugin.actions + +import com.dashwave.plugin.PluginStartup +import com.intellij.openapi.actionSystem.AnAction +import com.intellij.openapi.actionSystem.AnActionEvent + +class RunDebugAction : AnAction(){ + override fun update(e: AnActionEvent) { + super.update(e) + val presentation = e.presentation + + // Enable or disable the action based on your condition + + presentation.isEnabled = PluginStartup.dwWindows?.get(e.project?.name)?.runEnabled?:false + presentation.text = "Run debugger on dashwave" + presentation.description = "Run debugger on dashwave" + } + + override fun actionPerformed(e: AnActionEvent) { + PluginStartup.dwWindows?.get(e.project?.name)?.debugButton?.doClick() + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/dashwave/plugin/components/CollapseMenu.kt b/src/main/kotlin/com/dashwave/plugin/components/CollapseMenu.kt new file mode 100644 index 0000000..47d6966 --- /dev/null +++ b/src/main/kotlin/com/dashwave/plugin/components/CollapseMenu.kt @@ -0,0 +1,40 @@ +package com.dashwave.plugin.components + +import com.intellij.icons.AllIcons +import javax.swing.JButton +import javax.swing.JComponent +import javax.swing.JPopupMenu + +class CollapseMenu(label:String){ + private val menu:JPopupMenu = JPopupMenu() + private val btn:JButton = JButton() + private var displayOn:Boolean = false + lateinit var components:List + init { + val label:String = label + btn.apply { + text = label + icon = AllIcons.General.ArrowDown + addActionListener{ + if(displayOn){ + menu.setSize(0,0) + }else{ + menu.show(this, 0, this.height) + menu.setSize(preferredSize.width, preferredSize.height) + } + displayOn = !displayOn + } + } + + } + + fun add(comp:JComponent){ + menu.add(comp) + } + + fun getComponent():JComponent{ + return btn + } + + +} \ No newline at end of file diff --git a/src/main/kotlin/com/dashwave/plugin/dialogbox/CreateProjectDialog.kt b/src/main/kotlin/com/dashwave/plugin/dialogbox/CreateProjectDialog.kt index 8f9774b..b11558f 100644 --- a/src/main/kotlin/com/dashwave/plugin/dialogbox/CreateProjectDialog.kt +++ b/src/main/kotlin/com/dashwave/plugin/dialogbox/CreateProjectDialog.kt @@ -1,5 +1,6 @@ package com.dashwave.plugin.dialogbox +import com.intellij.openapi.project.Project import com.intellij.openapi.ui.ComboBox import com.intellij.openapi.ui.DialogWrapper import com.intellij.openapi.ui.LabeledComponent @@ -8,7 +9,7 @@ import com.intellij.ui.layout.panel import java.awt.* import javax.swing.* -class CreateProjectDialog: DialogWrapper(true) { +class CreateProjectDialog(project: Project): DialogWrapper(project) { private val projectNameTextField = JTextField() private val rootModulePathTextField = JTextField("./") private val nativeRadioButton = JRadioButton("Native (Java/Kotlin)") @@ -17,12 +18,12 @@ class CreateProjectDialog: DialogWrapper(true) { private val projectTypeButtonGroup = ButtonGroup() init { + init() title = "New Dashwave Project" projectTypeButtonGroup.add(nativeRadioButton) projectTypeButtonGroup.add(flutterRadioButton) projectTypeButtonGroup.add(rNativeRadioButton) nativeRadioButton.isSelected = true - init() } override fun createCenterPanel(): JComponent? { @@ -34,7 +35,7 @@ class CreateProjectDialog: DialogWrapper(true) { gbc.gridx = 0 gbc.gridy = 0 gbc.gridwidth = 2 - val icon = IconLoader.getIcon("/icons/dashwave13.svg") // Make sure to provide the correct path + val icon = IconLoader.getIcon("/icons/dashwave13.svg", CreateProjectDialog::class.java.classLoader) // Make sure to provide the correct path val labelWithIcon = JLabel("Create a new project to be able to run builds on dashwave", icon, SwingConstants.LEFT) panel.add(labelWithIcon, gbc) @@ -84,7 +85,7 @@ class CreateProjectDialog: DialogWrapper(true) { return when{ nativeRadioButton.isSelected -> "GRADLE" flutterRadioButton.isSelected -> "FLUTTER" - nativeRadioButton.isSelected -> "REACTNATIVE" + rNativeRadioButton.isSelected -> "REACTNATIVE" else -> "" } } diff --git a/src/main/kotlin/com/dashwave/plugin/dialogbox/GitNotConfiguredDialog.kt b/src/main/kotlin/com/dashwave/plugin/dialogbox/GitNotConfiguredDialog.kt new file mode 100644 index 0000000..dce66da --- /dev/null +++ b/src/main/kotlin/com/dashwave/plugin/dialogbox/GitNotConfiguredDialog.kt @@ -0,0 +1,27 @@ +package com.dashwave.plugin.dialogbox + +import com.dashwave.plugin.messages.Messages +import com.intellij.openapi.ui.DialogWrapper +import com.intellij.openapi.util.IconLoader +import javax.swing.JComponent +import javax.swing.JLabel +import javax.swing.JPanel +import javax.swing.Action + +class GitNotConfiguredDialog : DialogWrapper(true) { + init { + init() + title = "Git not configured" + } + + override fun createCenterPanel(): JComponent? { + val dialogPanel = JPanel() + dialogPanel.add(JLabel(Messages.GIT_NOT_CONFIGURED)) + return dialogPanel + } + + override fun createActions(): Array { + val okButton = okAction + return arrayOf(okButton) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/dashwave/plugin/dialogbox/LoginDialog.kt b/src/main/kotlin/com/dashwave/plugin/dialogbox/LoginDialog.kt new file mode 100644 index 0000000..5ba0572 --- /dev/null +++ b/src/main/kotlin/com/dashwave/plugin/dialogbox/LoginDialog.kt @@ -0,0 +1,104 @@ +package com.dashwave.plugin + +import com.intellij.ide.BrowserUtil +import com.intellij.openapi.ui.DialogWrapper +import com.intellij.openapi.util.IconLoader +import com.intellij.util.IconUtil +import java.awt.* +import java.awt.event.MouseAdapter +import java.awt.event.MouseEvent +import javax.swing.* + + +class LoginDialog : DialogWrapper(true) { + private val accessCodeTextField = JPasswordField(20) + + + init { + // TODO: add logo to title + title = "Authenticate with Dashwave Cloud" + init() + +// val icon = IconLoader.getIcon("/icons/dashwave13.svg") // Make sure to provide the correct path +// val image = IconUtil.toImage(icon) +// window.setIconImage(image) + } + + override fun createCenterPanel(): JComponent? { + val panel = JPanel(GridBagLayout()) + val gbc = GridBagConstraints() + + gbc.insets = Insets(4, 4, 4, 4) // You can adjust padding here + gbc.fill = GridBagConstraints.HORIZONTAL + gbc.gridx = 0 + gbc.gridy = 0 + gbc.gridwidth = 2 + + this.createTitlePane() + +// val labelWithIcon = JLabel("Login to Dashwave Cloud by entering your access code", icon, SwingConstants.LEFT) +// panel.add(labelWithIcon, gbc) + + val linkHeadLabel = JLabel("Authenticate the plugin with Dashwave cloud using your personal access code") + // TODO: add - to view your access code, visit this link: + gbc.gridy+=20 + gbc.gridx = 0 + gbc.gridwidth = 2 + linkHeadLabel.preferredSize = Dimension(600, linkHeadLabel.preferredSize.height+10) + panel.add(linkHeadLabel, gbc) + + val label = JLabel("To view your access code, visit this link:") + gbc.gridy+=20 + gbc.gridwidth = 2 + panel.add(label, gbc) + + val linkLabel = JLabel("https://console.dashwave.io/home?profile=true") + linkLabel.cursor = Cursor(Cursor.HAND_CURSOR) // Change the cursor to a hand cursor + + linkLabel.addMouseListener(object : MouseAdapter() { + override fun mouseClicked(e: MouseEvent?) { + BrowserUtil.browse("https://console.dashwave.io/home?profile=true") + } + + override fun mouseEntered(e: MouseEvent?) { + linkLabel.text = "https://console.dashwave.io/home?profile=true" // Underline on hover + } + + override fun mouseExited(e: MouseEvent?) { + linkLabel.text = "https://console.dashwave.io/home?profile=true" // Remove underline when not hovering + } + }) + + gbc.gridx = 0 + gbc.gridy+=1 + gbc.gridwidth = 10 + linkLabel.preferredSize = Dimension(600, linkLabel.preferredSize.height) + panel.add(linkLabel, gbc) + + gbc.gridy+=200 + gbc.gridwidth = 1 + panel.add(JLabel("Access Code:"), gbc) + + gbc.gridx++ + gbc.gridwidth = 1 + accessCodeTextField.preferredSize = Dimension(200, accessCodeTextField.preferredSize.height) // Adjust width as needed + panel.add(accessCodeTextField, gbc) + +// if (accessCodeTextField.text.isEmpty()) { +// setErrorText("Please enter your access code") +// } + + // change the ok button to login + setOKButtonText("Authenticate") + + return panel + } + + fun getAccessCode(): String { + if (accessCodeTextField.text.isEmpty()) { + return "" + } + return accessCodeTextField.text + } +} + diff --git a/src/main/kotlin/com/dashwave/plugin/dialogbox/ReadyForBuildDialog.kt b/src/main/kotlin/com/dashwave/plugin/dialogbox/ReadyForBuildDialog.kt index e190d52..e95c618 100644 --- a/src/main/kotlin/com/dashwave/plugin/dialogbox/ReadyForBuildDialog.kt +++ b/src/main/kotlin/com/dashwave/plugin/dialogbox/ReadyForBuildDialog.kt @@ -17,7 +17,7 @@ class ReadyForBuildDialog : DialogWrapper(true) { val dialogPanel = JPanel() // Load the image icon - val icon = IconLoader.getIcon("/icons/dashwave13.svg") + val icon = IconLoader.getIcon("/icons/dashwave13.svg", ReadyForBuildDialog::class.java.classLoader) val iconLabel = JLabel(icon) // Add components to the panel diff --git a/src/main/kotlin/com/dashwave/plugin/messages/Messages.kt b/src/main/kotlin/com/dashwave/plugin/messages/Messages.kt index 5742818..f7368ae 100644 --- a/src/main/kotlin/com/dashwave/plugin/messages/Messages.kt +++ b/src/main/kotlin/com/dashwave/plugin/messages/Messages.kt @@ -12,5 +12,7 @@ class Messages { val DW_LOGIN_FAILED = "⚠️ Login failed. Click here to retry\n\n" val PROJECT_CONNECTION_SUCCESS = "✅ Project is successfully connected to dashwave\n\n" val PROJECT_CONNECTION_FAILED = "❌ Dashwave project creation failed\n" + val GIT_NOT_CONFIGURED = "Your local codebase is not currently hosted on a Git repository (GitHub/GitLab). Please ensure your codebase is hosted on Git to use this plugin.\n" + val GRADLE_SYNC_CANCELLATION_FAILED = "Gradle sync needs to cancelled before proceeding. Please stop it manually.\n" } -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/dashwave/plugin/notif/NotifAlert.kt b/src/main/kotlin/com/dashwave/plugin/notif/NotifAlert.kt index 64781a3..db6e708 100644 --- a/src/main/kotlin/com/dashwave/plugin/notif/NotifAlert.kt +++ b/src/main/kotlin/com/dashwave/plugin/notif/NotifAlert.kt @@ -23,7 +23,7 @@ class BalloonNotif(title:String, actionTitle:String ,description:String,type: No this.type = type this.actionTitle = actionTitle } - fun show() { + fun show(project:Project) { val notification = Notification( "YourPluginNotificationGroup", this.title, @@ -37,6 +37,6 @@ class BalloonNotif(title:String, actionTitle:String ,description:String,type: No } }) } - Notifications.Bus.notify(notification, DashwaveWindow.p) + Notifications.Bus.notify(notification, project) } } \ No newline at end of file diff --git a/src/main/kotlin/com/dashwave/plugin/utils/DwBuild.kt b/src/main/kotlin/com/dashwave/plugin/utils/DwBuild.kt index 7d70b1c..338b8cc 100644 --- a/src/main/kotlin/com/dashwave/plugin/utils/DwBuild.kt +++ b/src/main/kotlin/com/dashwave/plugin/utils/DwBuild.kt @@ -1,5 +1,6 @@ package com.dashwave.plugin.utils +import com.dashwave.plugin.PluginMode import com.dashwave.plugin.doesFileExist import com.dashwave.plugin.openCreateProjectDialog import com.dashwave.plugin.windows.DashwaveWindow @@ -7,36 +8,58 @@ import com.intellij.openapi.project.Project import okhttp3.internal.wait import kotlin.concurrent.thread -class DwBuildConfig(clean:Boolean,debug:Boolean,openEmulator:Boolean, pwd:String?){ +class DwBuildConfig(clean:Boolean,debug:Boolean,openEmulator:Boolean,module:String,variant:String, pwd:String?){ var clean:Boolean = clean var debug:Boolean = debug var openEmulator:Boolean = openEmulator var pwd:String? = pwd + var module:String = module + var variant:String = variant + var attachDebugger:Boolean = false } -class DwBuild(config: DwBuildConfig){ +class DwBuild(config: DwBuildConfig, dwWindow: DashwaveWindow){ private var cmd:String = "plugin-build" private val openEmulator:Boolean private var pwd:String? + private var dwWindow:DashwaveWindow + private var attachDebugger:Boolean = false init { + if(PluginMode == "workspace"){ + cmd += " --workspace" + } if(config.clean){ cmd += " --clean" } if(config.debug){ cmd += " --debug" } + + if (config.module != ""){ + cmd += " --module ${config.module}" + } + + if (config.variant != ""){ + cmd += " --variant ${config.variant}" + } + if (config.attachDebugger){ + cmd += " --attach-debugger" + } + pwd = config.pwd openEmulator = config.openEmulator + attachDebugger = config.attachDebugger + this.dwWindow = dwWindow } private fun activateDashwaveWindow(){ - DashwaveWindow.show() +// this.dwWindow.show() } private fun execute(){ // DashwaveWindow.displayInfo() - val buildCmd = DwCmds(cmd, pwd, true) - buildCmd.executeBuild(pwd, openEmulator) + val buildCmd = DwCmds(cmd, pwd, true, this.dwWindow) + buildCmd.executeBuild(pwd, openEmulator,attachDebugger) } fun killEmulator(){ @@ -45,18 +68,17 @@ class DwBuild(config: DwBuildConfig){ fun run(p:Project){ activateDashwaveWindow() - DashwaveWindow.clearConsole() - DashwaveWindow.disableRunButton() - DashwaveWindow.enableCancelButton() - if(DashwaveWindow.lastEmulatorProcess != null){ - DashwaveWindow.lastEmulatorProcess!!.exit() + this.dwWindow.clearConsole() + this.dwWindow.disableRunButton() + this.dwWindow.enableCancelButton() + if(this.dwWindow.lastEmulatorProcess != null){ + this.dwWindow.lastEmulatorProcess!!.exit() } if (!doesFileExist("${pwd}/dashwave.yml")){ - if(!openCreateProjectDialog(pwd, false)){ - DashwaveWindow.enableRunButton() - DashwaveWindow.disableCancelButton() - return + openCreateProjectDialog(pwd, false, this.dwWindow){ + execute() } + return } execute() } diff --git a/src/main/kotlin/com/dashwave/plugin/utils/DwCmds.kt b/src/main/kotlin/com/dashwave/plugin/utils/DwCmds.kt index 6be2842..8bfccfd 100644 --- a/src/main/kotlin/com/dashwave/plugin/utils/DwCmds.kt +++ b/src/main/kotlin/com/dashwave/plugin/utils/DwCmds.kt @@ -1,66 +1,91 @@ package com.dashwave.plugin.utils import com.dashwave.plugin.installDW +import com.dashwave.plugin.listModulesAndVariants +import com.dashwave.plugin.listUsers +import com.dashwave.plugin.loginUser import com.dashwave.plugin.notif.BalloonNotif import com.dashwave.plugin.windows.DashwaveWindow import com.intellij.execution.filters.HyperlinkInfo import com.intellij.ide.BrowserUtil import com.intellij.notification.NotificationType -import com.intellij.openapi.keymap.impl.ui.Hyperlink import com.intellij.openapi.project.Project -import com.intellij.util.Futures.thenRunAsync -import okhttp3.internal.wait -import java.util.concurrent.CompletableFuture -import java.util.concurrent.Executors -import kotlin.system.exitProcess -class DwCmds(execCmd:String, wd:String?, log: Boolean){ +class DwCmds(execCmd:String, wd:String?, log: Boolean, dwWindow: DashwaveWindow){ private var cmd:String private var p:Process private var pwd:String? + private var shouldLog:Boolean + private var dwWindow:DashwaveWindow init { + shouldLog = log cmd = "dw $execCmd --plugin" pwd = wd - p = com.dashwave.plugin.utils.Process(cmd, pwd, log) + p = com.dashwave.plugin.utils.Process(cmd, pwd, log, dwWindow) + this.dwWindow = dwWindow } fun executeWithExitCode():Int{ - this.p.start() + this.p.start(this.shouldLog) val exitCode = this.p.wait() if(exitCode == 11){ - DashwaveWindow.displayError("Dashwave has a major update, you need to update dependencies\n") + this.dwWindow.displayError("Dashwave has a major update, you need to update dependencies\n") val hyperlink = HyperlinkInfo { p: Project -> - installDW(this.pwd) + installDW(this.pwd, this.dwWindow) } - DashwaveWindow.console.printHyperlink("Click here to update\n\n", hyperlink) + this.dwWindow.console.printHyperlink("Click here to update\n\n", hyperlink) } return exitCode } + fun executeWithOutput():Pair{ + this.p.start(this.shouldLog) + val exitCode = this.p.wait() + if(exitCode == 11){ + this.dwWindow.displayError("Dashwave has a major update, you need to update dependencies\n") + val hyperlink = HyperlinkInfo { p: Project -> + installDW(this.pwd, this.dwWindow) + } + this.dwWindow.console.printHyperlink("Click here to update\n\n", hyperlink) + } + + // get the stdout as string + return Pair(exitCode, this.p.getOutput()) + } + + fun exit(){ this.p.exit() } + fun executeBg(){ + Thread{ + this.p.start(this.shouldLog) + }.start() + } + - fun executeBuild(pwd:String?, openEmulator:Boolean){ - this.p.start() - DashwaveWindow.disableRunButton() - DashwaveWindow.enableCancelButton() - DashwaveWindow.currentBuild = this + fun executeBuild(pwd:String?, openEmulator:Boolean, attachDebugger:Boolean){ + this.p.start(this.shouldLog) + this.dwWindow.disableRunButton() + this.dwWindow.enableCancelButton() + this.dwWindow.currentBuild = this BalloonNotif( "Build started", "", "Build started on dashwave. Your build is running on a remote machine. You can view the logs in console and view emulation after build completes", NotificationType.INFORMATION - ){}.show() - DashwaveWindow.changeIcon(DashwaveWindow.loadIcon) + ){}.show(dwWindow.p) + this.dwWindow.changeIcon(this.dwWindow.loadIcon) Thread{ var ex = this.p.wait() - DashwaveWindow.currentBuild = null - DashwaveWindow.changeIcon(DashwaveWindow.dwIcon) - DashwaveWindow.enableRunButton() - DashwaveWindow.disableCancelButton() -// DashwaveWindow.show() + this.dwWindow.currentBuild = null + this.dwWindow.changeIcon(this.dwWindow.dwIcon) + this.dwWindow.enableRunButton() + this.dwWindow.disableCancelButton() + listModulesAndVariants(pwd, this.dwWindow) +// listUsers(pwd, this.dwWindow) + this.dwWindow.show() when(ex){ 0 -> { BalloonNotif( @@ -70,20 +95,20 @@ class DwCmds(execCmd:String, wd:String?, log: Boolean){ NotificationType.INFORMATION ){ // BrowserUtil.browse("https://console.dashwave.io/home?profile=true") - }.show() + }.show(dwWindow.p) - if(openEmulator){ - val emulatorCmd = DwCmds("emulator", pwd, false) - DashwaveWindow.lastEmulatorProcess = emulatorCmd + if(!attachDebugger && openEmulator){ + val emulatorCmd = DwCmds("emulator", pwd, false, this.dwWindow) + this.dwWindow.lastEmulatorProcess = emulatorCmd val ex = emulatorCmd.executeWithExitCode() } } 11 -> { - DashwaveWindow.displayError("Dashwave has a major update, you need to update dependencies\n") + this.dwWindow.displayError("Dashwave has a major update, you need to update dependencies\n") val hyperlink = HyperlinkInfo { p: Project -> - installDW(pwd) + installDW(pwd, this.dwWindow) } - DashwaveWindow.console.printHyperlink("Click here to update\n\n", hyperlink) + this.dwWindow.console.printHyperlink("Click here to update\n\n", hyperlink) } // exitcode = 12 means authorize scm failed 12 -> { @@ -93,8 +118,44 @@ class DwCmds(execCmd:String, wd:String?, log: Boolean){ "We could not fetch your project from github/gitlab. Please authorize to provide access", NotificationType.ERROR ){ - BrowserUtil.browse("https://console.dashwave.io/home?profile=true") - }.show() + BrowserUtil.browse("https://consoledev.dashwave.io/home?profile=true") + }.show(dwWindow.p) + } + 13 -> { + BalloonNotif( + "Not Authorized", + "Login here", + "Your auth token may have expired. Click here to login again", + NotificationType.ERROR + ){ + loginUser(pwd, dwWindow) + }.show(dwWindow.p) + + this.dwWindow.displayError("Your auth token seems to have expired. Click below to login again\n") + val hyperlink = HyperlinkInfo { p: Project -> + loginUser(pwd, this.dwWindow) + } + this.dwWindow.console.printHyperlink("Login here\n\n", hyperlink) + } + 14 -> { + BalloonNotif( + "Build Successful", + "", + "Build completed, attaching debugger", + NotificationType.INFORMATION + ){ +// BrowserUtil.browse("https://console.dashwave.io/home?profile=true") + }.show(dwWindow.p) + println("attaching debugger") + if(attachDebugger){ + val debuggerCmd = DwCmds("get-debugger", pwd, true, this.dwWindow) + debuggerCmd.executeBg() + } + if(openEmulator){ + val emulatorCmd = DwCmds("emulator", pwd, false, this.dwWindow) + this.dwWindow.lastEmulatorProcess = emulatorCmd + val ex = emulatorCmd.executeWithExitCode() + } } else -> { // add try again @@ -103,7 +164,7 @@ class DwCmds(execCmd:String, wd:String?, log: Boolean){ "", "Your build failed. Please check for logs in the console", NotificationType.ERROR - ){}.show() + ){}.show(dwWindow.p) } } }.start() diff --git a/src/main/kotlin/com/dashwave/plugin/utils/Process.kt b/src/main/kotlin/com/dashwave/plugin/utils/Process.kt index 58d3da9..e5db568 100644 --- a/src/main/kotlin/com/dashwave/plugin/utils/Process.kt +++ b/src/main/kotlin/com/dashwave/plugin/utils/Process.kt @@ -8,15 +8,21 @@ import com.intellij.openapi.util.Key import okhttp3.internal.wait import java.io.BufferedReader import java.io.File +import java.io.InputStreamReader import java.util.concurrent.CountDownLatch import java.util.concurrent.ExecutorService import java.util.concurrent.Executors import kotlin.concurrent.thread -class Process(cmd:String, pwd:String?, log:Boolean){ +class Process(cmd:String, pwd:String?, log:Boolean, dwWindow: DashwaveWindow){ private var ph:ProcessHandler private val latch = CountDownLatch(1) + private val outputBuilder = StringBuilder() + private var command:String + private var dwWind:DashwaveWindow init { + command = cmd + dwWind = dwWindow val cmd = GeneralCommandLine("/bin/bash","-c",cmd) if(pwd != null && pwd != ""){ cmd.setWorkDirectory(pwd) @@ -27,8 +33,13 @@ class Process(cmd:String, pwd:String?, log:Boolean){ ph = OSProcessHandler(cmd) ph.addProcessListener(object:ProcessAdapter(){ override fun onTextAvailable(event: ProcessEvent, outputType: Key<*>) { + val text = event.text.trim() + if (text.contains(cmd.commandLineString)){ + return + } + outputBuilder.append(text) if(log) { - decodeAndPrintString(event.text, outputType) + decodeAndPrintString(event.text, outputType, dwWindow) } } override fun processTerminated(event: ProcessEvent) { @@ -38,7 +49,10 @@ class Process(cmd:String, pwd:String?, log:Boolean){ }) } - fun start(){ + fun start(log: Boolean){ + if(log){ + this.dwWind.displayInfo("${this.command}\n\n") + } ph.startNotify() } @@ -50,11 +64,15 @@ class Process(cmd:String, pwd:String?, log:Boolean){ fun exit(){ ph.destroyProcess() } + + fun getOutput(): String { + return outputBuilder.toString() + } } -private fun decodeAndPrintString(s:String, p: Key<*>){ +private fun decodeAndPrintString(s:String, p: Key<*>, dwWindow: DashwaveWindow){ val decoder = AnsiEscapeDecoder() val outputListener = AnsiEscapeDecoder.ColoredTextAcceptor { text, attributes -> - DashwaveWindow.displayOutput(text, ConsoleViewContentType.getConsoleViewType(attributes)) + dwWindow.displayOutput(text, ConsoleViewContentType.getConsoleViewType(attributes)) } decoder.escapeText(s, p, outputListener) } \ No newline at end of file diff --git a/src/main/kotlin/com/dashwave/plugin/windows/DashwaveWindow.kt b/src/main/kotlin/com/dashwave/plugin/windows/DashwaveWindow.kt index aa3accd..510a127 100644 --- a/src/main/kotlin/com/dashwave/plugin/windows/DashwaveWindow.kt +++ b/src/main/kotlin/com/dashwave/plugin/windows/DashwaveWindow.kt @@ -1,5 +1,9 @@ package com.dashwave.plugin.windows +import com.dashwave.plugin.PluginStartup +import com.dashwave.plugin.components.CollapseMenu +import com.dashwave.plugin.loginUser +import com.dashwave.plugin.actions.BuildAction import com.dashwave.plugin.utils.DwBuild import com.dashwave.plugin.utils.DwBuildConfig import com.dashwave.plugin.utils.DwCmds @@ -13,107 +17,238 @@ import com.intellij.openapi.wm.ToolWindowAnchor import com.intellij.openapi.wm.ToolWindowFactory import com.intellij.ui.content.ContentFactory import java.awt.BorderLayout -import javax.swing.JButton -import javax.swing.JCheckBox -import javax.swing.JPanel -import javax.swing.JToolBar import com.intellij.icons.AllIcons; import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.fileEditor.FileDocumentManager import com.intellij.openapi.util.IconLoader import com.intellij.openapi.wm.ToolWindowManager import com.intellij.ui.AnimatedIcon +import com.intellij.ui.components.JBList +import com.jetbrains.rd.util.first +import com.sun.xml.bind.v2.Messages +import java.awt.Color +import java.awt.Dimension +import java.awt.RenderingHints import java.awt.event.ItemEvent import java.awt.event.ItemListener -import javax.swing.Icon -import javax.swing.JLabel - -class DashwaveWindow : ToolWindowFactory { - companion object { - var lastEmulatorProcess:DwCmds? = null - var currentBuild:DwCmds? = null - lateinit var console: ConsoleView - lateinit var runButton: JButton - lateinit var cancelButton: JButton - lateinit var p: Project - var dwIcon: Icon = IconLoader.getIcon("/icons/dashwave13.svg") - var loadIcon: Icon = AnimatedIcon.Default() - var runEnabled = false - private var cleanBuildCheckbox:JCheckBox = JCheckBox("Clean Build") - private var debugEnabledCheckBox: JCheckBox = JCheckBox("Enable Debug") - private var openEmulatorCheckbox:JCheckBox = JCheckBox("Open Emulator") - - fun show(){ - val toolWindowManager = ToolWindowManager.getInstance(p) - val myWindow = toolWindowManager.getToolWindow("Dashwave") - myWindow?.show() - } +import java.awt.image.BufferedImage +import javax.swing.* - fun changeIcon(icon:Icon){ - val toolWindowManager = ToolWindowManager.getInstance(p) - val myWindow = toolWindowManager.getToolWindow("Dashwave") - myWindow?.setIcon(icon) - } +class DashwaveWindow(project: Project){ + var lastEmulatorProcess:DwCmds? = null + var currentBuild:DwCmds? = null + lateinit var console: ConsoleView + lateinit var runButton: JButton + lateinit var debugButton: JButton + lateinit var cancelButton: JButton + lateinit var p: Project + var dwIcon: Icon = IconLoader.getIcon("/icons/dashwave13.svg", DashwaveWindow::class.java.classLoader) + var loadIcon: Icon = AnimatedIcon.Default() + var runEnabled = false + private var cleanBuildCheckbox:JCheckBox = JCheckBox("Clean Build") + private var verboseEnabledCheckBox: JCheckBox = JCheckBox("Verbose") + private var openEmulatorCheckbox:JCheckBox = JCheckBox("Open Emulator") + var window:ToolWindow? = null + private var buildOpts = CollapseMenu("Build opts") + private var modulesList: JComboBox = JComboBox() + private var variantsList: JComboBox = JComboBox() + private var usersList: JComboBox = JComboBox() + private var selectedUser: String = "" + private var lastSelectedUser: String = "" + var selectedModule:String="" + var selectedVariant:String="" - fun clearConsole(){ - console.clear() - } + init { + p = project + val toolWindowManager = ToolWindowManager.getInstance(project) + val toolWindow = toolWindowManager.registerToolWindow( + "Dashwave", // ID of the tool window + false, // canCloseContent - whether the tool window can be closed + ToolWindowAnchor.BOTTOM // The anchor location + ) + window = toolWindow + toolWindow.setIcon(IconLoader.getIcon("/icons/dashwave13.svg", DashwaveWindow::class.java.classLoader)) + createToolWindowContent() + } - fun getBuildConfigs(p:Project):DwBuildConfig{ - val cleanBuild = cleanBuildCheckbox.isSelected - val debugEnabled = debugEnabledCheckBox.isSelected - val openEmulator = openEmulatorCheckbox.isSelected - DashwaveWindow.displayInfo("open emulator is $openEmulator") - return DwBuildConfig(cleanBuild, debugEnabled, openEmulator,p.basePath) - } - fun displayOutput(s:String, type:ConsoleViewContentType){ - console.print(s, type) - } + fun show(){ + window?.show() + } + fun changeIcon(icon:Icon){ + window?.setIcon(icon) + } - fun displayInfo(s:String){ - console.print(s, ConsoleViewContentType.NORMAL_OUTPUT) - } + fun clearConsole(){ + console.clear() + } - fun displayError(s:String){ - console.print(s, ConsoleViewContentType.ERROR_OUTPUT) - } + fun getBuildConfigs(p:Project):DwBuildConfig{ + val cleanBuild = cleanBuildCheckbox.isSelected + val debugEnabled = verboseEnabledCheckBox.isSelected + val openEmulator = openEmulatorCheckbox.isSelected + val module: String = selectedModule + val variant: String = selectedVariant + this.displayInfo("open emulator is $openEmulator") + return DwBuildConfig(cleanBuild, debugEnabled, openEmulator, module, variant, p.basePath) + } + + fun displayOutput(s:String, type:ConsoleViewContentType){ + console.print(s, type) + } + + fun displayInfo(s:String){ + console.print(s, ConsoleViewContentType.NORMAL_OUTPUT) + } - fun disableRunButton(){ - runEnabled = false - runButton.isEnabled = false - cleanBuildCheckbox.isEnabled = false - debugEnabledCheckBox.isEnabled = false - openEmulatorCheckbox.isEnabled = false + fun displayError(s:String){ + console.print(s, ConsoleViewContentType.ERROR_OUTPUT) + } + + fun disableRunButton(){ + runEnabled = false + runButton.isEnabled = false + debugButton.isEnabled = false + cleanBuildCheckbox.isEnabled = false + verboseEnabledCheckBox.isEnabled = false + openEmulatorCheckbox.isEnabled = false + } + + fun enableRunButton(){ + runEnabled = true + runButton.isEnabled = true + debugButton.isEnabled = true + cleanBuildCheckbox.isEnabled = true + verboseEnabledCheckBox.isEnabled = true + openEmulatorCheckbox.isEnabled = true + } + + fun disableCancelButton(){ + cancelButton.isEnabled = false + } + fun enableCancelButton(){ + cancelButton.isEnabled = true + } + + private fun addNewUserButton(pwd:String?):JButton{ + val dwWindow:DashwaveWindow = this + val addUserButton = JButton(createPlusIcon(16,Color.GRAY)).apply { + toolTipText = "Add user" + addActionListener{ + loginUser(pwd, dwWindow) + } } + addUserButton.setSize(10,10) + return addUserButton + } - fun enableRunButton(){ - runEnabled = true - runButton.isEnabled = true - cleanBuildCheckbox.isEnabled = true - debugEnabledCheckBox.isEnabled = true - openEmulatorCheckbox.isEnabled = true + fun switchUser(user:String, wd:String?){ + val switchUserCmd = DwCmds("user switch $user", wd, true, this) + val exitCode = switchUserCmd.executeWithExitCode() + if (exitCode != 0){ + displayError("❌ Could not switch to user: $user\n\n") + return } + displayInfo("✅ Successfully switched to user: $user\n\n") + } + + fun addUsers(users: List?,activeUser:String, wd:String?){ + usersList.removeAllItems() - fun disableCancelButton(){ - cancelButton.isEnabled = false + if(users == null || users.size == 0){ + usersList.isEnabled = false + usersList.addItem("No users logged in") + return } - fun enableCancelButton(){ - cancelButton.isEnabled = true + if(PluginStartup.pluginMode != "workspace"){ + usersList.isEnabled = true } + users?.forEach{ user -> + usersList.addItem(user) + } + this.selectedUser = activeUser + usersList.selectedItem = activeUser + + usersList.addItemListener(ItemListener { + if (this.selectedUser == it.item.toString()){ + return@ItemListener + } + this.lastSelectedUser = selectedUser + this.selectedUser = it.item.toString() + switchUser(this.selectedUser, wd) + }) } + fun addModulesAndVariants(modulesVariants: Map>, defaultModule: String, defaultVariant: String){ + modulesList.removeAllItems() + variantsList.removeAllItems() + + if (modulesVariants.size == 0){ + modulesList.addItem("No modules detected yet") + variantsList.addItem("No variants detected yet") + modulesList.isEnabled = false + variantsList.isEnabled = false + return + } + modulesList.isEnabled = true + variantsList.isEnabled = true + + modulesList.addItem(defaultModule.lowercase()) + variantsList.addItem(defaultVariant.lowercase()) + + modulesVariants.keys.toTypedArray().forEach { module -> + modulesList.addItem(module.lowercase()) + } + + modulesList.addItemListener(ItemListener { + if (it.stateChange == ItemEvent.SELECTED) { + if(it.item.toString().contains("modules detected")){ + selectedModule = "" + selectedVariant = "" + return@ItemListener + } + selectedModule = modulesList.selectedItem as String + variantsList.removeAllItems() + modulesVariants[selectedModule]?.forEach { variant -> + variantsList.addItem(variant.lowercase()) + } + } + }) + + variantsList.addItemListener(ItemListener { + if (it.stateChange == ItemEvent.SELECTED) { + if(it.item.toString() == "variants detected"){ + selectedVariant = "" + selectedModule = "" + return@ItemListener + } + selectedVariant = variantsList.selectedItem as String + } + }) + } - override fun createToolWindowContent(project: Project, toolWindow: ToolWindow) { - println("\n\ncretate tool window content is called") - console = TextConsoleBuilderFactory.getInstance().createBuilder(project).console + private fun createToolWindowContent() { + println("\n\ncretate tool window content is called\n\n") + console = TextConsoleBuilderFactory.getInstance().createBuilder(p).console + val contentFactory = ContentFactory.getInstance() val panel = JPanel(BorderLayout()) // Set up the toolbar with a button val actionToolbar = JToolBar(JToolBar.HORIZONTAL) runButton = JButton(AllIcons.Actions.RunAll) runButton.addActionListener { - val configs = getBuildConfigs(p) - val build = DwBuild(configs) + FileDocumentManager.getInstance().saveAllDocuments(); + val configs = getBuildConfigs(this.p) + val build = DwBuild(configs, this) + build.run(p) + } + + debugButton = JButton(AllIcons.Actions.StartDebugger) + debugButton.addActionListener { + FileDocumentManager.getInstance().saveAllDocuments(); + val configs = getBuildConfigs(this.p) + configs.attachDebugger = true + val build = DwBuild(configs, this) build.run(p) } @@ -121,35 +256,95 @@ class DashwaveWindow : ToolWindowFactory { cancelButton.addActionListener{ disableCancelButton() currentBuild?.exit() - val stopBuild = DwCmds("stop-build", p.basePath, true) + val stopBuild = DwCmds("stop-build", p.basePath, true, this) stopBuild.executeWithExitCode() enableRunButton() } + + actionToolbar.add(runButton) + actionToolbar.add(debugButton) actionToolbar.add(cancelButton) disableCancelButton() disableRunButton() - val optionToolbar = JToolBar(JToolBar.VERTICAL) - val optionTitle = JLabel("Build Options") - optionToolbar.add(optionTitle) - optionToolbar.add(cleanBuildCheckbox) - optionToolbar.add(debugEnabledCheckBox) - optionToolbar.add(openEmulatorCheckbox) + actionToolbar.add(Box.createHorizontalStrut(50)) + val optsGroup = JPanel().apply { + layout = BoxLayout(this, BoxLayout.X_AXIS) + border = BorderFactory.createEtchedBorder() + } + +// val optionToolbar = JToolBar(JToolBar.VERTICAL) +// val optionTitle = JLabel("Build Options") +// optionToolbar.add(optionTitle) + buildOpts.add(cleanBuildCheckbox) + buildOpts.add(openEmulatorCheckbox) + buildOpts.add(verboseEnabledCheckBox) + + optsGroup.add(buildOpts.getComponent()) + + modulesList.preferredSize = Dimension(20, modulesList.preferredSize.height) + variantsList.preferredSize = Dimension(20, variantsList.preferredSize.height) + usersList.preferredSize = Dimension(20, usersList.preferredSize.height) + + // add non-item label to modules and variants +// optionToolbar.add(JLabel("Modules")) + optsGroup.add(modulesList) + +// optionToolbar.add(JLabel("Variants")) + optsGroup.add(variantsList) + + actionToolbar.add(optsGroup) + + val userGroup = JPanel().apply { + layout = BoxLayout(this, BoxLayout.X_AXIS) + border = BorderFactory.createEtchedBorder() + } + + actionToolbar.add(Box.createHorizontalStrut(300)) + + userGroup.add(usersList) + + val addUserBtn = addNewUserButton(this.p.basePath) + addUserBtn.setSize(10,10) + userGroup.add(addUserBtn) + actionToolbar.add(userGroup) + if(PluginStartup.pluginMode == "workspace") { + addUserBtn.isEnabled = false + usersList.isEnabled = false + } + openEmulatorCheckbox.isSelected = true + + panel.add(actionToolbar, BorderLayout.NORTH) - panel.add(optionToolbar, BorderLayout.WEST) +// panel.add(optionToolbar, BorderLayout.WEST) // Set up the console view panel.add(console.component, BorderLayout.CENTER) - val contentFactory = ContentFactory.getInstance() val content = contentFactory.createContent(panel, "", false) - toolWindow.setAnchor(ToolWindowAnchor.BOTTOM, null) - toolWindow.contentManager.addContent(content) + window?.setAnchor(ToolWindowAnchor.BOTTOM, null) + window?.contentManager?.addContent(content) } } + +fun createPlusIcon(size: Int, color: Color): Icon { + val image = BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB) + val g2d = image.createGraphics() + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON) + g2d.color = color + + // Draw plus sign + val thickness = size / 8 // Adjust thickness as desired + val pad = size / 4 + g2d.fillRect(pad, size / 2 - thickness / 2, size - 2 * pad, thickness) + g2d.fillRect(size / 2 - thickness / 2, pad, thickness, size - 2 * pad) + + g2d.dispose() + return ImageIcon(image) +} \ No newline at end of file diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 058f333..1c34112 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -10,6 +10,12 @@ Dashwave + + + com.dashwave.plugin.PluginConfiguration + + + @@ -58,17 +64,17 @@ - com.intellij.modules.androidstudio - + + com.intellij.modules.all - + + + + + @@ -77,5 +83,9 @@ description="dw-build" icon="icons/dashwave16.svg"> + + + \ No newline at end of file diff --git a/src/main/resources/icons/debugger16.svg b/src/main/resources/icons/debugger16.svg new file mode 100644 index 0000000..d760f37 --- /dev/null +++ b/src/main/resources/icons/debugger16.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + +