Skip to content

Commit

Permalink
0.7.6
Browse files Browse the repository at this point in the history
- Rework tool window populating, fixing crash in some cases (Fixes #26)
- Make loggers static
  • Loading branch information
WarningImHack3r committed Mar 24, 2024
1 parent 2baddad commit 7f1baaa
Show file tree
Hide file tree
Showing 13 changed files with 91 additions and 59 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@

## [Unreleased]

### Fixed

- Fix a crash when opening the tool window multiple times within a non-shadcn/ui project (#26)

## [0.7.5] - 2024-03-20

### Fixed
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pluginGroup = com.github.warningimhack3r.intellijshadcnplugin
pluginName = intellij-shadcn-plugin
pluginRepositoryUrl = https://github.com/WarningImHack3r/intellij-shadcn-plugin
# SemVer format -> https://semver.org
pluginVersion = 0.7.5
pluginVersion = 0.7.6

# Supported build number ranges and IntelliJ Platform versions -> https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html
pluginSinceBuild = 213
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive

object SourceScanner {
val log = logger<SourceScanner>()

fun findShadcnImplementation(project: Project): Source<*>? {
return FileManager(project).getFileContentsAtPath("components.json")?.let { componentsJson ->
Expand All @@ -28,7 +29,6 @@ object SourceScanner {
else -> null
}
}.also {
val log = logger<SourceScanner>()
if (it == null) log.warn("No shadcn implementation found")
else log.info("Found shadcn implementation: ${it.javaClass.name}")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import java.io.IOException
import java.nio.file.NoSuchFileException

class FileManager(private val project: Project) {
private val log = logger<FileManager>()
companion object {
private val log = logger<FileManager>()
}

fun saveFileAtPath(file: PsiFile, path: String) {
var deepest = getDeepestFileForPath(path)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import com.intellij.openapi.project.Project
import java.io.File

class ShellRunner(private val project: Project? = null) {
private val log = logger<ShellRunner>()
companion object {
private val log = logger<ShellRunner>()
}
private val failedCommands = mutableSetOf<String>()

private fun isWindows() = System.getProperty("os.name").lowercase().contains("win")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ import kotlinx.serialization.json.jsonPrimitive
import java.nio.file.NoSuchFileException

class ReactSource(project: Project) : Source<ReactConfig>(project, ReactConfig.serializer()) {
private val log = logger<ReactSource>()
companion object {
private val log = logger<ReactSource>()
}
override var framework = "React"

override fun usesDirectoriesForComponents() = false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ import kotlinx.serialization.json.jsonPrimitive
import java.nio.file.NoSuchFileException

class SolidSource(project: Project) : Source<SolidConfig>(project, SolidConfig.serializer()) {
private val log = logger<SolidSource>()
companion object {
private val log = logger<SolidSource>()
}
override var framework = "Solid"

override fun usesDirectoriesForComponents() = false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ import kotlinx.serialization.json.jsonPrimitive
import java.nio.file.NoSuchFileException

class SvelteSource(project: Project) : Source<SvelteConfig>(project, SvelteConfig.serializer()) {
private val log = logger<SvelteSource>()
companion object {
private val log = logger<SvelteSource>()
}
override var framework = "Svelte"

override fun usesDirectoriesForComponents() = true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ import kotlinx.serialization.json.jsonPrimitive
import java.nio.file.NoSuchFileException

class VueSource(project: Project) : Source<VueConfig>(project, VueConfig.serializer()) {
private val log = logger<VueSource>()
companion object {
private val log = logger<VueSource>()
}
override var framework = "Vue"

override fun usesDirectoriesForComponents() = true
Expand Down
Original file line number Diff line number Diff line change
@@ -1,28 +1,28 @@
package com.github.warningimhack3r.intellijshadcnplugin.listeners

import com.github.warningimhack3r.intellijshadcnplugin.ui.ISPWindowContents
import com.github.warningimhack3r.intellijshadcnplugin.ui.ISPPanelPopulator
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.project.Project
import com.intellij.openapi.wm.ToolWindowManager
import com.intellij.openapi.wm.ex.ToolWindowManagerListener

class ToolWindowListener(private val project: Project) : ToolWindowManagerListener {
private val log = logger<ToolWindowListener>()
private val toolWindowId = "shadcn/ui"
companion object {
private val log = logger<ToolWindowListener>()
private const val TOOL_WINDOW_ID = "shadcn/ui"
}
private var isToolWindowOpen: Boolean? = null

override fun stateChanged(toolWindowManager: ToolWindowManager) {
val previousState = isToolWindowOpen
isToolWindowOpen = toolWindowManager.getToolWindow(toolWindowId)?.isVisible ?: false
isToolWindowOpen = toolWindowManager.getToolWindow(TOOL_WINDOW_ID)?.isVisible ?: false
if (previousState == false && isToolWindowOpen == true) {
log.info("Tool window was closed and is now open, updating contents")
toolWindowManager.getToolWindow(toolWindowId)?.contentManager?.getContent(0)?.let {
toolWindowManager.getToolWindow(TOOL_WINDOW_ID)?.contentManager?.getContent(0)?.let {
with(it.component) {
if (components.isEmpty()) return@let
log.info("Removing old contents and adding new ones")
remove(components[0])
add(ISPWindowContents(project).panel())
revalidate()
ISPPanelPopulator(project).populateToolWindowPanel(this)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.github.warningimhack3r.intellijshadcnplugin.ui

import com.github.warningimhack3r.intellijshadcnplugin.backend.SourceScanner
import com.github.warningimhack3r.intellijshadcnplugin.backend.helpers.FileManager
import com.intellij.openapi.application.runReadAction
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.project.Project
import com.intellij.ui.components.JBLabel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.async
import kotlinx.coroutines.future.asCompletableFuture
import javax.swing.JComponent
import javax.swing.SwingConstants

class ISPPanelPopulator(private val project: Project) {
companion object {
private val log = logger<ISPPanelPopulator>()
}

fun populateToolWindowPanel(panel: JComponent) {
log.info("Initializing tool window content")
CoroutineScope(SupervisorJob() + Dispatchers.Default).async {
return@async Pair(runReadAction {
SourceScanner.findShadcnImplementation(project)
}, runReadAction {
FileManager(project).getVirtualFilesByName("package.json")
}.size)
}.asCompletableFuture().thenApplyAsync { (source, packageJsonCount) ->
log.info("Shadcn implementation detected: $source, package.json count: $packageJsonCount")
panel.removeAll()
if (source == null) {
panel.add(JBLabel("No shadcn/ui implementation detected.", SwingConstants.CENTER))
} else if (packageJsonCount > 1) {
panel.add(JBLabel("Multiple projects detected, not supported yet.", SwingConstants.CENTER))
} else {
panel.add(ISPWindowContents(source).panel())
}
panel.revalidate()
}
log.info("Tool window content initialized")
}
}
Original file line number Diff line number Diff line change
@@ -1,26 +1,19 @@
package com.github.warningimhack3r.intellijshadcnplugin.ui

import com.github.warningimhack3r.intellijshadcnplugin.backend.SourceScanner
import com.github.warningimhack3r.intellijshadcnplugin.backend.helpers.FileManager
import com.intellij.ide.plugins.PluginManagerCore
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.runReadAction
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.extensions.PluginId
import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.SimpleToolWindowPanel
import com.intellij.openapi.wm.ToolWindow
import com.intellij.openapi.wm.ToolWindowFactory
import com.intellij.ui.components.JBLabel
import icons.ISPIcons
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.async
import kotlinx.coroutines.future.asCompletableFuture
import javax.swing.SwingConstants

class ISPToolWindow : ToolWindowFactory {
private val log = logger<ISPToolWindow>()
companion object {
private val log = logger<ISPToolWindow>()
}

override fun init(toolWindow: ToolWindow) {
log.info("Initializing shadcn/ui plugin v${PluginManagerCore.getPlugin(
Expand All @@ -32,30 +25,14 @@ class ISPToolWindow : ToolWindowFactory {
}
}

@OptIn(DelicateCoroutinesApi::class)
override fun createToolWindowContent(project: Project, toolWindow: ToolWindow) {
with(toolWindow.contentManager) {
addContent(factory.createContent(SimpleToolWindowPanel(true).apply {
while (!project.isInitialized) {
// wait for project to be initialized
}
log.debug("Project initialized, starting to scan for shadcn implementation")
GlobalScope.async {
return@async Pair(runReadAction {
SourceScanner.findShadcnImplementation(project)
} != null, runReadAction {
FileManager(project).getVirtualFilesByName("package.json")
}.size)
}.asCompletableFuture().thenApplyAsync { (hasShadcn, count) ->
if (!hasShadcn) {
add(JBLabel("No shadcn/ui implementation detected.", SwingConstants.CENTER))
} else if (count > 1) {
add(JBLabel("Multiple projects detected, not supported yet.", SwingConstants.CENTER))
} else {
add(ISPWindowContents(project).panel())
}
}
log.info("Tool window content initialized")
ISPPanelPopulator(project).populateToolWindowPanel(this)
}, null, false))
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
package com.github.warningimhack3r.intellijshadcnplugin.ui

import com.github.warningimhack3r.intellijshadcnplugin.backend.SourceScanner
import com.github.warningimhack3r.intellijshadcnplugin.backend.sources.Source
import com.intellij.openapi.application.runReadAction
import com.intellij.openapi.application.runWriteAction
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.project.Project
import com.intellij.ui.components.JBScrollPane
import com.intellij.ui.components.JBTextField
import com.intellij.util.ui.JBUI
Expand All @@ -24,8 +22,10 @@ import javax.swing.border.CompoundBorder
import javax.swing.border.MatteBorder
import javax.swing.border.TitledBorder

class ISPWindowContents(private val project: Project) {
private val log = logger<ISPWindowContents>()
class ISPWindowContents(private val source: Source<*>) {
companion object {
private val log = logger<ISPWindowContents>()
}

data class Item(
val title: String,
Expand All @@ -50,31 +50,26 @@ class ISPWindowContents(private val project: Project) {
border = JBUI.Borders.empty(10)

val coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
var source: Source<*>? = null
var installedComponents = emptyList<String>()
coroutineScope.launch {
source = runReadAction { SourceScanner.findShadcnImplementation(project) }
if (source == null) {
throw IllegalStateException("No shadcn/ui source found")
}
installedComponents = runReadAction { source!!.getInstalledComponents() }
installedComponents = runReadAction { source.getInstalledComponents() }
}.invokeOnCompletion { throwable ->
if (throwable != null && throwable !is CancellationException) {
return@invokeOnCompletion
}
// Add a component panel
add(createPanel("Add a component") {
coroutineScope.async {
runReadAction { source!!.fetchAllComponents() }.map { component ->
runReadAction { source.fetchAllComponents() }.map { component ->
Item(
component.name,
"${component.name.replace("-", " ")
.replaceFirstChar {
if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString()
}} component for ${source!!.framework}",
}} component for ${source.framework}",
listOf(
LabeledAction("Add", CompletionAction.DISABLE_ROW) {
runWriteAction { source!!.addComponent(component.name) }
runWriteAction { source.addComponent(component.name) }
}
),
installedComponents.contains(component.name)
Expand All @@ -99,12 +94,12 @@ class ISPWindowContents(private val project: Project) {
null,
listOfNotNull(
LabeledAction("Update", CompletionAction.REMOVE_TRIGGER) {
runWriteAction { source!!.addComponent(component) }
runWriteAction { source.addComponent(component) }
}.takeIf {
runReadAction { !source!!.isComponentUpToDate(component) }
runReadAction { !source.isComponentUpToDate(component) }
},
LabeledAction("Remove", CompletionAction.REMOVE_ROW) {
runWriteAction { source!!.removeComponent(component) }
runWriteAction { source.removeComponent(component) }
}
)
)
Expand Down

0 comments on commit 7f1baaa

Please sign in to comment.