diff --git a/CHANGELOG.md b/CHANGELOG.md index dab29c4..a70100c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,12 +3,20 @@ ## [Unreleased] ### Added + +- Added support for Android Studio Otter. + +## [2025.2.1] - 2025-11-15 + +### Added + - Support for clean_framework v4. - Added support for Android Studio Koala. ## [2024.1.1] - 2023-12-28 ### Added + - Added support for Android Studio Iguana. - Added default `copyWith` override to generated Entity class. - Added test file generators at feature generation. @@ -16,9 +24,11 @@ ## [2023.2.23] - 2023-02-23 ### Added + - Added clean_framework package at project generation ## [2023.2.22] - 2023-02-22 ### Added + - Initial release diff --git a/README.md b/README.md index e48b0b2..f7e62e7 100644 --- a/README.md +++ b/README.md @@ -14,5 +14,9 @@ Support for developing Flutter project with Clean Framework package. The plugin - Gateway Generator - Feature Directory Generator; also generates corresponding use case provider for the feature< +#### Compatibility +- Built against IntelliJ Platform build 252.* (IntelliJ IDEA / Android Studio 2025.2.1). +- Depends on the bundled Dart plugin and resolves the most recent Flutter plugin compatible with that build. + The Clean Framework package can be found at [https://pub.dev/packages/clean_framework](https://pub.dev/packages/clean_framework). - \ No newline at end of file + diff --git a/build.gradle.kts b/build.gradle.kts index 0d99e65..1f7d752 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,5 +1,6 @@ import org.jetbrains.changelog.Changelog import org.jetbrains.changelog.markdownToHTML +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile fun properties(key: String) = project.findProperty(key).toString() fun environment(key: String) = providers.environmentVariable(key) @@ -28,7 +29,8 @@ kotlin { jvmToolchain { languageVersion = JavaLanguageVersion.of(17) vendor = JvmVendorSpec.JETBRAINS - }} + } +} intellij { pluginName = properties("pluginName") @@ -86,4 +88,10 @@ tasks { dependsOn("patchChangelog") token = environment("PUBLISH_TOKEN") } + + withType().configureEach { + compilerOptions { + freeCompilerArgs.add("-Xskip-metadata-version-check") + } + } } diff --git a/gradle.properties b/gradle.properties index 8a174a4..387cdb5 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,15 +1,15 @@ pluginName = Clean Framework -pluginVersion = 2024.5.1 +pluginVersion = 2025.2.1 pluginGroup = com.acmesoftware pluginRepositoryUrl = https://github.com/AcmeSoftwareLLC/clean-framework-intellij -pluginSinceBuild = 223 -pluginUntilBuild = 241.* +pluginSinceBuild = 252 +pluginUntilBuild = 252.* platformType = IC -platformVersion = 2022.3.3 +platformVersion = 2025.2.1 -platformPlugins = java, Kotlin, io.flutter:77.1.1, Dart:223.8977 +platformPlugins = com.intellij.java, Dart:253.28294.51, io.flutter:88.1.0 gradleVersion = 8.6 diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 999b1ba..d0b1b02 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,9 +2,9 @@ annotations = "24.1.0" # plugins -kotlin = "1.9.23" +kotlin = "2.0.20" changelog = "2.2.0" -gradleIntelliJPlugin = "1.17.2" +gradleIntelliJPlugin = "1.17.3" [libraries] annotations = { group = "org.jetbrains", name = "annotations", version.ref = "annotations" } diff --git a/src/main/kotlin/com/acmesoftware/cleanframework/actions/NewFeatureAction.kt b/src/main/kotlin/com/acmesoftware/cleanframework/actions/NewFeatureAction.kt index 9a5d03a..94e3056 100644 --- a/src/main/kotlin/com/acmesoftware/cleanframework/actions/NewFeatureAction.kt +++ b/src/main/kotlin/com/acmesoftware/cleanframework/actions/NewFeatureAction.kt @@ -22,40 +22,39 @@ import com.intellij.util.IncorrectOperationException import com.jetbrains.lang.dart.DartLanguage import com.jetbrains.lang.dart.util.PubspecYamlUtil -class NewFeatureAction : AnAction(), GenerateFeatureDialog.Callback { - private lateinit var dataContext: DataContext +class NewFeatureAction : AnAction() { override fun actionPerformed(e: AnActionEvent) { - val dialog = GenerateFeatureDialog(this) + val context = e.dataContext + val dialog = GenerateFeatureDialog { featureName -> + generateFeature(featureName, context) + } dialog.show() } override fun update(e: AnActionEvent) { - e.dataContext.let { - this.dataContext = it - val presentation = e.presentation - presentation.isEnabled = true - } + e.presentation.isEnabled = true } - override fun onGenerateFeature(featureName: String) { + private fun generateFeature(featureName: String, dataContext: DataContext) { val application = ApplicationManager.getApplication() - val project = CommonDataKeys.PROJECT.getData(dataContext) + val project = CommonDataKeys.PROJECT.getData(dataContext) ?: return val view = LangDataKeys.IDE_VIEW.getData(dataContext) val focusedVirtualFile = if (view == null) { - val editor = LangDataKeys.EDITOR.getData(dataContext)!! - FileDocumentManager.getInstance().getFile(editor.document)!!.parent + val editor = LangDataKeys.EDITOR.getData(dataContext) ?: return + FileDocumentManager.getInstance().getFile(editor.document)?.parent ?: return } else { - view.orChooseDirectory!!.virtualFile + view.orChooseDirectory?.virtualFile ?: return } - val pubspecFile = PubspecYamlUtil.findPubspecYamlFile(project!!, focusedVirtualFile) - val libDir = pubspecFile!!.parent.findChild(PubspecYamlUtil.LIB_DIR_NAME)!! - val packageName = PubspecYamlUtil.getDartProjectName(pubspecFile)!! + val pubspecFile = PubspecYamlUtil.findPubspecYamlFile(project, focusedVirtualFile) ?: return + val libDir = pubspecFile.parent.findChild(PubspecYamlUtil.LIB_DIR_NAME) ?: return + val packageName = PubspecYamlUtil.getDartProjectName(pubspecFile) ?: return val libDirectory = PsiDirectoryFactory.getInstance(project).createDirectory(libDir) - val featuresDirectory = libDirectory.findSubdirectory("features")!! + val featuresDirectory = libDirectory.findSubdirectory("features") + ?: libDirectory.createSubdirectory("features") application.runWriteAction { val runnable = Runnable { @@ -69,7 +68,7 @@ class NewFeatureAction : AnAction(), GenerateFeatureDialog.Callback { FeatureGeneratorFactory.getGenerators(packageName, featureName) ) - val root = libDirectory.parent!! + val root = libDirectory.parent ?: libDirectory val testDirectory = findOrCreateDirectory(root, "test") val featuresTestDirectory = findOrCreateDirectory(testDirectory, "features") @@ -180,4 +179,4 @@ class NewFeatureAction : AnAction(), GenerateFeatureDialog.Callback { private fun findOrCreateDirectory(parent: PsiDirectory, directoryName: String): PsiDirectory { return parent.findSubdirectory(directoryName) ?: return parent.createSubdirectory(directoryName) } -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/acmesoftware/cleanframework/actions/SetupProjectAction.kt b/src/main/kotlin/com/acmesoftware/cleanframework/actions/SetupProjectAction.kt index 957ec49..820e48d 100644 --- a/src/main/kotlin/com/acmesoftware/cleanframework/actions/SetupProjectAction.kt +++ b/src/main/kotlin/com/acmesoftware/cleanframework/actions/SetupProjectAction.kt @@ -13,36 +13,30 @@ import com.intellij.openapi.fileEditor.FileDocumentManager import com.intellij.openapi.fileEditor.FileEditorManager import com.intellij.openapi.fileEditor.OpenFileDescriptor import com.intellij.openapi.fileTypes.PlainTextLanguage -import com.intellij.psi.PsiDirectory -import com.intellij.psi.PsiFile -import com.intellij.psi.PsiFileFactory +import com.intellij.psi.* import com.intellij.psi.impl.file.PsiDirectoryFactory import com.jetbrains.lang.dart.DartLanguage import com.jetbrains.lang.dart.util.PubspecYamlUtil -import org.jetbrains.kotlin.idea.core.util.toPsiFile -import org.jetbrains.kotlin.utils.addToStdlib.UnsafeCastFunction -import org.jetbrains.kotlin.utils.addToStdlib.cast import org.yaml.snakeyaml.Yaml class SetupProjectAction : AnAction() { private lateinit var dataContext: DataContext - @OptIn(UnsafeCastFunction::class) override fun actionPerformed(e: AnActionEvent) { val application = ApplicationManager.getApplication() - val project = CommonDataKeys.PROJECT.getData(dataContext) + val project = CommonDataKeys.PROJECT.getData(dataContext) ?: return val view = LangDataKeys.IDE_VIEW.getData(dataContext) val focusedVirtualFile = if (view == null) { - val editor = LangDataKeys.EDITOR.getData(dataContext)!! - FileDocumentManager.getInstance().getFile(editor.document)!!.parent + val editor = LangDataKeys.EDITOR.getData(dataContext) ?: return + FileDocumentManager.getInstance().getFile(editor.document)?.parent ?: return } else { - view.orChooseDirectory!!.virtualFile + view.orChooseDirectory?.virtualFile ?: return } - val pubspecFile = PubspecYamlUtil.findPubspecYamlFile(project!!, focusedVirtualFile) - val libDir = pubspecFile!!.parent.findChild(PubspecYamlUtil.LIB_DIR_NAME)!! + val pubspecFile = PubspecYamlUtil.findPubspecYamlFile(project, focusedVirtualFile) ?: return + val libDir = pubspecFile.parent.findChild(PubspecYamlUtil.LIB_DIR_NAME)!! val packageName = PubspecYamlUtil.getDartProjectName(pubspecFile)!! val packageNamePascal = CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, packageName) @@ -105,12 +99,17 @@ class SetupProjectAction : AnAction() { findOrCreateDirectory(libDirectory, "features") - pubspecFile.toPsiFile(project)?.also { + PsiManager.getInstance(project).findFile(pubspecFile)?.also { val pubspecYamlBuilder = StringBuilder(it.text) - val yamlMap = Yaml().load>(pubspecFile.inputStream) - val dependencies = yamlMap[PubspecYamlUtil.DEPENDENCIES].cast>().keys - val devDependencies = yamlMap[PubspecYamlUtil.DEV_DEPENDENCIES].cast>().keys + val yamlMap = pubspecFile.inputStream.use { stream -> + @Suppress("UNCHECKED_CAST") + (Yaml().load>(stream) ?: emptyMap()) + } + val dependencies = (yamlMap[PubspecYamlUtil.DEPENDENCIES] as? Map<*, *>)?.keys + ?.filterIsInstance()?.toSet() ?: emptySet() + val devDependencies = (yamlMap[PubspecYamlUtil.DEV_DEPENDENCIES] as? Map<*, *>)?.keys + ?.filterIsInstance()?.toSet() ?: emptySet() listOf("clean_framework_router", "clean_framework").forEach { dep -> if (!dependencies.contains(dep)) insertDependency(pubspecYamlBuilder, dep) @@ -191,4 +190,4 @@ class SetupProjectAction : AnAction() { override fun getActionUpdateThread(): ActionUpdateThread { return ActionUpdateThread.EDT } -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/acmesoftware/cleanframework/actions/dialogs/GenerateFeatureDialog.kt b/src/main/kotlin/com/acmesoftware/cleanframework/actions/dialogs/GenerateFeatureDialog.kt index c84862d..7edfbdb 100644 --- a/src/main/kotlin/com/acmesoftware/cleanframework/actions/dialogs/GenerateFeatureDialog.kt +++ b/src/main/kotlin/com/acmesoftware/cleanframework/actions/dialogs/GenerateFeatureDialog.kt @@ -12,7 +12,7 @@ import javax.swing.JLabel import javax.swing.JPanel import javax.swing.JTextField -class GenerateFeatureDialog(private val callback: Callback) : DialogWrapper(true) { +class GenerateFeatureDialog(private val onGenerate: (String) -> Unit) : DialogWrapper(true) { private lateinit var featureNameTextField: JTextField private lateinit var errorLabel: JLabel @@ -53,14 +53,10 @@ class GenerateFeatureDialog(private val callback: Callback) : DialogWrapper(true if (featureNameRegex.matches(featureName)) { super.doOKAction() - callback.onGenerateFeature(featureName) + onGenerate(featureName) } else { errorLabel.text = "Feature name should be in PascalCase" errorLabel.foreground = JBColor(DarculaColors.RED, DarculaColors.RED) } } - - interface Callback { - fun onGenerateFeature(featureName: String) - } -} \ No newline at end of file +}